@tarviks/lexical-rich-editor 1.3.5 → 1.3.6

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, 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, $isTextNode, $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';
9
9
  import { mergeStyleSets, Stack, css, useTheme, Callout, TextField } from '@fluentui/react';
10
- import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, Button, ToolbarDivider, MenuItemRadio, Field, Input, MessageBar, MessageBarBody, Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-components';
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';
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, $removeList, $insertList, $isListItemNode } from '@lexical/list';
@@ -3143,33 +3143,6 @@ var getSelectedNode2 = (selection) => {
3143
3143
  return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
3144
3144
  }
3145
3145
  };
3146
- var VERTICAL_GAP = 10;
3147
- var HORIZONTAL_OFFSET = 5;
3148
- var VIEWPORT_MARGIN = 8;
3149
- var setFloatingElemPositionForLinkEditor = (targetRect, floatingElem, topBoundary = VIEWPORT_MARGIN, verticalGap = VERTICAL_GAP, horizontalOffset = HORIZONTAL_OFFSET) => {
3150
- if (targetRect === null) {
3151
- floatingElem.style.opacity = "0";
3152
- floatingElem.style.transform = "translate(-10000px, -10000px)";
3153
- return;
3154
- }
3155
- const floatingElemRect = floatingElem.getBoundingClientRect();
3156
- let top = targetRect.bottom + verticalGap;
3157
- let left = targetRect.left - horizontalOffset;
3158
- if (top + floatingElemRect.height > window.innerHeight - VIEWPORT_MARGIN) {
3159
- top = targetRect.top - floatingElemRect.height - verticalGap;
3160
- }
3161
- if (top < topBoundary) {
3162
- top = topBoundary;
3163
- }
3164
- left = Math.max(VIEWPORT_MARGIN, Math.min(left, window.innerWidth - floatingElemRect.width - VIEWPORT_MARGIN));
3165
- const origin = getFixedPositionOrigin(floatingElem);
3166
- top -= origin.top;
3167
- left -= origin.left;
3168
- floatingElem.style.opacity = "1";
3169
- floatingElem.style.transform = "none";
3170
- floatingElem.style.top = `${top}px`;
3171
- floatingElem.style.left = `${left}px`;
3172
- };
3173
3146
  var SUPPORTED_URL_PROTOCOLS = /* @__PURE__ */ new Set([
3174
3147
  "http:",
3175
3148
  "https:",
@@ -3195,7 +3168,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, isLinkEditMode, setIsLink
3195
3168
  const [editedLinkUrl, setEditedLinkUrl] = useState("https://");
3196
3169
  const [lastSelection, setLastSelection] = useState(null);
3197
3170
  const [linkUrl, setLinkUrl] = useState("");
3198
- const editorRef = useRef(null);
3171
+ const [target, setTarget] = useState(null);
3199
3172
  const inputRef = useRef(null);
3200
3173
  const $updateLinkEditor = useCallback(() => {
3201
3174
  const selection = $getSelection();
@@ -3213,25 +3186,17 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, isLinkEditMode, setIsLink
3213
3186
  setEditedLinkUrl(linkUrl);
3214
3187
  }
3215
3188
  }
3216
- const editorElem = editorRef.current;
3217
3189
  const nativeSelection = getDOMSelection(editor._window);
3218
3190
  const activeElement = document.activeElement;
3219
- if (editorElem === null) {
3220
- return;
3221
- }
3222
3191
  const rootElement = editor.getRootElement();
3223
3192
  if (isLink && selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) {
3224
3193
  const domRect = nativeSelection.focusNode?.parentElement?.getBoundingClientRect();
3225
3194
  if (domRect) {
3226
- const toolbarEl = rootElement.closest(".lexical-rich-editor-root")?.querySelector(".editor-toolbar-root");
3227
- const topBoundary = toolbarEl ? toolbarEl.getBoundingClientRect().bottom + 8 : 8;
3228
- setFloatingElemPositionForLinkEditor(domRect, editorElem, topBoundary);
3195
+ setTarget({ getBoundingClientRect: () => domRect });
3229
3196
  }
3230
3197
  setLastSelection(selection);
3231
- } else if (!activeElement || activeElement.className !== "aoLinkInput") {
3232
- if (rootElement !== null) {
3233
- setFloatingElemPositionForLinkEditor(null, editorElem);
3234
- }
3198
+ } else if (!(activeElement instanceof HTMLInputElement) || activeElement !== inputRef.current) {
3199
+ setTarget(null);
3235
3200
  setLastSelection(null);
3236
3201
  setIsLinkEditMode(false);
3237
3202
  setLinkUrl("");
@@ -3327,99 +3292,119 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, isLinkEditMode, setIsLink
3327
3292
  setIsLinkEditMode(false);
3328
3293
  }
3329
3294
  };
3330
- return /* @__PURE__ */ jsx("div", { ref: editorRef, className: "aoLinkEditor", children: !isLink ? null : isLinkEditMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
3331
- /* @__PURE__ */ jsx(
3332
- "input",
3333
- {
3334
- ref: inputRef,
3335
- className: "aoLinkInput",
3336
- value: editedLinkUrl,
3337
- onChange: (event) => {
3338
- setEditedLinkUrl(event.target.value);
3339
- },
3340
- onKeyDown: (event) => {
3341
- monitorInputInteraction(event);
3342
- }
3343
- }
3344
- ),
3345
- /* @__PURE__ */ jsxs("div", { className: "aoLinkInputActions", children: [
3346
- /* @__PURE__ */ jsx(
3347
- "div",
3348
- {
3349
- className: "aoLinkCancel",
3350
- role: "button",
3351
- tabIndex: 0,
3352
- title: "Cancel",
3353
- "aria-label": "Cancel",
3354
- onMouseDown: preventDefault,
3355
- onClick: () => {
3356
- setIsLinkEditMode(false);
3357
- },
3358
- children: /* @__PURE__ */ jsx(DismissRegular, { fontSize: 16 })
3359
- }
3360
- ),
3361
- /* @__PURE__ */ jsx(
3362
- "div",
3363
- {
3364
- className: "aoLinkConfirm",
3365
- role: "button",
3366
- tabIndex: 0,
3367
- title: "Confirm",
3368
- "aria-label": "Confirm",
3369
- onMouseDown: preventDefault,
3370
- onClick: handleLinkSubmission,
3371
- children: /* @__PURE__ */ jsx(CheckmarkRegular, { fontSize: 16 })
3372
- }
3373
- )
3374
- ] })
3375
- ] }) : /* @__PURE__ */ jsxs("div", { className: "aoLinkView", children: [
3376
- /* @__PURE__ */ jsx(
3377
- "a",
3378
- {
3379
- href: sanitizeUrl(linkUrl),
3380
- target: "_blank",
3381
- rel: "noopener noreferrer",
3382
- children: linkUrl
3383
- }
3384
- ),
3385
- /* @__PURE__ */ jsx(
3386
- "div",
3387
- {
3388
- className: "aoLinkEdit",
3389
- role: "button",
3390
- tabIndex: 0,
3391
- title: "Edit link",
3392
- "aria-label": "Edit link",
3393
- onMouseDown: preventDefault,
3394
- onClick: (event) => {
3395
- event.preventDefault();
3396
- setEditedLinkUrl(linkUrl);
3397
- setIsLinkEditMode(true);
3398
- },
3399
- children: /* @__PURE__ */ jsx(EditRegular, { fontSize: 16 })
3400
- }
3401
- ),
3402
- /* @__PURE__ */ jsx(
3403
- "div",
3404
- {
3405
- className: "aoLinkTrash",
3406
- role: "button",
3407
- tabIndex: 0,
3408
- title: "Remove link",
3409
- "aria-label": "Remove link",
3410
- onMouseDown: preventDefault,
3411
- onClick: () => {
3412
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
3413
- },
3414
- children: /* @__PURE__ */ jsx(DeleteRegular, { fontSize: 16 })
3415
- }
3416
- )
3417
- ] }) });
3295
+ return /* @__PURE__ */ jsx(
3296
+ Popover,
3297
+ {
3298
+ open: isLink && !!target,
3299
+ onOpenChange: (_, data) => {
3300
+ if (!data.open) setIsLink(false);
3301
+ },
3302
+ positioning: { target: target ?? void 0, position: "below", align: "start" },
3303
+ unstable_disableAutoFocus: true,
3304
+ children: /* @__PURE__ */ jsx(PopoverSurface, { style: { display: "flex", alignItems: "center", gap: 6, padding: 8, maxWidth: 360 }, children: isLinkEditMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
3305
+ /* @__PURE__ */ jsx(
3306
+ "input",
3307
+ {
3308
+ ref: inputRef,
3309
+ value: editedLinkUrl,
3310
+ style: {
3311
+ flex: 1,
3312
+ minWidth: 180,
3313
+ border: "none",
3314
+ outline: "none",
3315
+ background: "#f1f1f1",
3316
+ borderRadius: 15,
3317
+ padding: "8px 12px",
3318
+ fontSize: 14
3319
+ },
3320
+ onChange: (event) => {
3321
+ setEditedLinkUrl(event.target.value);
3322
+ },
3323
+ onKeyDown: (event) => {
3324
+ monitorInputInteraction(event);
3325
+ }
3326
+ }
3327
+ ),
3328
+ /* @__PURE__ */ jsx(
3329
+ Button,
3330
+ {
3331
+ appearance: "subtle",
3332
+ size: "small",
3333
+ icon: /* @__PURE__ */ jsx(DismissRegular, { fontSize: 16 }),
3334
+ title: "Cancel",
3335
+ "aria-label": "Cancel",
3336
+ onMouseDown: preventDefault,
3337
+ onClick: () => {
3338
+ setIsLinkEditMode(false);
3339
+ }
3340
+ }
3341
+ ),
3342
+ /* @__PURE__ */ jsx(
3343
+ Button,
3344
+ {
3345
+ appearance: "primary",
3346
+ size: "small",
3347
+ icon: /* @__PURE__ */ jsx(CheckmarkRegular, { fontSize: 16 }),
3348
+ title: "Confirm",
3349
+ "aria-label": "Confirm",
3350
+ onMouseDown: preventDefault,
3351
+ onClick: handleLinkSubmission
3352
+ }
3353
+ )
3354
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3355
+ /* @__PURE__ */ jsx(
3356
+ "a",
3357
+ {
3358
+ href: sanitizeUrl(linkUrl),
3359
+ target: "_blank",
3360
+ rel: "noopener noreferrer",
3361
+ style: {
3362
+ padding: "0 8px",
3363
+ overflow: "hidden",
3364
+ textOverflow: "ellipsis",
3365
+ whiteSpace: "nowrap",
3366
+ maxWidth: 260
3367
+ },
3368
+ children: linkUrl
3369
+ }
3370
+ ),
3371
+ /* @__PURE__ */ jsx(
3372
+ Button,
3373
+ {
3374
+ appearance: "subtle",
3375
+ size: "small",
3376
+ icon: /* @__PURE__ */ jsx(EditRegular, { fontSize: 16 }),
3377
+ title: "Edit link",
3378
+ "aria-label": "Edit link",
3379
+ onMouseDown: preventDefault,
3380
+ onClick: (event) => {
3381
+ event.preventDefault();
3382
+ setEditedLinkUrl(linkUrl);
3383
+ setIsLinkEditMode(true);
3384
+ }
3385
+ }
3386
+ ),
3387
+ /* @__PURE__ */ jsx(
3388
+ Button,
3389
+ {
3390
+ appearance: "subtle",
3391
+ size: "small",
3392
+ icon: /* @__PURE__ */ jsx(DeleteRegular, { fontSize: 16 }),
3393
+ title: "Remove link",
3394
+ "aria-label": "Remove link",
3395
+ onMouseDown: preventDefault,
3396
+ onClick: () => {
3397
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
3398
+ }
3399
+ }
3400
+ )
3401
+ ] }) })
3402
+ }
3403
+ );
3418
3404
  };
3419
- var useFloatingLinkEditorToolbar = (editor, anchorElem, isLinkEditMode, setIsLinkEditMode) => {
3405
+ var useFloatingLinkEditorToolbar = (editor, isLinkEditMode, setIsLinkEditMode) => {
3420
3406
  const [activeEditor, setActiveEditor] = useState(editor);
3421
3407
  const [isLink, setIsLink] = useState(false);
3422
- const portalContainer = useFloatingPortalContainer(editor);
3423
3408
  useEffect(() => {
3424
3409
  function $updateToolbar() {
3425
3410
  const selection = $getSelection();
@@ -3467,29 +3452,21 @@ var useFloatingLinkEditorToolbar = (editor, anchorElem, isLinkEditMode, setIsLin
3467
3452
  )
3468
3453
  );
3469
3454
  }, [editor]);
3470
- if (!anchorElem || !(anchorElem instanceof HTMLElement) || !portalContainer) {
3471
- return null;
3472
- }
3473
- return createPortal(
3474
- /* @__PURE__ */ jsx(
3475
- FloatingLinkEditor,
3476
- {
3477
- isLink,
3478
- editor: activeEditor,
3479
- setIsLink,
3480
- isLinkEditMode,
3481
- setIsLinkEditMode
3482
- }
3483
- ),
3484
- portalContainer
3455
+ return /* @__PURE__ */ jsx(
3456
+ FloatingLinkEditor,
3457
+ {
3458
+ isLink,
3459
+ editor: activeEditor,
3460
+ setIsLink,
3461
+ isLinkEditMode,
3462
+ setIsLinkEditMode
3463
+ }
3485
3464
  );
3486
3465
  };
3487
- var FloatingLinkEditorPlugin = ({ anchorElem, isLinkEditMode, setIsLinkEditMode }) => {
3466
+ var FloatingLinkEditorPlugin = ({ isLinkEditMode, setIsLinkEditMode }) => {
3488
3467
  const [editor] = useLexicalComposerContext();
3489
- const validAnchorElem = anchorElem && anchorElem instanceof HTMLElement ? anchorElem : document.body;
3490
3468
  return useFloatingLinkEditorToolbar(
3491
3469
  editor,
3492
- validAnchorElem,
3493
3470
  isLinkEditMode,
3494
3471
  setIsLinkEditMode
3495
3472
  );