@inshapardaz/likhari-react 0.1.25-dev.230 → 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":"
|
|
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,7 +1,7 @@
|
|
|
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
|
-
import { $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, } from 'lexical';
|
|
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';
|
|
6
6
|
import { onSpellWordsChange } from '../spellcheck/userWords.js';
|
|
7
7
|
import { WordIndex } from './wordIndex.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,
|
|
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.
|
|
@@ -123,11 +123,6 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
|
|
|
123
123
|
return;
|
|
124
124
|
if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && (current || prefixAtCaret(editor) !== null))
|
|
125
125
|
return;
|
|
126
|
-
if (event.key === 'Escape' && popupRef.current) {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
show(null);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
126
|
// Left, Right, Home and End move the caret away from the word, so the list closes.
|
|
132
127
|
if (MOVEMENT_KEYS.has(event.key)) {
|
|
133
128
|
typing.current = false;
|
|
@@ -139,6 +134,11 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
|
|
|
139
134
|
hide();
|
|
140
135
|
setTimeout(refresh, 0);
|
|
141
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
|
+
};
|
|
142
142
|
const onMouseDown = () => {
|
|
143
143
|
typing.current = false;
|
|
144
144
|
hide();
|
|
@@ -147,9 +147,11 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
|
|
|
147
147
|
const attach = (next) => {
|
|
148
148
|
root?.removeEventListener('keydown', onKeyDown);
|
|
149
149
|
root?.removeEventListener('mousedown', onMouseDown);
|
|
150
|
+
root?.removeEventListener('input', onInput);
|
|
150
151
|
root = next;
|
|
151
152
|
root?.addEventListener('keydown', onKeyDown);
|
|
152
153
|
root?.addEventListener('mousedown', onMouseDown);
|
|
154
|
+
root?.addEventListener('input', onInput);
|
|
153
155
|
};
|
|
154
156
|
const unregisterRoot = editor.registerRootListener((next) => attach(next));
|
|
155
157
|
const unregisterUpdate = editor.registerUpdateListener(() => {
|
|
@@ -166,6 +168,13 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
|
|
|
166
168
|
const unregisterCommands = [
|
|
167
169
|
editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => move(1, event), COMMAND_PRIORITY_HIGH),
|
|
168
170
|
editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => move(-1, event), COMMAND_PRIORITY_HIGH),
|
|
171
|
+
// While the list is open, Escape only closes it. Lexical would otherwise blur the editor.
|
|
172
|
+
editor.registerCommand(KEY_ESCAPE_COMMAND, () => {
|
|
173
|
+
if (!popupRef.current)
|
|
174
|
+
return false;
|
|
175
|
+
show(null);
|
|
176
|
+
return true;
|
|
177
|
+
}, COMMAND_PRIORITY_HIGH),
|
|
169
178
|
editor.registerCommand(KEY_TAB_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
|
|
170
179
|
editor.registerCommand(KEY_ENTER_COMMAND, (event) => (popupRef.current?.selected ? acceptHighlighted(event) : false), COMMAND_PRIORITY_HIGH),
|
|
171
180
|
];
|
|
@@ -195,11 +204,34 @@ export function AutocompletePlugin({ label, stores, language: chosenLanguage, })
|
|
|
195
204
|
}, [stores]);
|
|
196
205
|
if (!popup)
|
|
197
206
|
return null;
|
|
198
|
-
return
|
|
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) => {
|
|
199
233
|
event.preventDefault();
|
|
200
|
-
|
|
201
|
-
recordAcceptance(popup.language, item);
|
|
202
|
-
show(null);
|
|
234
|
+
onPick(item);
|
|
203
235
|
}, children: item }, item))) }));
|
|
204
236
|
}
|
|
205
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. */
|
|
@@ -227,10 +259,12 @@ function caretRect(editor) {
|
|
|
227
259
|
if (selection && selection.rangeCount > 0) {
|
|
228
260
|
const rect = selection.getRangeAt(0).getBoundingClientRect();
|
|
229
261
|
if (rect.width > 0 || rect.height > 0)
|
|
230
|
-
return { left: rect.left, bottom: rect.bottom };
|
|
262
|
+
return { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom };
|
|
231
263
|
}
|
|
232
264
|
const root = editor.getRootElement()?.getBoundingClientRect();
|
|
233
|
-
|
|
265
|
+
const left = root?.left ?? 0;
|
|
266
|
+
const top = root?.top ?? 0;
|
|
267
|
+
return { left, right: left, top, bottom: top };
|
|
234
268
|
}
|
|
235
269
|
function acceptItem(editor, item) {
|
|
236
270
|
editor.update(() => {
|