@tarviks/lexical-rich-editor 1.1.0 → 1.2.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/dist/index.mjs CHANGED
@@ -6,9 +6,9 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
6
6
  import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
7
7
  import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
8
8
  import { createCommand, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, $setSelection, $getSelection, $isRangeSelection, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $insertNodes, $isRootOrShadowRoot, $createParagraphNode, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getNodeByKey, $getRoot, $createTextNode, $isTextNode, $createRangeSelection, $isElementNode, $isNodeSelection, CLICK_COMMAND, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, SKIP_SELECTION_FOCUS_TAG, createEditor, KEY_ENTER_COMMAND } from 'lexical';
9
- import { mergeStyleSets, Stack, css, useTheme, Callout, TextField, DefaultButton } from '@fluentui/react';
10
- import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, ToolbarDivider, Button, Popover, PopoverTrigger, PopoverSurface, Field, Input } from '@fluentui/react-components';
11
- import { ErrorCircleRegular, ChevronDown12Regular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, DeleteRegular, 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';
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, ToolbarDivider, Button, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, Field, Input, DialogActions } from '@fluentui/react-components';
11
+ import { ErrorCircleRegular, ChevronDown12Regular, ArrowUpRegular, RowTripleRegular, ArrowDownRegular, ArrowLeftRegular, ColumnTripleRegular, ArrowRightRegular, DeleteRegular, TextCaseUppercaseFilled, TextCaseLowercaseFilled, TextCaseTitleFilled, TextStrikethroughFilled, TextSubscriptFilled, TextSuperscriptFilled, HighlightAccentFilled, TextBulletListLtrFilled, TextNumberListLtrFilled, DocumentPageBreakRegular, CommentQuoteRegular, AddRegular, TableAddRegular, ImageAddRegular, ImageEditRegular, VideoClipRegular, TextUnderlineFilled, TextItalicFilled, TextBold24Regular, DocumentRegular, TextAlignLeftFilled, TextAlignCenterFilled, TextAlignRightFilled, TextAlignJustifyFilled, AttachFilled, LinkAddRegular, TextColorRegular, PaintBucket16Filled, Dismiss16Regular, Checkmark16Regular, Edit16Regular, Delete16Regular, CodeFilled, LinkFilled } from '@fluentui/react-icons';
12
12
  import { CodeHighlightNode, CodeNode, $isCodeHighlightNode } from '@lexical/code';
13
13
  import { LinkNode, AutoLinkNode, $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND, $createLinkNode } from '@lexical/link';
