@lexical/react 0.12.1 → 0.12.3
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/LexicalAutoEmbedPlugin.d.ts +3 -2
- package/LexicalAutoEmbedPlugin.dev.js +4 -14
- package/LexicalAutoEmbedPlugin.prod.js +4 -3
- package/LexicalAutoFocusPlugin.dev.js +0 -1
- package/LexicalAutoLinkPlugin.dev.js +126 -73
- package/LexicalAutoLinkPlugin.prod.js +11 -7
- package/LexicalBlockWithAlignableContents.dev.js +0 -10
- package/LexicalCharacterLimitPlugin.dev.js +7 -46
- package/LexicalCheckListPlugin.dev.js +10 -48
- package/LexicalClearEditorPlugin.dev.js +1 -1
- package/LexicalClickableLinkPlugin.dev.js +2 -20
- package/LexicalCollaborationContext.dev.js +0 -3
- package/LexicalCollaborationPlugin.dev.js +8 -37
- package/LexicalComposer.d.ts +3 -7
- package/LexicalComposer.dev.js +9 -11
- package/LexicalComposer.js.flow +4 -4
- package/LexicalComposer.prod.js +2 -1
- package/LexicalComposerContext.dev.js +0 -6
- package/LexicalContentEditable.dev.js +1 -2
- package/LexicalContextMenuPlugin.d.ts +3 -2
- package/LexicalContextMenuPlugin.dev.js +28 -82
- package/LexicalContextMenuPlugin.prod.js +16 -15
- package/LexicalDecoratorBlockNode.dev.js +0 -6
- package/LexicalEditorRefPlugin.dev.js +0 -3
- package/LexicalHashtagPlugin.dev.js +73 -43
- package/LexicalHorizontalRuleNode.dev.js +0 -21
- package/LexicalHorizontalRulePlugin.dev.js +0 -4
- package/LexicalLinkPlugin.dev.js +4 -10
- package/LexicalListPlugin.dev.js +0 -2
- package/LexicalMarkdownShortcutPlugin.dev.js +2 -2
- package/LexicalNestedComposer.d.ts +2 -2
- package/LexicalNestedComposer.dev.js +18 -16
- package/LexicalNestedComposer.js.flow +7 -2
- package/LexicalNestedComposer.prod.js +4 -3
- package/LexicalNodeEventPlugin.dev.js +2 -6
- package/LexicalNodeMenuPlugin.d.ts +3 -2
- package/LexicalNodeMenuPlugin.dev.js +27 -83
- package/LexicalNodeMenuPlugin.prod.js +15 -15
- package/LexicalOnChangePlugin.dev.js +1 -1
- package/LexicalPlainTextPlugin.dev.js +8 -12
- package/LexicalRichTextPlugin.dev.js +8 -12
- package/LexicalTabIndentationPlugin.dev.js +7 -16
- package/LexicalTableOfContents.dev.js +5 -33
- package/LexicalTablePlugin.dev.js +11 -28
- package/LexicalTreeView.dev.js +14 -79
- package/LexicalTypeaheadMenuPlugin.d.ts +4 -3
- package/LexicalTypeaheadMenuPlugin.dev.js +39 -176
- package/LexicalTypeaheadMenuPlugin.prod.js +19 -20
- package/package.json +19 -19
- package/shared/LexicalMenu.d.ts +3 -2
- package/useLexicalEditable.dev.js +1 -5
- package/useLexicalIsTextContentEmpty.dev.js +1 -0
- package/useLexicalNodeSelection.dev.js +0 -7
- package/useLexicalSubscription.dev.js +1 -3
|
@@ -18,6 +18,7 @@ var utils = require('@lexical/utils');
|
|
|
18
18
|
* LICENSE file in the root directory of this source tree.
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
|
+
|
|
21
22
|
const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -45,140 +46,113 @@ class MenuOption {
|
|
|
45
46
|
};
|
|
46
47
|
this.setRefElement = this.setRefElement.bind(this);
|
|
47
48
|
}
|
|
48
|
-
|
|
49
49
|
setRefElement(element) {
|
|
50
50
|
this.ref = {
|
|
51
51
|
current: element
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
|
|
55
54
|
}
|
|
56
|
-
|
|
57
55
|
const scrollIntoViewIfNeeded = target => {
|
|
58
56
|
const typeaheadContainerNode = document.getElementById('typeahead-menu');
|
|
59
57
|
if (!typeaheadContainerNode) return;
|
|
60
58
|
const typeaheadRect = typeaheadContainerNode.getBoundingClientRect();
|
|
61
|
-
|
|
62
59
|
if (typeaheadRect.top + typeaheadRect.height > window.innerHeight) {
|
|
63
60
|
typeaheadContainerNode.scrollIntoView({
|
|
64
61
|
block: 'center'
|
|
65
62
|
});
|
|
66
63
|
}
|
|
67
|
-
|
|
68
64
|
if (typeaheadRect.top < 0) {
|
|
69
65
|
typeaheadContainerNode.scrollIntoView({
|
|
70
66
|
block: 'center'
|
|
71
67
|
});
|
|
72
68
|
}
|
|
73
|
-
|
|
74
69
|
target.scrollIntoView({
|
|
75
70
|
block: 'nearest'
|
|
76
71
|
});
|
|
77
72
|
};
|
|
73
|
+
|
|
78
74
|
/**
|
|
79
75
|
* Walk backwards along user input and forward through entity title to try
|
|
80
76
|
* and replace more of the user's text with entity.
|
|
81
77
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
78
|
function getFullMatchOffset(documentText, entryText, offset) {
|
|
85
79
|
let triggerOffset = offset;
|
|
86
|
-
|
|
87
80
|
for (let i = triggerOffset; i <= entryText.length; i++) {
|
|
88
81
|
if (documentText.substr(-i) === entryText.substr(0, i)) {
|
|
89
82
|
triggerOffset = i;
|
|
90
83
|
}
|
|
91
84
|
}
|
|
92
|
-
|
|
93
85
|
return triggerOffset;
|
|
94
86
|
}
|
|
87
|
+
|
|
95
88
|
/**
|
|
96
89
|
* Split Lexical TextNode and return a new TextNode only containing matched text.
|
|
97
90
|
* Common use cases include: removing the node, replacing with a new node.
|
|
98
91
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
92
|
function $splitNodeContainingQuery(match) {
|
|
102
93
|
const selection = lexical.$getSelection();
|
|
103
|
-
|
|
104
94
|
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
105
95
|
return null;
|
|
106
96
|
}
|
|
107
|
-
|
|
108
97
|
const anchor = selection.anchor;
|
|
109
|
-
|
|
110
98
|
if (anchor.type !== 'text') {
|
|
111
99
|
return null;
|
|
112
100
|
}
|
|
113
|
-
|
|
114
101
|
const anchorNode = anchor.getNode();
|
|
115
|
-
|
|
116
102
|
if (!anchorNode.isSimpleText()) {
|
|
117
103
|
return null;
|
|
118
104
|
}
|
|
119
|
-
|
|
120
105
|
const selectionOffset = anchor.offset;
|
|
121
106
|
const textContent = anchorNode.getTextContent().slice(0, selectionOffset);
|
|
122
107
|
const characterOffset = match.replaceableString.length;
|
|
123
108
|
const queryOffset = getFullMatchOffset(textContent, match.matchingString, characterOffset);
|
|
124
109
|
const startOffset = selectionOffset - queryOffset;
|
|
125
|
-
|
|
126
110
|
if (startOffset < 0) {
|
|
127
111
|
return null;
|
|
128
112
|
}
|
|
129
|
-
|
|
130
113
|
let newNode;
|
|
131
|
-
|
|
132
114
|
if (startOffset === 0) {
|
|
133
115
|
[newNode] = anchorNode.splitText(selectionOffset);
|
|
134
116
|
} else {
|
|
135
117
|
[, newNode] = anchorNode.splitText(startOffset, selectionOffset);
|
|
136
118
|
}
|
|
137
|
-
|
|
138
119
|
return newNode;
|
|
139
|
-
}
|
|
140
|
-
|
|
120
|
+
}
|
|
141
121
|
|
|
122
|
+
// Got from https://stackoverflow.com/a/42543908/2013580
|
|
142
123
|
function getScrollParent$1(element, includeHidden) {
|
|
143
124
|
let style = getComputedStyle(element);
|
|
144
125
|
const excludeStaticParent = style.position === 'absolute';
|
|
145
126
|
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
|
|
146
|
-
|
|
147
127
|
if (style.position === 'fixed') {
|
|
148
128
|
return document.body;
|
|
149
129
|
}
|
|
150
|
-
|
|
151
130
|
for (let parent = element; parent = parent.parentElement;) {
|
|
152
131
|
style = getComputedStyle(parent);
|
|
153
|
-
|
|
154
132
|
if (excludeStaticParent && style.position === 'static') {
|
|
155
133
|
continue;
|
|
156
134
|
}
|
|
157
|
-
|
|
158
135
|
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
|
|
159
136
|
return parent;
|
|
160
137
|
}
|
|
161
138
|
}
|
|
162
|
-
|
|
163
139
|
return document.body;
|
|
164
140
|
}
|
|
165
|
-
|
|
166
|
-
function isTriggerVisibleInNearestScrollContainer$1(targetElement, containerElement) {
|
|
141
|
+
function isTriggerVisibleInNearestScrollContainer(targetElement, containerElement) {
|
|
167
142
|
const tRect = targetElement.getBoundingClientRect();
|
|
168
143
|
const cRect = containerElement.getBoundingClientRect();
|
|
169
144
|
return tRect.top > cRect.top && tRect.top < cRect.bottom;
|
|
170
|
-
}
|
|
171
|
-
|
|
145
|
+
}
|
|
172
146
|
|
|
173
|
-
|
|
147
|
+
// Reposition the menu on scroll, window resize, and element resize.
|
|
148
|
+
function useDynamicPositioning(resolution, targetElement, onReposition, onVisibilityChange) {
|
|
174
149
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
175
150
|
React.useEffect(() => {
|
|
176
151
|
if (targetElement != null && resolution != null) {
|
|
177
152
|
const rootElement = editor.getRootElement();
|
|
178
153
|
const rootScrollParent = rootElement != null ? getScrollParent$1(rootElement, false) : document.body;
|
|
179
154
|
let ticking = false;
|
|
180
|
-
let previousIsInView = isTriggerVisibleInNearestScrollContainer
|
|
181
|
-
|
|
155
|
+
let previousIsInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
182
156
|
const handleScroll = function () {
|
|
183
157
|
if (!ticking) {
|
|
184
158
|
window.requestAnimationFrame(function () {
|
|
@@ -187,18 +161,14 @@ function useDynamicPositioning$1(resolution, targetElement, onReposition, onVisi
|
|
|
187
161
|
});
|
|
188
162
|
ticking = true;
|
|
189
163
|
}
|
|
190
|
-
|
|
191
|
-
const isInView = isTriggerVisibleInNearestScrollContainer$1(targetElement, rootScrollParent);
|
|
192
|
-
|
|
164
|
+
const isInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
193
165
|
if (isInView !== previousIsInView) {
|
|
194
166
|
previousIsInView = isInView;
|
|
195
|
-
|
|
196
167
|
if (onVisibilityChange != null) {
|
|
197
168
|
onVisibilityChange(isInView);
|
|
198
169
|
}
|
|
199
170
|
}
|
|
200
171
|
};
|
|
201
|
-
|
|
202
172
|
const resizeObserver = new ResizeObserver(onReposition);
|
|
203
173
|
window.addEventListener('resize', onReposition);
|
|
204
174
|
document.addEventListener('scroll', handleScroll, {
|
|
@@ -209,7 +179,7 @@ function useDynamicPositioning$1(resolution, targetElement, onReposition, onVisi
|
|
|
209
179
|
return () => {
|
|
210
180
|
resizeObserver.unobserve(targetElement);
|
|
211
181
|
window.removeEventListener('resize', onReposition);
|
|
212
|
-
document.removeEventListener('scroll', handleScroll);
|
|
182
|
+
document.removeEventListener('scroll', handleScroll, true);
|
|
213
183
|
};
|
|
214
184
|
}
|
|
215
185
|
}, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
|
|
@@ -223,7 +193,8 @@ function LexicalMenu({
|
|
|
223
193
|
options,
|
|
224
194
|
menuRenderFn,
|
|
225
195
|
onSelectOption,
|
|
226
|
-
shouldSplitNodeWithQuery = false
|
|
196
|
+
shouldSplitNodeWithQuery = false,
|
|
197
|
+
commandPriority = lexical.COMMAND_PRIORITY_LOW
|
|
227
198
|
}) {
|
|
228
199
|
const [selectedIndex, setHighlightedIndex] = React.useState(null);
|
|
229
200
|
const matchingString = resolution.match && resolution.match.matchingString;
|
|
@@ -238,7 +209,6 @@ function LexicalMenu({
|
|
|
238
209
|
}, [editor, shouldSplitNodeWithQuery, resolution.match, onSelectOption, close]);
|
|
239
210
|
const updateSelectedIndex = React.useCallback(index => {
|
|
240
211
|
const rootElem = editor.getRootElement();
|
|
241
|
-
|
|
242
212
|
if (rootElem !== null) {
|
|
243
213
|
rootElem.setAttribute('aria-activedescendant', 'typeahead-item-' + index);
|
|
244
214
|
setHighlightedIndex(index);
|
|
@@ -247,7 +217,6 @@ function LexicalMenu({
|
|
|
247
217
|
React.useEffect(() => {
|
|
248
218
|
return () => {
|
|
249
219
|
const rootElem = editor.getRootElement();
|
|
250
|
-
|
|
251
220
|
if (rootElem !== null) {
|
|
252
221
|
rootElem.removeAttribute('aria-activedescendant');
|
|
253
222
|
}
|
|
@@ -268,79 +237,66 @@ function LexicalMenu({
|
|
|
268
237
|
scrollIntoViewIfNeeded(option.ref.current);
|
|
269
238
|
return true;
|
|
270
239
|
}
|
|
271
|
-
|
|
272
240
|
return false;
|
|
273
|
-
},
|
|
274
|
-
}, [editor, updateSelectedIndex]);
|
|
241
|
+
}, commandPriority));
|
|
242
|
+
}, [editor, updateSelectedIndex, commandPriority]);
|
|
275
243
|
React.useEffect(() => {
|
|
276
244
|
return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
|
|
277
245
|
const event = payload;
|
|
278
|
-
|
|
279
246
|
if (options !== null && options.length && selectedIndex !== null) {
|
|
280
247
|
const newSelectedIndex = selectedIndex !== options.length - 1 ? selectedIndex + 1 : 0;
|
|
281
248
|
updateSelectedIndex(newSelectedIndex);
|
|
282
249
|
const option = options[newSelectedIndex];
|
|
283
|
-
|
|
284
250
|
if (option.ref != null && option.ref.current) {
|
|
285
251
|
editor.dispatchCommand(SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND$1, {
|
|
286
252
|
index: newSelectedIndex,
|
|
287
253
|
option
|
|
288
254
|
});
|
|
289
255
|
}
|
|
290
|
-
|
|
291
256
|
event.preventDefault();
|
|
292
257
|
event.stopImmediatePropagation();
|
|
293
258
|
}
|
|
294
|
-
|
|
295
259
|
return true;
|
|
296
|
-
},
|
|
260
|
+
}, commandPriority), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
|
|
297
261
|
const event = payload;
|
|
298
|
-
|
|
299
262
|
if (options !== null && options.length && selectedIndex !== null) {
|
|
300
263
|
const newSelectedIndex = selectedIndex !== 0 ? selectedIndex - 1 : options.length - 1;
|
|
301
264
|
updateSelectedIndex(newSelectedIndex);
|
|
302
265
|
const option = options[newSelectedIndex];
|
|
303
|
-
|
|
304
266
|
if (option.ref != null && option.ref.current) {
|
|
305
267
|
scrollIntoViewIfNeeded(option.ref.current);
|
|
306
268
|
}
|
|
307
|
-
|
|
308
269
|
event.preventDefault();
|
|
309
270
|
event.stopImmediatePropagation();
|
|
310
271
|
}
|
|
311
|
-
|
|
312
272
|
return true;
|
|
313
|
-
},
|
|
273
|
+
}, commandPriority), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
|
|
314
274
|
const event = payload;
|
|
315
275
|
event.preventDefault();
|
|
316
276
|
event.stopImmediatePropagation();
|
|
317
277
|
close();
|
|
318
278
|
return true;
|
|
319
|
-
},
|
|
279
|
+
}, commandPriority), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
|
|
320
280
|
const event = payload;
|
|
321
|
-
|
|
322
281
|
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
323
282
|
return false;
|
|
324
283
|
}
|
|
325
|
-
|
|
326
284
|
event.preventDefault();
|
|
327
285
|
event.stopImmediatePropagation();
|
|
328
286
|
selectOptionAndCleanUp(options[selectedIndex]);
|
|
329
287
|
return true;
|
|
330
|
-
},
|
|
288
|
+
}, commandPriority), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
|
|
331
289
|
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
332
290
|
return false;
|
|
333
291
|
}
|
|
334
|
-
|
|
335
292
|
if (event !== null) {
|
|
336
293
|
event.preventDefault();
|
|
337
294
|
event.stopImmediatePropagation();
|
|
338
295
|
}
|
|
339
|
-
|
|
340
296
|
selectOptionAndCleanUp(options[selectedIndex]);
|
|
341
297
|
return true;
|
|
342
|
-
},
|
|
343
|
-
}, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
|
|
298
|
+
}, commandPriority));
|
|
299
|
+
}, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex, commandPriority]);
|
|
344
300
|
const listItemProps = React.useMemo(() => ({
|
|
345
301
|
options,
|
|
346
302
|
selectOptionAndCleanUp,
|
|
@@ -353,10 +309,10 @@ function useMenuAnchorRef(resolution, setResolution, className) {
|
|
|
353
309
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
354
310
|
const anchorElementRef = React.useRef(document.createElement('div'));
|
|
355
311
|
const positionMenu = React.useCallback(() => {
|
|
312
|
+
anchorElementRef.current.style.top = anchorElementRef.current.style.bottom;
|
|
356
313
|
const rootElement = editor.getRootElement();
|
|
357
314
|
const containerDiv = anchorElementRef.current;
|
|
358
315
|
const menuEle = containerDiv.firstChild;
|
|
359
|
-
|
|
360
316
|
if (rootElement !== null && resolution !== null) {
|
|
361
317
|
const {
|
|
362
318
|
left,
|
|
@@ -364,33 +320,28 @@ function useMenuAnchorRef(resolution, setResolution, className) {
|
|
|
364
320
|
width,
|
|
365
321
|
height
|
|
366
322
|
} = resolution.getRect();
|
|
367
|
-
|
|
323
|
+
const anchorHeight = anchorElementRef.current.offsetHeight; // use to position under anchor
|
|
324
|
+
containerDiv.style.top = `${top + window.pageYOffset + anchorHeight + 3}px`;
|
|
368
325
|
containerDiv.style.left = `${left + window.pageXOffset}px`;
|
|
369
326
|
containerDiv.style.height = `${height}px`;
|
|
370
327
|
containerDiv.style.width = `${width}px`;
|
|
371
|
-
|
|
372
328
|
if (menuEle !== null) {
|
|
329
|
+
menuEle.style.top = `${top}`;
|
|
373
330
|
const menuRect = menuEle.getBoundingClientRect();
|
|
374
331
|
const menuHeight = menuRect.height;
|
|
375
332
|
const menuWidth = menuRect.width;
|
|
376
333
|
const rootElementRect = rootElement.getBoundingClientRect();
|
|
377
|
-
|
|
378
334
|
if (left + menuWidth > rootElementRect.right) {
|
|
379
335
|
containerDiv.style.left = `${rootElementRect.right - menuWidth + window.pageXOffset}px`;
|
|
380
336
|
}
|
|
381
|
-
|
|
382
|
-
const margin = 10;
|
|
383
|
-
|
|
384
337
|
if ((top + menuHeight > window.innerHeight || top + menuHeight > rootElementRect.bottom) && top - rootElementRect.top > menuHeight) {
|
|
385
|
-
containerDiv.style.top = `${top - menuHeight + window.pageYOffset -
|
|
338
|
+
containerDiv.style.top = `${top - menuHeight + window.pageYOffset - height}px`;
|
|
386
339
|
}
|
|
387
340
|
}
|
|
388
|
-
|
|
389
341
|
if (!containerDiv.isConnected) {
|
|
390
342
|
if (className != null) {
|
|
391
343
|
containerDiv.className = className;
|
|
392
344
|
}
|
|
393
|
-
|
|
394
345
|
containerDiv.setAttribute('aria-label', 'Typeahead menu');
|
|
395
346
|
containerDiv.setAttribute('id', 'typeahead-menu');
|
|
396
347
|
containerDiv.setAttribute('role', 'listbox');
|
|
@@ -398,23 +349,19 @@ function useMenuAnchorRef(resolution, setResolution, className) {
|
|
|
398
349
|
containerDiv.style.position = 'absolute';
|
|
399
350
|
document.body.append(containerDiv);
|
|
400
351
|
}
|
|
401
|
-
|
|
402
352
|
anchorElementRef.current = containerDiv;
|
|
403
353
|
rootElement.setAttribute('aria-controls', 'typeahead-menu');
|
|
404
354
|
}
|
|
405
355
|
}, [editor, resolution, className]);
|
|
406
356
|
React.useEffect(() => {
|
|
407
357
|
const rootElement = editor.getRootElement();
|
|
408
|
-
|
|
409
358
|
if (resolution !== null) {
|
|
410
359
|
positionMenu();
|
|
411
360
|
return () => {
|
|
412
361
|
if (rootElement !== null) {
|
|
413
362
|
rootElement.removeAttribute('aria-controls');
|
|
414
363
|
}
|
|
415
|
-
|
|
416
364
|
const containerDiv = anchorElementRef.current;
|
|
417
|
-
|
|
418
365
|
if (containerDiv !== null && containerDiv.isConnected) {
|
|
419
366
|
containerDiv.remove();
|
|
420
367
|
}
|
|
@@ -428,7 +375,7 @@ function useMenuAnchorRef(resolution, setResolution, className) {
|
|
|
428
375
|
}
|
|
429
376
|
}
|
|
430
377
|
}, [resolution, setResolution]);
|
|
431
|
-
useDynamicPositioning
|
|
378
|
+
useDynamicPositioning(resolution, anchorElementRef.current, positionMenu, onVisibilityChange);
|
|
432
379
|
return anchorElementRef;
|
|
433
380
|
}
|
|
434
381
|
|
|
@@ -440,166 +387,90 @@ function useMenuAnchorRef(resolution, setResolution, className) {
|
|
|
440
387
|
*
|
|
441
388
|
*/
|
|
442
389
|
const PUNCTUATION = '\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%\'"~=<>_:;';
|
|
443
|
-
|
|
444
390
|
function getTextUpToAnchor(selection) {
|
|
445
391
|
const anchor = selection.anchor;
|
|
446
|
-
|
|
447
392
|
if (anchor.type !== 'text') {
|
|
448
393
|
return null;
|
|
449
394
|
}
|
|
450
|
-
|
|
451
395
|
const anchorNode = anchor.getNode();
|
|
452
|
-
|
|
453
396
|
if (!anchorNode.isSimpleText()) {
|
|
454
397
|
return null;
|
|
455
398
|
}
|
|
456
|
-
|
|
457
399
|
const anchorOffset = anchor.offset;
|
|
458
400
|
return anchorNode.getTextContent().slice(0, anchorOffset);
|
|
459
401
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const domSelection = window.getSelection();
|
|
463
|
-
|
|
402
|
+
function tryToPositionRange(leadOffset, range, editorWindow) {
|
|
403
|
+
const domSelection = editorWindow.getSelection();
|
|
464
404
|
if (domSelection === null || !domSelection.isCollapsed) {
|
|
465
405
|
return false;
|
|
466
406
|
}
|
|
467
|
-
|
|
468
407
|
const anchorNode = domSelection.anchorNode;
|
|
469
408
|
const startOffset = leadOffset;
|
|
470
409
|
const endOffset = domSelection.anchorOffset;
|
|
471
|
-
|
|
472
410
|
if (anchorNode == null || endOffset == null) {
|
|
473
411
|
return false;
|
|
474
412
|
}
|
|
475
|
-
|
|
476
413
|
try {
|
|
477
414
|
range.setStart(anchorNode, startOffset);
|
|
478
415
|
range.setEnd(anchorNode, endOffset);
|
|
479
416
|
} catch (error) {
|
|
480
417
|
return false;
|
|
481
418
|
}
|
|
482
|
-
|
|
483
419
|
return true;
|
|
484
420
|
}
|
|
485
|
-
|
|
486
421
|
function getQueryTextForSearch(editor) {
|
|
487
422
|
let text = null;
|
|
488
423
|
editor.getEditorState().read(() => {
|
|
489
424
|
const selection = lexical.$getSelection();
|
|
490
|
-
|
|
491
425
|
if (!lexical.$isRangeSelection(selection)) {
|
|
492
426
|
return;
|
|
493
427
|
}
|
|
494
|
-
|
|
495
428
|
text = getTextUpToAnchor(selection);
|
|
496
429
|
});
|
|
497
430
|
return text;
|
|
498
431
|
}
|
|
499
|
-
|
|
500
432
|
function isSelectionOnEntityBoundary(editor, offset) {
|
|
501
433
|
if (offset !== 0) {
|
|
502
434
|
return false;
|
|
503
435
|
}
|
|
504
|
-
|
|
505
436
|
return editor.getEditorState().read(() => {
|
|
506
437
|
const selection = lexical.$getSelection();
|
|
507
|
-
|
|
508
438
|
if (lexical.$isRangeSelection(selection)) {
|
|
509
439
|
const anchor = selection.anchor;
|
|
510
440
|
const anchorNode = anchor.getNode();
|
|
511
441
|
const prevSibling = anchorNode.getPreviousSibling();
|
|
512
442
|
return lexical.$isTextNode(prevSibling) && prevSibling.isTextEntity();
|
|
513
443
|
}
|
|
514
|
-
|
|
515
444
|
return false;
|
|
516
445
|
});
|
|
517
446
|
}
|
|
518
|
-
|
|
519
447
|
function startTransition(callback) {
|
|
520
448
|
if (React.startTransition) {
|
|
521
449
|
React.startTransition(callback);
|
|
522
450
|
} else {
|
|
523
451
|
callback();
|
|
524
452
|
}
|
|
525
|
-
}
|
|
526
|
-
|
|
453
|
+
}
|
|
527
454
|
|
|
455
|
+
// Got from https://stackoverflow.com/a/42543908/2013580
|
|
528
456
|
function getScrollParent(element, includeHidden) {
|
|
529
457
|
let style = getComputedStyle(element);
|
|
530
458
|
const excludeStaticParent = style.position === 'absolute';
|
|
531
459
|
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
|
|
532
|
-
|
|
533
460
|
if (style.position === 'fixed') {
|
|
534
461
|
return document.body;
|
|
535
462
|
}
|
|
536
|
-
|
|
537
463
|
for (let parent = element; parent = parent.parentElement;) {
|
|
538
464
|
style = getComputedStyle(parent);
|
|
539
|
-
|
|
540
465
|
if (excludeStaticParent && style.position === 'static') {
|
|
541
466
|
continue;
|
|
542
467
|
}
|
|
543
|
-
|
|
544
468
|
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
|
|
545
469
|
return parent;
|
|
546
470
|
}
|
|
547
471
|
}
|
|
548
|
-
|
|
549
472
|
return document.body;
|
|
550
473
|
}
|
|
551
|
-
|
|
552
|
-
function isTriggerVisibleInNearestScrollContainer(targetElement, containerElement) {
|
|
553
|
-
const tRect = targetElement.getBoundingClientRect();
|
|
554
|
-
const cRect = containerElement.getBoundingClientRect();
|
|
555
|
-
return tRect.top > cRect.top && tRect.top < cRect.bottom;
|
|
556
|
-
} // Reposition the menu on scroll, window resize, and element resize.
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
function useDynamicPositioning(resolution, targetElement, onReposition, onVisibilityChange) {
|
|
560
|
-
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
561
|
-
React.useEffect(() => {
|
|
562
|
-
if (targetElement != null && resolution != null) {
|
|
563
|
-
const rootElement = editor.getRootElement();
|
|
564
|
-
const rootScrollParent = rootElement != null ? getScrollParent(rootElement, false) : document.body;
|
|
565
|
-
let ticking = false;
|
|
566
|
-
let previousIsInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
567
|
-
|
|
568
|
-
const handleScroll = function () {
|
|
569
|
-
if (!ticking) {
|
|
570
|
-
window.requestAnimationFrame(function () {
|
|
571
|
-
onReposition();
|
|
572
|
-
ticking = false;
|
|
573
|
-
});
|
|
574
|
-
ticking = true;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const isInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
578
|
-
|
|
579
|
-
if (isInView !== previousIsInView) {
|
|
580
|
-
previousIsInView = isInView;
|
|
581
|
-
|
|
582
|
-
if (onVisibilityChange != null) {
|
|
583
|
-
onVisibilityChange(isInView);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const resizeObserver = new ResizeObserver(onReposition);
|
|
589
|
-
window.addEventListener('resize', onReposition);
|
|
590
|
-
document.addEventListener('scroll', handleScroll, {
|
|
591
|
-
capture: true,
|
|
592
|
-
passive: true
|
|
593
|
-
});
|
|
594
|
-
resizeObserver.observe(targetElement);
|
|
595
|
-
return () => {
|
|
596
|
-
resizeObserver.unobserve(targetElement);
|
|
597
|
-
window.removeEventListener('resize', onReposition);
|
|
598
|
-
document.removeEventListener('scroll', handleScroll);
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
}, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
|
|
602
|
-
}
|
|
603
474
|
const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = lexical.createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
|
|
604
475
|
function useBasicTypeaheadTriggerMatch(trigger, {
|
|
605
476
|
minLength = 1,
|
|
@@ -609,11 +480,9 @@ function useBasicTypeaheadTriggerMatch(trigger, {
|
|
|
609
480
|
const validChars = '[^' + trigger + PUNCTUATION + '\\s]';
|
|
610
481
|
const TypeaheadTriggerRegex = new RegExp('(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$');
|
|
611
482
|
const match = TypeaheadTriggerRegex.exec(text);
|
|
612
|
-
|
|
613
483
|
if (match !== null) {
|
|
614
484
|
const maybeLeadingWhitespace = match[1];
|
|
615
485
|
const matchingString = match[3];
|
|
616
|
-
|
|
617
486
|
if (matchingString.length >= minLength) {
|
|
618
487
|
return {
|
|
619
488
|
leadOffset: match.index + maybeLeadingWhitespace.length,
|
|
@@ -622,7 +491,6 @@ function useBasicTypeaheadTriggerMatch(trigger, {
|
|
|
622
491
|
};
|
|
623
492
|
}
|
|
624
493
|
}
|
|
625
|
-
|
|
626
494
|
return null;
|
|
627
495
|
}, [maxLength, minLength, trigger]);
|
|
628
496
|
}
|
|
@@ -634,21 +502,20 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
634
502
|
onClose,
|
|
635
503
|
menuRenderFn,
|
|
636
504
|
triggerFn,
|
|
637
|
-
anchorClassName
|
|
505
|
+
anchorClassName,
|
|
506
|
+
commandPriority = lexical.COMMAND_PRIORITY_LOW
|
|
638
507
|
}) {
|
|
639
508
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
640
509
|
const [resolution, setResolution] = React.useState(null);
|
|
641
510
|
const anchorElementRef = useMenuAnchorRef(resolution, setResolution, anchorClassName);
|
|
642
511
|
const closeTypeahead = React.useCallback(() => {
|
|
643
512
|
setResolution(null);
|
|
644
|
-
|
|
645
513
|
if (onClose != null && resolution !== null) {
|
|
646
514
|
onClose();
|
|
647
515
|
}
|
|
648
516
|
}, [onClose, resolution]);
|
|
649
517
|
const openTypeahead = React.useCallback(res => {
|
|
650
518
|
setResolution(res);
|
|
651
|
-
|
|
652
519
|
if (onOpen != null && resolution === null) {
|
|
653
520
|
onOpen(res);
|
|
654
521
|
}
|
|
@@ -656,21 +523,18 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
656
523
|
React.useEffect(() => {
|
|
657
524
|
const updateListener = () => {
|
|
658
525
|
editor.getEditorState().read(() => {
|
|
659
|
-
const
|
|
526
|
+
const editorWindow = editor._window || window;
|
|
527
|
+
const range = editorWindow.document.createRange();
|
|
660
528
|
const selection = lexical.$getSelection();
|
|
661
529
|
const text = getQueryTextForSearch(editor);
|
|
662
|
-
|
|
663
530
|
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed() || text === null || range === null) {
|
|
664
531
|
closeTypeahead();
|
|
665
532
|
return;
|
|
666
533
|
}
|
|
667
|
-
|
|
668
534
|
const match = triggerFn(text, editor);
|
|
669
535
|
onQueryChange(match ? match.matchingString : null);
|
|
670
|
-
|
|
671
536
|
if (match !== null && !isSelectionOnEntityBoundary(editor, match.leadOffset)) {
|
|
672
|
-
const isRangePositioned = tryToPositionRange(match.leadOffset, range);
|
|
673
|
-
|
|
537
|
+
const isRangePositioned = tryToPositionRange(match.leadOffset, range, editorWindow);
|
|
674
538
|
if (isRangePositioned !== null) {
|
|
675
539
|
startTransition(() => openTypeahead({
|
|
676
540
|
getRect: () => range.getBoundingClientRect(),
|
|
@@ -679,11 +543,9 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
679
543
|
return;
|
|
680
544
|
}
|
|
681
545
|
}
|
|
682
|
-
|
|
683
546
|
closeTypeahead();
|
|
684
547
|
});
|
|
685
548
|
};
|
|
686
|
-
|
|
687
549
|
const removeUpdateListener = editor.registerUpdateListener(updateListener);
|
|
688
550
|
return () => {
|
|
689
551
|
removeUpdateListener();
|
|
@@ -697,7 +559,8 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
697
559
|
options: options,
|
|
698
560
|
menuRenderFn: menuRenderFn,
|
|
699
561
|
shouldSplitNodeWithQuery: true,
|
|
700
|
-
onSelectOption: onSelectOption
|
|
562
|
+
onSelectOption: onSelectOption,
|
|
563
|
+
commandPriority: commandPriority
|
|
701
564
|
});
|
|
702
565
|
}
|
|
703
566
|
|