@tarviks/lexical-rich-editor 1.0.1 → 1.0.2

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/index.mjs CHANGED
@@ -7,7 +7,7 @@ import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
7
7
  import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
8
8
  import { createCommand, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, SELECTION_CHANGE_COMMAND, $getNodeByKey, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $getSelection, $isRangeSelection, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $insertNodes, $isRootOrShadowRoot, $createParagraphNode, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getRoot, $createTextNode, $isTextNode, $createRangeSelection, $setSelection, $isElementNode, $isNodeSelection, CLICK_COMMAND, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, FORMAT_ELEMENT_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, createEditor, KEY_ENTER_COMMAND } from 'lexical';
9
9
  import { mergeStyleSets, Stack, css, useTheme, Callout, TextField, DefaultButton } from '@fluentui/react';
10
- import { makeStyles, FluentProvider, webLightTheme, Button, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, ToolbarDivider, Popover, PopoverTrigger, PopoverSurface, Field, Input } from '@fluentui/react-components';
10
+ import { makeStyles, FluentProvider, webLightTheme, Button, Menu, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, ToolbarDivider, Popover, PopoverTrigger, PopoverSurface, Field, Input } from '@fluentui/react-components';
11
11
  import { CodeHighlightNode, CodeNode, $isCodeHighlightNode } from '@lexical/code';
12
12
  import { LinkNode, AutoLinkNode, $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND, $createLinkNode } from '@lexical/link';
13
13
  import { ListNode, ListItemNode, $isListNode, REMOVE_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
@@ -25,7 +25,7 @@ import { HeadingNode, QuoteNode, $isHeadingNode, $createQuoteNode, $createHeadin
25
25
  import { TableNode, TableRowNode, TableCellNode, $isTableSelection, $isTableNode, $isTableCellNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $getTableNodeFromLexicalNodeOrThrow, getDOMCellFromTarget, getTableObserverFromTableElement, $getTableRowIndexFromTableCellNode, $isTableRowNode, $getTableColumnIndexFromTableCellNode, $createTableNodeWithDimensions } from '@lexical/table';
26
26
  import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents';
27
27
  import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
28
- import { DeleteRegular, ChevronDown12Regular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, TextCaseUppercaseFilled, TextCaseLowercaseFilled, TextCaseTitleFilled, TextStrikethroughFilled, TextSubscriptFilled, TextSuperscriptFilled, HighlightAccentFilled, TextBulletListLtrFilled, TextNumberListLtrFilled, DocumentPageBreakRegular, CommentQuoteRegular, TextUnderlineFilled, TextItalicFilled, TextBold24Regular, TextAlignLeftFilled, TextAlignCenterFilled, TextAlignRightFilled, TextAlignJustifyFilled, VideoClipRegular, ImageEditRegular, AttachFilled, ImageAddRegular, TableAddRegular, LinkAddRegular, TextColorRegular, PaintBucket16Filled, CodeFilled, LinkFilled } from '@fluentui/react-icons';
28
+ import { DeleteRegular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, TextCaseUppercaseFilled, TextCaseLowercaseFilled, TextCaseTitleFilled, TextStrikethroughFilled, TextSubscriptFilled, TextSuperscriptFilled, HighlightAccentFilled, TextBulletListLtrFilled, TextNumberListLtrFilled, DocumentPageBreakRegular, CommentQuoteRegular, TextUnderlineFilled, TextItalicFilled, TextBold24Regular, TextAlignLeftFilled, TextAlignCenterFilled, TextAlignRightFilled, TextAlignJustifyFilled, VideoClipRegular, ImageEditRegular, AttachFilled, ImageAddRegular, TableAddRegular, LinkAddRegular, TextColorRegular, PaintBucket16Filled, CodeFilled, LinkFilled } from '@fluentui/react-icons';
29
29
  import { $setBlocksType, $patchStyleText, $isAtNodeEnd, $getSelectionStyleValueForProperty } from '@lexical/selection';
30
30
  import { createPortal } from 'react-dom';
31
31
  import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
@@ -2442,6 +2442,8 @@ function CharacterStylesPopupPlugin(props) {
2442
2442
  var CustomOnChangePlugin = ({ value, onChange }) => {
2443
2443
  const [editor] = useLexicalComposerContext();
2444
2444
  const initializedRef = useRef(false);
2445
+ const onChangeRef = useRef(onChange);
2446
+ onChangeRef.current = onChange;
2445
2447
  useEffect(() => {
2446
2448
  if (!value || initializedRef.current) return;
2447
2449
  initializedRef.current = true;
@@ -2452,16 +2454,19 @@ var CustomOnChangePlugin = ({ value, onChange }) => {
2452
2454
  const dom = parser.parseFromString(value, "text/html");
2453
2455
  const nodes = $generateNodesFromDOM(editor, dom);
2454
2456
  root.append(...nodes);
2457
+ $setSelection(null);
2455
2458
  });
2456
2459
  }, [editor, value]);
2460
+ const handleChange = useCallback((editorState) => {
2461
+ editorState.read(() => {
2462
+ onChangeRef.current($generateHtmlFromNodes(editor));
2463
+ });
2464
+ }, [editor]);
2457
2465
  return /* @__PURE__ */ jsx(
2458
2466
  OnChangePlugin,
2459
2467
  {
2460
- onChange: (editorState) => {
2461
- editorState.read(() => {
2462
- onChange($generateHtmlFromNodes(editor));
2463
- });
2464
- }
2468
+ onChange: handleChange,
2469
+ ignoreSelectionChange: true
2465
2470
  }
2466
2471
  );
2467
2472
  };
@@ -4273,8 +4278,8 @@ function SpellCheckPlugin({
4273
4278
  function TableActionMenuPlugin({ disabled = false }) {
4274
4279
  const [editor] = useLexicalComposerContext();
4275
4280
  const [isInTable, setIsInTable] = React6.useState(false);
4276
- const [anchorRect, setAnchorRect] = React6.useState(null);
4277
4281
  const [open, setOpen] = React6.useState(false);
4282
+ const [menuPos, setMenuPos] = React6.useState(null);
4278
4283
  const updateFromSelection = React6.useCallback(() => {
4279
4284
  const root = editor.getRootElement();
4280
4285
  if (!root) return;
@@ -4283,34 +4288,21 @@ function TableActionMenuPlugin({ disabled = false }) {
4283
4288
  if ($isTableSelection(selection)) {
4284
4289
  const tableNode = selection.getNodes().find((n) => $isTableNode(n));
4285
4290
  if (tableNode) {
4286
- const dom = editor.getElementByKey(tableNode.getKey());
4287
- if (dom) {
4288
- setIsInTable(true);
4289
- setAnchorRect(dom.getBoundingClientRect());
4290
- return;
4291
- }
4291
+ setIsInTable(true);
4292
+ return;
4292
4293
  }
4293
4294
  }
4294
4295
  if (!$isRangeSelection(selection)) {
4295
4296
  setIsInTable(false);
4296
- setAnchorRect(null);
4297
4297
  return;
4298
4298
  }
4299
4299
  const anchorNode = selection.anchor.getNode();
4300
4300
  const cellNode = $isTableCellNode(anchorNode) ? anchorNode : $findMatchingParent(anchorNode, (n) => $isTableCellNode(n));
4301
4301
  if (!cellNode || !$isTableCellNode(cellNode)) {
4302
4302
  setIsInTable(false);
4303
- setAnchorRect(null);
4304
- return;
4305
- }
4306
- const cellDom = editor.getElementByKey(cellNode.getKey());
4307
- if (!cellDom) {
4308
- setIsInTable(false);
4309
- setAnchorRect(null);
4310
4303
  return;
4311
4304
  }
4312
4305
  setIsInTable(true);
4313
- setAnchorRect(cellDom.getBoundingClientRect());
4314
4306
  });
4315
4307
  }, [editor]);
4316
4308
  React6.useEffect(() => {
@@ -4372,18 +4364,33 @@ function TableActionMenuPlugin({ disabled = false }) {
4372
4364
  React6.useEffect(() => {
4373
4365
  if (!isInTable && open) setOpen(false);
4374
4366
  }, [isInTable, open]);
4375
- const canShow = isInTable && !!anchorRect && !disabled;
4376
- const handleStyle = React6.useMemo(() => {
4377
- if (!anchorRect) return void 0;
4378
- const top = Math.max(8, anchorRect.top + 6);
4379
- const left = Math.max(8, anchorRect.right - 34);
4380
- return {
4381
- position: "fixed",
4382
- top,
4383
- left,
4384
- zIndex: 9999
4367
+ React6.useEffect(() => {
4368
+ const root = editor.getRootElement();
4369
+ if (!root) return;
4370
+ const handleContextMenu = (e) => {
4371
+ if (disabled) return;
4372
+ let inTable = false;
4373
+ editor.getEditorState().read(() => {
4374
+ const selection = $getSelection();
4375
+ if ($isTableSelection(selection)) {
4376
+ inTable = true;
4377
+ return;
4378
+ }
4379
+ if ($isRangeSelection(selection)) {
4380
+ const node = selection.anchor.getNode();
4381
+ const cell = $isTableCellNode(node) ? node : $findMatchingParent(node, (n) => $isTableCellNode(n));
4382
+ if (cell) inTable = true;
4383
+ }
4384
+ });
4385
+ if (inTable) {
4386
+ e.preventDefault();
4387
+ setMenuPos({ x: e.clientX, y: e.clientY });
4388
+ setOpen(true);
4389
+ }
4385
4390
  };
4386
- }, [anchorRect]);
4391
+ root.addEventListener("contextmenu", handleContextMenu);
4392
+ return () => root.removeEventListener("contextmenu", handleContextMenu);
4393
+ }, [editor, disabled]);
4387
4394
  const dangerStyle = {
4388
4395
  color: "var(--colorPaletteRedForeground1)"
4389
4396
  };
@@ -4416,63 +4423,55 @@ function TableActionMenuPlugin({ disabled = false }) {
4416
4423
  const table = $getTableNodeFromLexicalNodeOrThrow(cell);
4417
4424
  table.remove();
4418
4425
  });
4419
- if (!canShow || !handleStyle) return null;
4426
+ const virtualTarget = React6.useMemo(() => {
4427
+ if (!menuPos) return void 0;
4428
+ return {
4429
+ getBoundingClientRect: () => new DOMRect(menuPos.x, menuPos.y, 0, 0)
4430
+ };
4431
+ }, [menuPos]);
4432
+ if (disabled) return null;
4420
4433
  return createPortal(
4421
- /* @__PURE__ */ jsx("div", { style: handleStyle, className: "aoTableActionHandleRoot", children: /* @__PURE__ */ jsxs(Menu, { open, onOpenChange: (_, data) => setOpen(data.open), children: [
4422
- /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
4423
- "button",
4424
- {
4425
- type: "button",
4426
- className: "aoTableActionHandleBtn",
4427
- "aria-label": "Table options",
4428
- onMouseDown: (e) => {
4429
- e.preventDefault();
4430
- },
4431
- children: /* @__PURE__ */ jsx(ChevronDown12Regular, {})
4432
- }
4433
- ) }),
4434
- /* @__PURE__ */ jsx(MenuPopover, { className: "aoTableActionPopover", children: /* @__PURE__ */ jsxs(MenuList, { children: [
4435
- /* @__PURE__ */ jsxs(MenuGroup, { children: [
4436
- /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Insert" }),
4437
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(RowTripleRegular, {}), onClick: insertRowAbove, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4438
- /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4439
- /* @__PURE__ */ jsx(ArrowUpRegular, {}),
4440
- " Row above"
4441
- ] }),
4442
- /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2191" })
4443
- ] }) }),
4444
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(RowTripleRegular, {}), onClick: insertRowBelow, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4445
- /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4446
- /* @__PURE__ */ jsx(ArrowDownRegular, {}),
4447
- " Row below"
4448
- ] }),
4449
- /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2193" })
4450
- ] }) }),
4451
- /* @__PURE__ */ jsx(MenuDivider, {}),
4452
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(ColumnTripleRegular, {}), onClick: insertColLeft, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4453
- /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4454
- /* @__PURE__ */ jsx(ArrowLeftRegular, {}),
4455
- " Column left"
4456
- ] }),
4457
- /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2190" })
4458
- ] }) }),
4459
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(ColumnTripleRegular, {}), onClick: insertColRight, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4460
- /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4461
- /* @__PURE__ */ jsx(ArrowRightRegular, {}),
4462
- " Column right"
4463
- ] }),
4464
- /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2192" })
4465
- ] }) })
4466
- ] }),
4434
+ /* @__PURE__ */ jsx(Menu, { open, onOpenChange: (_, data) => setOpen(data.open), positioning: { target: virtualTarget }, children: /* @__PURE__ */ jsx(MenuPopover, { className: "aoTableActionPopover", children: /* @__PURE__ */ jsxs(MenuList, { children: [
4435
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
4436
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Insert" }),
4437
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(RowTripleRegular, {}), onClick: insertRowAbove, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4438
+ /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4439
+ /* @__PURE__ */ jsx(ArrowUpRegular, {}),
4440
+ " Row above"
4441
+ ] }),
4442
+ /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2191" })
4443
+ ] }) }),
4444
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(RowTripleRegular, {}), onClick: insertRowBelow, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4445
+ /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4446
+ /* @__PURE__ */ jsx(ArrowDownRegular, {}),
4447
+ " Row below"
4448
+ ] }),
4449
+ /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2193" })
4450
+ ] }) }),
4467
4451
  /* @__PURE__ */ jsx(MenuDivider, {}),
4468
- /* @__PURE__ */ jsxs(MenuGroup, { children: [
4469
- /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Delete" }),
4470
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteRow, style: dangerStyle, children: "Delete row" }),
4471
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteCol, style: dangerStyle, children: "Delete column" }),
4472
- /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteTable, style: dangerStyle, children: "Delete table" })
4473
- ] })
4474
- ] }) })
4475
- ] }) }),
4452
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(ColumnTripleRegular, {}), onClick: insertColLeft, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4453
+ /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4454
+ /* @__PURE__ */ jsx(ArrowLeftRegular, {}),
4455
+ " Column left"
4456
+ ] }),
4457
+ /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2190" })
4458
+ ] }) }),
4459
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(ColumnTripleRegular, {}), onClick: insertColRight, children: /* @__PURE__ */ jsxs("span", { className: "aoMenuRow", children: [
4460
+ /* @__PURE__ */ jsxs("span", { className: "aoMenuLabel", children: [
4461
+ /* @__PURE__ */ jsx(ArrowRightRegular, {}),
4462
+ " Column right"
4463
+ ] }),
4464
+ /* @__PURE__ */ jsx("span", { className: "aoMenuShortcut", children: "Alt \u21E7 \u2192" })
4465
+ ] }) })
4466
+ ] }),
4467
+ /* @__PURE__ */ jsx(MenuDivider, {}),
4468
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
4469
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Delete" }),
4470
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteRow, style: dangerStyle, children: "Delete row" }),
4471
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteCol, style: dangerStyle, children: "Delete column" }),
4472
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(DeleteRegular, {}), onClick: deleteTable, style: dangerStyle, children: "Delete table" })
4473
+ ] })
4474
+ ] }) }) }),
4476
4475
  document.body
4477
4476
  );
4478
4477
  }
@@ -6465,6 +6464,9 @@ function FocusEventsPlugin({
6465
6464
  const next = e.relatedTarget;
6466
6465
  const stillInside = !!next && root.contains(next);
6467
6466
  if (stillInside) return;
6467
+ editor.update(() => {
6468
+ $setSelection(null);
6469
+ });
6468
6470
  setFocused(false);
6469
6471
  onBlur?.();
6470
6472
  };
@@ -6835,7 +6837,13 @@ var ContentEditorComponent = forwardRef(
6835
6837
  }
6836
6838
  ),
6837
6839
  !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsx(CharacterStylesPopupPlugin, {}),
6838
- /* @__PURE__ */ jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
6840
+ /* @__PURE__ */ jsx(
6841
+ CustomOnChangePlugin,
6842
+ {
6843
+ value: props.value,
6844
+ onChange: props.onChange
6845
+ }
6846
+ ),
6839
6847
  props.wordLimit !== void 0 && /* @__PURE__ */ jsx(WordCountPlugin, { onCountChange: handleWordCount }),
6840
6848
  /* @__PURE__ */ jsx(
6841
6849
  RefApiPlugin,