@inshapardaz/likhari-react 0.1.25-dev.226 → 0.1.25-dev.228

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../../src/autocomplete/AutocompletePlugin.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAmB1D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,cAAc,GACzB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,EAAE,aAAa,GAAG,MAAM,CAAC;CAClC,sCA6KA"}
1
+ {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../../src/autocomplete/AutocompletePlugin.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAqB1D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,cAAc,GACzB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,EAAE,aAAa,GAAG,MAAM,CAAC;CAClC,sCAsLA"}
@@ -67,26 +67,37 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
67
67
  const { prefix, rtl } = target;
68
68
  const language = languageRef.current === 'auto' ? (rtl ? 'ur' : 'en') : languageRef.current;
69
69
  void indexFor(language).then((loaded) => {
70
+ // Same word as the open list: keep the user's highlight rather than resetting it.
71
+ const open = popupRef.current;
72
+ if (open && open.prefix === prefix && open.dir === (rtl ? 'rtl' : 'ltr'))
73
+ return;
70
74
  const items = loaded.complete(prefix);
71
75
  if (items.length === 0)
72
76
  return hide();
73
77
  const rect = caretRect(editor);
74
- show({ items, index: 0, selected: false, x: rect.left, y: rect.bottom, dir: rtl ? 'rtl' : 'ltr' });
78
+ show({ prefix, items, index: 0, selected: false, x: rect.left, y: rect.bottom, dir: rtl ? 'rtl' : 'ltr' });
75
79
  });
76
80
  };
77
- const move = (delta) => {
81
+ // Handles an arrow key for the list, and cancels the browser's own caret movement.
82
+ const move = (delta, event) => {
78
83
  const current = popupRef.current;
79
- if (!current)
84
+ // Still loading the list for the word being typed: the arrow is for the list, not the caret.
85
+ if (!current && prefixAtCaret(editor) === null)
80
86
  return false;
81
- const count = current.items.length;
82
- show({ ...current, index: (current.index + delta + count) % count, selected: true });
87
+ event?.preventDefault();
88
+ if (current) {
89
+ const count = current.items.length;
90
+ show({ ...current, index: (current.index + delta + count) % count, selected: true });
91
+ }
83
92
  return true;
84
93
  };
85
94
  const onKeyDown = (event) => {
86
95
  // Up, Down, Tab and Enter act on the list through their editor commands, which
87
96
  // run after this listener, so the list must stay open for them.
88
97
  const current = popupRef.current;
89
- if (current && (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab' || (event.key === 'Enter' && current.selected)))
98
+ if (current && (event.key === 'Tab' || (event.key === 'Enter' && current.selected)))
99
+ return;
100
+ if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && (current || prefixAtCaret(editor) !== null))
90
101
  return;
91
102
  if (event.key === 'Escape' && popupRef.current) {
92
103
  event.preventDefault();
@@ -129,8 +140,8 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
129
140
  setTimeout(refresh, 0);
130
141
  });
131
142
  const unregisterCommands = [
132
- editor.registerCommand(KEY_ARROW_DOWN_COMMAND, () => move(1), COMMAND_PRIORITY_HIGH),
133
- editor.registerCommand(KEY_ARROW_UP_COMMAND, () => move(-1), COMMAND_PRIORITY_HIGH),
143
+ editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => move(1, event), COMMAND_PRIORITY_HIGH),
144
+ editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => move(-1, event), COMMAND_PRIORITY_HIGH),
134
145
  editor.registerCommand(KEY_TAB_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
135
146
  editor.registerCommand(KEY_ENTER_COMMAND, (event) => (popupRef.current?.selected ? acceptHighlighted(event) : false), COMMAND_PRIORITY_HIGH),
136
147
  ];
@@ -163,6 +174,25 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
163
174
  acceptItem(editor, item);
164
175
  }, children: item }, item))) }));
165
176
  }
177
+ /** The typed part of the word at the caret, when the caret is at its end and the word is long enough to complete. */
178
+ function prefixAtCaret(editor) {
179
+ let prefix = null;
180
+ editor.getEditorState().read(() => {
181
+ const selection = $getSelection();
182
+ if (!$isRangeSelection(selection) || !selection.isCollapsed())
183
+ return;
184
+ const node = selection.anchor.getNode();
185
+ if (!$isTextNode(node))
186
+ return;
187
+ const text = node.getTextContent();
188
+ const before = text.slice(0, selection.anchor.offset);
189
+ const word = WORD_END.exec(before)?.[0];
190
+ const after = text.charAt(selection.anchor.offset);
191
+ if (word && word.length >= MIN_PREFIX && !(after !== '' && LETTER.test(after)))
192
+ prefix = word;
193
+ });
194
+ return prefix;
195
+ }
166
196
  /** Where to put the popup: under the caret, or the editor's top-left if the caret has no position yet. */
167
197
  function caretRect(editor) {
168
198
  const selection = window.getSelection();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.25-dev.226",
3
+ "version": "0.1.25-dev.228",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {