@tarviks/lexical-rich-editor 1.3.12 → 1.3.14

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
@@ -5,7 +5,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
6
6
  import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
7
7
  import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
8
- import { createCommand, $isTextNode, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $getSelection, $isRangeSelection, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $getNodeByKey, $createRangeSelection, $setSelection, $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getRoot, $createTextNode, $isElementNode, FORMAT_ELEMENT_COMMAND, CLICK_COMMAND, $isNodeSelection, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, $isLineBreakNode, SKIP_SELECTION_FOCUS_TAG, createEditor, KEY_ENTER_COMMAND } from 'lexical';
8
+ import { createCommand, $isTextNode, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $getSelection, $isRangeSelection, KEY_DOWN_COMMAND, $isNodeSelection, $findMatchingParent, COMMAND_PRIORITY_LOW, $getNodeByKey, $createRangeSelection, $setSelection, $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getRoot, $createTextNode, $isElementNode, FORMAT_ELEMENT_COMMAND, CLICK_COMMAND, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, $isLineBreakNode, SKIP_SELECTION_FOCUS_TAG, createEditor, KEY_ENTER_COMMAND } from 'lexical';
9
9
  import { mergeStyleSets, Stack, css, useTheme, Callout, TextField } from '@fluentui/react';
10
10
  import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, Button, ToolbarDivider, MenuItemRadio, Field, Input, MessageBar, MessageBarBody, Popover, PopoverSurface, PopoverTrigger } from '@fluentui/react-components';
11
11
  import { CodeHighlightNode, CodeNode, $isCodeHighlightNode } from '@lexical/code';
@@ -3198,12 +3198,16 @@ function splitHeadingsAtBrSequences(html) {
3198
3198
  });
3199
3199
  return doc.body.innerHTML;
3200
3200
  }