14
14
  import { ListNode, ListItemNode, $isListNode, REMOVE_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
@@ -615,7 +615,6 @@ var init_ImageComponent = __esm({
615
615
  editor,
616
616
  buttonRef,
617
617
  imageRef,
618
- maxWidth,
619
618
  onResizeStart,
620
619
  onResizeEnd,
621
620
  captionsEnabled: !isLoadError && captionsEnabled
@@ -766,6 +765,9 @@ var init_ImageNode = __esm({
766
765
  const writable = this.getWritable();
767
766
  writable.__width = width;
768
767
  writable.__height = height;
768
+ if (typeof width === "number" && width > writable.__maxWidth) {
769
+ writable.__maxWidth = width;
770
+ }
769
771
  }
770
772
  setShowCaption(showCaption) {
771
773
  const writable = this.getWritable();
@@ -855,6 +857,7 @@ __export(InlineImageComponent_exports, {
855
857
  var imageCache2, useSuspenseImage2, LazyImage2, InlineImageComponent, InlineImageComponent_default;
856
858
  var init_InlineImageComponent = __esm({
857
859
  "src/Nodes/InlineImageComponent.tsx"() {
860
+ init_ImageResizer();
858
861
  init_InlineImage();
859
862
  init_InlineImageNode();
860
863
  imageCache2 = /* @__PURE__ */ new Set();
@@ -897,6 +900,29 @@ var init_InlineImageComponent = __esm({
897
900
  const buttonRef = useRef(null);
898
901
  const [editor] = useLexicalComposerContext();
899
902
  const isEditable = useLexicalEditable();
903
+ const [isResizing, setIsResizing] = useState(false);
904
+ const onResizeEnd = (nextWidth, nextHeight) => {
905
+ setTimeout(() => {
906
+ setIsResizing(false);
907
+ }, 200);
908
+ editor.update(() => {
909
+ const node = $getNodeByKey(nodeKey);
910
+ if ($isInlineImageNode(node)) {
911
+ node.setWidthAndHeight(nextWidth, nextHeight);
912
+ }
913
+ });
914
+ };
915
+ const onResizeStart = () => {
916
+ setIsResizing(true);
917
+ };
918
+ const setShowCaption = (show) => {
919
+ editor.update(() => {
920
+ const node = $getNodeByKey(nodeKey);
921
+ if ($isInlineImageNode(node)) {
922
+ node.setShowCaption(show);
923
+ }
924
+ });
925
+ };
900
926
  const $onDelete = useCallback(
901
927
  (payload) => {
902
928
  const deleteSelection = $getSelection();
@@ -972,6 +998,9 @@ var init_InlineImageComponent = __esm({
972
998
  CLICK_COMMAND,
973
999
  (payload) => {
974
1000
  const event = payload;
1001
+ if (isResizing) {
1002
+ return true;
1003
+ }
975
1004
  if (event.target === imageRef.current) {
976
1005
  if (event.shiftKey) {
977
1006
  setSelected(!isSelected);
@@ -1020,6 +1049,7 @@ var init_InlineImageComponent = __esm({
1020
1049
  }, [
1021
1050
  clearSelection,
1022
1051
  editor,
1052
+ isResizing,
1023
1053
  isSelected,
1024
1054
  nodeKey,
1025
1055
  $onDelete,
@@ -1027,20 +1057,35 @@ var init_InlineImageComponent = __esm({
1027
1057
  $onEscape,
1028
1058
  setSelected
1029
1059
  ]);
1030
- const draggable = isSelected && $isNodeSelection(selection);
1031
- const isFocused = isSelected && isEditable;
1032
- return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx("span", { draggable, children: /* @__PURE__ */ jsx(
1033
- LazyImage2,
1034
- {
1035
- className: isFocused ? `focused ${$isNodeSelection(selection) ? "draggable" : ""}` : null,
1036
- src,
1037
- altText,
1038
- imageRef,
1039
- width,
1040
- height,
1041
- position
1042
- }
1043
- ) }) });
1060
+ const draggable = isSelected && $isNodeSelection(selection) && !isResizing;
1061
+ const isFocused = (isSelected || isResizing) && isEditable;
1062
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsxs(Fragment, { children: [
1063
+ /* @__PURE__ */ jsx("span", { draggable, children: /* @__PURE__ */ jsx(
1064
+ LazyImage2,
1065
+ {
1066
+ className: isFocused ? `focused ${$isNodeSelection(selection) ? "draggable" : ""}` : null,
1067
+ src,
1068
+ altText,
1069
+ imageRef,
1070
+ width,
1071
+ height,
1072
+ position
1073
+ }
1074
+ ) }),
1075
+ isFocused && $isNodeSelection(selection) && /* @__PURE__ */ jsx(
1076
+ ImageResizer_default,
1077
+ {
1078
+ showCaption,
1079
+ setShowCaption,
1080
+ editor,
1081
+ buttonRef,
1082
+ imageRef,
1083
+ onResizeStart,
1084
+ onResizeEnd,
1085
+ captionsEnabled: false
1086
+ }
1087
+ )
1088
+ ] }) });
1044
1089
  };
1045
1090
  InlineImageComponent_default = InlineImageComponent;
1046
1091
  }
@@ -1228,6 +1273,46 @@ var ContentEditorLevel = /* @__PURE__ */ ((ContentEditorLevel2) => {
1228
1273
  ContentEditorLevel2["Pro"] = "pro";
1229
1274
  return ContentEditorLevel2;
1230
1275
  })(ContentEditorLevel || {});
1276
+
1277
+ // src/Types/PageSetup.ts
1278
+ var DEFAULT_PAGE_SETUP = {
1279
+ size: "pageless",
1280
+ orientation: "portrait",
1281
+ margin: "normal"
1282
+ };
1283
+ var PAGE_SIZE_OPTIONS = [
1284
+ { key: "a4", label: 'A4 (8.27" x 11.69")', widthIn: 8.27, heightIn: 11.69 },
1285
+ { key: "letter", label: 'Letter (8.5" x 11")', widthIn: 8.5, heightIn: 11 },
1286
+ { key: "legal", label: 'Legal (8.5" x 14")', widthIn: 8.5, heightIn: 14 },
1287
+ { key: "tabloid", label: 'Tabloid (11" x 17")', widthIn: 11, heightIn: 17 },
1288
+ { key: "a3", label: 'A3 (11.69" x 16.54")', widthIn: 11.69, heightIn: 16.54 },
1289
+ { key: "a5", label: 'A5 (5.83" x 8.27")', widthIn: 5.83, heightIn: 8.27 },
1290
+ { key: "b4", label: 'B4 (9.84" x 13.90")', widthIn: 9.84, heightIn: 13.9 },
1291
+ { key: "b5", label: 'B5 (6.93" x 9.84")', widthIn: 6.93, heightIn: 9.84 },
1292
+ { key: "statement", label: 'Statement (5.5" x 8.5")', widthIn: 5.5, heightIn: 8.5 },
1293
+ { key: "executive", label: 'Executive (7.25" x 10.5")', widthIn: 7.25, heightIn: 10.5 },
1294
+ { key: "folio", label: 'Folio (8.5" x 13")', widthIn: 8.5, heightIn: 13 }
1295
+ ];
1296
+ var MARGIN_OPTIONS = [
1297
+ { key: "narrow", label: 'Narrow (0.25")', valueIn: 0.25 },
1298
+ { key: "normal", label: 'Normal (0.4")', valueIn: 0.4 },
1299
+ { key: "moderate", label: 'Moderate (0.75")', valueIn: 0.75 },
1300
+ { key: "wide", label: 'Wide (1")', valueIn: 1 }
1301
+ ];
1302
+ var CSS_PX_PER_INCH = 96;
1303
+ function resolvePageCanvasMetrics(value) {
1304
+ const margin = MARGIN_OPTIONS.find((o) => o.key === value.margin) ?? MARGIN_OPTIONS[1];
1305
+ if (value.size === "pageless") {
1306
+ return { widthPx: void 0, paddingPx: 20 };
1307
+ }
1308
+ const size = PAGE_SIZE_OPTIONS.find((o) => o.key === value.size);
1309
+ if (!size) return { widthPx: void 0, paddingPx: 20 };
1310
+ const widthIn = value.orientation === "landscape" ? size.heightIn : size.widthIn;
1311
+ return {
1312
+ widthPx: Math.round(widthIn * CSS_PX_PER_INCH),
1313
+ paddingPx: Math.round(margin.valueIn * CSS_PX_PER_INCH)
1314
+ };
1315
+ }
1231
1316
  var AutocompleteNode = class _AutocompleteNode extends TextNode {
1232
1317
  static getType() {
1233
1318
  return "autocomplete";
@@ -2466,99 +2551,329 @@ function CharacterStylesPopupPlugin(props) {
2466
2551
  const [editor] = useLexicalComposerContext();
2467
2552
  return useCharacterStylesPopup(editor, props);
2468
2553
  }
2469
- var CustomOnChangePlugin = ({ value, onChange }) => {
2470
- const [editor] = useLexicalComposerContext();
2471
- const initializedRef = useRef(false);
2472
- const onChangeRef = useRef(onChange);
2473
- onChangeRef.current = onChange;
2474
- useEffect(() => {
2475
- if (!value || initializedRef.current) return;
2476
- initializedRef.current = true;
2477
- editor.update(() => {
2478
- const root = $getRoot();
2479
- root.clear();
2480
- const parser = new DOMParser();
2481
- const dom = parser.parseFromString(value, "text/html");
2482
- const nodes = $generateNodesFromDOM(editor, dom);
2483
- root.append(...nodes);
2484
- $setSelection(null);
2485
- });
2486
- }, [editor, value]);
2487
- const handleChange = useCallback((editorState) => {
2488
- editorState.read(() => {
2489
- onChangeRef.current($generateHtmlFromNodes(editor));
2490
- });
2491
- }, [editor]);
2492
- return /* @__PURE__ */ jsx(
2493
- OnChangePlugin,
2494
- {
2495
- onChange: handleChange,
2496
- ignoreSelectionChange: true
2497
- }
2498
- );
2499
- };
2500
- var getSelectedNode2 = (selection) => {
2501
- const anchor = selection.anchor;
2502
- const anchorNode = selection.anchor.getNode();
2503
- const focus = selection.focus;
2504
- const focusNode = selection.focus.getNode();
2505
- if (anchorNode === focusNode) {
2506
- return anchorNode;
2507
- }
2508
- const isBackward = selection.isBackward();
2509
- if (isBackward) {
2510
- return $isAtNodeEnd(focus) ? anchorNode : focusNode;
2511
- } else {
2512
- return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
2554
+
2555
+ // src/Utils/Sanitize.ts
2556
+ var DROP_ENTIRELY = /* @__PURE__ */ new Set([
2557
+ "script",
2558
+ "noscript",
2559
+ "style",
2560
+ "object",
2561
+ "embed",
2562
+ "form",
2563
+ "input",
2564
+ "button",
2565
+ "select",
2566
+ "textarea",
2567
+ "meta",
2568
+ "link",
2569
+ "base"
2570
+ ]);
2571
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
2572
+ // Block
2573
+ "p",
2574
+ "h1",
2575
+ "h2",
2576
+ "h3",
2577
+ "h4",
2578
+ "h5",
2579
+ "h6",
2580
+ "ul",
2581
+ "ol",
2582
+ "li",
2583
+ "blockquote",
2584
+ "pre",
2585
+ "div",
2586
+ "table",
2587
+ "thead",
2588
+ "tbody",
2589
+ "tfoot",
2590
+ "tr",
2591
+ "td",
2592
+ "th",
2593
+ // Inline
2594
+ "span",
2595
+ "a",
2596
+ "strong",
2597
+ "b",
2598
+ "em",
2599
+ "i",
2600
+ "u",
2601
+ "s",
2602
+ "del",
2603
+ "strike",
2604
+ "sub",
2605
+ "sup",
2606
+ "mark",
2607
+ "code",
2608
+ "br",
2609
+ "hr",
2610
+ // Media / embeds
2611
+ "img",
2612
+ "iframe"
2613
+ ]);
2614
+ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
2615
+ // Presentation
2616
+ "class",
2617
+ "style",
2618
+ "dir",
2619
+ "lang",
2620
+ // Anchors
2621
+ "href",
2622
+ "target",
2623
+ "rel",
2624
+ // Images
2625
+ "src",
2626
+ "alt",
2627
+ "width",
2628
+ "height",
2629
+ // Tables
2630
+ "colspan",
2631
+ "rowspan",
2632
+ // Lists — <ol type="a"> and <ol start="2">
2633
+ "start",
2634
+ "type",
2635
+ // Lexical-internal data markers
2636
+ "data-lex-block",
2637
+ "data-kind",
2638
+ "data-lexical-decorator",
2639
+ // Misc
2640
+ "title",
2641
+ "allowfullscreen"
2642
+ ]);
2643
+ var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
2644
+ function sanitizeElement(el) {
2645
+ for (const child of Array.from(el.children)) {
2646
+ sanitizeElement(child);
2513
2647
  }
2514
- };
2515
- var VERTICAL_GAP = 10;
2516
- var HORIZONTAL_OFFSET = 5;
2517
- var setFloatingElemPositionForLinkEditor = (targetRect, floatingElem, anchorElem, verticalGap = VERTICAL_GAP, horizontalOffset = HORIZONTAL_OFFSET) => {
2518
- const scrollerElem = anchorElem.parentElement;
2519
- if (targetRect === null || !scrollerElem) {
2520
- floatingElem.style.opacity = "0";
2521
- floatingElem.style.transform = "translate(-10000px, -10000px)";
2648
+ const tag = el.tagName.toLowerCase();
2649
+ if (DROP_ENTIRELY.has(tag)) {
2650
+ el.parentNode?.removeChild(el);
2522
2651
  return;
2523
2652
  }
2524
- const floatingElemRect = floatingElem.getBoundingClientRect();
2525
- const anchorElementRect = anchorElem.getBoundingClientRect();
2526
- const editorScrollerRect = scrollerElem.getBoundingClientRect();
2527
- let top = targetRect.top - verticalGap;
2528
- let left = targetRect.left - horizontalOffset;
2529
- if (top < editorScrollerRect.top) {
2530
- top += floatingElemRect.height + targetRect.height + verticalGap * 2;
2531
- }
2532
- if (left + floatingElemRect.width > editorScrollerRect.right) {
2533
- left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
2653
+ if (tag === "iframe") {
2654
+ const src = el.getAttribute("src") ?? "";
2655
+ const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
2656
+ if (!isYouTube) {
2657
+ el.parentNode?.removeChild(el);
2658
+ return;
2659
+ }
2534
2660
  }
2535
- top -= anchorElementRect.top;
2536
- left -= anchorElementRect.left;
2537
- floatingElem.style.opacity = "1";
2538
- floatingElem.style.transform = `translate(${left}px, ${top}px)`;
2539
- };
2540
- var SUPPORTED_URL_PROTOCOLS = /* @__PURE__ */ new Set([
2541
- "http:",
2542
- "https:",
2543
- "mailto:",
2544
- "sms:",
2545
- "tel:"
2546
- ]);
2547
- var sanitizeUrl = (url) => {
2548
- try {
2549
- const parsedUrl = new URL(url);
2550
- if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) {
2551
- return "about:blank";
2661
+ if (!ALLOWED_TAGS.has(tag)) {
2662
+ while (el.firstChild) {
2663
+ el.parentNode?.insertBefore(el.firstChild, el);
2552
2664
  }
2553
- } catch {
2554
- return url;
2665
+ el.parentNode?.removeChild(el);
2666
+ return;
2555
2667
  }
2556
- return url;
2557
- };
2558
- var preventDefault = (event) => {
2559
- event.preventDefault();
2560
- };
2561
- var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMode, setIsLinkEditMode }) => {
2668
+ for (const { name, value } of Array.from(el.attributes)) {
2669
+ const lname = name.toLowerCase();
2670
+ if (!ALLOWED_ATTRS.has(lname)) {
2671
+ el.removeAttribute(name);
2672
+ continue;
2673
+ }
2674
+ if (lname === "href" || lname === "src") {
2675
+ if (DANGEROUS_URL.test(value)) {
2676
+ el.removeAttribute(name);
2677
+ }
2678
+ }
2679
+ if (lname === "style") {
2680
+ const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
2681
+ el.setAttribute("style", safe);
2682
+ }
2683
+ }
2684
+ if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
2685
+ const rel = (el.getAttribute("rel") || "").trim();
2686
+ const tokens = new Set(
2687
+ rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
2688
+ );
2689
+ tokens.add("noopener");
2690
+ tokens.add("noreferrer");
2691
+ el.setAttribute("rel", Array.from(tokens).join(" "));
2692
+ }
2693
+ }
2694
+ function sanitizeHtml(html) {
2695
+ if (!html || typeof html !== "string") return "";
2696
+ const doc = new DOMParser().parseFromString(html, "text/html");
2697
+ for (const child of Array.from(doc.body.children)) {
2698
+ sanitizeElement(child);
2699
+ }
2700
+ return doc.body.innerHTML;
2701
+ }
2702
+ function postProcessOutput(html) {
2703
+ if (!html) return html;
2704
+ const doc = new DOMParser().parseFromString(html, "text/html");
2705
+ for (const inner of Array.from(doc.querySelectorAll("b > strong"))) {
2706
+ const outer = inner.parentElement;
2707
+ _mergeAttributes(inner, outer);
2708
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2709
+ outer.removeChild(inner);
2710
+ }
2711
+ for (const inner of Array.from(doc.querySelectorAll("i > em"))) {
2712
+ const outer = inner.parentElement;
2713
+ _mergeAttributes(inner, outer);
2714
+ while (inner.firstChild) outer.insertBefore(inner.firstChild, inner);
2715
+ outer.removeChild(inner);
2716
+ }
2717
+ for (const p of Array.from(doc.querySelectorAll("p"))) {
2718
+ const style = p.style;
2719
+ const hasMargin = !!(style.margin || style.marginTop || style.marginRight || style.marginBottom || style.marginLeft);
2720
+ if (!hasMargin) style.margin = "0";
2721
+ if (!style.lineHeight) style.lineHeight = "1.25";
2722
+ }
2723
+ return doc.body.innerHTML;
2724
+ }
2725
+ function _mergeAttributes(src, dst) {
2726
+ for (const { name, value } of Array.from(src.attributes)) {
2727
+ if (name === "style") {
2728
+ const prev = dst.getAttribute("style") || "";
2729
+ dst.setAttribute("style", prev ? `${prev}; ${value}` : value);
2730
+ } else if (name === "class") {
2731
+ const prev = dst.getAttribute("class") || "";
2732
+ dst.setAttribute("class", prev ? `${prev} ${value}` : value);
2733
+ }
2734
+ }
2735
+ }
2736
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
2737
+ "p",
2738
+ "h1",
2739
+ "h2",
2740
+ "h3",
2741
+ "h4",
2742
+ "h5",
2743
+ "h6",
2744
+ "div",
2745
+ "ul",
2746
+ "ol",
2747
+ "li",
2748
+ "table",
2749
+ "blockquote",
2750
+ "pre",
2751
+ "hr"
2752
+ ]);
2753
+ function normalizeToBlockHtml(html) {
2754
+ if (!html) return "";
2755
+ const doc = new DOMParser().parseFromString(html, "text/html");
2756
+ const body = doc.body;
2757
+ const childNodes = Array.from(body.childNodes);
2758
+ const needsWrap = childNodes.some((node) => {
2759
+ if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
2760
+ if (node.nodeType === Node.ELEMENT_NODE)
2761
+ return !BLOCK_TAGS.has(node.tagName.toLowerCase());
2762
+ return false;
2763
+ });
2764
+ if (!needsWrap) return html;
2765
+ while (body.firstChild) body.removeChild(body.firstChild);
2766
+ let pendingP = null;
2767
+ for (const node of childNodes) {
2768
+ const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
2769
+ const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
2770
+ if (isBlock) {
2771
+ if (pendingP) {
2772
+ body.appendChild(pendingP);
2773
+ pendingP = null;
2774
+ }
2775
+ body.appendChild(node);
2776
+ } else if (!isWhitespace) {
2777
+ if (!pendingP) pendingP = doc.createElement("p");
2778
+ pendingP.appendChild(node);
2779
+ }
2780
+ }
2781
+ if (pendingP) body.appendChild(pendingP);
2782
+ return body.innerHTML;
2783
+ }
2784
+ var CustomOnChangePlugin = ({ value, onChange }) => {
2785
+ const [editor] = useLexicalComposerContext();
2786
+ const initializedRef = useRef(false);
2787
+ const onChangeRef = useRef(onChange);
2788
+ onChangeRef.current = onChange;
2789
+ useEffect(() => {
2790
+ if (!value || initializedRef.current) return;
2791
+ initializedRef.current = true;
2792
+ editor.update(() => {
2793
+ const root = $getRoot();
2794
+ root.clear();
2795
+ const parser = new DOMParser();
2796
+ const dom = parser.parseFromString(value, "text/html");
2797
+ const nodes = $generateNodesFromDOM(editor, dom);
2798
+ root.append(...nodes);
2799
+ $setSelection(null);
2800
+ });
2801
+ }, [editor, value]);
2802
+ const handleChange = useCallback((editorState) => {
2803
+ editorState.read(() => {
2804
+ onChangeRef.current(postProcessOutput($generateHtmlFromNodes(editor)));
2805
+ });
2806
+ }, [editor]);
2807
+ return /* @__PURE__ */ jsx(
2808
+ OnChangePlugin,
2809
+ {
2810
+ onChange: handleChange,
2811
+ ignoreSelectionChange: true
2812
+ }
2813
+ );
2814
+ };
2815
+ var getSelectedNode2 = (selection) => {
2816
+ const anchor = selection.anchor;
2817
+ const anchorNode = selection.anchor.getNode();
2818
+ const focus = selection.focus;
2819
+ const focusNode = selection.focus.getNode();
2820
+ if (anchorNode === focusNode) {
2821
+ return anchorNode;
2822
+ }
2823
+ const isBackward = selection.isBackward();
2824
+ if (isBackward) {
2825
+ return $isAtNodeEnd(focus) ? anchorNode : focusNode;
2826
+ } else {
2827
+ return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
2828
+ }
2829
+ };
2830
+ var VERTICAL_GAP = 10;
2831
+ var HORIZONTAL_OFFSET = 5;
2832
+ var setFloatingElemPositionForLinkEditor = (targetRect, floatingElem, anchorElem, verticalGap = VERTICAL_GAP, horizontalOffset = HORIZONTAL_OFFSET) => {
2833
+ const scrollerElem = anchorElem.parentElement;
2834
+ if (targetRect === null || !scrollerElem) {
2835
+ floatingElem.style.opacity = "0";
2836
+ floatingElem.style.transform = "translate(-10000px, -10000px)";
2837
+ return;
2838
+ }
2839
+ const floatingElemRect = floatingElem.getBoundingClientRect();
2840
+ const anchorElementRect = anchorElem.getBoundingClientRect();
2841
+ const editorScrollerRect = scrollerElem.getBoundingClientRect();
2842
+ let top = targetRect.top - verticalGap;
2843
+ let left = targetRect.left - horizontalOffset;
2844
+ if (top < editorScrollerRect.top) {
2845
+ top += floatingElemRect.height + targetRect.height + verticalGap * 2;
2846
+ }
2847
+ if (left + floatingElemRect.width > editorScrollerRect.right) {
2848
+ left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
2849
+ }
2850
+ top -= anchorElementRect.top;
2851
+ left -= anchorElementRect.left;
2852
+ floatingElem.style.opacity = "1";
2853
+ floatingElem.style.transform = `translate(${left}px, ${top}px)`;
2854
+ };
2855
+ var SUPPORTED_URL_PROTOCOLS = /* @__PURE__ */ new Set([
2856
+ "http:",
2857
+ "https:",
2858
+ "mailto:",
2859
+ "sms:",
2860
+ "tel:"
2861
+ ]);
2862
+ var sanitizeUrl = (url) => {
2863
+ try {
2864
+ const parsedUrl = new URL(url);
2865
+ if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) {
2866
+ return "about:blank";
2867
+ }
2868
+ } catch {
2869
+ return url;
2870
+ }
2871
+ return url;
2872
+ };
2873
+ var preventDefault = (event) => {
2874
+ event.preventDefault();
2875
+ };
2876
+ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMode, setIsLinkEditMode }) => {
2562
2877
  const [editedLinkUrl, setEditedLinkUrl] = useState("https://");
2563
2878
  const [lastSelection, setLastSelection] = useState(null);
2564
2879
  const [linkUrl, setLinkUrl] = useState("");
@@ -2587,7 +2902,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2587
2902
  return;
2588
2903
  }
2589
2904
  const rootElement = editor.getRootElement();
2590
- if (selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) {
2905
+ if (isLink && selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) {
2591
2906
  const domRect = nativeSelection.focusNode?.parentElement?.getBoundingClientRect();
2592
2907
  if (domRect) {
2593
2908
  domRect.y += 40;
@@ -2603,7 +2918,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2603
2918
  setLinkUrl("");
2604
2919
  }
2605
2920
  return true;
2606
- }, [anchorElem, editor, setIsLinkEditMode, isLinkEditMode, linkUrl]);
2921
+ }, [anchorElem, editor, isLink, setIsLinkEditMode, isLinkEditMode, linkUrl]);
2607
2922
  useEffect(() => {
2608
2923
  const scrollerElem = anchorElem.parentElement;
2609
2924
  const update = () => {
@@ -2701,6 +3016,7 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2701
3016
  {
2702
3017
  ref: inputRef,
2703
3018
  className: "link-input",
3019
+ placeholder: "https://",
2704
3020
  value: editedLinkUrl,
2705
3021
  onChange: (event) => {
2706
3022
  setEditedLinkUrl(event.target.value);
@@ -2710,27 +3026,31 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2710
3026
  }
2711
3027
  }
2712
3028
  ),
2713
- /* @__PURE__ */ jsxs("div", { children: [
3029
+ /* @__PURE__ */ jsxs("div", { className: "link-actions", children: [
2714
3030
  /* @__PURE__ */ jsx(
2715
- "div",
3031
+ "button",
2716
3032
  {
2717
- className: "link-cancel",
2718
- role: "button",
2719
- tabIndex: 0,
3033
+ type: "button",
3034
+ className: "link-icon-btn link-cancel",
3035
+ title: "Cancel",
3036
+ "aria-label": "Cancel",
2720
3037
  onMouseDown: preventDefault,
2721
3038
  onClick: () => {
2722
3039
  setIsLinkEditMode(false);
2723
- }
3040
+ },
3041
+ children: /* @__PURE__ */ jsx(Dismiss16Regular, {})
2724
3042
  }
2725
3043
  ),
2726
3044
  /* @__PURE__ */ jsx(
2727
- "div",
3045
+ "button",
2728
3046
  {
2729
- className: "link-confirm",
2730
- role: "button",
2731
- tabIndex: 0,
3047
+ type: "button",
3048
+ className: "link-icon-btn link-confirm",
3049
+ title: "Confirm",
3050
+ "aria-label": "Confirm",
2732
3051
  onMouseDown: preventDefault,
2733
- onClick: handleLinkSubmission
3052
+ onClick: handleLinkSubmission,
3053
+ children: /* @__PURE__ */ jsx(Checkmark16Regular, {})
2734
3054
  }
2735
3055
  )
2736
3056
  ] })
@@ -2744,32 +3064,38 @@ var FloatingLinkEditor = ({ editor, isLink, setIsLink, anchorElem, isLinkEditMod
2744
3064
  children: linkUrl
2745
3065
  }
2746
3066
  ),
2747
- /* @__PURE__ */ jsx(
2748
- "div",
2749
- {
2750
- className: "link-edit",
2751
- role: "button",
2752
- tabIndex: 0,
2753
- onMouseDown: preventDefault,
2754
- onClick: (event) => {
2755
- event.preventDefault();
2756
- setEditedLinkUrl(linkUrl);
2757
- setIsLinkEditMode(true);
3067
+ /* @__PURE__ */ jsxs("div", { className: "link-actions", children: [
3068
+ /* @__PURE__ */ jsx(
3069
+ "button",
3070
+ {
3071
+ type: "button",
3072
+ className: "link-icon-btn link-edit",
3073
+ title: "Edit link",
3074
+ "aria-label": "Edit link",
3075
+ onMouseDown: preventDefault,
3076
+ onClick: (event) => {
3077
+ event.preventDefault();
3078
+ setEditedLinkUrl(linkUrl);
3079
+ setIsLinkEditMode(true);
3080
+ },
3081
+ children: /* @__PURE__ */ jsx(Edit16Regular, {})
2758
3082
  }
2759
- }
2760
- ),
2761
- /* @__PURE__ */ jsx(
2762
- "div",
2763
- {
2764
- className: "link-trash",
2765
- role: "button",
2766
- tabIndex: 0,
2767
- onMouseDown: preventDefault,
2768
- onClick: () => {
2769
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
3083
+ ),
3084
+ /* @__PURE__ */ jsx(
3085
+ "button",
3086
+ {
3087
+ type: "button",
3088
+ className: "link-icon-btn link-trash",
3089
+ title: "Remove link",
3090
+ "aria-label": "Remove link",
3091
+ onMouseDown: preventDefault,
3092
+ onClick: () => {
3093
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
3094
+ },
3095
+ children: /* @__PURE__ */ jsx(Delete16Regular, {})
2770
3096
  }
2771
- }
2772
- )
3097
+ )
3098
+ ] })
2773
3099
  ] }) });
2774
3100
  };
2775
3101
  var useFloatingLinkEditorToolbar = (editor, anchorElem, isLinkEditMode, setIsLinkEditMode) => {
@@ -2870,74 +3196,28 @@ var readClipboardImageAsDataURL = async (event) => {
2870
3196
  }
2871
3197
  return null;
2872
3198
  };
2873
- var InsertImageByURL = ({
2874
- setIsOpen,
2875
- onClick,
2876
- disabled
2877
- }) => {
2878
- const [altText, setAltText] = useState("");
2879
- const [src, setSrc] = useState("");
2880
- const isDisabled = disabled || src === "";
2881
- return /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 6, padding: "10px 0px 0px 0px" }, children: [
2882
- /* @__PURE__ */ jsx(Field, { label: "Enter URL", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
2883
- Input,
2884
- {
2885
- autoFocus: !disabled,
2886
- appearance: "underline",
2887
- placeholder: "Add URL",
2888
- disabled,
2889
- onChange: (_, v) => setSrc(v.value),
2890
- value: src
2891
- }
2892
- ) }),
2893
- /* @__PURE__ */ jsx(Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
2894
- Input,
2895
- {
2896
- placeholder: "Alt text",
2897
- disabled,
2898
- onChange: (_, v) => setAltText(v.value),
2899
- value: altText
2900
- },
2901
- "alt-text-url"
2902
- ) }),
2903
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
2904
- /* @__PURE__ */ jsx(
2905
- Button,
2906
- {
2907
- style: { width: "150px" },
2908
- onClick: () => !disabled && onClick({ altText, src }),
2909
- disabled: isDisabled,
2910
- size: "small",
2911
- children: "Confirm"
2912
- },
2913
- "url-confirm-btn"
2914
- ),
2915
- /* @__PURE__ */ jsx(
2916
- Button,
2917
- {
2918
- style: { width: "150px" },
2919
- onClick: () => setIsOpen(false),
2920
- disabled,
2921
- size: "small",
2922
- children: "Cancel"
2923
- },
2924
- "file-url-cancel"
2925
- )
2926
- ] })
2927
- ] });
2928
- };
2929
3199
  var InsertImageDialog = ({
2930
3200
  activeEditor,
2931
- disabled
3201
+ disabled,
3202
+ open: externalOpen,
3203
+ onClose
2932
3204
  }) => {
2933
3205
  const [src, setSrc] = useState("");
2934
3206
  const [altText, setAltText] = useState("");
2935
- const [isOpen, setIsOpen] = useState(false);
2936
- const [selectedValue, setSelectedValue] = useState("Upload");
3207
+ const [internalOpen, setInternalOpen] = useState(false);
2937
3208
  const [fileName, setFileName] = useState("");
2938
3209
  const hasModifier = useRef(false);
2939
3210
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
2940
- const isDisabled = disabled || src === "";
3211
+ const isControlled = externalOpen !== void 0;
3212
+ const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
3213
+ const isAddDisabled = disabled || src === "";
3214
+ const handleClose = () => {
3215
+ setSrc("");
3216
+ setAltText("");
3217
+ setFileName("");
3218
+ if (isControlled) onClose?.();
3219
+ else setInternalOpen(false);
3220
+ };
2941
3221
  useEffect(() => {
2942
3222
  hasModifier.current = false;
2943
3223
  const handler = (e) => {
@@ -2949,10 +3229,7 @@ var InsertImageDialog = ({
2949
3229
  const onClick = (payload) => {
2950
3230
  if (disabled) return;
2951
3231
  activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
2952
- setIsOpen(false);
2953
- setAltText("");
2954
- setSrc("");
2955
- setFileName("");
3232
+ handleClose();
2956
3233
  };
2957
3234
  const loadImage = (event) => {
2958
3235
  if (disabled) return;
@@ -2967,118 +3244,108 @@ var InsertImageDialog = ({
2967
3244
  };
2968
3245
  reader.readAsDataURL(files[0]);
2969
3246
  };
2970
- return /* @__PURE__ */ jsxs(
2971
- Popover,
2972
- {
2973
- trapFocus: true,
2974
- withArrow: true,
2975
- open: disabled ? false : isOpen,
2976
- onOpenChange: (_, data) => {
2977
- if (!disabled) setIsOpen(data.open);
3247
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3248
+ !isControlled && /* @__PURE__ */ jsx(
3249
+ Button,
3250
+ {
3251
+ size: "small",
3252
+ title: "Add Image",
3253
+ disabled,
3254
+ icon: /* @__PURE__ */ jsx(ImageAddRegular, { style: { color: iconColor } }),
3255
+ style: {
3256
+ background: isOpen && !disabled ? "#ebebeb" : "none",
3257
+ border: "none",
3258
+ margin: 2,
3259
+ opacity: disabled ? 0.55 : 1,
3260
+ cursor: disabled ? "not-allowed" : "pointer"
3261
+ },
3262
+ onClick: () => {
3263
+ if (disabled) return;
3264
+ setSrc("");
3265
+ setAltText("");
3266
+ setFileName("");
3267
+ setInternalOpen(true);
3268
+ }
2978
3269
  },
2979
- children: [
2980
- /* @__PURE__ */ jsx(PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
2981
- Button,
2982
- {
2983
- size: "small",
2984
- title: "Add Image",
2985
- disabled,
2986
- icon: /* @__PURE__ */ jsx(ImageAddRegular, { style: { color: iconColor } }),
2987
- style: {
2988
- background: isOpen && !disabled ? "#ebebeb" : "none",
2989
- border: "none",
2990
- margin: 2,
2991
- opacity: disabled ? 0.55 : 1,
2992
- cursor: disabled ? "not-allowed" : "pointer"
2993
- },
2994
- onClick: () => {
2995
- if (disabled) return;
2996
- setIsOpen((prev) => !prev);
2997
- setSrc("");
2998
- setAltText("");
2999
- setFileName("");
3000
- }
3001
- },
3002
- "upload-image"
3003
- ) }),
3004
- /* @__PURE__ */ jsxs(
3005
- PopoverSurface,
3006
- {
3007
- style: {
3008
- width: "320px",
3009
- opacity: disabled ? 0.6 : 1,
3010
- pointerEvents: disabled ? "none" : "auto"
3011
- },
3012
- children: [
3013
- /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 6 }, children: [
3014
- /* @__PURE__ */ jsx(Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3015
- "label",
3016
- {
3017
- style: {
3018
- cursor: disabled ? "not-allowed" : "pointer",
3019
- display: "flex",
3020
- alignItems: "center",
3021
- gap: 8,
3022
- opacity: disabled ? 0.75 : 1
3270
+ "upload-image"
3271
+ ),
3272
+ /* @__PURE__ */ jsx(
3273
+ Dialog,
3274
+ {
3275
+ open: isOpen,
3276
+ onOpenChange: (_, data) => {
3277
+ if (!data.open) handleClose();
3278
+ },
3279
+ children: /* @__PURE__ */ jsx(DialogSurface, { style: { maxWidth: "400px" }, children: /* @__PURE__ */ jsxs(DialogBody, { children: [
3280
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Insert Image" }),
3281
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
3282
+ /* @__PURE__ */ jsx(Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3283
+ "label",
3284
+ {
3285
+ style: {
3286
+ cursor: disabled ? "not-allowed" : "pointer",
3287
+ display: "flex",
3288
+ alignItems: "center",
3289
+ gap: 8,
3290
+ opacity: disabled ? 0.75 : 1
3291
+ },
3292
+ children: [
3293
+ /* @__PURE__ */ jsx(
3294
+ "input",
3295
+ {
3296
+ type: "file",
3297
+ accept: "image/*",
3298
+ style: { display: "none" },
3299
+ disabled,
3300
+ onChange: loadImage
3023
3301
  },
3024
- children: [
3025
- /* @__PURE__ */ jsx(
3026
- "input",
3027
- {
3028
- type: "file",
3029
- accept: "image/*",
3030
- style: { display: "none" },
3031
- disabled,
3032
- onChange: loadImage
3033
- },
3034
- "inline-image-upload"
3035
- ),
3036
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, children: [
3037
- /* @__PURE__ */ jsx(
3038
- AttachFilled,
3039
- {
3040
- style: {
3041
- fontSize: "16px",
3042
- color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3043
- marginTop: 2
3044
- }
3045
- }
3046
- ),
3047
- !fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3048
- ] }),
3049
- fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3050
- ]
3051
- }
3052
- ) }),
3053
- /* @__PURE__ */ jsx(Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
3054
- Input,
3055
- {
3056
- placeholder: "Alt text",
3057
- appearance: "underline",
3058
- disabled,
3059
- onChange: (_, d) => setAltText(d.value),
3060
- value: altText
3061
- }
3062
- ) }),
3063
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
3064
- /* @__PURE__ */ jsx(Button, { size: "small", disabled: isDisabled, onClick: () => onClick({ altText, src }), children: "Add" }),
3065
- /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
3066
- ] })
3067
- ] }),
3068
- selectedValue === "URL" && /* @__PURE__ */ jsx(
3069
- InsertImageByURL,
3070
- {
3071
- disabled,
3072
- setIsOpen: (open) => setIsOpen(open),
3073
- onClick: (payload) => onClick(payload)
3074
- }
3075
- )
3076
- ]
3077
- }
3078
- )
3079
- ]
3080
- }
3081
- );
3302
+ "inline-image-upload"
3303
+ ),
3304
+ /* @__PURE__ */ jsxs(Stack, { horizontal: true, children: [
3305
+ /* @__PURE__ */ jsx(
3306
+ AttachFilled,
3307
+ {
3308
+ style: {
3309
+ fontSize: "16px",
3310
+ color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3311
+ marginTop: 2
3312
+ }
3313
+ }
3314
+ ),
3315
+ !fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3316
+ ] }),
3317
+ fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3318
+ ]
3319
+ }
3320
+ ) }),
3321
+ /* @__PURE__ */ jsx(Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
3322
+ Input,
3323
+ {
3324
+ placeholder: "Alt text",
3325
+ appearance: "underline",
3326
+ disabled,
3327
+ onChange: (_, d) => setAltText(d.value),
3328
+ value: altText
3329
+ }
3330
+ ) })
3331
+ ] }) }),
3332
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
3333
+ /* @__PURE__ */ jsx(
3334
+ Button,
3335
+ {
3336
+ appearance: "primary",
3337
+ size: "small",
3338
+ disabled: isAddDisabled,
3339
+ onClick: () => onClick({ altText, src }),
3340
+ children: "Add"
3341
+ }
3342
+ ),
3343
+ /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
3344
+ ] })
3345
+ ] }) })
3346
+ }
3347
+ )
3348
+ ] });
3082
3349
  };
