arnog / mathlive

A web component for easy math input
https://cortexjs.io/mathlive
MIT License
1.56k stars 277 forks source link

Unable to remove keybindings #2358

Closed rosinghal closed 5 months ago

rosinghal commented 5 months ago

I want to remove some default keybindings namely "alt+[BracketLeft]" and "shift+alt+[BracketLeft]" since it is converting square brackets into format which I don't want on being typed from physical keyboard, eg, it is converting [a] into \\left\\lbrack a\\right\\rbrack.

Here is the code that I have used:

import { createElement, DetailedHTMLProps, HTMLAttributes, ReactElement, useEffect, useRef } from 'react';
import Mathlive, { MathfieldElement } from 'mathlive';
import { KEYBINDINGS } from './keybindings';

type MathfieldProps = DetailedHTMLProps<HTMLAttributes<MathfieldElement>, MathfieldElement>;

export const Mathfield = ({ children, className, ...props }: MathfieldProps): ReactElement<MathfieldProps> => {
  const ref = useRef<MathfieldElement>(null);

  useEffect(() => {
    const mathfield = ref.current;

    mathfield?.addEventListener('focus', () => {
      mathfield.mathModeSpace = '\\:';
      mathfield.keybindings = [...KEYBINDINGS];
    });
  }, []);

  return createElement('math-field', { ref, class: className, 'data-testid': 'math-field', ...props }, children);
};

KEYBINDINGS variable is a copy of https://github.com/arnog/mathlive/blob/ae2f0429962798b61cad23143295bac20585fec6/src/editor/keybindings-definitions.ts#L4C50-L4C51 and I have just removed alt+[BracketLeft] and shift+alt+[BracketLeft] but still the above conversion of [a] into \\left\\lbrack a\\right\\rbrack is happening.

Can someone please help with this?

arnog commented 5 months ago

The issue is most likely occurring because the mathfield is not properly mounted by the time you attempt top customize it (by the time you set the event listener). React lifecycle can be a bit tricky.

To ensure a mathfield is customized at the right time, I recommend two options:

1/ Use the onMount event handler. The TypeScript bindings for the math-field element currently do not have this event handler type appropriately, so you may need to do some convincing to have TypeScript let you use it.

2/ Use a useEffect hook, but wait on the ref to be ready.

  useEffect(() => {
    const mathfield = ref.current;
    // ...
  }, [ref.current]);

Also, note you do not need to copy the set of keybindings from source. You can simply filter the ones you don't want at runtime:

    mathfield.keybindings = mathfield.keybindings.filter((x) => !x.key.includes("[BracketLeft]"));
rosinghal commented 5 months ago

Thanks for the suggestion. However, it does not work either. Here is my code

import { createElement, DetailedHTMLProps, HTMLAttributes, ReactElement, useEffect, useMemo, useRef } from 'react';

import Mathlive, { MathfieldElement } from 'mathlive';

type MathfieldProps = DetailedHTMLProps<HTMLAttributes<MathfieldElement>, MathfieldElement>;

export const Mathfield = ({ children, className, ...props }: MathfieldProps): ReactElement<MathfieldProps> => {
  const ref = useRef<MathfieldElement>(null);
  const mfe = useMemo(() => new MathfieldElement(), []);

  useEffect(() => {
    const mathfield = ref.current;

    if (mathfield) {
      mathfield.keybindings = mathfield.keybindings.filter(x => !x.key.includes('[BracketLeft]'));
    }
  }, [ref, mfe]);

  return createElement('math-field', { ref, class: className, 'data-testid': 'math-field', ...props }, children);
};
rosinghal commented 5 months ago

Please check https://codesandbox.io/p/sandbox/exciting-aryabhata-th68p9

arnog commented 5 months ago

I think it should work if you use the code I included. Try https://codesandbox.io/p/sandbox/exciting-aryabhata-forked-tll5zh?file=%2Fsrc%2FMathField.jsx

Note that the dependency in the useEffect is on ref.current. Also, you don't need the reference to mfe.

rosinghal commented 5 months ago

@arnog even in the code you sent, if I type [ab] I get the value as \left\lbrack ab\right\rbrack even though we have filtered out keybindings with [BracketLeft].

You will see the output after typing in the math field and then click outside.

arnog commented 5 months ago

@rosinghal yes, because there is no keybinding for "[". The keybinding that got filtered out was for "alt+leftbracket". If you want "leftbracket" to do something else, you can add a keybinding for it.

rosinghal commented 5 months ago

Understood, I have updated the keybindings to

mathfield.keybindings = [
  ...mathfield.keybindings.filter(
    (x) => !x.key.includes("[BracketLeft]")
  ),
  {
    key: "[BracketLeft]",
    ifLayout: ["apple.en-intl", "windows.en-intl", "linux.en"],
    ifMode: "math",
    command: ["insert", "[#0]"],
  },
];

This is what I was looking for. Thanks a lot. Closing the issue.