@lexical/react 0.5.0 → 0.5.1-next.1
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/DEPRECATED_useLexicalDecorators.d.ts +2 -1
- package/DEPRECATED_useLexicalDecorators.dev.js +125 -10
- package/DEPRECATED_useLexicalDecorators.js.flow +8 -0
- package/DEPRECATED_useLexicalDecorators.prod.js +6 -2
- package/LexicalAutoEmbedPlugin.d.ts +3 -11
- package/LexicalAutoEmbedPlugin.dev.js +3 -18
- package/LexicalAutoEmbedPlugin.js.flow +3 -11
- package/LexicalAutoEmbedPlugin.prod.js +4 -5
- package/LexicalAutoLinkPlugin.dev.js +49 -46
- package/LexicalAutoLinkPlugin.prod.js +6 -6
- package/LexicalHorizontalRuleNode.dev.js +1 -1
- package/LexicalHorizontalRuleNode.prod.js +1 -1
- package/LexicalPlainTextPlugin.d.ts +3 -1
- package/LexicalPlainTextPlugin.dev.js +121 -5
- package/LexicalPlainTextPlugin.js.flow +3 -0
- package/LexicalPlainTextPlugin.prod.js +6 -4
- package/LexicalRichTextPlugin.d.ts +3 -1
- package/LexicalRichTextPlugin.dev.js +121 -5
- package/LexicalRichTextPlugin.js.flow +3 -0
- package/LexicalRichTextPlugin.prod.js +6 -4
- package/LexicalTreeView.dev.js +24 -9
- package/LexicalTreeView.prod.js +14 -13
- package/LexicalTypeaheadMenuPlugin.d.ts +20 -11
- package/LexicalTypeaheadMenuPlugin.dev.js +199 -59
- package/LexicalTypeaheadMenuPlugin.js.flow +19 -21
- package/LexicalTypeaheadMenuPlugin.prod.js +20 -16
- package/README.md +3 -3
- package/package.json +19 -19
- package/shared/ReactErrorBoundary.d.ts +63 -0
- package/shared/useDecorators.d.ts +8 -1
|
@@ -213,12 +213,90 @@ function startTransition(callback) {
|
|
|
213
213
|
} else {
|
|
214
214
|
callback();
|
|
215
215
|
}
|
|
216
|
+
} // Got from https://stackoverflow.com/a/42543908/2013580
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
function getScrollParent(element, includeHidden) {
|
|
220
|
+
let style = getComputedStyle(element);
|
|
221
|
+
const excludeStaticParent = style.position === 'absolute';
|
|
222
|
+
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
|
|
223
|
+
|
|
224
|
+
if (style.position === 'fixed') {
|
|
225
|
+
return document.body;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for (let parent = element; parent = parent.parentElement;) {
|
|
229
|
+
style = getComputedStyle(parent);
|
|
230
|
+
|
|
231
|
+
if (excludeStaticParent && style.position === 'static') {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
|
|
236
|
+
return parent;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return document.body;
|
|
216
241
|
}
|
|
217
242
|
|
|
243
|
+
function isTriggerVisibleInNearestScrollContainer(targetElement, containerElement) {
|
|
244
|
+
const tRect = targetElement.getBoundingClientRect();
|
|
245
|
+
const cRect = containerElement.getBoundingClientRect();
|
|
246
|
+
return tRect.top > cRect.top && tRect.top < cRect.bottom;
|
|
247
|
+
} // Reposition the menu on scroll, window resize, and element resize.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
function useDynamicPositioning(resolution, targetElement, onReposition, onVisibilityChange) {
|
|
251
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
252
|
+
React.useEffect(() => {
|
|
253
|
+
if (targetElement != null && resolution != null) {
|
|
254
|
+
const rootElement = editor.getRootElement();
|
|
255
|
+
const rootScrollParent = rootElement != null ? getScrollParent(rootElement, false) : document.body;
|
|
256
|
+
let ticking = false;
|
|
257
|
+
let previousIsInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
258
|
+
|
|
259
|
+
const handleScroll = function () {
|
|
260
|
+
if (!ticking) {
|
|
261
|
+
window.requestAnimationFrame(function () {
|
|
262
|
+
onReposition();
|
|
263
|
+
ticking = false;
|
|
264
|
+
});
|
|
265
|
+
ticking = true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const isInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
|
|
269
|
+
|
|
270
|
+
if (isInView !== previousIsInView) {
|
|
271
|
+
previousIsInView = isInView;
|
|
272
|
+
|
|
273
|
+
if (onVisibilityChange != null) {
|
|
274
|
+
onVisibilityChange(isInView);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const resizeObserver = new ResizeObserver(onReposition);
|
|
280
|
+
window.addEventListener('resize', onReposition);
|
|
281
|
+
document.addEventListener('scroll', handleScroll, {
|
|
282
|
+
capture: true,
|
|
283
|
+
passive: true
|
|
284
|
+
});
|
|
285
|
+
resizeObserver.observe(targetElement);
|
|
286
|
+
return () => {
|
|
287
|
+
resizeObserver.unobserve(targetElement);
|
|
288
|
+
window.removeEventListener('resize', onReposition);
|
|
289
|
+
document.removeEventListener('scroll', handleScroll);
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
|
|
293
|
+
}
|
|
294
|
+
const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = lexical.createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
|
|
295
|
+
|
|
218
296
|
function LexicalPopoverMenu({
|
|
219
297
|
close,
|
|
220
298
|
editor,
|
|
221
|
-
|
|
299
|
+
anchorElementRef,
|
|
222
300
|
resolution,
|
|
223
301
|
options,
|
|
224
302
|
menuRenderFn,
|
|
@@ -258,6 +336,18 @@ function LexicalPopoverMenu({
|
|
|
258
336
|
updateSelectedIndex(0);
|
|
259
337
|
}
|
|
260
338
|
}, [options, selectedIndex, updateSelectedIndex]);
|
|
339
|
+
React.useEffect(() => {
|
|
340
|
+
return utils.mergeRegister(editor.registerCommand(SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND, ({
|
|
341
|
+
option
|
|
342
|
+
}) => {
|
|
343
|
+
if (option.ref && option.ref.current != null) {
|
|
344
|
+
scrollIntoViewIfNeeded(option.ref.current);
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return false;
|
|
349
|
+
}, lexical.COMMAND_PRIORITY_LOW));
|
|
350
|
+
}, [editor, updateSelectedIndex]);
|
|
261
351
|
React.useEffect(() => {
|
|
262
352
|
return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
|
|
263
353
|
const event = payload;
|
|
@@ -268,7 +358,10 @@ function LexicalPopoverMenu({
|
|
|
268
358
|
const option = options[newSelectedIndex];
|
|
269
359
|
|
|
270
360
|
if (option.ref != null && option.ref.current) {
|
|
271
|
-
|
|
361
|
+
editor.dispatchCommand(SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND, {
|
|
362
|
+
index: newSelectedIndex,
|
|
363
|
+
option
|
|
364
|
+
});
|
|
272
365
|
}
|
|
273
366
|
|
|
274
367
|
event.preventDefault();
|
|
@@ -276,7 +369,7 @@ function LexicalPopoverMenu({
|
|
|
276
369
|
}
|
|
277
370
|
|
|
278
371
|
return true;
|
|
279
|
-
}, lexical.
|
|
372
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
|
|
280
373
|
const event = payload;
|
|
281
374
|
|
|
282
375
|
if (options !== null && options.length && selectedIndex !== null) {
|
|
@@ -293,13 +386,13 @@ function LexicalPopoverMenu({
|
|
|
293
386
|
}
|
|
294
387
|
|
|
295
388
|
return true;
|
|
296
|
-
}, lexical.
|
|
389
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
|
|
297
390
|
const event = payload;
|
|
298
391
|
event.preventDefault();
|
|
299
392
|
event.stopImmediatePropagation();
|
|
300
393
|
close();
|
|
301
394
|
return true;
|
|
302
|
-
}, lexical.
|
|
395
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
|
|
303
396
|
const event = payload;
|
|
304
397
|
|
|
305
398
|
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
@@ -310,7 +403,7 @@ function LexicalPopoverMenu({
|
|
|
310
403
|
event.stopImmediatePropagation();
|
|
311
404
|
selectOptionAndCleanUp(options[selectedIndex]);
|
|
312
405
|
return true;
|
|
313
|
-
}, lexical.
|
|
406
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
|
|
314
407
|
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
315
408
|
return false;
|
|
316
409
|
}
|
|
@@ -322,14 +415,15 @@ function LexicalPopoverMenu({
|
|
|
322
415
|
|
|
323
416
|
selectOptionAndCleanUp(options[selectedIndex]);
|
|
324
417
|
return true;
|
|
325
|
-
}, lexical.
|
|
418
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL));
|
|
326
419
|
}, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
|
|
327
420
|
const listItemProps = React.useMemo(() => ({
|
|
421
|
+
options,
|
|
328
422
|
selectOptionAndCleanUp,
|
|
329
423
|
selectedIndex,
|
|
330
424
|
setHighlightedIndex
|
|
331
|
-
}), [selectOptionAndCleanUp, selectedIndex]);
|
|
332
|
-
return menuRenderFn(
|
|
425
|
+
}), [selectOptionAndCleanUp, selectedIndex, options]);
|
|
426
|
+
return menuRenderFn(anchorElementRef, listItemProps, resolution.match.matchingString);
|
|
333
427
|
}
|
|
334
428
|
|
|
335
429
|
function useBasicTypeaheadTriggerMatch(trigger, {
|
|
@@ -358,51 +452,68 @@ function useBasicTypeaheadTriggerMatch(trigger, {
|
|
|
358
452
|
}, [maxLength, minLength, trigger]);
|
|
359
453
|
}
|
|
360
454
|
|
|
361
|
-
function
|
|
455
|
+
function useMenuAnchorRef(resolution, setResolution, className) {
|
|
362
456
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
363
457
|
const anchorElementRef = React.useRef(document.createElement('div'));
|
|
364
|
-
React.
|
|
458
|
+
const positionMenu = React.useCallback(() => {
|
|
365
459
|
const rootElement = editor.getRootElement();
|
|
460
|
+
const containerDiv = anchorElementRef.current;
|
|
461
|
+
|
|
462
|
+
if (rootElement !== null && resolution !== null) {
|
|
463
|
+
const {
|
|
464
|
+
left,
|
|
465
|
+
top,
|
|
466
|
+
width,
|
|
467
|
+
height
|
|
468
|
+
} = resolution.getRect();
|
|
469
|
+
containerDiv.style.top = `${top + window.pageYOffset}px`;
|
|
470
|
+
containerDiv.style.left = `${left + window.pageXOffset}px`;
|
|
471
|
+
containerDiv.style.height = `${height}px`;
|
|
472
|
+
containerDiv.style.width = `${width}px`;
|
|
473
|
+
|
|
474
|
+
if (!containerDiv.isConnected) {
|
|
475
|
+
if (className != null) {
|
|
476
|
+
containerDiv.className = className;
|
|
477
|
+
}
|
|
366
478
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
containerDiv.setAttribute('id', 'typeahead-menu');
|
|
371
|
-
containerDiv.setAttribute('role', 'listbox');
|
|
372
|
-
|
|
373
|
-
if (rootElement !== null && resolution !== null) {
|
|
374
|
-
const {
|
|
375
|
-
left,
|
|
376
|
-
top,
|
|
377
|
-
width,
|
|
378
|
-
height
|
|
379
|
-
} = resolution.getRect();
|
|
380
|
-
containerDiv.style.top = `${top + height + 5 + window.pageYOffset}px`;
|
|
381
|
-
containerDiv.style.left = `${left + (resolution.position === 'start' ? 0 : width) + window.pageXOffset}px`;
|
|
479
|
+
containerDiv.setAttribute('aria-label', 'Typeahead menu');
|
|
480
|
+
containerDiv.setAttribute('id', 'typeahead-menu');
|
|
481
|
+
containerDiv.setAttribute('role', 'listbox');
|
|
382
482
|
containerDiv.style.display = 'block';
|
|
383
483
|
containerDiv.style.position = 'absolute';
|
|
384
|
-
|
|
385
|
-
if (!containerDiv.isConnected) {
|
|
386
|
-
document.body.append(containerDiv);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
anchorElementRef.current = containerDiv;
|
|
390
|
-
rootElement.setAttribute('aria-controls', 'typeahead-menu');
|
|
484
|
+
document.body.append(containerDiv);
|
|
391
485
|
}
|
|
486
|
+
|
|
487
|
+
anchorElementRef.current = containerDiv;
|
|
488
|
+
rootElement.setAttribute('aria-controls', 'typeahead-menu');
|
|
392
489
|
}
|
|
490
|
+
}, [editor, resolution, className]);
|
|
491
|
+
React.useEffect(() => {
|
|
492
|
+
const rootElement = editor.getRootElement();
|
|
393
493
|
|
|
394
494
|
if (resolution !== null) {
|
|
395
495
|
positionMenu();
|
|
396
|
-
window.addEventListener('resize', positionMenu);
|
|
397
496
|
return () => {
|
|
398
|
-
window.removeEventListener('resize', positionMenu);
|
|
399
|
-
|
|
400
497
|
if (rootElement !== null) {
|
|
401
498
|
rootElement.removeAttribute('aria-controls');
|
|
402
499
|
}
|
|
500
|
+
|
|
501
|
+
const containerDiv = anchorElementRef.current;
|
|
502
|
+
|
|
503
|
+
if (containerDiv !== null && containerDiv.isConnected) {
|
|
504
|
+
containerDiv.remove();
|
|
505
|
+
}
|
|
403
506
|
};
|
|
404
507
|
}
|
|
405
|
-
}, [editor, resolution]);
|
|
508
|
+
}, [editor, positionMenu, resolution]);
|
|
509
|
+
const onVisibilityChange = React.useCallback(isInView => {
|
|
510
|
+
if (resolution !== null) {
|
|
511
|
+
if (!isInView) {
|
|
512
|
+
setResolution(null);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}, [resolution, setResolution]);
|
|
516
|
+
useDynamicPositioning(resolution, anchorElementRef.current, positionMenu, onVisibilityChange);
|
|
406
517
|
return anchorElementRef;
|
|
407
518
|
}
|
|
408
519
|
|
|
@@ -410,13 +521,29 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
410
521
|
options,
|
|
411
522
|
onQueryChange,
|
|
412
523
|
onSelectOption,
|
|
524
|
+
onOpen,
|
|
525
|
+
onClose,
|
|
413
526
|
menuRenderFn,
|
|
414
527
|
triggerFn,
|
|
415
|
-
|
|
528
|
+
anchorClassName
|
|
416
529
|
}) {
|
|
417
530
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
418
531
|
const [resolution, setResolution] = React.useState(null);
|
|
419
|
-
const anchorElementRef =
|
|
532
|
+
const anchorElementRef = useMenuAnchorRef(resolution, setResolution, anchorClassName);
|
|
533
|
+
const closeTypeahead = React.useCallback(() => {
|
|
534
|
+
setResolution(null);
|
|
535
|
+
|
|
536
|
+
if (onClose != null && resolution !== null) {
|
|
537
|
+
onClose();
|
|
538
|
+
}
|
|
539
|
+
}, [onClose, resolution]);
|
|
540
|
+
const openTypeahead = React.useCallback(res => {
|
|
541
|
+
setResolution(res);
|
|
542
|
+
|
|
543
|
+
if (onOpen != null && resolution === null) {
|
|
544
|
+
onOpen(res);
|
|
545
|
+
}
|
|
546
|
+
}, [onOpen, resolution]);
|
|
420
547
|
React.useEffect(() => {
|
|
421
548
|
let activeRange = document.createRange();
|
|
422
549
|
|
|
@@ -427,7 +554,7 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
427
554
|
const text = getQueryTextForSearch(editor);
|
|
428
555
|
|
|
429
556
|
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed() || text === null || range === null) {
|
|
430
|
-
|
|
557
|
+
closeTypeahead();
|
|
431
558
|
return;
|
|
432
559
|
}
|
|
433
560
|
|
|
@@ -438,16 +565,15 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
438
565
|
const isRangePositioned = tryToPositionRange(match.leadOffset, range);
|
|
439
566
|
|
|
440
567
|
if (isRangePositioned !== null) {
|
|
441
|
-
startTransition(() =>
|
|
568
|
+
startTransition(() => openTypeahead({
|
|
442
569
|
getRect: () => range.getBoundingClientRect(),
|
|
443
|
-
match
|
|
444
|
-
position
|
|
570
|
+
match
|
|
445
571
|
}));
|
|
446
572
|
return;
|
|
447
573
|
}
|
|
448
574
|
}
|
|
449
575
|
|
|
450
|
-
|
|
576
|
+
closeTypeahead();
|
|
451
577
|
});
|
|
452
578
|
};
|
|
453
579
|
|
|
@@ -456,15 +582,12 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
456
582
|
activeRange = null;
|
|
457
583
|
removeUpdateListener();
|
|
458
584
|
};
|
|
459
|
-
}, [editor, triggerFn, onQueryChange, resolution,
|
|
460
|
-
const closeTypeahead = React.useCallback(() => {
|
|
461
|
-
setResolution(null);
|
|
462
|
-
}, []);
|
|
585
|
+
}, [editor, triggerFn, onQueryChange, resolution, closeTypeahead, openTypeahead]);
|
|
463
586
|
return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(LexicalPopoverMenu, {
|
|
464
587
|
close: closeTypeahead,
|
|
465
588
|
resolution: resolution,
|
|
466
589
|
editor: editor,
|
|
467
|
-
|
|
590
|
+
anchorElementRef: anchorElementRef,
|
|
468
591
|
options: options,
|
|
469
592
|
menuRenderFn: menuRenderFn,
|
|
470
593
|
onSelectOption: onSelectOption
|
|
@@ -473,14 +596,29 @@ function LexicalTypeaheadMenuPlugin({
|
|
|
473
596
|
function LexicalNodeMenuPlugin({
|
|
474
597
|
options,
|
|
475
598
|
nodeKey,
|
|
476
|
-
position = 'end',
|
|
477
599
|
onClose,
|
|
600
|
+
onOpen,
|
|
478
601
|
onSelectOption,
|
|
479
|
-
menuRenderFn
|
|
602
|
+
menuRenderFn,
|
|
603
|
+
anchorClassName
|
|
480
604
|
}) {
|
|
481
605
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
482
606
|
const [resolution, setResolution] = React.useState(null);
|
|
483
|
-
const anchorElementRef =
|
|
607
|
+
const anchorElementRef = useMenuAnchorRef(resolution, setResolution, anchorClassName);
|
|
608
|
+
const closeNodeMenu = React.useCallback(() => {
|
|
609
|
+
setResolution(null);
|
|
610
|
+
|
|
611
|
+
if (onClose != null && resolution !== null) {
|
|
612
|
+
onClose();
|
|
613
|
+
}
|
|
614
|
+
}, [onClose, resolution]);
|
|
615
|
+
const openNodeMenu = React.useCallback(res => {
|
|
616
|
+
setResolution(res);
|
|
617
|
+
|
|
618
|
+
if (onOpen != null && resolution === null) {
|
|
619
|
+
onOpen(res);
|
|
620
|
+
}
|
|
621
|
+
}, [onOpen, resolution]);
|
|
484
622
|
React.useEffect(() => {
|
|
485
623
|
if (nodeKey && resolution == null) {
|
|
486
624
|
editor.update(() => {
|
|
@@ -489,26 +627,25 @@ function LexicalNodeMenuPlugin({
|
|
|
489
627
|
|
|
490
628
|
if (node != null && domElement != null) {
|
|
491
629
|
const text = node.getTextContent();
|
|
492
|
-
startTransition(() =>
|
|
630
|
+
startTransition(() => openNodeMenu({
|
|
493
631
|
getRect: () => domElement.getBoundingClientRect(),
|
|
494
632
|
match: {
|
|
495
633
|
leadOffset: text.length,
|
|
496
634
|
matchingString: text,
|
|
497
635
|
replaceableString: text
|
|
498
|
-
}
|
|
499
|
-
position
|
|
636
|
+
}
|
|
500
637
|
}));
|
|
501
638
|
}
|
|
502
639
|
});
|
|
503
640
|
} else if (nodeKey == null && resolution != null) {
|
|
504
|
-
|
|
641
|
+
closeNodeMenu();
|
|
505
642
|
}
|
|
506
|
-
}, [editor, nodeKey,
|
|
643
|
+
}, [closeNodeMenu, editor, nodeKey, openNodeMenu, resolution]);
|
|
507
644
|
return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(LexicalPopoverMenu, {
|
|
508
|
-
close:
|
|
645
|
+
close: closeNodeMenu,
|
|
509
646
|
resolution: resolution,
|
|
510
647
|
editor: editor,
|
|
511
|
-
|
|
648
|
+
anchorElementRef: anchorElementRef,
|
|
512
649
|
options: options,
|
|
513
650
|
menuRenderFn: menuRenderFn,
|
|
514
651
|
onSelectOption: onSelectOption
|
|
@@ -518,5 +655,8 @@ function LexicalNodeMenuPlugin({
|
|
|
518
655
|
exports.LexicalNodeMenuPlugin = LexicalNodeMenuPlugin;
|
|
519
656
|
exports.LexicalTypeaheadMenuPlugin = LexicalTypeaheadMenuPlugin;
|
|
520
657
|
exports.PUNCTUATION = PUNCTUATION;
|
|
658
|
+
exports.SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND;
|
|
521
659
|
exports.TypeaheadOption = TypeaheadOption;
|
|
660
|
+
exports.getScrollParent = getScrollParent;
|
|
522
661
|
exports.useBasicTypeaheadTriggerMatch = useBasicTypeaheadTriggerMatch;
|
|
662
|
+
exports.useDynamicPositioning = useDynamicPositioning;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @flow strict
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {LexicalEditor, NodeKey, TextNode} from 'lexical';
|
|
10
|
+
import type {LexicalCommand, LexicalEditor, NodeKey, TextNode} from 'lexical';
|
|
11
11
|
import * as React from 'react';
|
|
12
12
|
|
|
13
13
|
export type QueryMatch = {
|
|
@@ -26,40 +26,33 @@ export const PUNCTUATION: string =
|
|
|
26
26
|
|
|
27
27
|
declare export class TypeaheadOption {
|
|
28
28
|
key: string;
|
|
29
|
-
ref:
|
|
29
|
+
ref: {current: HTMLElement | null};
|
|
30
30
|
constructor(key: string): void;
|
|
31
31
|
setRefElement(element: HTMLElement | null): void;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
declare export var SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND: LexicalCommand<{
|
|
35
|
+
index: number,
|
|
36
|
+
option: TypeaheadOption,
|
|
37
|
+
}>;
|
|
38
|
+
|
|
34
39
|
export type MenuRenderFn<TOption> = (
|
|
35
|
-
|
|
40
|
+
anchorElementRef: {current: HTMLElement | null},
|
|
36
41
|
itemProps: {
|
|
37
42
|
selectedIndex: number | null,
|
|
38
43
|
selectOptionAndCleanUp: (option: TOption) => void,
|
|
39
44
|
setHighlightedIndex: (index: number) => void,
|
|
45
|
+
options: Array<TOption>,
|
|
40
46
|
},
|
|
41
47
|
matchingString: string,
|
|
42
48
|
) => React.Portal | React.MixedElement | null;
|
|
43
49
|
|
|
44
|
-
const scrollIntoViewIfNeeded = (target: HTMLElement) => {
|
|
45
|
-
const container = document.getElementById('typeahead-menu');
|
|
46
|
-
if (container) {
|
|
47
|
-
const containerRect = container.getBoundingClientRect();
|
|
48
|
-
const targetRect = target.getBoundingClientRect();
|
|
49
|
-
if (targetRect.bottom > containerRect.bottom) {
|
|
50
|
-
target.scrollIntoView(false);
|
|
51
|
-
} else if (targetRect.top < containerRect.top) {
|
|
52
|
-
target.scrollIntoView();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
50
|
declare export function useBasicTypeaheadTriggerMatch(
|
|
58
51
|
trigger: string,
|
|
59
52
|
options: {minLength?: number, maxLength?: number},
|
|
60
53
|
): TriggerFn;
|
|
61
54
|
|
|
62
|
-
export type
|
|
55
|
+
export type TypeaheadMenuPluginProps<TOption> = {
|
|
63
56
|
onQueryChange: (matchingString: string | null) => void,
|
|
64
57
|
onSelectOption: (
|
|
65
58
|
option: TOption,
|
|
@@ -70,6 +63,9 @@ export type TypeaheadMenuPluginArgs<TOption> = {
|
|
|
70
63
|
options: Array<TOption>,
|
|
71
64
|
menuRenderFn: MenuRenderFn<TOption>,
|
|
72
65
|
triggerFn: TriggerFn,
|
|
66
|
+
onOpen?: (resolution: Resolution) => void,
|
|
67
|
+
onClose?: () => void,
|
|
68
|
+
anchorClassName?: string,
|
|
73
69
|
};
|
|
74
70
|
|
|
75
71
|
export type TriggerFn = (
|
|
@@ -78,10 +74,10 @@ export type TriggerFn = (
|
|
|
78
74
|
) => QueryMatch | null;
|
|
79
75
|
|
|
80
76
|
declare export function LexicalTypeaheadMenuPlugin<TOption>(
|
|
81
|
-
options:
|
|
77
|
+
options: TypeaheadMenuPluginProps<TOption>,
|
|
82
78
|
): React.MixedElement | null;
|
|
83
79
|
|
|
84
|
-
type
|
|
80
|
+
type NodeMenuPluginProps<TOption> = {
|
|
85
81
|
onSelectOption: (
|
|
86
82
|
option: TOption,
|
|
87
83
|
textNodeContainingQuery: TextNode | null,
|
|
@@ -90,10 +86,12 @@ type NodeMenuPluginArgs<TOption> = {
|
|
|
90
86
|
) => void,
|
|
91
87
|
options: Array<TOption>,
|
|
92
88
|
nodeKey: NodeKey | null,
|
|
93
|
-
onClose
|
|
89
|
+
onClose?: () => void,
|
|
90
|
+
onOpen?: (resolution: Resolution) => void,
|
|
94
91
|
menuRenderFn: MenuRenderFn<TOption>,
|
|
92
|
+
anchorClassName?: string,
|
|
95
93
|
};
|
|
96
94
|
|
|
97
95
|
declare export function LexicalNodeMenuPlugin<TOption>(
|
|
98
|
-
options:
|
|
96
|
+
options: NodeMenuPluginProps<TOption>,
|
|
99
97
|
): React.MixedElement | null;
|
|
@@ -4,19 +4,23 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var
|
|
8
|
-
let
|
|
9
|
-
function D(b){let a=null;b.getEditorState().read(()=>{var c=
|
|
10
|
-
function E(b,a){b=
|
|
11
|
-
function F(b,a){return 0!==a?!1:b.getEditorState().read(()=>{var c=
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
f
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
'use strict';var k=require("@lexical/react/LexicalComposerContext"),n=require("@lexical/utils"),w=require("lexical"),x=require("react"),z="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?x.useLayoutEffect:x.useEffect;class A{constructor(b){this.key=b;this.ref={current:null};this.setRefElement=this.setRefElement.bind(this)}setRefElement(b){this.ref={current:b}}}
|
|
8
|
+
let B=b=>{var a=document.getElementById("typeahead-menu");if(a){a=a.getBoundingClientRect();const c=b.getBoundingClientRect();c.bottom>a.bottom?b.scrollIntoView(!1):c.top<a.top&&b.scrollIntoView()}};function C(b,a){var c=window.getSelection();if(null===c||!c.isCollapsed)return!1;let e=c.anchorNode;c=c.anchorOffset;if(null==e||null==c)return!1;try{a.setStart(e,b),a.setEnd(e,c)}catch(f){return!1}return!0}
|
|
9
|
+
function D(b){let a=null;b.getEditorState().read(()=>{var c=w.$getSelection();if(w.$isRangeSelection(c)){var e=c.anchor;"text"!==e.type?a=null:(c=e.getNode(),c.isSimpleText()?(e=e.offset,a=c.getTextContent().slice(0,e)):a=null)}});return a}
|
|
10
|
+
function E(b,a){b=w.$getSelection();if(!w.$isRangeSelection(b)||!b.isCollapsed())return null;var c=b.anchor;if("text"!==c.type)return null;b=c.getNode();if(!b.isSimpleText())return null;c=c.offset;let e=b.getTextContent().slice(0,c);var f=a.matchingString;a=a.replaceableString.length;for(let p=a;p<=f.length;p++)e.substr(-p)===f.substr(0,p)&&(a=p);a=c-a;if(0>a)return null;let q;0===a?[q]=b.splitText(c):[,q]=b.splitText(a,c);return q}
|
|
11
|
+
function F(b,a){return 0!==a?!1:b.getEditorState().read(()=>{var c=w.$getSelection();return w.$isRangeSelection(c)?(c=c.anchor.getNode().getPreviousSibling(),w.$isTextNode(c)&&c.isTextEntity()):!1})}function G(b){x.startTransition?x.startTransition(b):b()}
|
|
12
|
+
function J(b,a){let c=getComputedStyle(b),e="absolute"===c.position;a=a?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===c.position)return document.body;for(;b=b.parentElement;)if(c=getComputedStyle(b),(!e||"static"!==c.position)&&a.test(c.overflow+c.overflowY+c.overflowX))return b;return document.body}function K(b,a){b=b.getBoundingClientRect();a=a.getBoundingClientRect();return b.top>a.top&&b.top<a.bottom}
|
|
13
|
+
function L(b,a,c,e){let [f]=k.useLexicalComposerContext();x.useEffect(()=>{if(null!=a&&null!=b){let q=f.getRootElement(),p=null!=q?J(q,!1):document.body,g=!1,d=K(a,p),m=function(){g||(window.requestAnimationFrame(function(){c();g=!1}),g=!0);const t=K(a,p);t!==d&&(d=t,null!=e&&e(t))},r=new ResizeObserver(c);window.addEventListener("resize",c);document.addEventListener("scroll",m,{capture:!0,passive:!0});r.observe(a);return()=>{r.unobserve(a);window.removeEventListener("resize",c);document.removeEventListener("scroll",
|
|
14
|
+
m)}}},[a,f,e,c,b])}let M=w.createCommand("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");
|
|
15
|
+
function N({close:b,editor:a,anchorElementRef:c,resolution:e,options:f,menuRenderFn:q,onSelectOption:p}){let [g,d]=x.useState(null);x.useEffect(()=>{d(0)},[e.match.matchingString]);let m=x.useCallback(h=>{a.update(()=>{const l=E(a,e.match);p(h,l,b,e.match.matchingString)})},[b,a,e.match,p]),r=x.useCallback(h=>{const l=a.getRootElement();null!==l&&(l.setAttribute("aria-activedescendant","typeahead-item-"+h),d(h))},[a]);x.useEffect(()=>()=>{let h=a.getRootElement();null!==h&&h.removeAttribute("aria-activedescendant")},
|
|
16
|
+
[a]);z(()=>{null===f?d(null):null===g&&r(0)},[f,g,r]);x.useEffect(()=>n.mergeRegister(a.registerCommand(M,({option:h})=>h.ref&&null!=h.ref.current?(B(h.ref.current),!0):!1,w.COMMAND_PRIORITY_LOW)),[a,r]);x.useEffect(()=>n.mergeRegister(a.registerCommand(w.KEY_ARROW_DOWN_COMMAND,h=>{if(null!==f&&f.length&&null!==g){let l=g!==f.length-1?g+1:0;r(l);let u=f[l];null!=u.ref&&u.ref.current&&a.dispatchCommand(M,{index:l,option:u});h.preventDefault();h.stopImmediatePropagation()}return!0},w.COMMAND_PRIORITY_CRITICAL),
|
|
17
|
+
a.registerCommand(w.KEY_ARROW_UP_COMMAND,h=>{if(null!==f&&f.length&&null!==g){var l=0!==g?g-1:f.length-1;r(l);l=f[l];null!=l.ref&&l.ref.current&&B(l.ref.current);h.preventDefault();h.stopImmediatePropagation()}return!0},w.COMMAND_PRIORITY_CRITICAL),a.registerCommand(w.KEY_ESCAPE_COMMAND,h=>{h.preventDefault();h.stopImmediatePropagation();b();return!0},w.COMMAND_PRIORITY_CRITICAL),a.registerCommand(w.KEY_TAB_COMMAND,h=>{if(null===f||null===g||null==f[g])return!1;h.preventDefault();h.stopImmediatePropagation();
|
|
18
|
+
m(f[g]);return!0},w.COMMAND_PRIORITY_CRITICAL),a.registerCommand(w.KEY_ENTER_COMMAND,h=>{if(null===f||null===g||null==f[g])return!1;null!==h&&(h.preventDefault(),h.stopImmediatePropagation());m(f[g]);return!0},w.COMMAND_PRIORITY_CRITICAL)),[m,b,a,f,g,r]);let t=x.useMemo(()=>({options:f,selectOptionAndCleanUp:m,selectedIndex:g,setHighlightedIndex:d}),[m,g,f]);return q(c,t,e.match.matchingString)}
|
|
19
|
+
function O(b,a,c){let [e]=k.useLexicalComposerContext(),f=x.useRef(document.createElement("div")),q=x.useCallback(()=>{const g=e.getRootElement(),d=f.current;if(null!==g&&null!==b){const {left:m,top:r,width:t,height:h}=b.getRect();d.style.top=`${r+window.pageYOffset}px`;d.style.left=`${m+window.pageXOffset}px`;d.style.height=`${h}px`;d.style.width=`${t}px`;d.isConnected||(null!=c&&(d.className=c),d.setAttribute("aria-label","Typeahead menu"),d.setAttribute("id","typeahead-menu"),d.setAttribute("role",
|
|
20
|
+
"listbox"),d.style.display="block",d.style.position="absolute",document.body.append(d));f.current=d;g.setAttribute("aria-controls","typeahead-menu")}},[e,b,c]);x.useEffect(()=>{let g=e.getRootElement();if(null!==b)return q(),()=>{null!==g&&g.removeAttribute("aria-controls");let d=f.current;null!==d&&d.isConnected&&d.remove()}},[e,q,b]);let p=x.useCallback(g=>{null!==b&&(g||a(null))},[b,a]);L(b,f.current,q,p);return f}
|
|
21
|
+
exports.LexicalNodeMenuPlugin=function({options:b,nodeKey:a,onClose:c,onOpen:e,onSelectOption:f,menuRenderFn:q,anchorClassName:p}){let [g]=k.useLexicalComposerContext(),[d,m]=x.useState(null);p=O(d,m,p);let r=x.useCallback(()=>{m(null);null!=c&&null!==d&&c()},[c,d]),t=x.useCallback(h=>{m(h);null!=e&&null===d&&e(h)},[e,d]);x.useEffect(()=>{a&&null==d?g.update(()=>{let h=w.$getNodeByKey(a),l=g.getElementByKey(a);if(null!=h&&null!=l){let u=h.getTextContent();G(()=>t({getRect:()=>l.getBoundingClientRect(),
|
|
22
|
+
match:{leadOffset:u.length,matchingString:u,replaceableString:u}}))}}):null==a&&null!=d&&r()},[r,g,a,t,d]);return null===d||null===g?null:x.createElement(N,{close:r,resolution:d,editor:g,anchorElementRef:p,options:b,menuRenderFn:q,onSelectOption:f})};
|
|
23
|
+
exports.LexicalTypeaheadMenuPlugin=function({options:b,onQueryChange:a,onSelectOption:c,onOpen:e,onClose:f,menuRenderFn:q,triggerFn:p,anchorClassName:g}){let [d]=k.useLexicalComposerContext(),[m,r]=x.useState(null);g=O(m,r,g);let t=x.useCallback(()=>{r(null);null!=f&&null!==m&&f()},[f,m]),h=x.useCallback(l=>{r(l);null!=e&&null===m&&e(l)},[e,m]);x.useEffect(()=>{let l=document.createRange(),u=d.registerUpdateListener(()=>{d.getEditorState().read(()=>{const y=l,H=w.$getSelection(),I=D(d);if(w.$isRangeSelection(H)&&
|
|
24
|
+
H.isCollapsed()&&null!==I&&null!==y){var v=p(I,d);a(v?v.matchingString:null);null===v||F(d,v.leadOffset)||null===C(v.leadOffset,y)?t():G(()=>h({getRect:()=>y.getBoundingClientRect(),match:v}))}else t()})});return()=>{l=null;u()}},[d,p,a,m,t,h]);return null===m||null===d?null:x.createElement(N,{close:t,resolution:m,editor:d,anchorElementRef:g,options:b,menuRenderFn:q,onSelectOption:c})};exports.PUNCTUATION="\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";
|
|
25
|
+
exports.SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND=M;exports.TypeaheadOption=A;exports.getScrollParent=J;exports.useBasicTypeaheadTriggerMatch=function(b,{minLength:a=1,maxLength:c=75}){return x.useCallback(e=>{e=(new RegExp("(^|\\s|\\()(["+b+"]((?:[^"+(b+"\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;\\s]){0,")+c+"}))$")).exec(e);if(null!==e){let f=e[1],q=e[3];if(q.length>=a)return{leadOffset:e.index+f.length,matchingString:q,replaceableString:e[2]}}return null},[c,a,b])};
|
|
26
|
+
exports.useDynamicPositioning=L
|
package/README.md
CHANGED
|
@@ -73,11 +73,11 @@ function Editor() {
|
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
75
|
<LexicalComposer initialConfig={initialConfig}>
|
|
76
|
-
<
|
|
77
|
-
contentEditable={<
|
|
76
|
+
<PlainTextPlugin
|
|
77
|
+
contentEditable={<ContentEditable />}
|
|
78
78
|
placeholder={<div>Enter some text...</div>}
|
|
79
79
|
/>
|
|
80
|
-
<
|
|
80
|
+
<OnChangePlugin onChange={onChange} />
|
|
81
81
|
<HistoryPlugin />
|
|
82
82
|
<MyCustomAutoFocusPlugin />
|
|
83
83
|
</LexicalComposer>
|
package/package.json
CHANGED
|
@@ -8,28 +8,28 @@
|
|
|
8
8
|
"rich-text"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.5.
|
|
11
|
+
"version": "0.5.1-next.1",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@lexical/clipboard": "0.5.
|
|
14
|
-
"@lexical/code": "0.5.
|
|
15
|
-
"@lexical/dragon": "0.5.
|
|
16
|
-
"@lexical/hashtag": "0.5.
|
|
17
|
-
"@lexical/history": "0.5.
|
|
18
|
-
"@lexical/link": "0.5.
|
|
19
|
-
"@lexical/list": "0.5.
|
|
20
|
-
"@lexical/mark": "0.5.
|
|
21
|
-
"@lexical/markdown": "0.5.
|
|
22
|
-
"@lexical/overflow": "0.5.
|
|
23
|
-
"@lexical/plain-text": "0.5.
|
|
24
|
-
"@lexical/rich-text": "0.5.
|
|
25
|
-
"@lexical/selection": "0.5.
|
|
26
|
-
"@lexical/table": "0.5.
|
|
27
|
-
"@lexical/text": "0.5.
|
|
28
|
-
"@lexical/utils": "0.5.
|
|
29
|
-
"@lexical/yjs": "0.5.
|
|
13
|
+
"@lexical/clipboard": "0.5.1-next.1",
|
|
14
|
+
"@lexical/code": "0.5.1-next.1",
|
|
15
|
+
"@lexical/dragon": "0.5.1-next.1",
|
|
16
|
+
"@lexical/hashtag": "0.5.1-next.1",
|
|
17
|
+
"@lexical/history": "0.5.1-next.1",
|
|
18
|
+
"@lexical/link": "0.5.1-next.1",
|
|
19
|
+
"@lexical/list": "0.5.1-next.1",
|
|
20
|
+
"@lexical/mark": "0.5.1-next.1",
|
|
21
|
+
"@lexical/markdown": "0.5.1-next.1",
|
|
22
|
+
"@lexical/overflow": "0.5.1-next.1",
|
|
23
|
+
"@lexical/plain-text": "0.5.1-next.1",
|
|
24
|
+
"@lexical/rich-text": "0.5.1-next.1",
|
|
25
|
+
"@lexical/selection": "0.5.1-next.1",
|
|
26
|
+
"@lexical/table": "0.5.1-next.1",
|
|
27
|
+
"@lexical/text": "0.5.1-next.1",
|
|
28
|
+
"@lexical/utils": "0.5.1-next.1",
|
|
29
|
+
"@lexical/yjs": "0.5.1-next.1"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"lexical": "0.5.
|
|
32
|
+
"lexical": "0.5.1-next.1",
|
|
33
33
|
"react": ">=17.x",
|
|
34
34
|
"react-dom": ">=17.x"
|
|
35
35
|
},
|