3083
3350
  var ImagesPlugin = ({ captionsEnabled }) => {
3084
3351
  const [editor] = useLexicalComposerContext();
@@ -3244,17 +3511,28 @@ var useStyles = makeStyles({
3244
3511
  });
3245
3512
  var InsertInlineImageDialog = ({
3246
3513
  disabled,
3247
- activeEditor
3514
+ activeEditor,
3515
+ open: externalOpen,
3516
+ onClose
3248
3517
  }) => {
3249
3518
  const hasModifier = useRef(false);
3250
3519
  const [src, setSrc] = useState("");
3251
- const [isOpen, setIsOpen] = useState(false);
3520
+ const [internalOpen, setInternalOpen] = useState(false);
3252
3521
  const [altText, setAltText] = useState("");
3253
3522
  const [fileName, setFileName] = useState("");
3254
3523
  const [position, setPosition] = useState("left");
3255
3524
  const styles = useStyles();
3256
3525
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
3257
- const isDisabled = disabled || src === "";
3526
+ const isControlled = externalOpen !== void 0;
3527
+ const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
3528
+ const isAddDisabled = disabled || src === "";
3529
+ const handleClose = () => {
3530
+ setSrc("");
3531
+ setAltText("");
3532
+ setFileName("");
3533
+ if (isControlled) onClose?.();
3534
+ else setInternalOpen(false);
3535
+ };
3258
3536
  const loadImage = (event) => {
3259
3537
  if (disabled) return;
3260
3538
  const files = event.target.files;
@@ -3279,146 +3557,135 @@ var InsertInlineImageDialog = ({
3279
3557
  if (disabled) return;
3280
3558
  const payload = { altText, position, src };
3281
3559
  activeEditor.dispatchCommand(INSERT_INLINE_IMAGE_COMMAND, payload);
3282
- setIsOpen(false);
3283
- setAltText("");
3284
- setSrc("");
3285
- setFileName("");
3560
+ handleClose();
3286
3561
  };
3287
- return /* @__PURE__ */ jsxs(
3288
- Popover,
3289
- {
3290
- trapFocus: true,
3291
- withArrow: true,
3292
- open: disabled ? false : isOpen,
3293
- onOpenChange: (_, data) => {
3294
- if (!disabled) setIsOpen(data.open);
3562
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3563
+ !isControlled && /* @__PURE__ */ jsx(
3564
+ Button,
3565
+ {
3566
+ size: "small",
3567
+ title: "Add Inline Image",
3568
+ disabled,
3569
+ icon: /* @__PURE__ */ jsx(ImageEditRegular, { style: { color: iconColor } }),
3570
+ style: {
3571
+ background: isOpen && !disabled ? "#ebebeb" : "none",
3572
+ border: "none",
3573
+ margin: 2,
3574
+ opacity: disabled ? 0.55 : 1,
3575
+ cursor: disabled ? "not-allowed" : "pointer"
3576
+ },
3577
+ onClick: () => {
3578
+ if (disabled) return;
3579
+ setSrc("");
3580
+ setAltText("");
3581
+ setFileName("");
3582
+ setInternalOpen(true);
3583
+ }
3295
3584
  },
3296
- children: [
3297
- /* @__PURE__ */ jsx(PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
3298
- Button,
3299
- {
3300
- size: "small",
3301
- title: "Add Inline Image",
3302
- disabled,
3303
- icon: /* @__PURE__ */ jsx(ImageEditRegular, { style: { color: iconColor } }),
3304
- style: {
3305
- background: isOpen && !disabled ? "#ebebeb" : "none",
3306
- border: "none",
3307
- margin: 2,
3308
- opacity: disabled ? 0.55 : 1,
3309
- cursor: disabled ? "not-allowed" : "pointer"
3310
- },
3311
- onClick: () => {
3312
- if (disabled) return;
3313
- setIsOpen((prev) => !prev);
3314
- setAltText("");
3315
- setSrc("");
3316
- setFileName("");
3317
- }
3318
- },
3319
- "upload-inline-image"
3320
- ) }),
3321
- /* @__PURE__ */ jsx(
3322
- PopoverSurface,
3323
- {
3324
- style: {
3325
- width: "400px",
3326
- opacity: disabled ? 0.6 : 1,
3327
- pointerEvents: disabled ? "none" : "auto"
3328
- },
3329
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 6, padding: "10px 0px 0px 0px" }, children: [
3330
- /* @__PURE__ */ jsx(Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3331
- "label",
3332
- {
3333
- style: {
3334
- cursor: disabled ? "not-allowed" : "pointer",
3335
- display: "flex",
3336
- alignItems: "center",
3337
- gap: 8,
3338
- opacity: disabled ? 0.75 : 1
3339
- },
3340
- children: [
3585
+ "upload-inline-image"
3586
+ ),
3587
+ /* @__PURE__ */ jsx(
3588
+ Dialog,
3589
+ {
3590
+ open: isOpen,
3591
+ onOpenChange: (_, data) => {
3592
+ if (!data.open) handleClose();
3593
+ },
3594
+ children: /* @__PURE__ */ jsx(DialogSurface, { style: { maxWidth: "440px" }, children: /* @__PURE__ */ jsxs(DialogBody, { children: [
3595
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Insert Inline Image" }),
3596
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
3597
+ /* @__PURE__ */ jsx(Field, { label: "Upload", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3598
+ "label",
3599
+ {
3600
+ style: {
3601
+ cursor: disabled ? "not-allowed" : "pointer",
3602
+ display: "flex",
3603
+ alignItems: "center",
3604
+ gap: 8,
3605
+ opacity: disabled ? 0.75 : 1
3606
+ },
3607
+ children: [
3608
+ /* @__PURE__ */ jsx(
3609
+ "input",
3610
+ {
3611
+ type: "file",
3612
+ accept: "image/*",
3613
+ style: { display: "none" },
3614
+ disabled,
3615
+ onChange: loadImage
3616
+ },
3617
+ "inline-image-upload"
3618
+ ),
3619
+ /* @__PURE__ */ jsxs(Stack, { horizontal: true, children: [
3341
3620
  /* @__PURE__ */ jsx(
3342
- "input",
3621
+ AttachFilled,
3343
3622
  {
3344
- type: "file",
3345
- accept: "image/*",
3346
- style: { display: "none" },
3347
- disabled,
3348
- onChange: loadImage
3349
- },
3350
- "inline-image-upload"
3351
- ),
3352
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, children: [
3353
- /* @__PURE__ */ jsx(
3354
- AttachFilled,
3355
- {
3356
- style: {
3357
- fontSize: "16px",
3358
- color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3359
- marginTop: 2
3360
- }
3361
- }
3362
- ),
3363
- !fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3364
- ] }),
3365
- fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3366
- ]
3367
- }
3368
- ) }),
3369
- /* @__PURE__ */ jsx(Field, { label: "Position", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3370
- Dropdown,
3371
- {
3372
- placeholder: "Left Align",
3373
- className: styles.alignDropdown,
3374
- disabled,
3375
- listbox: { style: { width: "120px" } },
3376
- root: { style: { borderBottom: "1px solid black" } },
3377
- children: [
3378
- /* @__PURE__ */ jsx(Option, { text: "full", onClick: () => setPosition("full"), children: "Full" }, "full"),
3379
- /* @__PURE__ */ jsx(Option, { text: "left", onClick: () => setPosition("left"), children: "Left" }, "left"),
3380
- /* @__PURE__ */ jsx(Option, { text: "right", onClick: () => setPosition("right"), children: "Right" }, "right")
3381
- ]
3382
- }
3383
- ) }),
3384
- /* @__PURE__ */ jsx(Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
3385
- Input,
3386
- {
3387
- placeholder: "Alt text",
3388
- appearance: "underline",
3389
- disabled,
3390
- value: altText,
3391
- onChange: (_, d) => setAltText(d.value)
3392
- }
3393
- ) }),
3394
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
3395
- /* @__PURE__ */ jsx(
3396
- Button,
3397
- {
3398
- size: "small",
3399
- disabled: isDisabled,
3400
- onClick: handleOnClick,
3401
- children: "Add"
3402
- },
3403
- "file-inline-upload-btn"
3404
- ),
3405
- /* @__PURE__ */ jsx(
3406
- Button,
3407
- {
3408
- size: "small",
3409
- disabled,
3410
- onClick: () => setIsOpen(false),
3411
- children: "Cancel"
3412
- },
3413
- "file-inline-upload-cancel"
3414
- )
3415
- ] })
3416
- ] })
3417
- }
3418
- )
3419
- ]
3420
- }
3421
- );
3623
+ style: {
3624
+ fontSize: "16px",
3625
+ color: disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#808080",
3626
+ marginTop: 2
3627
+ }
3628
+ }
3629
+ ),
3630
+ !fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: "Upload File" })
3631
+ ] }),
3632
+ fileName && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#808080" }, children: fileName })
3633
+ ]
3634
+ }
3635
+ ) }),
3636
+ /* @__PURE__ */ jsx(Field, { label: "Position", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsxs(
3637
+ Dropdown,
3638
+ {
3639
+ placeholder: "Left Align",
3640
+ className: styles.alignDropdown,
3641
+ disabled,
3642
+ listbox: { style: { width: "120px" } },
3643
+ root: { style: { borderBottom: "1px solid black" } },
3644
+ children: [
3645
+ /* @__PURE__ */ jsx(Option, { text: "full", onClick: () => setPosition("full"), children: "Full" }, "full"),
3646
+ /* @__PURE__ */ jsx(Option, { text: "left", onClick: () => setPosition("left"), children: "Left" }, "left"),
3647
+ /* @__PURE__ */ jsx(Option, { text: "right", onClick: () => setPosition("right"), children: "Right" }, "right")
3648
+ ]
3649
+ }
3650
+ ) }),
3651
+ /* @__PURE__ */ jsx(Field, { label: "Alt Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
3652
+ Input,
3653
+ {
3654
+ placeholder: "Alt text",
3655
+ appearance: "underline",
3656
+ disabled,
3657
+ value: altText,
3658
+ onChange: (_, d) => setAltText(d.value)
3659
+ }
3660
+ ) })
3661
+ ] }) }),
3662
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
3663
+ /* @__PURE__ */ jsx(
3664
+ Button,
3665
+ {
3666
+ appearance: "primary",
3667
+ size: "small",
3668
+ disabled: isAddDisabled,
3669
+ onClick: handleOnClick,
3670
+ children: "Add"
3671
+ },
3672
+ "file-inline-upload-btn"
3673
+ ),
3674
+ /* @__PURE__ */ jsx(
3675
+ Button,
3676
+ {
3677
+ size: "small",
3678
+ disabled,
3679
+ onClick: handleClose,
3680
+ children: "Cancel"
3681
+ },
3682
+ "file-inline-upload-cancel"
3683
+ )
3684
+ ] })
3685
+ ] }) })
3686
+ }
3687
+ )
3688
+ ] });
3422
3689
  };
3423
3690
  var InlineImagePlugin = () => {
3424
3691
  const [editor] = useLexicalComposerContext();
@@ -3573,204 +3840,6 @@ function PageBreakPlugin() {
3573
3840
  }, [editor]);
3574
3841
  return null;
3575
3842
  }
3576
-
3577
- // src/Utils/Sanitize.ts
3578
- var DROP_ENTIRELY = /* @__PURE__ */ new Set([
3579
- "script",
3580
- "noscript",
3581
- "style",
3582
- "object",
3583
- "embed",
3584
- "form",
3585
- "input",
3586
- "button",
3587
- "select",
3588
- "textarea",
3589
- "meta",
3590
- "link",
3591
- "base"
3592
- ]);
3593
- var ALLOWED_TAGS = /* @__PURE__ */ new Set([
3594
- // Block
3595
- "p",
3596
- "h1",
3597
- "h2",
3598
- "h3",
3599
- "h4",
3600
- "h5",
3601
- "h6",
3602
- "ul",
3603
- "ol",
3604
- "li",
3605
- "blockquote",
3606
- "pre",
3607
- "div",
3608
- "table",
3609
- "thead",
3610
- "tbody",
3611
- "tfoot",
3612
- "tr",
3613
- "td",
3614
- "th",
3615
- // Inline
3616
- "span",
3617
- "a",
3618
- "strong",
3619
- "b",
3620
- "em",
3621
- "i",
3622
- "u",
3623
- "s",
3624
- "del",
3625
- "strike",
3626
- "sub",
3627
- "sup",
3628
- "mark",
3629
- "code",
3630
- "br",
3631
- "hr",
3632
- // Media / embeds
3633
- "img",
3634
- "iframe"
3635
- ]);
3636
- var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
3637
- // Presentation
3638
- "class",
3639
- "style",
3640
- "dir",
3641
- "lang",
3642
- // Anchors
3643
- "href",
3644
- "target",
3645
- "rel",
3646
- // Images
3647
- "src",
3648
- "alt",
3649
- "width",
3650
- "height",
3651
- // Tables
3652
- "colspan",
3653
- "rowspan",
3654
- // Lists — <ol type="a"> and <ol start="2">
3655
- "start",
3656
- "type",
3657
- // Lexical-internal data markers
3658
- "data-lex-block",
3659
- "data-kind",
3660
- "data-lexical-decorator",
3661
- // Misc
3662
- "title",
3663
- "allowfullscreen"
3664
- ]);
3665
- var DANGEROUS_URL = /^\s*(javascript|data\s*:|vbscript)/i;
3666
- function sanitizeElement(el) {
3667
- for (const child of Array.from(el.children)) {
3668
- sanitizeElement(child);
3669
- }
3670
- const tag = el.tagName.toLowerCase();
3671
- if (DROP_ENTIRELY.has(tag)) {
3672
- el.parentNode?.removeChild(el);
3673
- return;
3674
- }
3675
- if (tag === "iframe") {
3676
- const src = el.getAttribute("src") ?? "";
3677
- const isYouTube = /^\s*https:\/\/(www\.)?(youtube\.com|youtube-nocookie\.com)\/embed\/[^?&/]+/i.test(src);
3678
- if (!isYouTube) {
3679
- el.parentNode?.removeChild(el);
3680
- return;
3681
- }
3682
- }
3683
- if (!ALLOWED_TAGS.has(tag)) {
3684
- while (el.firstChild) {
3685
- el.parentNode?.insertBefore(el.firstChild, el);
3686
- }
3687
- el.parentNode?.removeChild(el);
3688
- return;
3689
- }
3690
- for (const { name, value } of Array.from(el.attributes)) {
3691
- const lname = name.toLowerCase();
3692
- if (!ALLOWED_ATTRS.has(lname)) {
3693
- el.removeAttribute(name);
3694
- continue;
3695
- }
3696
- if (lname === "href" || lname === "src") {
3697
- if (DANGEROUS_URL.test(value)) {
3698
- el.removeAttribute(name);
3699
- }
3700
- }
3701
- if (lname === "style") {
3702
- const safe = value.replace(/expression\s*\(/gi, "(").replace(/url\s*\(\s*['"]?\s*javascript:/gi, "url(");
3703
- el.setAttribute("style", safe);
3704
- }
3705
- }
3706
- if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
3707
- const rel = (el.getAttribute("rel") || "").trim();
3708
- const tokens = new Set(
3709
- rel.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase())
3710
- );
3711
- tokens.add("noopener");
3712
- tokens.add("noreferrer");
3713
- el.setAttribute("rel", Array.from(tokens).join(" "));
3714
- }
3715
- }
3716
- function sanitizeHtml(html) {
3717
- if (!html || typeof html !== "string") return "";
3718
- const doc = new DOMParser().parseFromString(html, "text/html");
3719
- for (const child of Array.from(doc.body.children)) {
3720
- sanitizeElement(child);
3721
- }
3722
- return doc.body.innerHTML;
3723
- }
3724
- var BLOCK_TAGS = /* @__PURE__ */ new Set([
3725
- "p",
3726
- "h1",
3727
- "h2",
3728
- "h3",
3729
- "h4",
3730
- "h5",
3731
- "h6",
3732
- "div",
3733
- "ul",
3734
- "ol",
3735
- "li",
3736
- "table",
3737
- "blockquote",
3738
- "pre",
3739
- "hr"
3740
- ]);
3741
- function normalizeToBlockHtml(html) {
3742
- if (!html) return "";
3743
- const doc = new DOMParser().parseFromString(html, "text/html");
3744
- const body = doc.body;
3745
- const childNodes = Array.from(body.childNodes);
3746
- const needsWrap = childNodes.some((node) => {
3747
- if (node.nodeType === Node.TEXT_NODE) return !!node.nodeValue?.trim();
3748
- if (node.nodeType === Node.ELEMENT_NODE)
3749
- return !BLOCK_TAGS.has(node.tagName.toLowerCase());
3750
- return false;
3751
- });
3752
- if (!needsWrap) return html;
3753
- while (body.firstChild) body.removeChild(body.firstChild);
3754
- let pendingP = null;
3755
- for (const node of childNodes) {
3756
- const isBlock = node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has(node.tagName.toLowerCase());
3757
- const isWhitespace = node.nodeType === Node.TEXT_NODE && !node.nodeValue?.trim();
3758
- if (isBlock) {
3759
- if (pendingP) {
3760
- body.appendChild(pendingP);
3761
- pendingP = null;
3762
- }
3763
- body.appendChild(node);
3764
- } else if (!isWhitespace) {
3765
- if (!pendingP) pendingP = doc.createElement("p");
3766
- pendingP.appendChild(node);
3767
- }
3768
- }
3769
- if (pendingP) body.appendChild(pendingP);
3770
- return body.innerHTML;
3771
- }
3772
-
3773
- // src/Utils/Helper.ts
3774
3843
  function findBlockByKind(kind) {
3775
3844
  const root = $getRoot();
3776
3845
  for (const child of root.getChildren()) {
@@ -3842,7 +3911,7 @@ function RefApiPlugin({
3842
3911
  editor.getEditorState().read(() => {
3843
3912
  html = $generateHtmlFromNodes(editor, null);
3844
3913
  });
3845
- return html;
3914
+ return postProcessOutput(html);
3846
3915
  },
3847
3916
  clear: () => {
3848
3917
  editor.update(() => {
@@ -4985,13 +5054,14 @@ function getToolbarGroupsByLevel(level) {
4985
5054
  // ['undo', 'redo', '|'],
4986
5055
  ["Bold", "Italic", "Underline", "|"],
4987
5056
  ["ColorPicker", "|"],
4988
- ["Link", "Table", "|"],
4989
- // ['Image', 'Youtube', 'InlineImage', '|'],
5057
+ ["Link", "|"],
5058
+ ["Insert", "|"],
4990
5059
  ["Heading", "|"],
4991
5060
  ["FontFamily", "|"],
4992
5061
  ["FontSize", "|"],
4993
5062
  ["Decorators", "|"],
4994
- ["Align"]
5063
+ ["Align", "|"],
5064
+ ["PageSetup"]
4995
5065
  ];
4996
5066
  case "pro" /* Pro */:
4997
5067
  default:
@@ -4999,14 +5069,14 @@ function getToolbarGroupsByLevel(level) {
4999
5069
  // ['undo', 'redo', '|'],
5000
5070
  ["Bold", "Italic", "Underline", "|"],
5001
5071
  ["ColorPicker", "|"],
5002
- ["Link", "Table", "|"],
5003
- // ['CodeBlock', '|'],
5004
- ["Image", "Youtube", "InlineImage", "|"],
5072
+ ["Link", "|"],
5073
+ ["Insert", "|"],
5005
5074
  ["Heading", "|"],
5006
5075
  ["FontFamily", "|"],
5007
5076
  ["FontSize", "|"],
5008
5077
  ["Decorators", "|"],
5009
- ["Align"]
5078
+ ["Align", "|"],
5079
+ ["PageSetup"]
5010
5080
  ];
5011
5081
  }
5012
5082
  }
@@ -5044,6 +5114,7 @@ var normalizeHex = (v) => {
5044
5114
  if (hex.length === 4 || hex.length === 7) return hex.toLowerCase();
5045
5115
  return "#000000";
5046
5116
  };
5117
+ var isCompleteHex = (v) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test((v ?? "").trim());
5047
5118
  var hexToRgb = (hex) => {
5048
5119
  const h = normalizeHex(hex).replace("#", "");
5049
5120
  if (h.length === 3) {
@@ -5126,62 +5197,118 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5126
5197
  };
5127
5198
  }, [open]);
5128
5199
  const appliedHex = React9.useMemo(() => normalizeHex(value || "#000000"), [value]);
5129
- const [hex, setHex] = React9.useState(appliedHex);
5200
+ const [hex, setHexState] = React9.useState(appliedHex);
5201
+ const [hexText, setHexText] = React9.useState(appliedHex);
5130
5202
  const { r, g, b } = React9.useMemo(() => hexToRgb(hex), [hex]);
5131
5203
  const hsv = React9.useMemo(() => rgbToHsv(r, g, b), [r, g, b]);
5132
5204
  const [h, setH] = React9.useState(hsv.h);
5133
5205
  const [s, setS] = React9.useState(hsv.s);
5134
5206
  const [v, setV] = React9.useState(hsv.v);
5135
- const setDraft = React9.useCallback((nextHex) => {
5136
- setHex(nextHex);
5137
- const rgb = hexToRgb(nextHex);
5138
- const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5139
- setH(next.h);
5140
- setS(next.s);
5141
- setV(next.v);
5142
- }, []);
5143
- const setDraftFromHsv = React9.useCallback((hh, ss, vv) => {
5144
- const rgb = hsvToRgb(hh, ss, vv);
5145
- setHex(rgbToHex(rgb.r, rgb.g, rgb.b));
5146
- setH(hh);
5147
- setS(ss);
5148
- setV(vv);
5149
- }, []);
5207
+ const commit = React9.useCallback(
5208
+ (nextHex) => {
5209
+ const rgb = hexToRgb(nextHex);
5210
+ const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5211
+ setHexState(nextHex);
5212
+ setHexText(nextHex);
5213
+ setH(next.h);
5214
+ setS(next.s);
5215
+ setV(next.v);
5216
+ onChange(nextHex);
5217
+ },
5218
+ [onChange]
5219
+ );
5220
+ const commitFromHsv = React9.useCallback(
5221
+ (hh, ss, vv) => {
5222
+ const rgb = hsvToRgb(hh, ss, vv);
5223
+ const nextHex = rgbToHex(rgb.r, rgb.g, rgb.b);
5224
+ setHexState(nextHex);
5225
+ setHexText(nextHex);
5226
+ setH(hh);
5227
+ setS(ss);
5228
+ setV(vv);
5229
+ onChange(nextHex);
5230
+ },
5231
+ [onChange]
5232
+ );
5150
5233
  const wasOpenRef = React9.useRef(open);
5151
5234
  React9.useEffect(() => {
5152
5235
  const justOpened = open && !wasOpenRef.current;
5153
5236
  wasOpenRef.current = open;
5154
5237
  if (!justOpened) return;
5155
- setDraft(appliedHex);
5156
- }, [appliedHex, open, setDraft]);
5238
+ setHexState(appliedHex);
5239
+ setHexText(appliedHex);
5240
+ const rgb = hexToRgb(appliedHex);
5241
+ const next = rgbToHsv(rgb.r, rgb.g, rgb.b);
5242
+ setH(next.h);
5243
+ setS(next.s);
5244
+ setV(next.v);
5245
+ }, [appliedHex, open]);
5157
5246
  const svRef = React9.useRef(null);
5158
- const handleSVClick = React9.useCallback(
5247
+ const svPointFromEvent = React9.useCallback((clientX, clientY) => {
5248
+ if (!svRef.current) return null;
5249
+ const rect = svRef.current.getBoundingClientRect();
5250
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5251
+ const y = clamp3(clientY - rect.top, 0, rect.height);
5252
+ const ss = rect.width === 0 ? 0 : x / rect.width;
5253
+ const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5254
+ return { ss, vv };
5255
+ }, []);
5256
+ const hRef = React9.useRef(h);
5257
+ hRef.current = h;
5258
+ const handleSVPointerDown = React9.useCallback(
5259
+ (e) => {
5260
+ e.currentTarget.setPointerCapture(e.pointerId);
5261
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5262
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5263
+ },
5264
+ [svPointFromEvent, commitFromHsv]
5265
+ );
5266
+ const handleSVPointerMove = React9.useCallback(
5159
5267
  (e) => {
5160
- if (!svRef.current) return;
5161
- const rect = svRef.current.getBoundingClientRect();
5162
- const x = clamp3(e.clientX - rect.left, 0, rect.width);
5163
- const y = clamp3(e.clientY - rect.top, 0, rect.height);
5164
- const ss = rect.width === 0 ? 0 : x / rect.width;
5165
- const vv = rect.height === 0 ? 0 : 1 - y / rect.height;
5166
- setDraftFromHsv(h, ss, vv);
5268
+ if (e.buttons !== 1) return;
5269
+ const pt = svPointFromEvent(e.clientX, e.clientY);
5270
+ if (pt) commitFromHsv(hRef.current, pt.ss, pt.vv);
5167
5271
  },
5168
- [h, setDraftFromHsv]
5272
+ [svPointFromEvent, commitFromHsv]
5169
5273
  );
5170
5274
  const hueRef = React9.useRef(null);
5171
- const handleHueClick = React9.useCallback(
5275
+ const sRef = React9.useRef(s);
5276
+ sRef.current = s;
5277
+ const vRef = React9.useRef(v);
5278
+ vRef.current = v;
5279
+ const huePointFromEvent = React9.useCallback((clientX) => {
5280
+ if (!hueRef.current) return null;
5281
+ const rect = hueRef.current.getBoundingClientRect();
5282
+ const x = clamp3(clientX - rect.left, 0, rect.width);
5283
+ return rect.width === 0 ? 0 : x / rect.width * 360;
5284
+ }, []);
5285
+ const handleHuePointerDown = React9.useCallback(
5286
+ (e) => {
5287
+ e.currentTarget.setPointerCapture(e.pointerId);
5288
+ const hh = huePointFromEvent(e.clientX);
5289
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5290
+ },
5291
+ [huePointFromEvent, commitFromHsv]
5292
+ );
5293
+ const handleHuePointerMove = React9.useCallback(
5172
5294
  (e) => {
5173
- if (!hueRef.current) return;
5174
- const rect = hueRef.current.getBoundingClientRect();
5175
- const x = clamp3(e.clientX - rect.left, 0, rect.width);
5176
- const hh = rect.width === 0 ? 0 : x / rect.width * 360;
5177
- setDraftFromHsv(hh, s, v);
5295
+ if (e.buttons !== 1) return;
5296
+ const hh = huePointFromEvent(e.clientX);
5297
+ if (hh != null) commitFromHsv(hh, sRef.current, vRef.current);
5178
5298
  },
5179
- [s, v, setDraftFromHsv]
5299
+ [huePointFromEvent, commitFromHsv]
5180
5300
  );
5181
- const handleApply = React9.useCallback(() => {
5182
- onChange(hex);
5183
- setOpenAndNotify(false);
5184
- }, [onChange, hex, setOpenAndNotify]);
5301
+ const handleHexChange = React9.useCallback(
5302
+ (_, val) => {
5303
+ const next = val ?? "";
5304
+ setHexText(next);
5305
+ if (isCompleteHex(next.trim())) commit(normalizeHex(next));
5306
+ },
5307
+ [commit]
5308
+ );
5309
+ const handleHexBlur = React9.useCallback(() => {
5310
+ commit(normalizeHex(hexText));
5311
+ }, [hexText, commit]);
5185
5312
  const svThumb = React9.useMemo(() => ({ left: `${s * 100}%`, top: `${(1 - v) * 100}%` }), [s, v]);
5186
5313
  const hueThumb = React9.useMemo(() => ({ left: `${h / 360 * 100}%` }), [h]);
5187
5314
  const hueColor = React9.useMemo(() => {
@@ -5236,44 +5363,192 @@ var ColorPickerControl = ({ value, title, disabled, onChange, icon, onOpenChange
5236
5363
  directionalHint: 4,
5237
5364
  className: "aoColorCallout",
5238
5365
  preventDismissOnEvent,
5239
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, styles: { root: { padding: 12, width: 320 } }, children: [
5240
- /* @__PURE__ */ jsxs("div", { className: "aoLexRow", children: [
5241
- /* @__PURE__ */ jsx("div", { className: "aoLexSwatch", style: { background: hex } }),
5242
- /* @__PURE__ */ jsx("div", { className: "aoLexTitle", children: title })
5366
+ children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 14 }, styles: { root: { padding: "14px 16px 16px", width: 288 } }, children: [
5367
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
5368
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#242424", letterSpacing: 0.1 }, children: title }),
5369
+ /* @__PURE__ */ jsx(
5370
+ "button",
5371
+ {
5372
+ type: "button",
5373
+ "aria-label": "Close",
5374
+ onClick: () => setOpenAndNotify(false),
5375
+ style: {
5376
+ display: "flex",
5377
+ alignItems: "center",
5378
+ justifyContent: "center",
5379
+ width: 24,
5380
+ height: 24,
5381
+ padding: 0,
5382
+ border: "none",
5383
+ borderRadius: 4,
5384
+ background: "transparent",
5385
+ color: "#616161",
5386
+ cursor: "pointer"
5387
+ },
5388
+ onMouseEnter: (e) => e.currentTarget.style.background = "#f0f0f0",
5389
+ onMouseLeave: (e) => e.currentTarget.style.background = "transparent",
5390
+ children: /* @__PURE__ */ jsx(Dismiss16Regular, {})
5391
+ }
5392
+ )
5243
5393
  ] }),
5244
- /* @__PURE__ */ jsxs("div", { className: "aoLexRow", children: [
5245
- /* @__PURE__ */ jsx("div", { className: "aoLexLabel", children: "Hex" }),
5394
+ /* @__PURE__ */ jsxs(
5395
+ "div",
5396
+ {
5397
+ ref: svRef,
5398
+ onPointerDown: handleSVPointerDown,
5399
+ onPointerMove: handleSVPointerMove,
5400
+ style: {
5401
+ position: "relative",
5402
+ width: "100%",
5403
+ height: 150,
5404
+ borderRadius: 8,
5405
+ overflow: "hidden",
5406
+ cursor: "crosshair",
5407
+ touchAction: "none",
5408
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5409
+ },
5410
+ children: [
5411
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: hueColor } }),
5412
+ /* @__PURE__ */ jsx(
5413
+ "div",
5414
+ {
5415
+ style: {
5416
+ position: "absolute",
5417
+ inset: 0,
5418
+ background: "linear-gradient(to right, #fff, rgba(255,255,255,0))"
5419
+ }
5420
+ }
5421
+ ),
5422
+ /* @__PURE__ */ jsx(
5423
+ "div",
5424
+ {
5425
+ style: {
5426
+ position: "absolute",
5427
+ inset: 0,
5428
+ background: "linear-gradient(to top, #000, rgba(0,0,0,0))"
5429
+ }
5430
+ }
5431
+ ),
5432
+ /* @__PURE__ */ jsx(
5433
+ "div",
5434
+ {
5435
+ style: {
5436
+ position: "absolute",
5437
+ width: 16,
5438
+ height: 16,
5439
+ borderRadius: "50%",
5440
+ border: "2px solid #fff",
5441
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5442
+ transform: "translate(-50%, -50%)",
5443
+ pointerEvents: "none",
5444
+ ...svThumb
5445
+ }
5446
+ }
5447
+ )
5448
+ ]
5449
+ }
5450
+ ),
5451
+ /* @__PURE__ */ jsx(
5452
+ "div",
5453
+ {
5454
+ ref: hueRef,
5455
+ onPointerDown: handleHuePointerDown,
5456
+ onPointerMove: handleHuePointerMove,
5457
+ style: {
5458
+ position: "relative",
5459
+ width: "100%",
5460
+ height: 12,
5461
+ borderRadius: 999,
5462
+ cursor: "pointer",
5463
+ touchAction: "none",
5464
+ background: "linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
5465
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)"
5466
+ },
5467
+ children: /* @__PURE__ */ jsx(
5468
+ "div",
5469
+ {
5470
+ style: {
5471
+ position: "absolute",
5472
+ top: "50%",
5473
+ width: 16,
5474
+ height: 16,
5475
+ borderRadius: "50%",
5476
+ background: hueColor,
5477
+ border: "2px solid #fff",
5478
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.4)",
5479
+ transform: "translate(-50%, -50%)",
5480
+ pointerEvents: "none",
5481
+ ...hueThumb
5482
+ }
5483
+ }
5484
+ )
5485
+ }
5486
+ ),
5487
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5488
+ /* @__PURE__ */ jsx(
5489
+ "div",
5490
+ {
5491
+ style: {
5492
+ width: 32,
5493
+ height: 32,
5494
+ borderRadius: 6,
5495
+ flexShrink: 0,
5496
+ background: hex,
5497
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.12)"
5498
+ }
5499
+ }
5500
+ ),
5246
5501
  /* @__PURE__ */ jsx(
5247
5502
  TextField,
5248
5503
  {
5249
- value: hex,
5250
- onChange: (_, val) => setHex(normalizeHex(val || "")),
5251
- onBlur: () => setDraft(normalizeHex(hex))
5504
+ value: hexText,
5505
+ onChange: handleHexChange,
5506
+ onBlur: handleHexBlur,
5507
+ onKeyDown: (e) => {
5508
+ if (e.key === "Enter") commit(normalizeHex(hexText));
5509
+ },
5510
+ styles: { root: { flex: 1 }, fieldGroup: { borderRadius: 6 } }
5252
5511
  }
5253
5512
  )
5254
5513
  ] }),
5255
- /* @__PURE__ */ jsx("div", { className: "aoLexSwatches", children: PRESET.map((c) => /* @__PURE__ */ jsx(
5256
- "button",
5257
- {
5258
- type: "button",
5259
- className: "aoLexSwatchBtn",
5260
- style: { background: c },
5261
- onClick: () => setDraft(c),
5262
- title: c
5263
- },
5264
- c
5265
- )) }),
5266
- /* @__PURE__ */ jsxs("div", { className: "aoLexSV", ref: svRef, onClick: handleSVClick, children: [
5267
- /* @__PURE__ */ jsx("div", { className: "aoLexSVHue", style: { background: hueColor } }),
5268
- /* @__PURE__ */ jsx("div", { className: "aoLexSVWhite" }),
5269
- /* @__PURE__ */ jsx("div", { className: "aoLexSVBlack" }),
5270
- /* @__PURE__ */ jsx("div", { className: "aoLexSVThumb", style: svThumb })
5271
- ] }),
5272
- /* @__PURE__ */ jsx("div", { className: "aoLexHue", ref: hueRef, onClick: handleHueClick, children: /* @__PURE__ */ jsx("div", { className: "aoLexHueThumb", style: hueThumb }) }),
5273
- /* @__PURE__ */ jsx("div", { className: "aoLexPreview", style: { background: hex } }),
5274
- /* @__PURE__ */ jsxs("div", { className: "aoLexActions", children: [
5275
- /* @__PURE__ */ jsx(DefaultButton, { type: "button", text: "Apply", onClick: handleApply }),
5276
- /* @__PURE__ */ jsx(DefaultButton, { type: "button", text: "Close", onClick: () => setOpenAndNotify(false) })
5514
+ /* @__PURE__ */ jsxs("div", { children: [
5515
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "#8a8a8a", marginBottom: 6, letterSpacing: 0.3 }, children: "STANDARD COLORS" }),
5516
+ /* @__PURE__ */ jsx(
5517
+ "div",
5518
+ {
5519
+ style: {
5520
+ display: "grid",
5521
+ gridTemplateColumns: "repeat(9, 1fr)",
5522
+ gap: 6
5523
+ },
5524
+ children: PRESET.map((c) => {
5525
+ const isSelected = c.toLowerCase() === hex.toLowerCase();
5526
+ return /* @__PURE__ */ jsx(
5527
+ "button",
5528
+ {
5529
+ type: "button",
5530
+ onClick: () => commit(c),
5531
+ title: c,
5532
+ "aria-label": c,
5533
+ style: {
5534
+ width: 22,
5535
+ height: 22,
5536
+ padding: 0,
5537
+ borderRadius: 5,
5538
+ background: c,
5539
+ cursor: "pointer",
5540
+ boxShadow: isSelected ? "0 0 0 2px #fff, 0 0 0 3px #4a86e8" : "inset 0 0 0 1px rgba(0,0,0,0.15)",
5541
+ border: "none",
5542
+ transition: "transform 80ms ease"
5543
+ },
5544
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.12)",
5545
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)"
5546
+ },
5547
+ c
5548
+ );
5549
+ })
5550
+ }
5551
+ )
5277
5552
  ] })
5278
5553
  ] })
5279
5554
  }
@@ -5605,294 +5880,464 @@ var FontSizePlugin = ({ disabled }) => {
5605
5880
  "fontsize"
5606
5881
  ) });
5607
5882
  };
5608
- var InsertLinkPlugin = ({ disabled }) => {
5883
+ var VERBATIM_LINK_RE = /^https?:\/\/|^mailto:|^tel:|^#|^\//i;
5884
+ function getLinkValidationMessage(raw) {
5885
+ const trimmed = raw.trim();
5886
+ if (!trimmed) return void 0;
5887
+ if (/\s/.test(trimmed)) return "URL cannot contain spaces";
5888
+ if (!VERBATIM_LINK_RE.test(trimmed) && !trimmed.includes(".")) {
5889
+ return "Enter a valid URL (e.g. example.com)";
5890
+ }
5891
+ return void 0;
5892
+ }
5893
+ var InsertLinkPlugin = ({
5894
+ disabled,
5895
+ open: externalOpen,
5896
+ onClose
5897
+ }) => {
5609
5898
  const [editor] = useLexicalComposerContext();
5610
- const [isOpen, setIsOpen] = useState(false);
5899
+ const [internalOpen, setInternalOpen] = useState(false);
5611
5900
  const [text, setText] = useState("");
5612
5901
  const [link, setLink] = useState("");
5613
5902
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
5903
+ const isControlled = externalOpen !== void 0;
5904
+ const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
5905
+ const linkError = getLinkValidationMessage(link);
5906
+ const handleClose = () => {
5907
+ setText("");
5908
+ setLink("");
5909
+ if (isControlled) onClose?.();
5910
+ else setInternalOpen(false);
5911
+ };
5614
5912
  const insertLink = (text2, link2) => {
5615
5913
  if (disabled) return;
5914
+ if (getLinkValidationMessage(link2)) return;
5915
+ const trimmedLink = link2.trim();
5916
+ const href = VERBATIM_LINK_RE.test(trimmedLink) ? trimmedLink : `https://${trimmedLink}`;
5616
5917
  editor.update(() => {
5617
- setText("");
5618
- setLink("");
5619
5918
  const selection = $getSelection();
5620
5919
  if ($isRangeSelection(selection)) {
5621
5920
  const textNode = new TextNode(text2);
5622
- const linkNode = $createLinkNode(link2.startsWith("http") ? link2 : `https://${link2}`);
5921
+ const linkNode = $createLinkNode(href);
5623
5922
  linkNode.append(textNode);
5624
5923
  selection.insertNodes([linkNode]);
5625
5924
  }
5626
- setIsOpen(false);
5627
5925
  });
5926
+ handleClose();
5628
5927
  };
5928
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5929
+ !isControlled && /* @__PURE__ */ jsx(
5930
+ Button,
5931
+ {
5932
+ size: "small",
5933
+ title: "Add link",
5934
+ disabled,
5935
+ icon: /* @__PURE__ */ jsx(LinkAddRegular, { style: { color: iconColor } }),
5936
+ style: {
5937
+ background: isOpen && !disabled ? "#ebebeb" : "none",
5938
+ border: "none",
5939
+ margin: 2,
5940
+ opacity: disabled ? 0.55 : 1,
5941
+ cursor: disabled ? "not-allowed" : "pointer"
5942
+ },
5943
+ onClick: () => {
5944
+ if (disabled) return;
5945
+ setInternalOpen((prev) => !prev);
5946
+ }
5947
+ },
5948
+ "upload-link"
5949
+ ),
5950
+ /* @__PURE__ */ jsx(
5951
+ Dialog,
5952
+ {
5953
+ open: isOpen,
5954
+ onOpenChange: (_, data) => {
5955
+ if (!data.open) handleClose();
5956
+ },
5957
+ children: /* @__PURE__ */ jsx(DialogSurface, { style: { maxWidth: "380px" }, children: /* @__PURE__ */ jsxs(DialogBody, { children: [
5958
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Insert Link" }),
5959
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
5960
+ /* @__PURE__ */ jsx(Field, { label: "Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5961
+ Input,
5962
+ {
5963
+ autoFocus: !disabled,
5964
+ value: text,
5965
+ appearance: "underline",
5966
+ placeholder: "Text",
5967
+ disabled,
5968
+ onChange: (_, v) => setText(v.value)
5969
+ }
5970
+ ) }),
5971
+ /* @__PURE__ */ jsx(
5972
+ Field,
5973
+ {
5974
+ label: "Link",
5975
+ orientation: "horizontal",
5976
+ size: "small",
5977
+ validationState: linkError ? "error" : "none",
5978
+ validationMessage: linkError,
5979
+ children: /* @__PURE__ */ jsx(
5980
+ Input,
5981
+ {
5982
+ value: link,
5983
+ appearance: "underline",
5984
+ placeholder: "Link",
5985
+ disabled,
5986
+ onChange: (_, v) => setLink(v.value),
5987
+ onKeyDown: (e) => {
5988
+ if (e.key === "Enter" && text && link && !linkError) insertLink(text, link);
5989
+ }
5990
+ }
5991
+ )
5992
+ }
5993
+ )
5994
+ ] }) }),
5995
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
5996
+ /* @__PURE__ */ jsx(
5997
+ Button,
5998
+ {
5999
+ appearance: "primary",
6000
+ size: "small",
6001
+ disabled: disabled || !text || !link || !!linkError,
6002
+ onClick: () => insertLink(text, link),
6003
+ children: "Add"
6004
+ }
6005
+ ),
6006
+ /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6007
+ ] })
6008
+ ] }) })
6009
+ }
6010
+ )
6011
+ ] });
6012
+ };
6013
+ function PageSetupPlugin({ disabled, value, onChange }) {
6014
+ const sizeLabel = value.size === "pageless" ? "Pageless" : PAGE_SIZE_OPTIONS.find((o) => o.key === value.size)?.label ?? "Pageless";
6015
+ const isPaged = value.size !== "pageless";
5629
6016
  return /* @__PURE__ */ jsxs(
5630
- Popover,
6017
+ Menu,
5631
6018
  {
5632
- trapFocus: true,
5633
- withArrow: true,
5634
- open: disabled ? false : isOpen,
5635
- onOpenChange: (_, data) => {
5636
- if (!disabled) setIsOpen(data.open);
6019
+ checkedValues: {
6020
+ size: [value.size],
6021
+ orientation: [value.orientation],
6022
+ margin: [value.margin]
6023
+ },
6024
+ onCheckedValueChange: (_, data) => {
6025
+ const selected = data.checkedItems[0];
6026
+ if (!selected) return;
6027
+ if (data.name === "size") onChange({ ...value, size: selected });
6028
+ else if (data.name === "orientation")
6029
+ onChange({ ...value, orientation: selected });
6030
+ else if (data.name === "margin") onChange({ ...value, margin: selected });
5637
6031
  },
5638
6032
  children: [
5639
- /* @__PURE__ */ jsx(PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
6033
+ /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
5640
6034
  Button,
5641
6035
  {
6036
+ appearance: "subtle",
5642
6037
  size: "small",
5643
- title: "Add link",
5644
6038
  disabled,
5645
- icon: /* @__PURE__ */ jsx(LinkAddRegular, { style: { color: iconColor } }),
5646
- style: {
5647
- background: isOpen && !disabled ? "#ebebeb" : "none",
5648
- border: "none",
5649
- margin: 2,
5650
- opacity: disabled ? 0.55 : 1,
5651
- cursor: disabled ? "not-allowed" : "pointer"
5652
- },
5653
- onClick: () => {
5654
- if (!disabled) setIsOpen((prev) => !prev);
5655
- }
5656
- },
5657
- "upload-link"
5658
- ) }),
5659
- /* @__PURE__ */ jsx(
5660
- PopoverSurface,
5661
- {
5662
- style: {
5663
- width: "270px",
5664
- opacity: disabled ? 0.6 : 1,
5665
- pointerEvents: disabled ? "none" : "auto"
5666
- },
5667
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, children: [
5668
- /* @__PURE__ */ jsx(Field, { label: "Text", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5669
- Input,
5670
- {
5671
- autoFocus: !disabled,
5672
- value: text,
5673
- appearance: "underline",
5674
- placeholder: "Text",
5675
- disabled,
5676
- onChange: (_, v) => setText(v.value)
5677
- }
5678
- ) }),
5679
- /* @__PURE__ */ jsx(Field, { label: "Link", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5680
- Input,
5681
- {
5682
- value: link,
5683
- appearance: "underline",
5684
- placeholder: "Link",
5685
- disabled,
5686
- onChange: (_, v) => setLink(v.value)
5687
- }
5688
- ) }),
5689
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
5690
- /* @__PURE__ */ jsx(
5691
- Button,
5692
- {
5693
- size: "small",
5694
- disabled: disabled || !text || !link,
5695
- onClick: () => insertLink(text, link),
5696
- children: "Add"
5697
- }
5698
- ),
5699
- /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
5700
- ] })
5701
- ] })
6039
+ icon: /* @__PURE__ */ jsx(DocumentRegular, {}),
6040
+ title: "Page setup",
6041
+ style: { minWidth: "auto" },
6042
+ children: sizeLabel
5702
6043
  }
5703
- )
6044
+ ) }),
6045
+ /* @__PURE__ */ jsx(MenuPopover, { style: { minWidth: 220 }, children: /* @__PURE__ */ jsxs(MenuList, { children: [
6046
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6047
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Page size" }),
6048
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "size", value: "pageless", children: "Pageless" }),
6049
+ PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ jsx(MenuItemRadio, { name: "size", value: opt.key, children: opt.label }, opt.key))
6050
+ ] }),
6051
+ /* @__PURE__ */ jsx(MenuDivider, {}),
6052
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6053
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Orientation" }),
6054
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "orientation", value: "portrait", disabled: !isPaged, children: "Portrait" }),
6055
+ /* @__PURE__ */ jsx(MenuItemRadio, { name: "orientation", value: "landscape", disabled: !isPaged, children: "Landscape" })
6056
+ ] }),
6057
+ /* @__PURE__ */ jsx(MenuDivider, {}),
6058
+ /* @__PURE__ */ jsxs(MenuGroup, { children: [
6059
+ /* @__PURE__ */ jsx(MenuGroupHeader, { children: "Margins" }),
6060
+ MARGIN_OPTIONS.map((opt) => /* @__PURE__ */ jsx(MenuItemRadio, { name: "margin", value: opt.key, disabled: !isPaged, children: opt.label }, opt.key))
6061
+ ] })
6062
+ ] }) })
5704
6063
  ]
5705
6064
  }
5706
6065
  );
