@inshapardaz/likhari-react 0.1.25-dev.232 → 0.1.25-dev.234

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":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAsB1D;;;;;;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,sCAqNA"}
1
+ {"version":3,"file":"AutocompletePlugin.d.ts","sourceRoot":"","sources":["../../src/autocomplete/AutocompletePlugin.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAyB1D;;;;;;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,sCAuMA"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
3
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
4
  import { $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, } from 'lexical';
5
5
  import { $replaceMatch } from '../find/findReplaceActions.js';
@@ -99,7 +99,7 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
99
99
  if (items.length === 0)
100
100
  return hide();
101
101
  const rect = caretRect(editor);
102
- show({ prefix, language, items, index: 0, selected: false, x: rect.left, y: rect.bottom, dir: rtl ? 'rtl' : 'ltr' });
102
+ show({ prefix, language, items, index: 0, selected: false, ...rect, dir: rtl ? 'rtl' : 'ltr' });
103
103
  });
104
104
  };
105
105
  // Handles an arrow key for the list, and cancels the browser's own caret movement.
@@ -134,6 +134,11 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
134
134
  hide();
135
135
  setTimeout(refresh, 0);
136
136
  };
137
+ // Text that arrives without a keydown (input methods, some keyboard layouts) is typing too.
138
+ const onInput = () => {
139
+ typing.current = true;
140
+ setTimeout(refresh, 0);
141
+ };
137
142
  const onMouseDown = () => {
138
143
  typing.current = false;
139
144
  hide();
@@ -142,9 +147,11 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
142
147
  const attach = (next) => {
143
148
  root?.removeEventListener('keydown', onKeyDown);
144
149
  root?.removeEventListener('mousedown', onMouseDown);
150
+ root?.removeEventListener('input', onInput);
145
151
  root = next;
146
152
  root?.addEventListener('keydown', onKeyDown);
147
153
  root?.addEventListener('mousedown', onMouseDown);
154
+ root?.addEventListener('input', onInput);
148
155
  };
149
156
  const unregisterRoot = editor.registerRootListener((next) => attach(next));
150
157
  const unregisterUpdate = editor.registerUpdateListener(() => {
@@ -197,11 +204,34 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
197
204
  }, [stores]);
198
205
  if (!popup)
199
206
  return null;
200
- return (_jsx("div", { className: "likhari-autocomplete", role: "listbox", "aria-label": label, dir: popup.dir, style: { position: 'fixed', left: popup.x, top: popup.y + 4 }, children: popup.items.map((item, i) => (_jsx("div", { role: "option", "aria-selected": i === popup.index, className: i === popup.index ? 'likhari-autocomplete-item likhari-autocomplete-item--active' : 'likhari-autocomplete-item', onMouseDown: (event) => {
207
+ return _jsx(SuggestionList, { popup: popup, label: label, onPick: (item) => {
208
+ acceptItem(editor, item);
209
+ recordAcceptance(popup.language, item);
210
+ show(null);
211
+ } });
212
+ }
213
+ function SuggestionList({ popup, label, onPick }) {
214
+ const ref = useRef(null);
215
+ // Placed after it is drawn, so its real size is known: kept inside the window, on the side of the
216
+ // caret that has room, and right-to-left lists align their right edge with the caret.
217
+ useLayoutEffect(() => {
218
+ const element = ref.current;
219
+ if (!element)
220
+ return;
221
+ const margin = 8;
222
+ const width = element.offsetWidth;
223
+ const height = element.offsetHeight;
224
+ const preferredLeft = popup.dir === 'rtl' ? popup.right - width : popup.left;
225
+ const left = Math.min(Math.max(preferredLeft, margin), window.innerWidth - width - margin);
226
+ const below = popup.bottom + 4;
227
+ const fitsBelow = below + height <= window.innerHeight - margin;
228
+ const top = fitsBelow ? below : Math.max(popup.top - height - 4, margin);
229
+ element.style.left = `${Math.max(left, margin)}px`;
230
+ element.style.top = `${top}px`;
231
+ });
232
+ return (_jsx("div", { ref: ref, className: "likhari-autocomplete", role: "listbox", "aria-label": label, dir: popup.dir, style: { position: 'fixed' }, children: popup.items.map((item, i) => (_jsx("div", { role: "option", "aria-selected": i === popup.index, className: i === popup.index ? 'likhari-autocomplete-item likhari-autocomplete-item--active' : 'likhari-autocomplete-item', onMouseDown: (event) => {
201
233
  event.preventDefault();
202
- acceptItem(editor, item);
203
- recordAcceptance(popup.language, item);
204
- show(null);
234
+ onPick(item);
205
235
  }, children: item }, item))) }));
206
236
  }
207
237
  /** The typed part of the word at the caret, when the caret is at its end and the word is long enough to complete. */
@@ -229,10 +259,12 @@ function caretRect(editor) {
229
259
  if (selection && selection.rangeCount > 0) {
230
260
  const rect = selection.getRangeAt(0).getBoundingClientRect();
231
261
  if (rect.width > 0 || rect.height > 0)
232
- return { left: rect.left, bottom: rect.bottom };
262
+ return { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom };
233
263
  }
234
264
  const root = editor.getRootElement()?.getBoundingClientRect();
235
- return { left: root?.left ?? 0, bottom: root?.top ?? 0 };
265
+ const left = root?.left ?? 0;
266
+ const top = root?.top ?? 0;
267
+ return { left, right: left, top, bottom: top };
236
268
  }
237
269
  function acceptItem(editor, item) {
238
270
  editor.update(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.25-dev.232",
3
+ "version": "0.1.25-dev.234",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {