@inshapardaz/likhari-react 0.1.25-dev.224 → 0.1.25-dev.226
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.
- package/dist/EditorRoot.d.ts +6 -0
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +6 -2
- package/dist/autocomplete/AutocompletePlugin.d.ts +6 -1
- package/dist/autocomplete/AutocompletePlugin.d.ts.map +1 -1
- package/dist/autocomplete/AutocompletePlugin.js +31 -18
- package/dist/autocomplete/completionStores.d.ts +25 -0
- package/dist/autocomplete/completionStores.d.ts.map +1 -0
- package/dist/autocomplete/completionStores.js +33 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/EditorRoot.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ import { type DraftRestoreMode } from './components/DraftRestore.js';
|
|
|
7
7
|
import { type AutoCorrectStore } from './autocorrect/autoCorrectStores.js';
|
|
8
8
|
import type { UrduNormalizationOptions } from './normalization/urduNormalize.js';
|
|
9
9
|
import type { PunctuationOptions } from './autocorrect/punctuationRules.js';
|
|
10
|
+
import { type CompletionStore } from './autocomplete/completionStores.js';
|
|
10
11
|
import { type UserWordStore } from './spellcheck/userWords.js';
|
|
12
|
+
import type { SpellLanguage } from './spellcheck/spellDictionaries.js';
|
|
11
13
|
import { type Locale } from './i18n/index.js';
|
|
12
14
|
/** What happens when the user is about to leave with unsaved changes. */
|
|
13
15
|
export type NavigationGuardMode = 'confirm' | 'save-draft' | 'off';
|
|
@@ -21,6 +23,10 @@ export interface EditorRootProps {
|
|
|
21
23
|
autoCorrectStores?: AutoCorrectStore[];
|
|
22
24
|
/** Where words added to the spelling dictionary are saved, in priority order. Pass a stable array. */
|
|
23
25
|
dictionaryStores?: UserWordStore[];
|
|
26
|
+
/** Where autocomplete words come from, combined per language. Defaults to the dictionaries and the user's words. Pass a stable array. */
|
|
27
|
+
completionStores?: CompletionStore[];
|
|
28
|
+
/** The language autocomplete works in: 'auto' follows each block's direction (English for LTR, Urdu for RTL). */
|
|
29
|
+
autoCompleteLanguage?: SpellLanguage | 'auto';
|
|
24
30
|
/** Urdu normalisation applied as part of auto-correct. Diacritics are kept unless `removeDiacritics` is set. */
|
|
25
31
|
urduNormalization?: UrduNormalizationOptions;
|
|
26
32
|
/** Common punctuation fixes, and whether a straight " becomes ”. Both on by default. */
|
package/dist/EditorRoot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAOjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIhF,OAAO,EAAgC,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACtG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"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"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -33,6 +33,7 @@ import { localStorageAutoCorrectStore } from './autocorrect/autoCorrectStores.js
|
|
|
33
33
|
import { SpellcheckPanel } from './components/SpellcheckPanel.js';
|
|
34
34
|
import { SpellHighlightPlugin } from './spellcheck/SpellHighlightPlugin.js';
|
|
35
35
|
import { AutocompletePlugin } from './autocomplete/AutocompletePlugin.js';
|
|
36
|
+
import { dictionaryCompletionStore } from './autocomplete/completionStores.js';
|
|
36
37
|
import { loadUserWords, localStorageUserWordStore } from './spellcheck/userWords.js';
|
|
37
38
|
import { LeaveDialog } from './components/LeaveDialog.js';
|
|
38
39
|
import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
|
|
@@ -44,6 +45,8 @@ const DEFAULT_AUTOSAVE_MAX_BYTES = 2000000;
|
|
|
44
45
|
const DEFAULT_AUTOSAVE_MAX_DRAFTS = 20;
|
|
45
46
|
/** Used when the host passes no stores: corrections are kept in this browser. */
|
|
46
47
|
const DEFAULT_AUTOCORRECT_STORES = [localStorageAutoCorrectStore()];
|
|
48
|
+
/** Used when the host passes no completion stores: the dictionary's words and the user's added words. */
|
|
49
|
+
const DEFAULT_COMPLETION_STORES = [dictionaryCompletionStore()];
|
|
47
50
|
/** Used when the host passes no dictionary stores: added words are kept in this browser. */
|
|
48
51
|
const DEFAULT_DICTIONARY_STORES = [localStorageUserWordStore()];
|
|
49
52
|
function initialEditorStateJson(initialContent) {
|
|
@@ -52,7 +55,7 @@ function initialEditorStateJson(initialContent) {
|
|
|
52
55
|
const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
|
|
53
56
|
return JSON.stringify(state);
|
|
54
57
|
}
|
|
55
|
-
export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores, dictionaryStores, urduNormalization, punctuation, initialContent, featureConfig, featurePreset, theme, colorScheme, accentColor, locale = 'en', placeholder, height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions, autosave = true, restoreDraft = 'prompt', navigationGuard = 'confirm', onDraftRestored, autosaveDelayMs = DEFAULT_AUTOSAVE_DELAY_MS, autosaveMaxBytes = DEFAULT_AUTOSAVE_MAX_BYTES, autosaveMaxDrafts = DEFAULT_AUTOSAVE_MAX_DRAFTS, }, ref) {
|
|
58
|
+
export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorrectStores, dictionaryStores, completionStores, autoCompleteLanguage = 'auto', urduNormalization, punctuation, initialContent, featureConfig, featurePreset, theme, colorScheme, accentColor, locale = 'en', placeholder, height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions, autosave = true, restoreDraft = 'prompt', navigationGuard = 'confirm', onDraftRestored, autosaveDelayMs = DEFAULT_AUTOSAVE_DELAY_MS, autosaveMaxBytes = DEFAULT_AUTOSAVE_MAX_BYTES, autosaveMaxDrafts = DEFAULT_AUTOSAVE_MAX_DRAFTS, }, ref) {
|
|
56
59
|
const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
|
|
57
60
|
const strings = useMemo(() => getStrings(locale), [locale]);
|
|
58
61
|
const resolvedPlaceholder = placeholder ?? strings.editor.placeholder;
|
|
@@ -88,6 +91,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
|
|
|
88
91
|
const [autoCorrectVersion, setAutoCorrectVersion] = useState(0);
|
|
89
92
|
const stores = autoCorrectStores ?? DEFAULT_AUTOCORRECT_STORES;
|
|
90
93
|
const wordStores = dictionaryStores ?? DEFAULT_DICTIONARY_STORES;
|
|
94
|
+
const resolvedCompletionStores = completionStores ?? DEFAULT_COMPLETION_STORES;
|
|
91
95
|
useEffect(() => {
|
|
92
96
|
void loadUserWords(wordStores);
|
|
93
97
|
}, [wordStores]);
|
|
@@ -306,7 +310,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, autoCorre
|
|
|
306
310
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
307
311
|
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, onSave: handleSave, findOpen: openPanel === 'find', onToggleFind: () => togglePanel('find'), spellOpen: openPanel === 'spell', onToggleSpell: () => togglePanel('spell'), autoCorrectOpen: openPanel === 'autocorrect', onToggleAutoCorrect: () => togglePanel('autocorrect'), isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
|
|
308
312
|
? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
|
|
309
|
-
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, initialFrom: autoCorrectPrefill?.from, initialLanguage: autoCorrectPrefill?.language, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) }, autoCorrectPrefill?.id ?? 0)), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, spellCheck: false, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) })] }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.language.spellCheck && _jsx(SpellHighlightPlugin, {}), config.language.autocomplete && _jsx(AutocompletePlugin, { label: strings.autoComplete.label }), (config.language.autocorrect || config.language.textCleanup) && _jsx(AutoCorrectPlugin, { stores: stores, version: autoCorrectVersion, enabled: true, typing: config.language.autocorrect, urduNormalization: urduNormalization, punctuation: punctuation }), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
313
|
+
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && openPanel === 'find' && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setOpenPanel(null) }), config.language.spellCheck && openPanel === 'spell' && (_jsx(SpellcheckPanel, { strings: strings, dir: dir, dictionaryStores: wordStores, onAddAutoCorrect: config.language.autocorrect ? openAutoCorrectFor : undefined, onClose: () => setOpenPanel(null) })), config.language.autocorrect && openPanel === 'autocorrect' && (_jsx(AutoCorrectPanel, { strings: strings, dir: dir, stores: stores, initialFrom: autoCorrectPrefill?.from, initialLanguage: autoCorrectPrefill?.language, onSaved: () => setAutoCorrectVersion((v) => v + 1), onClose: () => setOpenPanel(null) }, autoCorrectPrefill?.id ?? 0)), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, spellCheck: false, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) })] }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.language.spellCheck && _jsx(SpellHighlightPlugin, {}), config.language.autocomplete && (_jsx(AutocompletePlugin, { label: strings.autoComplete.label, stores: resolvedCompletionStores, language: autoCompleteLanguage })), (config.language.autocorrect || config.language.textCleanup) && _jsx(AutoCorrectPlugin, { stores: stores, version: autoCorrectVersion, enabled: true, typing: config.language.autocorrect, urduNormalization: urduNormalization, punctuation: punctuation }), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
310
314
|
handleSave();
|
|
311
315
|
settleLeave(true);
|
|
312
316
|
}, onSaveDraft: () => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
2
|
+
import type { CompletionStore } from './completionStores.js';
|
|
1
3
|
/**
|
|
2
4
|
* Suggests completions for the word being typed, from the dictionary's words and
|
|
3
5
|
* the user's own. Shown only while typing, at the caret. Tab or Enter accepts the
|
|
@@ -5,7 +7,10 @@
|
|
|
5
7
|
* moving the caret) dismisses it. Left-to-right text uses the English dictionary,
|
|
6
8
|
* right-to-left text the Urdu one.
|
|
7
9
|
*/
|
|
8
|
-
export declare function AutocompletePlugin({ label }: {
|
|
10
|
+
export declare function AutocompletePlugin({ label, stores, language: chosenLanguage, }: {
|
|
9
11
|
label: string;
|
|
12
|
+
stores: CompletionStore[];
|
|
13
|
+
/** The language to complete in, or 'auto' to follow the block's direction. */
|
|
14
|
+
language: SpellLanguage | 'auto';
|
|
10
15
|
}): import("react").JSX.Element | null;
|
|
11
16
|
//# sourceMappingURL=AutocompletePlugin.d.ts.map
|
|
@@ -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":"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"}
|
|
@@ -3,12 +3,12 @@ import { useEffect, 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_TAB_COMMAND, } from 'lexical';
|
|
5
5
|
import { $replaceMatch } from '../find/findReplaceActions.js';
|
|
6
|
-
import {
|
|
7
|
-
import { acceptedWords, onSpellWordsChange } from '../spellcheck/userWords.js';
|
|
6
|
+
import { onSpellWordsChange } from '../spellcheck/userWords.js';
|
|
8
7
|
import { WordIndex } from './wordIndex.js';
|
|
9
8
|
/** The letters at the end of the text before the caret. */
|
|
10
9
|
const WORD_END = /[\p{L}\p{M}]+$/u;
|
|
11
10
|
const LETTER = /[\p{L}\p{M}]/u;
|
|
11
|
+
const MOVEMENT_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown']);
|
|
12
12
|
/** Suggestions only appear once the typed part of a word is this long. */
|
|
13
13
|
const MIN_PREFIX = 2;
|
|
14
14
|
/**
|
|
@@ -18,8 +18,13 @@ const MIN_PREFIX = 2;
|
|
|
18
18
|
* moving the caret) dismisses it. Left-to-right text uses the English dictionary,
|
|
19
19
|
* right-to-left text the Urdu one.
|
|
20
20
|
*/
|
|
21
|
-
export function AutocompletePlugin({ label }) {
|
|
21
|
+
export function AutocompletePlugin({ label, stores, language: chosenLanguage, }) {
|
|
22
22
|
const [editor] = useLexicalComposerContext();
|
|
23
|
+
// Effects run once per editor, so they read the latest props through refs.
|
|
24
|
+
const storesRef = useRef(stores);
|
|
25
|
+
storesRef.current = stores;
|
|
26
|
+
const languageRef = useRef(chosenLanguage);
|
|
27
|
+
languageRef.current = chosenLanguage;
|
|
23
28
|
const [popup, setPopup] = useState(null);
|
|
24
29
|
const popupRef = useRef(null);
|
|
25
30
|
const typing = useRef(false);
|
|
@@ -32,10 +37,7 @@ export function AutocompletePlugin({ label }) {
|
|
|
32
37
|
const indexFor = (language) => {
|
|
33
38
|
let index = indexes.current.get(language);
|
|
34
39
|
if (!index) {
|
|
35
|
-
|
|
36
|
-
if (!words)
|
|
37
|
-
return null;
|
|
38
|
-
index = words.then((dictionary) => new WordIndex([...dictionary, ...acceptedWords(language)]));
|
|
40
|
+
index = Promise.all(storesRef.current.map((store) => store.load(language).catch(() => []))).then((lists) => new WordIndex(lists.flat()));
|
|
39
41
|
indexes.current.set(language, index);
|
|
40
42
|
}
|
|
41
43
|
return index;
|
|
@@ -58,21 +60,18 @@ export function AutocompletePlugin({ label }) {
|
|
|
58
60
|
const after = text.charAt(selection.anchor.offset);
|
|
59
61
|
if (!prefix || prefix.length < MIN_PREFIX || (after !== '' && LETTER.test(after)))
|
|
60
62
|
return;
|
|
61
|
-
|
|
62
|
-
target = { prefix, language: rtl ? 'ur' : 'en', dir: rtl ? 'rtl' : 'ltr' };
|
|
63
|
+
target = { prefix, rtl: node.getParentOrThrow().getDirection() === 'rtl' };
|
|
63
64
|
});
|
|
64
65
|
if (target === null)
|
|
65
66
|
return hide();
|
|
66
|
-
const { prefix,
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
return hide();
|
|
70
|
-
void index.then((loaded) => {
|
|
67
|
+
const { prefix, rtl } = target;
|
|
68
|
+
const language = languageRef.current === 'auto' ? (rtl ? 'ur' : 'en') : languageRef.current;
|
|
69
|
+
void indexFor(language).then((loaded) => {
|
|
71
70
|
const items = loaded.complete(prefix);
|
|
72
71
|
if (items.length === 0)
|
|
73
72
|
return hide();
|
|
74
73
|
const rect = caretRect(editor);
|
|
75
|
-
show({ items, index: 0, x: rect.left, y: rect.bottom, dir });
|
|
74
|
+
show({ items, index: 0, selected: false, x: rect.left, y: rect.bottom, dir: rtl ? 'rtl' : 'ltr' });
|
|
76
75
|
});
|
|
77
76
|
};
|
|
78
77
|
const move = (delta) => {
|
|
@@ -80,16 +79,26 @@ export function AutocompletePlugin({ label }) {
|
|
|
80
79
|
if (!current)
|
|
81
80
|
return false;
|
|
82
81
|
const count = current.items.length;
|
|
83
|
-
show({ ...current, index: (current.index + delta + count) % count });
|
|
82
|
+
show({ ...current, index: (current.index + delta + count) % count, selected: true });
|
|
84
83
|
return true;
|
|
85
84
|
};
|
|
86
85
|
const onKeyDown = (event) => {
|
|
86
|
+
// Up, Down, Tab and Enter act on the list through their editor commands, which
|
|
87
|
+
// run after this listener, so the list must stay open for them.
|
|
88
|
+
const current = popupRef.current;
|
|
89
|
+
if (current && (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab' || (event.key === 'Enter' && current.selected)))
|
|
90
|
+
return;
|
|
87
91
|
if (event.key === 'Escape' && popupRef.current) {
|
|
88
92
|
event.preventDefault();
|
|
89
93
|
show(null);
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
92
|
-
//
|
|
96
|
+
// Left, Right, Home and End move the caret away from the word, so the list closes.
|
|
97
|
+
if (MOVEMENT_KEYS.has(event.key)) {
|
|
98
|
+
typing.current = false;
|
|
99
|
+
return hide();
|
|
100
|
+
}
|
|
101
|
+
// Printable keys and Backspace are typing; anything else (Enter, Tab, ...) ends the word.
|
|
93
102
|
typing.current = (event.key.length === 1 && !event.ctrlKey && !event.metaKey) || event.key === 'Backspace';
|
|
94
103
|
if (!typing.current)
|
|
95
104
|
hide();
|
|
@@ -123,7 +132,7 @@ export function AutocompletePlugin({ label }) {
|
|
|
123
132
|
editor.registerCommand(KEY_ARROW_DOWN_COMMAND, () => move(1), COMMAND_PRIORITY_HIGH),
|
|
124
133
|
editor.registerCommand(KEY_ARROW_UP_COMMAND, () => move(-1), COMMAND_PRIORITY_HIGH),
|
|
125
134
|
editor.registerCommand(KEY_TAB_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
|
|
126
|
-
editor.registerCommand(KEY_ENTER_COMMAND, (event) => acceptHighlighted(event), COMMAND_PRIORITY_HIGH),
|
|
135
|
+
editor.registerCommand(KEY_ENTER_COMMAND, (event) => (popupRef.current?.selected ? acceptHighlighted(event) : false), COMMAND_PRIORITY_HIGH),
|
|
127
136
|
];
|
|
128
137
|
function acceptHighlighted(event) {
|
|
129
138
|
const current = popupRef.current;
|
|
@@ -143,6 +152,10 @@ export function AutocompletePlugin({ label }) {
|
|
|
143
152
|
show(null);
|
|
144
153
|
};
|
|
145
154
|
}, [editor]);
|
|
155
|
+
// The index is per store set and language; a change of stores starts again.
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
indexes.current.clear();
|
|
158
|
+
}, [stores]);
|
|
146
159
|
if (!popup)
|
|
147
160
|
return null;
|
|
148
161
|
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) => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type SpellLanguage } from '../spellcheck/spellDictionaries.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where autocomplete words come from. The host can pass several; their words
|
|
4
|
+
* are combined per language. Like the other stores, a failing store is skipped.
|
|
5
|
+
*/
|
|
6
|
+
export interface CompletionStore {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
load(language: SpellLanguage): Promise<string[]>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The default: the dictionary's words plus the words the user has added. Nothing
|
|
12
|
+
* to configure; it follows whichever spelling dictionaries are registered.
|
|
13
|
+
*/
|
|
14
|
+
export declare function dictionaryCompletionStore(): CompletionStore;
|
|
15
|
+
/**
|
|
16
|
+
* Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
|
|
17
|
+
* array of strings.
|
|
18
|
+
*/
|
|
19
|
+
export declare function apiCompletionStore(options: {
|
|
20
|
+
id?: string;
|
|
21
|
+
url: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
}): CompletionStore;
|
|
25
|
+
//# sourceMappingURL=completionStores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completionStores.d.ts","sourceRoot":"","sources":["../../src/autocomplete/completionStores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGzF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,eAAe,CAS3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB,GAAG,eAAe,CAWlB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getDictionaryWords } from '../spellcheck/spellDictionaries.js';
|
|
2
|
+
import { acceptedWords } from '../spellcheck/userWords.js';
|
|
3
|
+
/**
|
|
4
|
+
* The default: the dictionary's words plus the words the user has added. Nothing
|
|
5
|
+
* to configure; it follows whichever spelling dictionaries are registered.
|
|
6
|
+
*/
|
|
7
|
+
export function dictionaryCompletionStore() {
|
|
8
|
+
return {
|
|
9
|
+
id: 'dictionary',
|
|
10
|
+
async load(language) {
|
|
11
|
+
const words = getDictionaryWords(language);
|
|
12
|
+
const dictionary = words ? await words : [];
|
|
13
|
+
return [...dictionary, ...acceptedWords(language)];
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Words served by an API. `load` calls `GET {url}?language=xx` and expects a JSON
|
|
19
|
+
* array of strings.
|
|
20
|
+
*/
|
|
21
|
+
export function apiCompletionStore(options) {
|
|
22
|
+
const doFetch = options.fetch ?? fetch;
|
|
23
|
+
return {
|
|
24
|
+
id: options.id ?? 'api',
|
|
25
|
+
async load(language) {
|
|
26
|
+
const response = await doFetch(`${options.url}?language=${encodeURIComponent(language)}`, { headers: options.headers });
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
throw new Error(`Completion API failed: ${response.status}`);
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
return Array.isArray(data) ? data.filter((w) => typeof w === 'string') : [];
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,4 +16,6 @@ export type { UrduNormalizationOptions } from './normalization/urduNormalize.js'
|
|
|
16
16
|
export type { PunctuationOptions, PunctuationRule } from './autocorrect/punctuationRules.js';
|
|
17
17
|
export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
|
|
18
18
|
export type { UserWordStore } from './spellcheck/userWords.js';
|
|
19
|
+
export { dictionaryCompletionStore, apiCompletionStore } from './autocomplete/completionStores.js';
|
|
20
|
+
export type { CompletionStore } from './autocomplete/completionStores.js';
|
|
19
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC1G,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACzF,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACrI,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC1H,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAChI,YAAY,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC1G,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACzF,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACrI,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC1H,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAChI,YAAY,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAChG,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { registerSpellDictionary, hasSpellDictionary } from './spellcheck/spellD
|
|
|
7
7
|
export { localStorageAutoCorrectStore, apiAutoCorrectStore, fileAutoCorrectStore } from './autocorrect/autoCorrectStores.js';
|
|
8
8
|
export { normalizeUrdu, normalizeUrduCharacters, removeUrduDiacritics, replaceUrduDigits } from './normalization/urduNormalize.js';
|
|
9
9
|
export { localStorageUserWordStore, apiUserWordStore, ignoreWord, addUserWord, onSpellWordsChange } from './spellcheck/userWords.js';
|
|
10
|
+
export { dictionaryCompletionStore, apiCompletionStore } from './autocomplete/completionStores.js';
|