5707
- };
5708
- var TableItemPlugin = ({ disabled }) => {
6066
+ }
6067
+ var MAX_ROWS = 50;
6068
+ var MAX_COLS = 50;
6069
+ var TableItemPlugin = ({
6070
+ disabled,
6071
+ open: externalOpen,
6072
+ onClose
6073
+ }) => {
5709
6074
  const [editor] = useLexicalComposerContext();
5710
6075
  const [columns, setColumns] = useState("");
5711
6076
  const [rows, setRows] = useState("");
5712
- const [isOpen, setIsOpen] = useState(false);
6077
+ const [internalOpen, setInternalOpen] = useState(false);
6078
+ const [rowError, setRowError] = useState("");
6079
+ const [colError, setColError] = useState("");
6080
+ const isControlled = externalOpen !== void 0;
6081
+ const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
5713
6082
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#333333";
6083
+ const handleClose = () => {
6084
+ setRows("");
6085
+ setColumns("");
6086
+ setRowError("");
6087
+ setColError("");
6088
+ if (isControlled) onClose?.();
6089
+ else setInternalOpen(false);
6090
+ };
6091
+ const onRowsChange = (val) => {
6092
+ const clean = val.replace(/\D/g, "");
6093
+ setRows(clean);
6094
+ const n = Number(clean);
6095
+ if (clean && n > MAX_ROWS) setRowError(`Maximum ${MAX_ROWS} rows allowed`);
6096
+ else setRowError("");
6097
+ };
6098
+ const onColsChange = (val) => {
6099
+ const clean = val.replace(/\D/g, "");
6100
+ setColumns(clean);
6101
+ const n = Number(clean);
6102
+ if (clean && n > MAX_COLS) setColError(`Maximum ${MAX_COLS} columns allowed`);
6103
+ else setColError("");
6104
+ };
5714
6105
  const onAddTable = () => {
5715
6106
  if (disabled) return;
5716
6107
  const row = Number(rows);
5717
6108
  const col = Number(columns);
5718
6109
  if (!row || !col) return;
6110
+ if (row > MAX_ROWS || col > MAX_COLS) return;
5719
6111
  editor.update(() => {
5720
6112
  const tableNode = $createTableNodeWithDimensions(row, col, true);
5721
6113
  $insertNodeToNearestRoot(tableNode);
5722
6114
  });
5723
- setRows("");
5724
- setColumns("");
5725
- setIsOpen(false);
6115
+ handleClose();
5726
6116
  };
5727
- return /* @__PURE__ */ jsxs(
5728
- Popover,
5729
- {
5730
- trapFocus: true,
5731
- withArrow: true,
5732
- open: disabled ? false : isOpen,
5733
- onOpenChange: (_, data) => {
5734
- if (!disabled) setIsOpen(data.open);
6117
+ const isAddDisabled = disabled || !rows || !columns || !!rowError || !!colError;
6118
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6119
+ !isControlled && /* @__PURE__ */ jsx(
6120
+ Button,
6121
+ {
6122
+ size: "small",
6123
+ title: "Add table",
6124
+ disabled,
6125
+ icon: /* @__PURE__ */ jsx(TableAddRegular, { style: { color: iconColor } }),
6126
+ style: {
6127
+ background: isOpen && !disabled ? "#ebebeb" : "none",
6128
+ border: "none",
6129
+ margin: 2,
6130
+ opacity: disabled ? 0.55 : 1,
6131
+ cursor: disabled ? "not-allowed" : "pointer"
6132
+ },
6133
+ onClick: () => {
6134
+ if (disabled) return;
6135
+ setRows("");
6136
+ setColumns("");
6137
+ setRowError("");
6138
+ setColError("");
6139
+ setInternalOpen(true);
6140
+ }
5735
6141
  },
5736
- children: [
5737
- /* @__PURE__ */ jsx(PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
5738
- Button,
5739
- {
5740
- size: "small",
5741
- title: "Add table",
5742
- disabled,
5743
- icon: /* @__PURE__ */ jsx(TableAddRegular, { style: { color: iconColor } }),
5744
- style: {
5745
- background: isOpen && !disabled ? "#ebebeb" : "none",
5746
- border: "none",
5747
- margin: 2,
5748
- opacity: disabled ? 0.55 : 1,
5749
- cursor: disabled ? "not-allowed" : "pointer"
5750
- },
5751
- onClick: () => {
5752
- if (disabled) return;
5753
- setIsOpen((prev) => !prev);
5754
- setRows("");
5755
- setColumns("");
5756
- }
5757
- },
5758
- "insert-table-nodes"
5759
- ) }),
5760
- /* @__PURE__ */ jsx(
5761
- PopoverSurface,
5762
- {
5763
- style: {
5764
- width: "270px",
5765
- opacity: disabled ? 0.6 : 1,
5766
- pointerEvents: disabled ? "none" : "auto"
5767
- },
5768
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, children: [
5769
- /* @__PURE__ */ jsx(Field, { label: "Rows", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5770
- Input,
5771
- {
5772
- autoFocus: !disabled,
5773
- type: "number",
5774
- min: 1,
5775
- value: rows,
5776
- placeholder: "Rows",
5777
- appearance: "underline",
5778
- disabled,
5779
- input: { style: { textAlign: "left" } },
5780
- onChange: (_, v) => setRows(v.value.replace(/\D/g, ""))
5781
- }
5782
- ) }),
5783
- /* @__PURE__ */ jsx(Field, { label: "Columns", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5784
- Input,
5785
- {
5786
- type: "number",
5787
- min: 1,
5788
- value: columns,
5789
- placeholder: "Columns",
5790
- appearance: "underline",
5791
- disabled,
5792
- input: { style: { textAlign: "left" } },
5793
- onChange: (_, v) => setColumns(v.value.replace(/\D/g, ""))
5794
- }
5795
- ) }),
5796
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
5797
- /* @__PURE__ */ jsx(
5798
- Button,
6142
+ "insert-table-nodes"
6143
+ ),
6144
+ /* @__PURE__ */ jsx(
6145
+ Dialog,
6146
+ {
6147
+ open: isOpen,
6148
+ onOpenChange: (_, data) => {
6149
+ if (!data.open) handleClose();
6150
+ },
6151
+ children: /* @__PURE__ */ jsx(DialogSurface, { style: { maxWidth: "380px" }, children: /* @__PURE__ */ jsxs(DialogBody, { children: [
6152
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Insert Table" }),
6153
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
6154
+ /* @__PURE__ */ jsx(
6155
+ Field,
6156
+ {
6157
+ label: "Rows",
6158
+ orientation: "horizontal",
6159
+ size: "small",
6160
+ validationMessage: rowError || void 0,
6161
+ validationState: rowError ? "error" : "none",
6162
+ children: /* @__PURE__ */ jsx(
6163
+ Input,
5799
6164
  {
5800
- size: "small",
5801
- appearance: "primary",
5802
- disabled: disabled || !rows || !columns,
5803
- onClick: onAddTable,
5804
- children: "Add"
6165
+ autoFocus: !disabled,
6166
+ type: "number",
6167
+ min: 1,
6168
+ max: MAX_ROWS,
6169
+ value: rows,
6170
+ placeholder: "Rows",
6171
+ appearance: "underline",
6172
+ disabled,
6173
+ input: { style: { textAlign: "left" } },
6174
+ onChange: (_, v) => onRowsChange(v.value)
5805
6175
  }
5806
- ),
5807
- /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
5808
- ] })
5809
- ] })
5810
- }
5811
- )
5812
- ]
5813
- }
5814
- );
6176
+ )
6177
+ }
6178
+ ),
6179
+ /* @__PURE__ */ jsx(
6180
+ Field,
6181
+ {
6182
+ label: "Columns",
6183
+ orientation: "horizontal",
6184
+ size: "small",
6185
+ validationMessage: colError || void 0,
6186
+ validationState: colError ? "error" : "none",
6187
+ children: /* @__PURE__ */ jsx(
6188
+ Input,
6189
+ {
6190
+ type: "number",
6191
+ min: 1,
6192
+ max: MAX_COLS,
6193
+ value: columns,
6194
+ placeholder: "Columns",
6195
+ appearance: "underline",
6196
+ disabled,
6197
+ input: { style: { textAlign: "left" } },
6198
+ onChange: (_, v) => onColsChange(v.value)
6199
+ }
6200
+ )
6201
+ }
6202
+ )
6203
+ ] }) }),
6204
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
6205
+ /* @__PURE__ */ jsx(
6206
+ Button,
6207
+ {
6208
+ appearance: "primary",
6209
+ size: "small",
6210
+ disabled: isAddDisabled,
6211
+ onClick: onAddTable,
6212
+ children: "Add"
6213
+ }
6214
+ ),
6215
+ /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6216
+ ] })
6217
+ ] }) })
6218
+ }
6219
+ )
6220
+ ] });
5815
6221
  };
5816
- var YoutubeUploadPlugin = ({ disabled }) => {
6222
+ function extractYouTubeId(url) {
6223
+ const trimmed = url.trim();
6224
+ if (/^[\w-]{11}$/.test(trimmed)) return trimmed;
6225
+ const match = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/|v\/|shorts\/|live\/|u\/\w\/))([^#&?]{11})/.exec(trimmed);
6226
+ return match ? match[1] : null;
6227
+ }
6228
+ var YoutubeUploadPlugin = ({
6229
+ disabled,
6230
+ open: externalOpen,
6231
+ onClose
6232
+ }) => {
5817
6233
  const [url, setURL] = useState("");
5818
- const [isOpen, setIsOpen] = useState(false);
6234
+ const [urlError, setUrlError] = useState("");
6235
+ const [internalOpen, setInternalOpen] = useState(false);
5819
6236
  const [editor] = useLexicalComposerContext();
5820
6237
  const iconColor = disabled ? "var(--colorNeutralForegroundDisabled, #A6A6A6)" : "#424242";
6238
+ const isControlled = externalOpen !== void 0;
6239
+ const isOpen = isControlled ? !!externalOpen && !disabled : internalOpen && !disabled;
6240
+ const handleClose = () => {
6241
+ setURL("");
6242
+ setUrlError("");
6243
+ if (isControlled) onClose?.();
6244
+ else setInternalOpen(false);
6245
+ };
5821
6246
  const onHandleEmbeded = () => {
5822
6247
  if (disabled) return;
5823
6248
  if (!url) return;
5824
- const match = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/.exec(url);
5825
- const id = match && match[2]?.length === 11 ? match[2] : null;
5826
- if (!id) return;
6249
+ const id = extractYouTubeId(url);
6250
+ if (!id) {
6251
+ setUrlError("Invalid YouTube URL. Supported: watch?v=, youtu.be/, /shorts/, /live/");
6252
+ return;
6253
+ }
6254
+ setUrlError("");
5827
6255
  editor.update(() => {
5828
6256
  const node = $createYouTubeNode(id);
5829
6257
  $insertNodes([node]);
5830
6258
  });
5831
- setURL("");
5832
- setIsOpen(false);
6259
+ handleClose();
5833
6260
  };
5834
- return /* @__PURE__ */ jsxs(
5835
- Popover,
5836
- {
5837
- trapFocus: true,
5838
- withArrow: true,
5839
- open: disabled ? false : isOpen,
5840
- onOpenChange: (_, data) => {
5841
- if (!disabled) setIsOpen(data.open);
5842
- },
5843
- children: [
5844
- /* @__PURE__ */ jsx(PopoverTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
5845
- Button,
5846
- {
5847
- title: "Add youtube URL",
5848
- size: "small",
5849
- disabled,
5850
- icon: /* @__PURE__ */ jsx(VideoClipRegular, { style: { color: iconColor } }),
5851
- style: {
5852
- background: isOpen && !disabled ? "#ebebeb" : "none",
5853
- border: "none",
5854
- margin: 2,
5855
- opacity: disabled ? 0.55 : 1,
5856
- cursor: disabled ? "not-allowed" : "pointer"
5857
- },
5858
- onClick: () => {
5859
- if (disabled) return;
5860
- setIsOpen((prev) => !prev);
5861
- setURL("");
5862
- }
5863
- },
5864
- "upload-video"
5865
- ) }),
5866
- /* @__PURE__ */ jsx(
5867
- PopoverSurface,
5868
- {
5869
- style: {
5870
- width: "270px",
5871
- opacity: disabled ? 0.6 : 1,
5872
- pointerEvents: disabled ? "none" : "auto"
5873
- },
5874
- children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, children: [
5875
- /* @__PURE__ */ jsx(Field, { label: "URL", orientation: "horizontal", size: "small", children: /* @__PURE__ */ jsx(
5876
- Input,
5877
- {
5878
- autoFocus: !disabled,
5879
- disabled,
5880
- value: url,
5881
- appearance: "underline",
5882
- placeholder: "Add Youtube video URL",
5883
- onChange: (_, v) => setURL(v.value)
5884
- }
5885
- ) }),
5886
- /* @__PURE__ */ jsxs(Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 6 }, children: [
5887
- /* @__PURE__ */ jsx(Button, { size: "small", disabled: disabled || !url, onClick: onHandleEmbeded, children: "Add" }),
5888
- /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: () => setIsOpen(false), children: "Cancel" })
5889
- ] })
5890
- ] })
5891
- }
5892
- )
5893
- ]
5894
- }
5895
- );
6261
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6262
+ !isControlled && /* @__PURE__ */ jsx(
6263
+ Button,
6264
+ {
6265
+ title: "Add youtube URL",
6266
+ size: "small",
6267
+ disabled,
6268
+ icon: /* @__PURE__ */ jsx(VideoClipRegular, { style: { color: iconColor } }),
6269
+ style: {
6270
+ background: isOpen && !disabled ? "#ebebeb" : "none",
6271
+ border: "none",
6272
+ margin: 2,
6273
+ opacity: disabled ? 0.55 : 1,
6274
+ cursor: disabled ? "not-allowed" : "pointer"
6275
+ },
6276
+ onClick: () => {
6277
+ if (disabled) return;
6278
+ setURL("");
6279
+ setUrlError("");
6280
+ setInternalOpen(true);
6281
+ }
6282
+ },
6283
+ "upload-video"
6284
+ ),
6285
+ /* @__PURE__ */ jsx(
6286
+ Dialog,
6287
+ {
6288
+ open: isOpen,
6289
+ onOpenChange: (_, data) => {
6290
+ if (!data.open) handleClose();
6291
+ },
6292
+ children: /* @__PURE__ */ jsx(DialogSurface, { style: { maxWidth: "420px" }, children: /* @__PURE__ */ jsxs(DialogBody, { children: [
6293
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Insert YouTube Video" }),
6294
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Stack, { tokens: { childrenGap: 10 }, style: { paddingTop: 8 }, children: [
6295
+ /* @__PURE__ */ jsx(
6296
+ Field,
6297
+ {
6298
+ label: "URL",
6299
+ orientation: "horizontal",
6300
+ size: "small",
6301
+ validationState: urlError ? "error" : "none",
6302
+ validationMessage: urlError || void 0,
6303
+ children: /* @__PURE__ */ jsx(
6304
+ Input,
6305
+ {
6306
+ autoFocus: !disabled,
6307
+ disabled,
6308
+ value: url,
6309
+ appearance: "underline",
6310
+ placeholder: "Paste YouTube URL or video ID\u2026",
6311
+ onChange: (_, v) => {
6312
+ setURL(v.value);
6313
+ if (urlError) setUrlError("");
6314
+ },
6315
+ onKeyDown: (e) => {
6316
+ if (e.key === "Enter") onHandleEmbeded();
6317
+ }
6318
+ }
6319
+ )
6320
+ }
6321
+ ),
6322
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "#94a3b8", lineHeight: 1.5 }, children: "Supports: youtube.com/watch?v=\u2026, youtu.be/\u2026, /shorts/\u2026, /live/\u2026" })
6323
+ ] }) }),
6324
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
6325
+ /* @__PURE__ */ jsx(
6326
+ Button,
6327
+ {
6328
+ appearance: "primary",
6329
+ size: "small",
6330
+ disabled: disabled || !url,
6331
+ onClick: onHandleEmbeded,
6332
+ children: "Add"
6333
+ }
6334
+ ),
6335
+ /* @__PURE__ */ jsx(Button, { size: "small", disabled, onClick: handleClose, children: "Cancel" })
6336
+ ] })
6337
+ ] }) })
6338
+ }
6339
+ )
6340
+ ] });
5896
6341
  };