3201
- var CustomOnChangePlugin = ({ value, onChange }) => {
3201
+ var CustomOnChangePlugin = ({ value, onChange, isReadOnly }) => {
3202
3202
  const [editor] = useLexicalComposerContext();
3203
3203
  const initializedRef = useRef(false);
3204
+ const lastImportedValueRef = useRef(null);
3204
3205
  useEffect(() => {
3205
- if (!value || initializedRef.current) return;
3206
+ if (!value) return;
3207
+ if (!isReadOnly && initializedRef.current) return;
3208
+ if (value === lastImportedValueRef.current) return;
3206
3209
  initializedRef.current = true;
3210
+ lastImportedValueRef.current = value;
3207
3211
  editor.update(() => {
3208
3212
  const root = $getRoot();
3209
3213
  root.clear();
@@ -3214,14 +3218,17 @@ var CustomOnChangePlugin = ({ value, onChange }) => {
3214
3218
  const nodes = $generateNodesFromDOM(editor, dom);
3215
3219
  root.append(...nodes);
3216
3220
  });
3217
- }, [editor, value]);
3221
+ }, [editor, value, isReadOnly]);
3218
3222
  return /* @__PURE__ */ jsx(
3219
3223
  OnChangePlugin,
3220
3224
  {
3225
+ ignoreSelectionChange: true,
3221
3226
  onChange: (editorState) => {
3222
3227
  editorState.read(() => {
3223
3228
  const raw = $generateHtmlFromNodes(editor);
3224
- onChange(postProcessOutput(splitHeadingsAtBrSequences(raw)));
3229
+ const html = postProcessOutput(splitHeadingsAtBrSequences(raw));
3230
+ lastImportedValueRef.current = html;
3231
+ onChange(html);
3225
3232
  });
3226
3233
  }
3227
3234
  }
@@ -4547,6 +4554,44 @@ function RefApiPlugin({
4547
4554
  );
4548
4555
  return null;
4549
4556
  }
4557
+ function RobustCutPlugin() {
4558
+ const [editor] = useLexicalComposerContext();
4559
+ useEffect(() => {
4560
+ return editor.registerCommand(
4561
+ KEY_DOWN_COMMAND,
4562
+ (event) => {
4563
+ const isShiftDelete = event.key === "Delete" && event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey;
4564
+ if (!isShiftDelete) return false;
4565
+ let plainText = null;
4566
+ editor.getEditorState().read(() => {
4567
+ const selection = $getSelection();
4568
+ if ($isRangeSelection(selection) && !selection.isCollapsed()) {
4569
+ plainText = selection.getTextContent();
4570
+ } else if ($isNodeSelection(selection) && selection.getNodes().length > 0) {
4571
+ plainText = "";
4572
+ }
4573
+ });
4574
+ if (plainText === null) return false;
4575
+ event.preventDefault();
4576
+ editor.update(() => {
4577
+ const selection = $getSelection();
4578
+ if ($isRangeSelection(selection)) {
4579
+ selection.removeText();
4580
+ } else if ($isNodeSelection(selection)) {
4581
+ selection.getNodes().forEach((node) => node.remove());
4582
+ }
4583
+ });
4584
+ if (plainText && navigator.clipboard?.writeText) {
4585
+ navigator.clipboard.writeText(plainText).catch(() => {
4586
+ });
4587
+ }
4588
+ return true;
4589
+ },
4590
+ COMMAND_PRIORITY_HIGH
4591
+ );
4592
+ }, [editor]);
4593
+ return null;
4594
+ }
4550
4595
  var CSS_ID = "__sc_styles__";
4551
4596
  function injectCSS() {
4552
4597
  if (document.getElementById(CSS_ID)) return;
@@ -5354,9 +5399,25 @@ function TableActionMenuPlugin({ disabled = false }) {
5354
5399
  const canShow = isInTable && !!anchorRect && isAnchorVisible && !disabled;
5355
5400
  const handleStyle = React9.useMemo(() => {
5356
5401
  if (!anchorRect) return void 0;
5357
- const top = Math.max(8, anchorRect.top + 6);
5358
- const clampedCellRight = Math.min(anchorRect.right, window.innerWidth - 8);
5359
- const left = contentRight !== null ? Math.max(anchorRect.left + 4, Math.min(contentRight + 4, clampedCellRight - 32)) : Math.max(8, anchorRect.left + 8);
5402
+ const BUTTON_SIZE = 28;
5403
+ const PAD = 8;
5404
+ const editorRect = editor.getRootElement()?.getBoundingClientRect();
5405
+ const clipRect = editorRect ? {
5406
+ top: Math.max(editorRect.top, 0),
5407
+ left: Math.max(editorRect.left, 0),
5408
+ right: Math.min(editorRect.right, window.innerWidth),
5409
+ bottom: Math.min(editorRect.bottom, window.innerHeight)
5410
+ } : { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight };
5411
+ const isCellVisible = anchorRect.bottom > clipRect.top && anchorRect.top < clipRect.bottom && anchorRect.right > clipRect.left && anchorRect.left < clipRect.right;
5412
+ if (!isCellVisible) return void 0;
5413
+ const minTop = editorRect ? editorRect.top + PAD : PAD;
5414
+ const maxTop = editorRect ? Math.max(minTop, editorRect.bottom - BUTTON_SIZE - PAD) : window.innerHeight - BUTTON_SIZE - PAD;
5415
+ const minLeft = editorRect ? editorRect.left + PAD : PAD;
5416
+ const maxLeft = editorRect ? Math.max(minLeft, editorRect.right - BUTTON_SIZE - PAD) : window.innerWidth - BUTTON_SIZE - PAD;
5417
+ const top = Math.min(Math.max(anchorRect.top + 6, minTop), maxTop);
5418
+ const clampedCellRight = Math.min(anchorRect.right, maxLeft + BUTTON_SIZE);
5419
+ const rawLeft = contentRight !== null ? Math.max(anchorRect.left + 4, Math.min(contentRight + 4, clampedCellRight - 32)) : anchorRect.left + 8;
5420
+ const left = Math.min(Math.max(rawLeft, minLeft), maxLeft);
5360
5421
  return {
5361
5422
  position: "fixed",
5362
5423
  top,
@@ -5366,7 +5427,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5366
5427
  // button renders behind the modal chrome in a full-page/Panel view.
5367
5428
  zIndex: 10000010
5368
5429
  };
5369
- }, [anchorRect, contentRight]);
5430
+ }, [anchorRect, contentRight, editor]);
5370
5431
  const dangerStyle = {
5371
5432
  color: "var(--colorPaletteRedForeground1)"
5372
5433
  };
@@ -8606,6 +8667,7 @@ var ContentEditorComponent = forwardRef(
8606
8667
  /* @__PURE__ */ jsx(AutoLinkPlugin, { matchers: MATCHERS }),
8607
8668
  /* @__PURE__ */ jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
8608
8669
  !isReadOnly && /* @__PURE__ */ jsx(YoutubeDeletePlugin, {}),
8670
+ !isReadOnly && /* @__PURE__ */ jsx(RobustCutPlugin, {}),
8609
8671
  !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableActionMenuPlugin, {}),
8610
8672
  !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
8611
8673
  !isReadOnly && /* @__PURE__ */ jsx(
@@ -8641,7 +8703,14 @@ var ContentEditorComponent = forwardRef(
8641
8703
  }
8642
8704
  ),
8643
8705
  !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsx(CharacterStylesPopupPlugin, {}),
8644
- /* @__PURE__ */ jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
8706
+ /* @__PURE__ */ jsx(
8707
+ CustomOnChangePlugin,
8708
+ {
8709
+ value: props.value,
8710
+ onChange: props.onChange,
8711
+ isReadOnly
8712
+ }
8713
+ ),
8645
8714
  (props.wordLimit !== void 0 || props.required || props.minWords !== void 0 || props.maxWords !== void 0 || props.minChars !== void 0 || props.maxChars !== void 0 || props.noImages || props.maxImages !== void 0 || props.noLinks || props.maxLinks !== void 0 || props.noTables) && /* @__PURE__ */ jsx(ContentMetricsPlugin, { onMetricsChange: handleMetrics }),
8646
8715
  /* @__PURE__ */ jsx(
8647
8716
  RefApiPlugin,