5897
6342
  var useStyles4 = makeStyles({
5898
6343
  dropdown: {
@@ -5917,12 +6362,14 @@ var ALLOWED_TOKENS = {
5917
6362
  Image: true,
5918
6363
  InlineImage: true,
5919
6364
  Youtube: true,
6365
+ Insert: true,
5920
6366
  Heading: true,
5921
6367
  FontFamily: true,
5922
6368
  FontSize: true,
5923
6369
  Decorators: true,
5924
6370
  CodeBlock: true,
5925
- Align: true
6371
+ Align: true,
6372
+ PageSetup: true
5926
6373
  };
5927
6374
  function sanitizePluginGroups(groups) {
5928
6375
  if (!groups || groups.length === 0) return [];
@@ -5947,6 +6394,7 @@ var ToolBarPlugins = (props) => {
5947
6394
  const [isLowercase, setIsLowercase] = useState(false);
5948
6395
  const [isCapitalize, setIsCapitalize] = useState(false);
5949
6396
  const [alignment, setAlignment] = useState("left");
6397
+ const [activeInsertDialog, setActiveInsertDialog] = useState(null);
5950
6398
  const lastSelectionRef = React9__default.useRef(null);
5951
6399
  const presetGroups = getToolbarGroupsByLevel(props.level);
5952
6400
  const pluginGroups = useMemo(() => sanitizePluginGroups(presetGroups), [presetGroups]);
@@ -6192,6 +6640,7 @@ var ToolBarPlugins = (props) => {
6192
6640
  marginRight: 8,
6193
6641
  verticalAlign: "middle"
6194
6642
  };
6643
+ const isDisabled = !isEditable || !!props.readOnly;
6195
6644
  switch (token) {
6196
6645
  case "Bold":
6197
6646
  return /* @__PURE__ */ jsx(
@@ -6255,6 +6704,95 @@ var ToolBarPlugins = (props) => {
6255
6704
  );
6256
6705
  case "Youtube":
6257
6706
  return /* @__PURE__ */ jsx(YoutubeUploadPlugin, { disabled: !isEditable || props.readOnly }, key);
6707
+ case "Insert": {
6708
+ const menuIconColor = isDisabled ? fgDisabled : fg;
6709
+ return /* @__PURE__ */ jsxs(React9__default.Fragment, { children: [
6710
+ /* @__PURE__ */ jsxs(Menu, { children: [
6711
+ /* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
6712
+ Button,
6713
+ {
6714
+ size: "small",
6715
+ disabled: isDisabled,
6716
+ icon: /* @__PURE__ */ jsx(AddRegular, { style: { color: menuIconColor } }),
6717
+ style: {
6718
+ ...getButtonStyle(false),
6719
+ gap: 4,
6720
+ paddingInline: 8
6721
+ },
6722
+ children: "Insert"
6723
+ }
6724
+ ) }),
6725
+ /* @__PURE__ */ jsx(MenuPopover, { children: /* @__PURE__ */ jsxs(MenuList, { children: [
6726
+ /* @__PURE__ */ jsx(
6727
+ MenuItem,
6728
+ {
6729
+ icon: /* @__PURE__ */ jsx(TableAddRegular, { style: { color: menuIconColor } }),
6730
+ onClick: () => !isDisabled && setActiveInsertDialog("table"),
6731
+ children: "Table"
6732
+ }
6733
+ ),
6734
+ /* @__PURE__ */ jsx(
6735
+ MenuItem,
6736
+ {
6737
+ icon: /* @__PURE__ */ jsx(ImageAddRegular, { style: { color: menuIconColor } }),
6738
+ onClick: () => !isDisabled && setActiveInsertDialog("image"),
6739
+ children: "Image"
6740
+ }
6741
+ ),
6742
+ /* @__PURE__ */ jsx(
6743
+ MenuItem,
6744
+ {
6745
+ icon: /* @__PURE__ */ jsx(ImageEditRegular, { style: { color: menuIconColor } }),
6746
+ onClick: () => !isDisabled && setActiveInsertDialog("inlineImage"),
6747
+ children: "Inline Image"
6748
+ }
6749
+ ),
6750
+ /* @__PURE__ */ jsx(
6751
+ MenuItem,
6752
+ {
6753
+ icon: /* @__PURE__ */ jsx(VideoClipRegular, { style: { color: menuIconColor } }),
6754
+ onClick: () => !isDisabled && setActiveInsertDialog("youtube"),
6755
+ children: "YouTube"
6756
+ }
6757
+ )
6758
+ ] }) })
6759
+ ] }),
6760
+ /* @__PURE__ */ jsx(
6761
+ TableItemPlugin,
6762
+ {
6763
+ disabled: isDisabled,
6764
+ open: activeInsertDialog === "table",
6765
+ onClose: () => setActiveInsertDialog(null)
6766
+ }
6767
+ ),
6768
+ /* @__PURE__ */ jsx(
6769
+ InsertImageDialog,
6770
+ {
6771
+ activeEditor: editor,
6772
+ disabled: isDisabled,
6773
+ open: activeInsertDialog === "image",
6774
+ onClose: () => setActiveInsertDialog(null)
6775
+ }
6776
+ ),
6777
+ /* @__PURE__ */ jsx(
6778
+ InsertInlineImageDialog,
6779
+ {
6780
+ activeEditor: editor,
6781
+ disabled: isDisabled,
6782
+ open: activeInsertDialog === "inlineImage",
6783
+ onClose: () => setActiveInsertDialog(null)
6784
+ }
6785
+ ),
6786
+ /* @__PURE__ */ jsx(
6787
+ YoutubeUploadPlugin,
6788
+ {
6789
+ disabled: isDisabled,
6790
+ open: activeInsertDialog === "youtube",
6791
+ onClose: () => setActiveInsertDialog(null)
6792
+ }
6793
+ )
6794
+ ] }, key);
6795
+ }
6258
6796
  case "Heading": {
6259
6797
  const headingLabel = selectNodeType === "paragraph" ? "Normal" : HEADING_OPTION.find((h) => h.key === selectNodeType)?.text ?? selectNodeType;
6260
6798
  return /* @__PURE__ */ jsxs(
@@ -6461,6 +6999,16 @@ var ToolBarPlugins = (props) => {
6461
6999
  key
6462
7000
  );
6463
7001
  }
7002
+ case "PageSetup":
7003
+ return /* @__PURE__ */ jsx(
7004
+ PageSetupPlugin,
7005
+ {
7006
+ disabled: !isEditable || props.readOnly,
7007
+ value: props.pageSetup,
7008
+ onChange: props.onPageSetupChange
7009
+ },
7010
+ key
7011
+ );
6464
7012
  default:
6465
7013
  return null;
6466
7014
  }
@@ -6640,7 +7188,9 @@ function BrowserSpellCheckPlugin({ enabled }) {
6640
7188
  }, [editor, enabled]);
6641
7189
  return null;
6642
7190
  }
6643
- function WordCountPlugin({ onCountChange }) {
7191
+ function WordCountPlugin({
7192
+ onCountChange
7193
+ }) {
6644
7194
  const [editor] = useLexicalComposerContext();
6645
7195
  useEffect(() => {
6646
7196
  return editor.registerUpdateListener(({ editorState }) => {
@@ -6653,7 +7203,9 @@ function WordCountPlugin({ onCountChange }) {
6653
7203
  }, [editor, onCountChange]);
6654
7204
  return null;
6655
7205
  }
6656
- function CharCountPlugin({ onCountChange }) {
7206
+ function CharCountPlugin({
7207
+ onCountChange
7208
+ }) {
6657
7209
  const [editor] = useLexicalComposerContext();
6658
7210
  useEffect(() => {
6659
7211
  return editor.registerUpdateListener(({ editorState }) => {
@@ -6737,7 +7289,10 @@ function _adaptRawSpell(data, text) {
6737
7289
  }
6738
7290
  const rawGrammar = data.grammar_correction ?? data.improved_text;
6739
7291
  const grammarCorrection = rawGrammar && rawGrammar.trim() !== text.trim() ? rawGrammar.trim() : void 0;
6740
- return { issues: issues.sort((a, b) => a.offset - b.offset), grammarCorrection };
7292
+ return {
7293
+ issues: issues.sort((a, b) => a.offset - b.offset),
7294
+ grammarCorrection
7295
+ };
6741
7296
  }
6742
7297
  function _adaptRawSuggest(data) {
6743
7298
  if (!data) return null;
@@ -6792,348 +7347,385 @@ function _makeQueryFn(fn) {
6792
7347
  };
6793
7348
  };
6794
7349
  }
6795
- var ContentEditorComponent = forwardRef(
6796
- (props, ref) => {
6797
- const isReadOnly = !!props.readOnly;
6798
- const resolvedSpellCheck = React9__default.useMemo(
6799
- () => props.spellCheckFn ? _makeSpellCheckFn(props.spellCheckFn) : props.useSpellCheck,
6800
- // eslint-disable-next-line react-hooks/exhaustive-deps
6801
- [props.spellCheckFn, props.useSpellCheck]
6802
- );
6803
- const resolvedQuery = React9__default.useMemo(
6804
- () => props.suggestFn ? _makeQueryFn(props.suggestFn) : props.useQuery,
6805
- // eslint-disable-next-line react-hooks/exhaustive-deps
6806
- [props.suggestFn, props.useQuery]
6807
- );
6808
- const [floatingAnchorElem, setFloatingAnchorElem] = useState(null);
6809
- const [isLinkEditMode, setIsLinkEditMode] = useState(false);
6810
- const [wordCount, setWordCount] = useState(0);
6811
- const handleWordCount = useCallback((count) => setWordCount(count), []);
6812
- const [charCount, setCharCount] = useState(0);
6813
- const handleCharCount = useCallback((count) => setCharCount(count), []);
6814
- const [refErrors, setRefErrors] = useState([]);
6815
- const contentEditableDomRef = useRef(null);
6816
- const previousOverLimitRef = useRef(false);
6817
- const focusedRef = useRef(false);
6818
- const setFocused = (focused) => {
6819
- focusedRef.current = focused;
6820
- };
6821
- const containerRef = useRef(null);
6822
- const onAnchorRef = (elem) => {
6823
- if (elem) setFloatingAnchorElem(elem);
6824
- };
6825
- const initialConfig = {
6826
- namespace: props.namespace,
6827
- theme,
6828
- onError: () => {
6829
- },
6830
- nodes: [
6831
- HeadingNode,
6832
- QuoteNode,
6833
- CodeHighlightNode,
6834
- CodeNode,
6835
- ListNode,
6836
- ListItemNode,
6837
- LinkNode,
6838
- AutoLinkNode,
6839
- TableNode,
6840
- TableRowNode,
6841
- TableCellNode,
6842
- ImageNode,
6843
- InlineImageNode,
6844
- YouTubeNode,
6845
- PageBreakNode,
6846
- AutocompleteNode,
6847
- SpellErrorNode,
6848
- HtmlBlockNode
6849
- ]
6850
- };
6851
- const EditorStyles = mergeStyleSets({
6852
- editorPlaceholder: {
6853
- color: "var(--colorNeutralForeground3, grey)",
6854
- position: "absolute",
6855
- top: props.level !== "none" /* None */ ? "17px" : "27px",
6856
- left: 20,
6857
- right: 20,
6858
- fontSize: "14px",
6859
- pointerEvents: "none",
6860
- userSelect: "none"
6861
- },
6862
- contentEditor: {
6863
- zIndex: 0,
6864
- flex: "auto",
6865
- outline: "none",
6866
- overflow: "auto",
6867
- marginTop: "0px",
6868
- paddingLeft: "20px",
6869
- paddingRight: "20px",
6870
- position: "relative",
6871
- background: "var(--colorNeutralBackground1, #ffffff)",
6872
- justifyContent: "center",
6873
- height: props.contentHeight ?? "100%",
6874
- ...isReadOnly && {
6875
- cursor: "not-allowed",
6876
- opacity: 0.75,
6877
- userSelect: "text"
6878
- }
6879
- }
6880
- });
6881
- const urlRegExp = new RegExp(
6882
- /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/
6883
- );
6884
- const validateUrl = (url) => url === "https://" || urlRegExp.test(url);
6885
- const handleReadOnlyClickCapture = (e) => {
6886
- if (!isReadOnly) return;
6887
- const target = e.target;
6888
- const anchor = target?.closest?.("a");
6889
- if (anchor) {
6890
- e.preventDefault();
6891
- e.stopPropagation();
7350
+ var ContentEditorComponent = forwardRef((props, ref) => {
7351
+ const isReadOnly = !!props.readOnly;
7352
+ const resolvedSpellCheck = React9__default.useMemo(
7353
+ () => props.spellCheckFn ? _makeSpellCheckFn(props.spellCheckFn) : props.useSpellCheck,
7354
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7355
+ [props.spellCheckFn, props.useSpellCheck]
7356
+ );
7357
+ const resolvedQuery = React9__default.useMemo(
7358
+ () => props.suggestFn ? _makeQueryFn(props.suggestFn) : props.useQuery,
7359
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7360
+ [props.suggestFn, props.useQuery]
7361
+ );
7362
+ const [floatingAnchorElem, setFloatingAnchorElem] = useState(null);
7363
+ const [isLinkEditMode, setIsLinkEditMode] = useState(false);
7364
+ const [wordCount, setWordCount] = useState(0);
7365
+ const handleWordCount = useCallback(
7366
+ (count) => setWordCount(count),
7367
+ []
7368
+ );
7369
+ const [charCount, setCharCount] = useState(0);
7370
+ const handleCharCount = useCallback(
7371
+ (count) => setCharCount(count),
7372
+ []
7373
+ );
7374
+ const [refErrors, setRefErrors] = useState([]);
7375
+ const [pageSetup, setPageSetup] = useState(DEFAULT_PAGE_SETUP);
7376
+ const pageCanvas = resolvePageCanvasMetrics(pageSetup);
7377
+ const contentEditableDomRef = useRef(null);
7378
+ const previousOverLimitRef = useRef(false);
7379
+ const focusedRef = useRef(false);
7380
+ const setFocused = (focused) => {
7381
+ focusedRef.current = focused;
7382
+ };
7383
+ const containerRef = useRef(null);
7384
+ const onAnchorRef = (elem) => {
7385
+ if (elem) setFloatingAnchorElem(elem);
7386
+ };
7387
+ const initialConfig = {
7388
+ namespace: props.namespace,
7389
+ theme,
7390
+ onError: () => {
7391
+ },
7392
+ nodes: [
7393
+ HeadingNode,
7394
+ QuoteNode,
7395
+ CodeHighlightNode,
7396
+ CodeNode,
7397
+ ListNode,
7398
+ ListItemNode,
7399
+ LinkNode,
7400
+ AutoLinkNode,
7401
+ TableNode,
7402
+ TableRowNode,
7403
+ TableCellNode,
7404
+ ImageNode,
7405
+ InlineImageNode,
7406
+ YouTubeNode,
7407
+ PageBreakNode,
7408
+ AutocompleteNode,
7409
+ SpellErrorNode,
7410
+ HtmlBlockNode
7411
+ ]
7412
+ };
7413
+ const EditorStyles = mergeStyleSets({
7414
+ editorPlaceholder: {
7415
+ color: "var(--colorNeutralForeground3, grey)",
7416
+ position: "absolute",
7417
+ top: props.level !== "none" /* None */ ? "17px" : "27px",
7418
+ left: pageCanvas.paddingPx,
7419
+ right: pageCanvas.paddingPx,
7420
+ fontSize: "14px",
7421
+ pointerEvents: "none",
7422
+ userSelect: "none"
7423
+ },
7424
+ contentEditor: {
7425
+ zIndex: 0,
7426
+ flex: "auto",
7427
+ outline: "none",
7428
+ overflow: "auto",
7429
+ marginTop: "0px",
7430
+ position: "relative",
7431
+ background: "var(--colorNeutralBackground1, #ffffff)",
7432
+ justifyContent: "center",
7433
+ height: props.contentHeight ?? "100%",
7434
+ ...isReadOnly && {
7435
+ cursor: "not-allowed",
7436
+ opacity: 0.75,
7437
+ userSelect: "text"
6892
7438
  }
6893
- };
6894
- const [touched, setTouched] = useState(false);
6895
- const isOverLimit = props.wordLimit !== void 0 && wordCount > props.wordLimit;
6896
- const internalErrors = [];
6897
- if (isOverLimit) {
6898
- const m = props.errorMessages?.wordLimitExceeded;
6899
- internalErrors.push(
6900
- typeof m === "function" ? m(wordCount, props.wordLimit) : m ?? `Word limit exceeded (${wordCount} / ${props.wordLimit} words used)`
6901
- );
6902
- }
6903
- if (props.required && touched && wordCount === 0) {
6904
- internalErrors.push(
6905
- props.errorMessages?.required ?? "This field is required"
6906
- );
6907
7439
  }
6908
- if (props.minWords !== void 0 && touched && wordCount < props.minWords) {
6909
- const m = props.errorMessages?.minWords;
6910
- internalErrors.push(
6911
- typeof m === "function" ? m(wordCount, props.minWords) : m ?? `Minimum ${props.minWords} words required (${wordCount} entered)`
6912
- );
6913
- }
6914
- if (props.maxChars !== void 0 && charCount > props.maxChars) {
6915
- const m = props.errorMessages?.maxCharsExceeded;
6916
- internalErrors.push(
6917
- typeof m === "function" ? m(charCount, props.maxChars) : m ?? `Character limit exceeded (${charCount} / ${props.maxChars} characters used)`
6918
- );
7440
+ });
7441
+ const urlRegExp = new RegExp(
7442
+ /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/
7443
+ );
7444
+ const validateUrl = (url) => url === "https://" || urlRegExp.test(url);
7445
+ const handleReadOnlyClickCapture = (e) => {
7446
+ if (!isReadOnly) return;
7447
+ const target = e.target;
7448
+ const anchor = target?.closest?.("a");
7449
+ if (anchor) {
7450
+ e.preventDefault();
7451
+ e.stopPropagation();
6919
7452
  }
6920
- if (props.minChars !== void 0 && touched && charCount < props.minChars && charCount > 0) {
6921
- const m = props.errorMessages?.minCharsRequired;
6922
- internalErrors.push(
6923
- typeof m === "function" ? m(charCount, props.minChars) : m ?? `Minimum ${props.minChars} characters required (${charCount} entered)`
6924
- );
7453
+ };
7454
+ const [touched, setTouched] = useState(false);
7455
+ const isOverLimit = props.wordLimit !== void 0 && wordCount > props.wordLimit;
7456
+ const internalErrors = [];
7457
+ if (isOverLimit) {
7458
+ const m = props.errorMessages?.wordLimitExceeded;
7459
+ internalErrors.push(
7460
+ typeof m === "function" ? m(wordCount, props.wordLimit) : m ?? `Word limit exceeded (${wordCount} / ${props.wordLimit} words used)`
7461
+ );
7462
+ }
7463
+ if (props.required && touched && wordCount === 0) {
7464
+ internalErrors.push(
7465
+ props.errorMessages?.required ?? "This field is required"
7466
+ );
7467
+ }
7468
+ if (props.minWords !== void 0 && touched && wordCount < props.minWords) {
7469
+ const m = props.errorMessages?.minWords;
7470
+ internalErrors.push(
7471
+ typeof m === "function" ? m(wordCount, props.minWords) : m ?? `Minimum ${props.minWords} words required (${wordCount} entered)`
7472
+ );
7473
+ }
7474
+ if (props.maxChars !== void 0 && charCount > props.maxChars) {
7475
+ const m = props.errorMessages?.maxCharsExceeded;
7476
+ internalErrors.push(
7477
+ typeof m === "function" ? m(charCount, props.maxChars) : m ?? `Character limit exceeded (${charCount} / ${props.maxChars} characters used)`
7478
+ );
7479
+ }
7480
+ if (props.minChars !== void 0 && touched && charCount < props.minChars && charCount > 0) {
7481
+ const m = props.errorMessages?.minCharsRequired;
7482
+ internalErrors.push(
7483
+ typeof m === "function" ? m(charCount, props.minChars) : m ?? `Minimum ${props.minChars} characters required (${charCount} entered)`
7484
+ );
7485
+ }
7486
+ const allErrors = [
7487
+ ...internalErrors,
7488
+ ...props.errors ?? [],
7489
+ ...refErrors
7490
+ ];
7491
+ const hasErrors = allErrors.length > 0;
7492
+ const hasRedBorder = hasErrors;
7493
+ useEffect(() => {
7494
+ if (props.wordLimit === void 0 || !props.onWordLimitExceeded) return;
7495
+ const wasOverLimit = previousOverLimitRef.current;
7496
+ if (isOverLimit !== wasOverLimit) {
7497
+ props.onWordLimitExceeded({
7498
+ wordCount,
7499
+ wordLimit: props.wordLimit,
7500
+ exceeded: isOverLimit
7501
+ });
7502
+ previousOverLimitRef.current = isOverLimit;
6925
7503
  }
6926
- const allErrors = [...internalErrors, ...props.errors ?? [], ...refErrors];
6927
- const hasErrors = allErrors.length > 0;
6928
- const hasRedBorder = hasErrors;
6929
- useEffect(() => {
6930
- if (props.wordLimit === void 0 || !props.onWordLimitExceeded) return;
6931
- const wasOverLimit = previousOverLimitRef.current;
6932
- if (isOverLimit !== wasOverLimit) {
6933
- props.onWordLimitExceeded({
6934
- wordCount,
6935
- wordLimit: props.wordLimit,
6936
- exceeded: isOverLimit
6937
- });
6938
- previousOverLimitRef.current = isOverLimit;
6939
- }
6940
- }, [isOverLimit, wordCount, props.wordLimit, props.onWordLimitExceeded]);
6941
- return /* @__PURE__ */ jsx(FluentProvider, { theme: webLightTheme, style: { height: "100%" }, children: /* @__PURE__ */ jsx(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsx("div", { ref: containerRef, style: { height: "100%" }, children: /* @__PURE__ */ jsxs(
6942
- Stack,
6943
- {
6944
- style: {
6945
- zIndex: 1e3,
6946
- background: "#fff",
6947
- borderRadius: "2px",
6948
- width: props.width ?? "100%",
6949
- height: props.height ?? "100%",
6950
- margin: props.margin ?? "5px auto",
6951
- border: `1px solid ${hasRedBorder ? "#c4272c" : "var(--colorNeutralStroke1, #ccced1)"}`,
6952
- transition: "border-color 0.2s",
6953
- display: "flex",
6954
- flexDirection: "column"
6955
- },
6956
- children: [
6957
- /* @__PURE__ */ jsx(
6958
- "div",
6959
- {
6960
- style: {
6961
- pointerEvents: isReadOnly ? "none" : "auto",
6962
- position: "sticky",
6963
- opacity: isReadOnly ? 0.85 : 1
6964
- },
6965
- children: /* @__PURE__ */ jsx(
6966
- ToolBarPlugins,
7504
+ }, [isOverLimit, wordCount, props.wordLimit, props.onWordLimitExceeded]);
7505
+ return /* @__PURE__ */ jsx(FluentProvider, { theme: webLightTheme, style: { height: "100%" }, children: /* @__PURE__ */ jsx(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsx("div", { ref: containerRef, style: { height: "100%" }, children: /* @__PURE__ */ jsxs(
7506
+ Stack,
7507
+ {
7508
+ style: {
7509
+ zIndex: 1e3,
7510
+ background: "#fff",
7511
+ borderRadius: "2px",
7512
+ width: props.width ?? "100%",
7513
+ height: props.height ?? "100%",
7514
+ margin: props.margin ?? "5px auto",
7515
+ border: `1px solid ${hasRedBorder ? "#c4272c" : "var(--colorNeutralStroke1, #ccced1)"}`,
7516
+ transition: "border-color 0.2s",
7517
+ display: "flex",
7518
+ flexDirection: "column"
7519
+ },
7520
+ children: [
7521
+ /* @__PURE__ */ jsx(
7522
+ "div",
7523
+ {
7524
+ style: {
7525
+ pointerEvents: isReadOnly ? "none" : "auto",
7526
+ position: "sticky",
7527
+ opacity: isReadOnly ? 0.85 : 1
7528
+ },
7529
+ children: /* @__PURE__ */ jsx(
7530
+ ToolBarPlugins,
7531
+ {
7532
+ level: props.level ?? "basic" /* Basic */,
7533
+ readOnly: props.readOnly,
7534
+ pageSetup,
7535
+ onPageSetupChange: setPageSetup
7536
+ }
7537
+ )
7538
+ }
7539
+ ),
7540
+ /* @__PURE__ */ jsxs(
7541
+ "div",
7542
+ {
7543
+ style: {
7544
+ position: "relative",
7545
+ flexGrow: 1,
7546
+ padding: "15px 0px",
7547
+ overflowY: "scroll",
7548
+ overflowX: "auto",
7549
+ minWidth: 0,
7550
+ background: pageCanvas.widthPx !== void 0 ? "#eef0f2" : void 0
7551
+ },
7552
+ onClickCapture: handleReadOnlyClickCapture,
7553
+ children: [
7554
+ /* @__PURE__ */ jsx(
7555
+ RichTextPlugin,
7556
+ {
7557
+ ErrorBoundary: LexicalErrorBoundary,
7558
+ contentEditable: /* @__PURE__ */ jsx(
7559
+ "div",
7560
+ {
7561
+ className: "editor",
7562
+ style: { height: "100%", position: "relative" },
7563
+ ref: onAnchorRef,
7564
+ children: /* @__PURE__ */ jsx(
7565
+ ContentEditable,
7566
+ {
7567
+ ref: contentEditableDomRef,
7568
+ className: css(EditorStyles.contentEditor),
7569
+ style: {
7570
+ paddingTop: props.level !== "none" /* None */ ? 0 : 10,
7571
+ paddingLeft: pageCanvas.paddingPx,
7572
+ paddingRight: pageCanvas.paddingPx,
7573
+ maxWidth: pageCanvas.widthPx,
7574
+ marginLeft: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7575
+ marginRight: pageCanvas.widthPx !== void 0 ? "auto" : void 0,
7576
+ boxShadow: pageCanvas.widthPx !== void 0 ? "0 0 0 1px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.08)" : void 0
7577
+ },
7578
+ spellCheck: !resolvedSpellCheck,
7579
+ autoCorrect: resolvedSpellCheck ? "off" : void 0,
7580
+ autoCapitalize: resolvedSpellCheck ? "off" : void 0
7581
+ }
7582
+ )
7583
+ }
7584
+ ),
7585
+ placeholder: /* @__PURE__ */ jsx(Stack, { className: css(EditorStyles.editorPlaceholder), children: props.placeholder })
7586
+ }
7587
+ ),
7588
+ props.wordLimit !== void 0 && /* @__PURE__ */ jsx(
7589
+ "div",
6967
7590
  {
6968
- level: props.level ?? "basic" /* Basic */,
6969
- readOnly: props.readOnly
7591
+ style: {
7592
+ position: "sticky",
7593
+ bottom: 0,
7594
+ display: "flex",
7595
+ justifyContent: "flex-end",
7596
+ paddingRight: 14,
7597
+ pointerEvents: "none",
7598
+ userSelect: "none"
7599
+ },
7600
+ children: /* @__PURE__ */ jsxs(
7601
+ "span",
7602
+ {
7603
+ style: {
7604
+ fontSize: "11px",
7605
+ color: isOverLimit ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
7606
+ fontWeight: isOverLimit ? 600 : 400,
7607
+ transition: "color 0.2s, font-weight 0.2s"
7608
+ },
7609
+ children: [
7610
+ wordCount,
7611
+ " / ",
7612
+ props.wordLimit,
7613
+ " words"
7614
+ ]
7615
+ }
7616
+ )
6970
7617
  }
6971
7618
  )
6972
- }
6973
- ),
6974
- /* @__PURE__ */ jsxs(
6975
- "div",
6976
- {
6977
- style: {
6978
- position: "relative",
6979
- flexGrow: 1,
6980
- padding: "15px 0px",
6981
- overflowY: "scroll",
6982
- overflowX: "auto",
6983
- minWidth: 0
6984
- },
6985
- onClickCapture: handleReadOnlyClickCapture,
6986
- children: [
6987
- /* @__PURE__ */ jsx(
6988
- RichTextPlugin,
6989
- {
6990
- ErrorBoundary: LexicalErrorBoundary,
6991
- contentEditable: /* @__PURE__ */ jsx(
6992
- "div",
6993
- {
6994
- className: "editor",
6995
- style: { height: "100%", position: "relative" },
6996
- ref: onAnchorRef,
6997
- children: /* @__PURE__ */ jsx(
6998
- ContentEditable,
6999
- {
7000
- ref: contentEditableDomRef,
7001
- className: css(EditorStyles.contentEditor),
7002
- style: { paddingTop: props.level !== "none" /* None */ ? 0 : 10 },
7003
- spellCheck: !resolvedSpellCheck,
7004
- autoCorrect: resolvedSpellCheck ? "off" : void 0,
7005
- autoCapitalize: resolvedSpellCheck ? "off" : void 0
7006
- }
7007
- )
7008
- }
7009
- ),
7010
- placeholder: /* @__PURE__ */ jsx(Stack, { className: css(EditorStyles.editorPlaceholder), children: props.placeholder })
7011
- }
7012
- ),
7013
- props.wordLimit !== void 0 && /* @__PURE__ */ jsx(
7014
- "div",
7015
- {
7016
- style: {
7017
- position: "sticky",
7018
- bottom: 0,
7019
- display: "flex",
7020
- justifyContent: "flex-end",
7021
- paddingRight: 14,
7022
- pointerEvents: "none",
7023
- userSelect: "none"
7024
- },
7025
- children: /* @__PURE__ */ jsxs(
7026
- "span",
7027
- {
7028
- style: {
7029
- fontSize: "11px",
7030
- color: isOverLimit ? "#c4272c" : "var(--colorNeutralForeground3, #aaa)",
7031
- fontWeight: isOverLimit ? 600 : 400,
7032
- transition: "color 0.2s, font-weight 0.2s"
7033
- },
7034
- children: [
7035
- wordCount,
7036
- " / ",
7037
- props.wordLimit,
7038
- " words"
7039
- ]
7040
- }
7041
- )
7042
- }
7043
- )
7044
- ]
7045
- }
7046
- ),
7047
- hasErrors && /* @__PURE__ */ jsx(
7048
- "div",
7049
- {
7050
- style: {
7051
- borderTop: "1px solid #fbd5d5",
7052
- background: "#fff8f8",
7053
- padding: "6px 12px 8px",
7054
- display: "flex",
7055
- flexDirection: "column",
7056
- gap: 4
7057
- },
7058
- children: allErrors.map((err, i) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
7059
- /* @__PURE__ */ jsx(ErrorCircleRegular, { style: { fontSize: 14, color: "#c4272c", flexShrink: 0 } }),
7060
- /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#c4272c" }, children: err })
7061
- ] }, i))
7062
- }
7063
- ),
7064
- /* @__PURE__ */ jsx(ReadOnlyPlugin, { readonly: isReadOnly }),
7065
- /* @__PURE__ */ jsx(BrowserSpellCheckPlugin, { enabled: !resolvedSpellCheck }),
7066
- /* @__PURE__ */ jsx(
7067
- FocusEventsPlugin,
7068
- {
7069
- onFocus: props.onFocus,
7070
- onBlur: () => {
7071
- setTouched(true);
7072
- props.onBlur?.();
7619
+ ]
7620
+ }
7621
+ ),
7622
+ hasErrors && /* @__PURE__ */ jsx(
7623
+ "div",
7624
+ {
7625
+ style: {
7626
+ borderTop: "1px solid #fbd5d5",
7627
+ background: "#fff8f8",
7628
+ padding: "6px 12px 8px",
7629
+ display: "flex",
7630
+ flexDirection: "column",
7631
+ gap: 4
7632
+ },
7633
+ children: allErrors.map((err, i) => /* @__PURE__ */ jsxs(
7634
+ "div",
7635
+ {
7636
+ style: { display: "flex", alignItems: "center", gap: 6 },
7637
+ children: [
7638
+ /* @__PURE__ */ jsx(
7639
+ ErrorCircleRegular,
7640
+ {
7641
+ style: { fontSize: 14, color: "#c4272c", flexShrink: 0 }
7642
+ }
7643
+ ),
7644
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#c4272c" }, children: err })
7645
+ ]
7073
7646
  },
7074
- setFocused,
7075
- containerRef
7076
- }
7077
- ),
7078
- props.autoFocus && !isReadOnly && /* @__PURE__ */ jsx(AutoFocusPlugin, {}),
7079
- /* @__PURE__ */ jsx(HistoryPlugin, {}),
7080
- /* @__PURE__ */ jsx(ListPlugin, {}),
7081
- /* @__PURE__ */ jsx(LinkPlugin, { validateUrl }),
7082
- /* @__PURE__ */ jsx(AutoLinkPlugin, { matchers: MATCHERS }),
7083
- /* @__PURE__ */ jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
7084
- !isReadOnly && /* @__PURE__ */ jsx(YoutubeDeletePlugin, {}),
7085
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableActionMenuPlugin, {}),
7086
- !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
7087
- !isReadOnly && /* @__PURE__ */ jsx(
7088
- FloatingLinkEditorPlugin,
7089
- {
7090
- anchorElem: floatingAnchorElem,
7091
- isLinkEditMode,
7092
- setIsLinkEditMode
7093
- }
7094
- ),
7095
- !isReadOnly && /* @__PURE__ */ jsx(ImagePlugin_default, {}),
7096
- !isReadOnly && /* @__PURE__ */ jsx(InlineImage_default, {}),
7097
- !isReadOnly && /* @__PURE__ */ jsx(PageBreakPlugin, {}),
7098
- !!resolvedQuery && !isReadOnly && /* @__PURE__ */ jsx(
7099
- AutocompletePlugin,
7100
- {
7101
- useQuery: resolvedQuery,
7102
- isReadOnly,
7103
- onSuggestionShown: props.onSuggestionShown,
7104
- onSuggestionAccept: props.onSuggestionAccept,
7105
- idleMs: props.suggestIdleMs ?? 300,
7106
- minWords: 4,
7107
- prefixWindow: 300
7108
- }
7109
- ),
7110
- !!resolvedSpellCheck && !isReadOnly && /* @__PURE__ */ jsx(
7111
- SpellCheckPlugin,
7112
- {
7113
- useSpellCheck: resolvedSpellCheck,
7114
- onSpellCheckAccept: props.onSpellCheckAccept,
7115
- idleMs: props.spellCheckIdleMs ?? 1200,
7116
- enabled: props.spellCheckEnabled !== false
7117
- }
7118
- ),
7119
- !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsx(CharacterStylesPopupPlugin, {}),
7120
- /* @__PURE__ */ jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
7121
- (props.wordLimit !== void 0 || props.required || props.minWords !== void 0) && /* @__PURE__ */ jsx(WordCountPlugin, { onCountChange: handleWordCount }),
7122
- (props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsx(CharCountPlugin, { onCountChange: handleCharCount }),
7123
- /* @__PURE__ */ jsx(
7124
- RefApiPlugin,
7125
- {
7126
- forwardedRef: ref,
7127
- contentEditableDomRef,
7128
- focusedRef,
7129
- setRefErrors
7130
- }
7131
- )
7132
- ]
7133
- }
7134
- ) }) }) });
7135
- }
7136
- );
7647
+ i
7648
+ ))
7649
+ }
7650
+ ),
7651
+ /* @__PURE__ */ jsx(ReadOnlyPlugin, { readonly: isReadOnly }),
7652
+ /* @__PURE__ */ jsx(BrowserSpellCheckPlugin, { enabled: !resolvedSpellCheck }),
7653
+ /* @__PURE__ */ jsx(
7654
+ FocusEventsPlugin,
7655
+ {
7656
+ onFocus: props.onFocus,
7657
+ onBlur: () => {
7658
+ setTouched(true);
7659
+ props.onBlur?.();
7660
+ },
7661
+ setFocused,
7662
+ containerRef
7663
+ }
7664
+ ),
7665
+ props.autoFocus && !isReadOnly && /* @__PURE__ */ jsx(AutoFocusPlugin, {}),
7666
+ /* @__PURE__ */ jsx(HistoryPlugin, {}),
7667
+ /* @__PURE__ */ jsx(ListPlugin, {}),
7668
+ /* @__PURE__ */ jsx(LinkPlugin, { validateUrl }),
7669
+ /* @__PURE__ */ jsx(AutoLinkPlugin, { matchers: MATCHERS }),
7670
+ /* @__PURE__ */ jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true }),
7671
+ !isReadOnly && /* @__PURE__ */ jsx(YoutubeDeletePlugin, {}),
7672
+ !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableActionMenuPlugin, {}),
7673
+ !isReadOnly && floatingAnchorElem && /* @__PURE__ */ jsx(TableCellResizerPlugin, { anchorElem: floatingAnchorElem }),
7674
+ !isReadOnly && /* @__PURE__ */ jsx(
7675
+ FloatingLinkEditorPlugin,
7676
+ {
7677
+ anchorElem: floatingAnchorElem,
7678
+ isLinkEditMode,
7679
+ setIsLinkEditMode
7680
+ }
7681
+ ),
7682
+ !isReadOnly && /* @__PURE__ */ jsx(ImagePlugin_default, {}),
7683
+ !isReadOnly && /* @__PURE__ */ jsx(InlineImage_default, {}),
7684
+ !isReadOnly && /* @__PURE__ */ jsx(PageBreakPlugin, {}),
7685
+ !!resolvedQuery && !isReadOnly && /* @__PURE__ */ jsx(
7686
+ AutocompletePlugin,
7687
+ {
7688
+ useQuery: resolvedQuery,
7689
+ isReadOnly,
7690
+ onSuggestionShown: props.onSuggestionShown,
7691
+ onSuggestionAccept: props.onSuggestionAccept,
7692
+ idleMs: props.suggestIdleMs ?? 300,
7693
+ minWords: 4,
7694
+ prefixWindow: 300
7695
+ }
7696
+ ),
7697
+ !!resolvedSpellCheck && !isReadOnly && /* @__PURE__ */ jsx(
7698
+ SpellCheckPlugin,
7699
+ {
7700
+ useSpellCheck: resolvedSpellCheck,
7701
+ onSpellCheckAccept: props.onSpellCheckAccept,
7702
+ idleMs: props.spellCheckIdleMs ?? 1200,
7703
+ enabled: props.spellCheckEnabled !== false
7704
+ }
7705
+ ),
7706
+ !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsx(CharacterStylesPopupPlugin, {}),
7707
+ /* @__PURE__ */ jsx(
7708
+ CustomOnChangePlugin,
7709
+ {
7710
+ value: props.value,
7711
+ onChange: props.onChange
7712
+ }
7713
+ ),
7714
+ (props.wordLimit !== void 0 || props.required || props.minWords !== void 0) && /* @__PURE__ */ jsx(WordCountPlugin, { onCountChange: handleWordCount }),
7715
+ (props.maxChars !== void 0 || props.minChars !== void 0) && /* @__PURE__ */ jsx(CharCountPlugin, { onCountChange: handleCharCount }),
7716
+ /* @__PURE__ */ jsx(
7717
+ RefApiPlugin,
7718
+ {
7719
+ forwardedRef: ref,
7720
+ contentEditableDomRef,
7721
+ focusedRef,
7722
+ setRefErrors
7723
+ }
7724
+ )
7725
+ ]
7726
+ }
7727
+ ) }) }) });
7728
+ });
7137
7729
 
7138
7730
  export { ContentEditorComponent, ContentEditorLevel };
7139
7731
  //# sourceMappingURL=index.mjs.map