@webround/editor 0.0.0-stage → 0.2.0

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.
Files changed (227) hide show
  1. package/dist/blocks.d.ts +90 -0
  2. package/dist/blocks.js +119 -0
  3. package/dist/bridge.d.ts +96 -0
  4. package/dist/bridge.js +970 -0
  5. package/dist/check/aria.d.ts +19 -0
  6. package/dist/check/aria.js +106 -0
  7. package/dist/check/cli.d.ts +3 -0
  8. package/dist/check/cli.js +30 -0
  9. package/dist/check/css.d.ts +13 -0
  10. package/dist/check/css.js +63 -0
  11. package/dist/check/html.d.ts +8 -0
  12. package/dist/check/html.js +29 -0
  13. package/dist/check/index.d.ts +4 -0
  14. package/dist/check/index.js +5 -0
  15. package/dist/check/links.d.ts +7 -0
  16. package/dist/check/links.js +23 -0
  17. package/dist/check/orphans.d.ts +20 -0
  18. package/dist/check/orphans.js +57 -0
  19. package/dist/check/producer-code.d.ts +53 -0
  20. package/dist/check/producer-code.js +379 -0
  21. package/dist/check/producer-dom.d.ts +21 -0
  22. package/dist/check/producer-dom.js +52 -0
  23. package/dist/check/project.d.ts +20 -0
  24. package/dist/check/project.js +51 -0
  25. package/dist/check/rules.d.ts +7 -0
  26. package/dist/check/rules.js +517 -0
  27. package/dist/check/types.d.ts +160 -0
  28. package/dist/check/types.js +22 -0
  29. package/dist/components/WrAltcha.d.ts +5 -0
  30. package/dist/components/WrAltcha.js +8 -0
  31. package/dist/components/WrButton.d.ts +8 -0
  32. package/dist/components/WrButton.js +34 -0
  33. package/dist/components/WrForm.d.ts +6 -0
  34. package/dist/components/WrForm.js +29 -0
  35. package/dist/components/WrFormPrivacy.d.ts +4 -0
  36. package/dist/components/WrFormPrivacy.js +7 -0
  37. package/dist/components/WrFormSuccess.d.ts +4 -0
  38. package/dist/components/WrFormSuccess.js +12 -0
  39. package/dist/components/WrIframe.d.ts +12 -0
  40. package/dist/components/WrIframe.js +30 -0
  41. package/dist/components/WrImage.d.ts +9 -0
  42. package/dist/components/WrImage.js +28 -0
  43. package/dist/components/WrInput.d.ts +8 -0
  44. package/dist/components/WrInput.js +29 -0
  45. package/dist/components/WrLink.d.ts +10 -0
  46. package/dist/components/WrLink.js +38 -0
  47. package/dist/components/WrNav.d.ts +26 -0
  48. package/dist/components/WrNav.js +30 -0
  49. package/dist/components/WrSection.d.ts +11 -0
  50. package/dist/components/WrSection.js +5 -0
  51. package/dist/components/WrShared.d.ts +12 -0
  52. package/dist/components/WrShared.js +20 -0
  53. package/dist/components/WrText.d.ts +11 -0
  54. package/dist/components/WrText.js +41 -0
  55. package/dist/components/WrVideo.d.ts +9 -0
  56. package/dist/components/WrVideo.js +73 -0
  57. package/dist/components/text-editor/CodeThemeContext.d.ts +8 -0
  58. package/dist/components/text-editor/CodeThemeContext.js +10 -0
  59. package/dist/components/text-editor/ExampleTheme.d.ts +77 -0
  60. package/dist/components/text-editor/ExampleTheme.js +76 -0
  61. package/dist/components/text-editor/ToolbarContext.d.ts +17 -0
  62. package/dist/components/text-editor/ToolbarContext.js +32 -0
  63. package/dist/components/text-editor/WrTextEditor.d.ts +13 -0
  64. package/dist/components/text-editor/WrTextEditor.js +48 -0
  65. package/dist/components/text-editor/common/constants.d.ts +109 -0
  66. package/dist/components/text-editor/common/constants.js +58 -0
  67. package/dist/components/text-editor/common/surface.d.ts +3 -0
  68. package/dist/components/text-editor/common/surface.js +8 -0
  69. package/dist/components/text-editor/common/utils.d.ts +7 -0
  70. package/dist/components/text-editor/common/utils.js +78 -0
  71. package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.d.ts +5 -0
  72. package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.js +41 -0
  73. package/dist/components/text-editor/hooks/useLexicalMarkdown.d.ts +33 -0
  74. package/dist/components/text-editor/hooks/useLexicalMarkdown.js +284 -0
  75. package/dist/components/text-editor/nodes/CodeBlockNode.d.ts +23 -0
  76. package/dist/components/text-editor/nodes/CodeBlockNode.js +138 -0
  77. package/dist/components/text-editor/nodes/HorizontalRuleNode.d.ts +13 -0
  78. package/dist/components/text-editor/nodes/HorizontalRuleNode.js +22 -0
  79. package/dist/components/text-editor/plugins/CodeBlockPlugin.d.ts +2 -0
  80. package/dist/components/text-editor/plugins/CodeBlockPlugin.js +38 -0
  81. package/dist/components/text-editor/plugins/FontPlugin.d.ts +12 -0
  82. package/dist/components/text-editor/plugins/FontPlugin.js +114 -0
  83. package/dist/components/text-editor/plugins/FontSizePlugin.d.ts +7 -0
  84. package/dist/components/text-editor/plugins/FontSizePlugin.js +105 -0
  85. package/dist/components/text-editor/plugins/FontWeightPlugin.d.ts +6 -0
  86. package/dist/components/text-editor/plugins/FontWeightPlugin.js +103 -0
  87. package/dist/components/text-editor/plugins/HTMLTagPlugin.d.ts +7 -0
  88. package/dist/components/text-editor/plugins/HTMLTagPlugin.js +82 -0
  89. package/dist/components/text-editor/plugins/HorizontalRulePlugin.d.ts +2 -0
  90. package/dist/components/text-editor/plugins/HorizontalRulePlugin.js +35 -0
  91. package/dist/components/text-editor/plugins/LineSpacingPlugin.d.ts +7 -0
  92. package/dist/components/text-editor/plugins/LineSpacingPlugin.js +83 -0
  93. package/dist/components/text-editor/plugins/LinkPlugin.d.ts +8 -0
  94. package/dist/components/text-editor/plugins/LinkPlugin.js +183 -0
  95. package/dist/components/text-editor/plugins/ListToolbarPlugin.d.ts +7 -0
  96. package/dist/components/text-editor/plugins/ListToolbarPlugin.js +40 -0
  97. package/dist/components/text-editor/plugins/LoadContentPlugin.d.ts +4 -0
  98. package/dist/components/text-editor/plugins/LoadContentPlugin.js +63 -0
  99. package/dist/components/text-editor/plugins/MarkdownExportPlugin.d.ts +4 -0
  100. package/dist/components/text-editor/plugins/MarkdownExportPlugin.js +17 -0
  101. package/dist/components/text-editor/plugins/MarkdownPastePlugin.d.ts +2 -0
  102. package/dist/components/text-editor/plugins/MarkdownPastePlugin.js +45 -0
  103. package/dist/components/text-editor/plugins/SanitizePastePlugin.d.ts +3 -0
  104. package/dist/components/text-editor/plugins/SanitizePastePlugin.js +40 -0
  105. package/dist/components/text-editor/plugins/TextAlignPlugin.d.ts +7 -0
  106. package/dist/components/text-editor/plugins/TextAlignPlugin.js +65 -0
  107. package/dist/components/text-editor/plugins/TextColorPlugin.d.ts +7 -0
  108. package/dist/components/text-editor/plugins/TextColorPlugin.js +117 -0
  109. package/dist/components/text-editor/plugins/ToolbarPlugin.d.ts +10 -0
  110. package/dist/components/text-editor/plugins/ToolbarPlugin.js +73 -0
  111. package/dist/components/text-editor/theme-colors.d.ts +11 -0
  112. package/dist/components/text-editor/theme-colors.js +28 -0
  113. package/dist/context/WrAppContext.d.ts +35 -0
  114. package/dist/context/WrAppContext.js +113 -0
  115. package/dist/context/WrLayout.d.ts +15 -0
  116. package/dist/context/WrLayout.js +22 -0
  117. package/dist/context/WrPageContext.d.ts +28 -0
  118. package/dist/context/WrPageContext.js +282 -0
  119. package/dist/context/restoreValues.d.ts +2 -0
  120. package/dist/context/restoreValues.js +6 -0
  121. package/dist/contrast.d.ts +45 -0
  122. package/dist/contrast.js +122 -0
  123. package/dist/drop.d.ts +7 -0
  124. package/dist/drop.js +51 -0
  125. package/dist/gaps.d.ts +14 -0
  126. package/dist/gaps.js +50 -0
  127. package/dist/hooks/useWrMessage.d.ts +5 -0
  128. package/dist/hooks/useWrMessage.js +24 -0
  129. package/dist/hooks/useWrSave.d.ts +2 -0
  130. package/dist/hooks/useWrSave.js +11 -0
  131. package/dist/index.css +467 -0
  132. package/dist/index.d.ts +20 -0
  133. package/dist/index.js +20 -0
  134. package/dist/interactive.d.ts +8 -0
  135. package/dist/interactive.js +13 -0
  136. package/dist/measure-rules.d.ts +34 -0
  137. package/dist/measure-rules.js +54 -0
  138. package/dist/measure.d.ts +11 -0
  139. package/dist/measure.js +139 -0
  140. package/dist/messages.d.ts +860 -0
  141. package/dist/messages.js +2 -0
  142. package/dist/nav.d.ts +5 -0
  143. package/dist/nav.js +10 -0
  144. package/dist/operations/animations-store.d.ts +6 -0
  145. package/dist/operations/animations-store.js +63 -0
  146. package/dist/operations/cli.d.ts +3 -0
  147. package/dist/operations/cli.js +52 -0
  148. package/dist/operations/comments-store.d.ts +17 -0
  149. package/dist/operations/comments-store.js +62 -0
  150. package/dist/operations/context.d.ts +12 -0
  151. package/dist/operations/context.js +35 -0
  152. package/dist/operations/define.d.ts +81 -0
  153. package/dist/operations/define.js +86 -0
  154. package/dist/operations/index.d.ts +6 -0
  155. package/dist/operations/index.js +6 -0
  156. package/dist/operations/ops/check.d.ts +5 -0
  157. package/dist/operations/ops/check.js +53 -0
  158. package/dist/operations/ops/comments.d.ts +6 -0
  159. package/dist/operations/ops/comments.js +133 -0
  160. package/dist/operations/ops/content.d.ts +16 -0
  161. package/dist/operations/ops/content.js +73 -0
  162. package/dist/operations/ops/json.d.ts +4 -0
  163. package/dist/operations/ops/json.js +27 -0
  164. package/dist/operations/ops/langs.d.ts +4 -0
  165. package/dist/operations/ops/langs.js +32 -0
  166. package/dist/operations/ops/nodes.d.ts +8 -0
  167. package/dist/operations/ops/nodes.js +270 -0
  168. package/dist/operations/ops/pages.d.ts +8 -0
  169. package/dist/operations/ops/pages.js +154 -0
  170. package/dist/operations/ops/project.d.ts +36 -0
  171. package/dist/operations/ops/project.js +97 -0
  172. package/dist/operations/ops/seo.d.ts +17 -0
  173. package/dist/operations/ops/seo.js +358 -0
  174. package/dist/operations/ops/style.d.ts +76 -0
  175. package/dist/operations/ops/style.js +268 -0
  176. package/dist/operations/ops/stylesets.d.ts +9 -0
  177. package/dist/operations/ops/stylesets.js +145 -0
  178. package/dist/operations/ops/theme.d.ts +6 -0
  179. package/dist/operations/ops/theme.js +304 -0
  180. package/dist/operations/registry.d.ts +8 -0
  181. package/dist/operations/registry.js +19 -0
  182. package/dist/operations/stylesets-store.d.ts +50 -0
  183. package/dist/operations/stylesets-store.js +216 -0
  184. package/dist/registry.d.ts +5 -0
  185. package/dist/registry.js +24 -0
  186. package/dist/save.d.ts +65 -0
  187. package/dist/save.js +86 -0
  188. package/dist/schema.d.ts +69 -0
  189. package/dist/schema.js +110 -0
  190. package/dist/scope.d.ts +41 -0
  191. package/dist/scope.js +44 -0
  192. package/dist/vite-plugins/ast.d.ts +3 -0
  193. package/dist/vite-plugins/ast.js +238 -0
  194. package/dist/vite-plugins/core-swap.d.ts +3 -0
  195. package/dist/vite-plugins/core-swap.js +57 -0
  196. package/dist/vite-plugins/create-page.d.ts +4 -0
  197. package/dist/vite-plugins/create-page.js +59 -0
  198. package/dist/vite-plugins/guard.d.ts +15 -0
  199. package/dist/vite-plugins/guard.js +35 -0
  200. package/dist/vite-plugins/head.d.ts +13 -0
  201. package/dist/vite-plugins/head.js +53 -0
  202. package/dist/vite-plugins/index.d.ts +7 -0
  203. package/dist/vite-plugins/index.js +7 -0
  204. package/dist/vite-plugins/jsx-tree.d.ts +118 -0
  205. package/dist/vite-plugins/jsx-tree.js +686 -0
  206. package/dist/vite-plugins/langs.d.ts +3 -0
  207. package/dist/vite-plugins/langs.js +44 -0
  208. package/dist/vite-plugins/operations.d.ts +10 -0
  209. package/dist/vite-plugins/operations.js +66 -0
  210. package/dist/vite-plugins/pages-api.d.ts +3 -0
  211. package/dist/vite-plugins/pages-api.js +112 -0
  212. package/dist/vite-plugins/pages.d.ts +3 -0
  213. package/dist/vite-plugins/pages.js +64 -0
  214. package/dist/vite-plugins/read.d.ts +3 -0
  215. package/dist/vite-plugins/read.js +45 -0
  216. package/dist/vite-plugins/seo-files.d.ts +8 -0
  217. package/dist/vite-plugins/seo-files.js +42 -0
  218. package/dist/vite-plugins/theme.d.ts +3 -0
  219. package/dist/vite-plugins/theme.js +207 -0
  220. package/dist/vite-plugins/update.d.ts +3 -0
  221. package/dist/vite-plugins/update.js +46 -0
  222. package/dist/vite-plugins/wr-plugins.d.ts +15 -0
  223. package/dist/vite-plugins/wr-plugins.js +34 -0
  224. package/dist/vite-plugins.d.ts +11 -0
  225. package/dist/vite-plugins.js +518 -0
  226. package/package.json +96 -5
  227. package/README.md +0 -3
@@ -0,0 +1,105 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useEffect, useState, useCallback } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
5
+ import { $getSelection, $isRangeSelection } from 'lexical';
6
+ import { $patchStyleText, $getSelectionStyleValueForProperty } from '@lexical/selection';
7
+ import { BiCheckCircle } from 'react-icons/bi';
8
+ import { WR_SURFACE_STYLE } from '../common/surface';
9
+ // Punti di riferimento fissi per l'interpolazione fluida (fluid typography).
10
+ // Sotto MIN_VIEWPORT il size resta fisso a min, sopra MAX_VIEWPORT resta fisso a max,
11
+ // in mezzo scala linearmente in base alla viewport reale.
12
+ const MIN_VIEWPORT = 375;
13
+ const MAX_VIEWPORT = 1440;
14
+ function buildFontSizeValue(minPx, maxPx) {
15
+ if (minPx === maxPx)
16
+ return `${minPx}px`; // nessuno scaling: valore fisso, CSS più pulito
17
+ const lo = Math.min(minPx, maxPx);
18
+ const hi = Math.max(minPx, maxPx);
19
+ const slope = (maxPx - minPx) / (MAX_VIEWPORT - MIN_VIEWPORT);
20
+ const intercept = minPx - slope * MIN_VIEWPORT;
21
+ const preferred = `${intercept.toFixed(3)}px + ${(slope * 100).toFixed(3)}vw`;
22
+ return `clamp(${lo}px, ${preferred}, ${hi}px)`;
23
+ }
24
+ function parseFontSizeValue(value) {
25
+ const clampMatch = value.match(/^clamp\(\s*([\d.]+)px\s*,.*?,\s*([\d.]+)px\s*\)$/);
26
+ if (clampMatch) {
27
+ return { min: parseFloat(clampMatch[1]), max: parseFloat(clampMatch[2]) };
28
+ }
29
+ const flat = parseFloat(value);
30
+ const size = Number.isFinite(flat) ? flat : 16;
31
+ return { min: size, max: size };
32
+ }
33
+ function FontSizePlugin({ editorPathKey = '' }) {
34
+ const [editor] = useLexicalComposerContext();
35
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
36
+ const [minSize, setMinSize] = useState(16);
37
+ const [maxSize, setMaxSize] = useState(16);
38
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
39
+ const buttonRef = useRef(null);
40
+ const menuRef = useRef(null);
41
+ useEffect(() => {
42
+ return editor.registerUpdateListener(({ editorState }) => {
43
+ editorState.read(() => {
44
+ const selection = $getSelection();
45
+ if (!$isRangeSelection(selection))
46
+ return;
47
+ const fontSize = $getSelectionStyleValueForProperty(selection, 'font-size', '16px');
48
+ if (fontSize) {
49
+ const { min, max } = parseFontSizeValue(fontSize);
50
+ setMinSize(min);
51
+ setMaxSize(max);
52
+ }
53
+ });
54
+ });
55
+ }, [editor]);
56
+ const applyFontSize = useCallback((min, max) => {
57
+ const value = buildFontSizeValue(min, max);
58
+ editor.update(() => {
59
+ const selection = $getSelection();
60
+ if ($isRangeSelection(selection)) {
61
+ $patchStyleText(selection, { 'font-size': value });
62
+ }
63
+ });
64
+ setMinSize(min);
65
+ setMaxSize(max);
66
+ setIsMenuOpen(false);
67
+ }, [editor]);
68
+ const calculateMenuPosition = useCallback(() => {
69
+ if (!buttonRef.current)
70
+ return;
71
+ const rect = buttonRef.current.getBoundingClientRect();
72
+ const menuWidth = 168;
73
+ const pageWidth = document.body.clientWidth;
74
+ let left = rect.left;
75
+ if (left + menuWidth > pageWidth)
76
+ left = pageWidth - menuWidth - 10;
77
+ if (left < 0)
78
+ left = 10;
79
+ setMenuPosition({ top: rect.bottom, left });
80
+ }, []);
81
+ useEffect(() => {
82
+ if (!isMenuOpen)
83
+ return;
84
+ calculateMenuPosition();
85
+ const handleClickOutside = (e) => {
86
+ if (menuRef.current?.contains(e.target) ||
87
+ buttonRef.current?.contains(e.target))
88
+ return;
89
+ setIsMenuOpen(false);
90
+ };
91
+ const timer = setTimeout(() => {
92
+ document.addEventListener('click', handleClickOutside);
93
+ }, 0);
94
+ return () => {
95
+ clearTimeout(timer);
96
+ document.removeEventListener('click', handleClickOutside);
97
+ };
98
+ }, [isMenuOpen, calculateMenuPosition]);
99
+ const label = minSize === maxSize ? `${minSize}px` : `${minSize}–${maxSize}px`;
100
+ return (_jsxs("div", { className: `toolbarClass path-${editorPathKey}`, style: { position: 'relative', display: 'inline-block', textAlign: 'left' }, onClick: e => e.stopPropagation(), children: [_jsxs("button", { ref: buttonRef, type: "button", onPointerDown: e => e.preventDefault(), onClick: e => { e.stopPropagation(); setIsMenuOpen(p => !p); }, "aria-expanded": isMenuOpen, className: "wr-toolbar-trigger", children: [_jsx("span", { children: label }), _jsx("svg", { className: `wr-toolbar-chevron ${isMenuOpen ? 'wr-toolbar-chevron--open' : ''}`, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] }), isMenuOpen && createPortal(_jsx("div", { ref: menuRef, className: `toolbarClass path-${editorPathKey} wr-toolbar-menu`, style: { ...WR_SURFACE_STYLE, top: menuPosition.top, left: menuPosition.left, width: 168 }, onPointerDown: e => e.preventDefault(), children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6, padding: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [_jsx("label", { style: { fontSize: 11, width: 24 }, children: "min" }), _jsx("input", { type: "number", className: "wr-core-input", value: minSize, onChange: e => setMinSize(Number(e.target.value)), onKeyDown: e => { if (e.key === 'Enter')
101
+ applyFontSize(minSize, maxSize); }, onPointerDown: e => e.stopPropagation() })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [_jsx("label", { style: { fontSize: 11, width: 24 }, children: "max" }), _jsx("input", { type: "number", className: "wr-core-input", value: maxSize, onChange: e => setMaxSize(Number(e.target.value)), onKeyDown: e => { if (e.key === 'Enter')
102
+ applyFontSize(minSize, maxSize); }, onPointerDown: e => e.stopPropagation() })] }), _jsx("button", { onClick: () => applyFontSize(minSize, maxSize), className: "wr-primary-btn", style: { marginTop: 0, width: '100%', padding: 4, display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: _jsx(BiCheckCircle, { size: 18 }) })] }) }), document.body)] }));
103
+ }
104
+ export default FontSizePlugin;
105
+ //# sourceMappingURL=FontSizePlugin.js.map
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ editorPathKey?: string;
3
+ }
4
+ declare function FontWeightPlugin({ editorPathKey }: Props): import("react").JSX.Element;
5
+ export default FontWeightPlugin;
6
+ //# sourceMappingURL=FontWeightPlugin.d.ts.map
@@ -0,0 +1,103 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND } from 'lexical';
5
+ import { $patchStyleText } from '@lexical/selection';
6
+ import { createPortal } from 'react-dom';
7
+ import { MdFormatBold } from 'react-icons/md';
8
+ import { WR_SURFACE_STYLE } from '../common/surface';
9
+ const fontWeights = [
10
+ { value: '100', label: 'Thin 100' },
11
+ { value: '200', label: 'ExtraLight 200' },
12
+ { value: '300', label: 'Light 300' },
13
+ { value: '400', label: 'Regular 400' },
14
+ { value: '500', label: 'Medium 500' },
15
+ { value: '600', label: 'SemiBold 600' },
16
+ { value: '700', label: 'Bold 700' },
17
+ { value: '800', label: 'ExtraBold 800' },
18
+ { value: '900', label: 'Black 900' },
19
+ ];
20
+ function FontWeightPlugin({ editorPathKey = '' }) {
21
+ const [editor] = useLexicalComposerContext();
22
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
23
+ const [selectedWeight, setSelectedWeight] = useState(fontWeights[3].value);
24
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
25
+ const buttonRef = useRef(null);
26
+ const menuRef = useRef(null);
27
+ const applyFontWeight = useCallback((weight) => {
28
+ editor.update(() => {
29
+ const selection = $getSelection();
30
+ if ($isRangeSelection(selection)) {
31
+ $patchStyleText(selection, { 'font-weight': weight });
32
+ }
33
+ });
34
+ editor.focus();
35
+ setSelectedWeight(weight);
36
+ setIsMenuOpen(false);
37
+ }, [editor]);
38
+ const calculateMenuPosition = () => {
39
+ if (buttonRef.current && menuRef.current) {
40
+ const buttonRect = buttonRef.current.getBoundingClientRect();
41
+ const menuRect = menuRef.current.getBoundingClientRect();
42
+ let top = buttonRect.bottom;
43
+ let left = buttonRect.left;
44
+ const pageWidth = document.body.clientWidth;
45
+ if (left + menuRect.width > pageWidth)
46
+ left = pageWidth - menuRect.width - 10;
47
+ if (left < 0)
48
+ left = 10;
49
+ setMenuPosition({ top, left });
50
+ }
51
+ };
52
+ useEffect(() => {
53
+ if (!isMenuOpen)
54
+ return;
55
+ const handler = (e) => {
56
+ if (!menuRef.current?.contains(e.target) && !buttonRef.current?.contains(e.target)) {
57
+ setIsMenuOpen(false);
58
+ }
59
+ };
60
+ document.addEventListener('mousedown', handler, true);
61
+ const raf = requestAnimationFrame(calculateMenuPosition);
62
+ return () => {
63
+ document.removeEventListener('mousedown', handler, true);
64
+ cancelAnimationFrame(raf);
65
+ };
66
+ }, [isMenuOpen]);
67
+ useEffect(() => {
68
+ return editor.registerUpdateListener(() => {
69
+ editor.getEditorState().read(() => {
70
+ const selection = $getSelection();
71
+ if (!$isRangeSelection(selection))
72
+ return;
73
+ const styleString = selection.anchor.getNode().getStyle?.();
74
+ if (typeof styleString === 'string') {
75
+ const match = styleString.match(/font-weight:\s*(\d+)/i);
76
+ if (match)
77
+ setSelectedWeight(match[1]);
78
+ }
79
+ });
80
+ });
81
+ }, [editor]);
82
+ useEffect(() => {
83
+ return editor.registerCommand(FORMAT_TEXT_COMMAND, (formatType) => {
84
+ if (formatType !== 'bold')
85
+ return false;
86
+ editor.update(() => {
87
+ const selection = $getSelection();
88
+ if (!$isRangeSelection(selection))
89
+ return;
90
+ const styleString = selection.anchor.getNode().getStyle?.() ?? '';
91
+ const match = styleString.match(/font-weight:\s*(\d+)/i);
92
+ const current = match ? parseInt(match[1], 10) : 400;
93
+ const newWeight = current >= 700 ? '400' : '700';
94
+ $patchStyleText(selection, { 'font-weight': newWeight });
95
+ setSelectedWeight(newWeight);
96
+ });
97
+ return true;
98
+ }, COMMAND_PRIORITY_LOW);
99
+ }, [editor]);
100
+ return (_jsxs("div", { className: `path-${editorPathKey} toolbarClass`, style: { position: 'relative', display: 'inline-block', textAlign: 'left' }, onClick: e => e.stopPropagation(), children: [_jsxs("button", { ref: buttonRef, type: "button", onPointerDown: e => e.preventDefault(), onClick: e => { e.stopPropagation(); setIsMenuOpen(p => !p); }, "aria-expanded": isMenuOpen, className: "wr-toolbar-trigger", children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(MdFormatBold, { style: { fontSize: 18 } }), _jsx("span", { children: fontWeights.find(fw => fw.value === selectedWeight)?.label })] }), _jsx("svg", { className: `wr-toolbar-chevron ${isMenuOpen ? 'wr-toolbar-chevron--open' : ''}`, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] }), isMenuOpen && createPortal(_jsx("div", { ref: menuRef, className: `toolbarClass path-${editorPathKey} wr-toolbar-menu`, style: { ...WR_SURFACE_STYLE, top: menuPosition.top, left: menuPosition.left, width: 160 }, onPointerDown: e => e.preventDefault(), children: _jsx("div", { style: { padding: '4px 0' }, children: fontWeights.map(fw => (_jsx("button", { onClick: () => applyFontWeight(fw.value), className: `wr-toolbar-item ${fw.value === selectedWeight ? 'wr-toolbar-item--active' : ''}`, children: fw.label }, fw.value))) }) }), document.body)] }));
101
+ }
102
+ export default FontWeightPlugin;
103
+ //# sourceMappingURL=FontWeightPlugin.js.map
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface Props {
3
+ editorPathKey?: string;
4
+ }
5
+ declare function HTMLTagPlugin({ editorPathKey }: Props): React.JSX.Element;
6
+ export default HTMLTagPlugin;
7
+ //# sourceMappingURL=HTMLTagPlugin.d.ts.map
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useEffect, useState, useCallback } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { $getSelection, $isRangeSelection, $createParagraphNode } from 'lexical';
5
+ import { $createHeadingNode, $isHeadingNode } from '@lexical/rich-text';
6
+ import { $setBlocksType } from '@lexical/selection';
7
+ import { createPortal } from 'react-dom';
8
+ import { WR_SURFACE_STYLE } from '../common/surface';
9
+ const headings = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
10
+ function HTMLTagPlugin({ editorPathKey = '' }) {
11
+ const [editor] = useLexicalComposerContext();
12
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
13
+ const [selectedHeading, setSelectedHeading] = useState('p');
14
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
15
+ const buttonRef = useRef(null);
16
+ const menuRef = useRef(null);
17
+ const formatHeading = useCallback((tag) => {
18
+ editor.update(() => {
19
+ const selection = $getSelection();
20
+ if ($isRangeSelection(selection)) {
21
+ if (tag === 'p') {
22
+ $setBlocksType(selection, () => $createParagraphNode());
23
+ }
24
+ else {
25
+ $setBlocksType(selection, () => $createHeadingNode(tag));
26
+ }
27
+ }
28
+ });
29
+ setSelectedHeading(tag);
30
+ setIsMenuOpen(false);
31
+ }, [editor]);
32
+ const calculateMenuPosition = () => {
33
+ if (buttonRef.current && menuRef.current) {
34
+ const buttonRect = buttonRef.current.getBoundingClientRect();
35
+ const menuRect = menuRef.current.getBoundingClientRect();
36
+ let top = buttonRect.bottom;
37
+ let left = buttonRect.left;
38
+ const pageWidth = document.body.clientWidth;
39
+ if (left + menuRect.width > pageWidth)
40
+ left = pageWidth - menuRect.width - 10;
41
+ if (left < 0)
42
+ left = 10;
43
+ setMenuPosition({ top, left });
44
+ }
45
+ };
46
+ useEffect(() => {
47
+ const handleClickOutside = (e) => {
48
+ if (menuRef.current && !menuRef.current.contains(e.target) && !buttonRef.current?.contains(e.target)) {
49
+ setIsMenuOpen(false);
50
+ }
51
+ };
52
+ if (isMenuOpen) {
53
+ document.addEventListener('click', handleClickOutside);
54
+ calculateMenuPosition();
55
+ }
56
+ return () => document.removeEventListener('click', handleClickOutside);
57
+ }, [isMenuOpen]);
58
+ useEffect(() => {
59
+ return editor.registerUpdateListener(() => {
60
+ editor.getEditorState().read(() => {
61
+ const selection = $getSelection();
62
+ if (!$isRangeSelection(selection))
63
+ return;
64
+ const anchorNode = selection.anchor.getNode();
65
+ let element = anchorNode.getKey() === 'root' ? anchorNode : anchorNode.getParentOrThrow();
66
+ while (element != null) {
67
+ if ($isHeadingNode(element)) {
68
+ setSelectedHeading(element.getTag());
69
+ return;
70
+ }
71
+ if (element.getType() === 'root')
72
+ break;
73
+ element = element.getParent();
74
+ }
75
+ setSelectedHeading('p');
76
+ });
77
+ });
78
+ }, [editor]);
79
+ return (_jsxs("div", { className: `path-${editorPathKey} toolbarClass`, style: { position: 'relative', display: 'inline-block', textAlign: 'left' }, onClick: e => e.stopPropagation(), children: [_jsxs("button", { ref: buttonRef, type: "button", onClick: e => { e.stopPropagation(); setIsMenuOpen(p => !p); }, "aria-expanded": isMenuOpen, className: "wr-toolbar-trigger", style: { padding: '8px' }, children: [_jsx("span", { children: selectedHeading.toUpperCase() }), _jsx("svg", { className: `wr-toolbar-chevron ${isMenuOpen ? 'wr-toolbar-chevron--open' : ''}`, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] }), isMenuOpen && createPortal(_jsx("div", { ref: menuRef, className: `path-${editorPathKey} toolbarClass wr-toolbar-menu`, style: { ...WR_SURFACE_STYLE, top: menuPosition.top, left: menuPosition.left, width: 128, maxHeight: 288 }, children: _jsx("div", { style: { padding: '4px 0' }, children: headings.map(tag => (_jsx("button", { onClick: () => formatHeading(tag), className: `wr-toolbar-item ${tag === selectedHeading ? 'wr-toolbar-item--active' : ''}`, children: tag.toUpperCase() }, tag))) }) }), document.body)] }));
80
+ }
81
+ export default HTMLTagPlugin;
82
+ //# sourceMappingURL=HTMLTagPlugin.js.map
@@ -0,0 +1,2 @@
1
+ export default function HorizontalRulePlugin(): null;
2
+ //# sourceMappingURL=HorizontalRulePlugin.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { useEffect } from "react";
2
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
+ import { $getSelection, $isRangeSelection, $isParagraphNode, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_LOW, $createRangeSelection, $setSelection, } from "lexical";
4
+ import { $createHorizontalRuleNode } from "../nodes/HorizontalRuleNode";
5
+ export default function HorizontalRulePlugin() {
6
+ const [editor] = useLexicalComposerContext();
7
+ useEffect(() => {
8
+ return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
9
+ const selection = $getSelection();
10
+ if (!$isRangeSelection(selection))
11
+ return false;
12
+ const anchor = selection.anchor.getNode();
13
+ const parent = anchor.getParent();
14
+ if (!$isParagraphNode(parent))
15
+ return false;
16
+ if (parent.getChildrenSize() !== 1)
17
+ return false;
18
+ if (anchor.getTextContent() !== "---")
19
+ return false;
20
+ event?.preventDefault();
21
+ const para = $createParagraphNode();
22
+ const hr = $createHorizontalRuleNode();
23
+ parent.replace(para);
24
+ para.insertBefore(hr);
25
+ const newSelection = $createRangeSelection();
26
+ const key = para.getKey();
27
+ newSelection.anchor.set(key, 0, "element");
28
+ newSelection.focus.set(key, 0, "element");
29
+ $setSelection(newSelection);
30
+ return true;
31
+ }, COMMAND_PRIORITY_LOW);
32
+ }, [editor]);
33
+ return null;
34
+ }
35
+ //# sourceMappingURL=HorizontalRulePlugin.js.map
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface Props {
3
+ editorPathKey?: string;
4
+ }
5
+ declare function LineSpacingPlugin({ editorPathKey }: Props): React.JSX.Element;
6
+ export default LineSpacingPlugin;
7
+ //# sourceMappingURL=LineSpacingPlugin.d.ts.map
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useEffect, useState } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { $getSelection, $isRangeSelection } from 'lexical';
5
+ import { $patchStyleText } from '@lexical/selection';
6
+ import { createPortal } from 'react-dom';
7
+ import { MdFormatLineSpacing } from 'react-icons/md';
8
+ import { WR_SURFACE_STYLE } from '../common/surface';
9
+ const lineSpacings = [
10
+ { label: '1.0 (Single)', value: '1' },
11
+ { label: '1.15', value: '1.15' },
12
+ { label: '1.5', value: '1.5' },
13
+ { label: '2.0 (Double)', value: '2' },
14
+ { label: '2.5', value: '2.5' },
15
+ { label: '3.0', value: '3' },
16
+ ];
17
+ function LineSpacingPlugin({ editorPathKey = '' }) {
18
+ const [editor] = useLexicalComposerContext();
19
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
20
+ const [selectedSpacing, setSelectedSpacing] = useState(lineSpacings[1].value);
21
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
22
+ const buttonRef = useRef(null);
23
+ const menuRef = useRef(null);
24
+ const applyLineSpacing = (spacing) => {
25
+ editor.update(() => {
26
+ const selection = $getSelection();
27
+ if ($isRangeSelection(selection)) {
28
+ $patchStyleText(selection, { 'line-height': spacing });
29
+ }
30
+ });
31
+ setSelectedSpacing(spacing);
32
+ setIsMenuOpen(false);
33
+ };
34
+ const calculateMenuPosition = () => {
35
+ if (buttonRef.current && menuRef.current) {
36
+ const buttonRect = buttonRef.current.getBoundingClientRect();
37
+ const menuRect = menuRef.current.getBoundingClientRect();
38
+ let top = buttonRect.bottom;
39
+ let left = buttonRect.left;
40
+ const pageWidth = document.body.clientWidth;
41
+ if (left + menuRect.width > pageWidth)
42
+ left = pageWidth - menuRect.width - 10;
43
+ if (left < 0)
44
+ left = 10;
45
+ setMenuPosition({ top, left });
46
+ }
47
+ };
48
+ useEffect(() => {
49
+ const handleClickOutside = (e) => {
50
+ if (menuRef.current && !menuRef.current.contains(e.target) && !buttonRef.current?.contains(e.target)) {
51
+ setIsMenuOpen(false);
52
+ }
53
+ };
54
+ if (isMenuOpen) {
55
+ document.addEventListener('click', handleClickOutside);
56
+ calculateMenuPosition();
57
+ }
58
+ return () => document.removeEventListener('click', handleClickOutside);
59
+ }, [isMenuOpen]);
60
+ useEffect(() => {
61
+ return editor.registerUpdateListener(() => {
62
+ editor.getEditorState().read(() => {
63
+ const selection = $getSelection();
64
+ if (!$isRangeSelection(selection))
65
+ return;
66
+ const anchorNode = selection.anchor.getNode();
67
+ if (!anchorNode)
68
+ return;
69
+ const styleString = anchorNode.getStyle?.();
70
+ if (anchorNode.getTextContent().trim() === '')
71
+ return;
72
+ if (styleString && typeof styleString === 'string') {
73
+ const match = styleString.match(/line-height:\s*([\d.]+)/i);
74
+ if (match)
75
+ setSelectedSpacing(match[1].trim());
76
+ }
77
+ });
78
+ });
79
+ }, [editor]);
80
+ return (_jsxs("div", { className: `path-${editorPathKey} toolbarClass`, style: { position: 'relative', display: 'inline-block', textAlign: 'left' }, onClick: e => e.stopPropagation(), children: [_jsxs("button", { ref: buttonRef, type: "button", onClick: e => { e.stopPropagation(); setIsMenuOpen(p => !p); }, className: "wr-toolbar-trigger", "aria-expanded": isMenuOpen, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(MdFormatLineSpacing, { style: { fontSize: 18 } }), _jsx("span", { children: lineSpacings.find(s => s.value === selectedSpacing)?.label })] }), _jsx("svg", { className: `wr-toolbar-chevron ${isMenuOpen ? 'wr-toolbar-chevron--open' : ''}`, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] }), isMenuOpen && createPortal(_jsx("div", { ref: menuRef, className: `toolbarClass path-${editorPathKey} wr-toolbar-menu`, style: { ...WR_SURFACE_STYLE, top: menuPosition.top, left: menuPosition.left, width: 160 }, children: _jsx("div", { style: { padding: '4px 0' }, children: lineSpacings.map(spacing => (_jsx("button", { onClick: () => applyLineSpacing(spacing.value), className: `wr-toolbar-item ${spacing.value === selectedSpacing ? 'wr-toolbar-item--active' : ''}`, children: spacing.label }, spacing.value))) }) }), document.body)] }));
81
+ }
82
+ export default LineSpacingPlugin;
83
+ //# sourceMappingURL=LineSpacingPlugin.js.map
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface Props {
3
+ availablePages?: string[];
4
+ editorPathKey?: string;
5
+ }
6
+ declare function LinkPlugin({ availablePages, editorPathKey }: Props): React.JSX.Element;
7
+ export default LinkPlugin;
8
+ //# sourceMappingURL=LinkPlugin.d.ts.map
@@ -0,0 +1,183 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { $getSelection, $isRangeSelection } from 'lexical';
5
+ import { $toggleLink, $isLinkNode } from '@lexical/link';
6
+ import { createPortal } from 'react-dom';
7
+ import { CiLink } from 'react-icons/ci';
8
+ import { IoCall, IoNavigate } from 'react-icons/io5';
9
+ import { MdAlternateEmail } from 'react-icons/md';
10
+ import { FaWhatsapp } from 'react-icons/fa';
11
+ import { WR_SURFACE_STYLE } from '../common/surface';
12
+ const t = {
13
+ title: 'Inserisci link',
14
+ linkTypeLabel: 'Tipo di link',
15
+ externalLink: 'Esterno',
16
+ internalLink: 'Interno',
17
+ phone: 'Telefono',
18
+ email: 'Email',
19
+ whatsapp: 'WhatsApp',
20
+ urlPlaceholder: 'https://...',
21
+ phonePlaceholder: '+39...',
22
+ emailPlaceholder: 'email@esempio.com',
23
+ anchorLabel: 'Ancora',
24
+ anchorPlaceholder: 'es. sezione-1',
25
+ selectPage: 'Seleziona pagina',
26
+ phoneHelp: 'Formato internazionale: +39...',
27
+ invalidValue: 'Valore non valido',
28
+ confirm: 'Applica',
29
+ };
30
+ const urlRegExp = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/;
31
+ const phoneRegExp = /^\+[1-9]\d{1,14}$/;
32
+ const emailRegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
33
+ function validateInput(value, type) {
34
+ switch (type) {
35
+ case 'external': return urlRegExp.test(value);
36
+ case 'phone': return phoneRegExp.test(value);
37
+ case 'email': return emailRegExp.test(value);
38
+ case 'internal': return value !== '';
39
+ case 'whatsapp': return phoneRegExp.test(value);
40
+ }
41
+ }
42
+ function LinkPlugin({ availablePages = [], editorPathKey = '' }) {
43
+ const [editor] = useLexicalComposerContext();
44
+ const [isLinkEditorOpen, setIsLinkEditorOpen] = useState(false);
45
+ const [isValidUrl, setIsValidUrl] = useState(false);
46
+ const [linkUrl, setLinkUrl] = useState('');
47
+ const [anchorLink, setAnchorLink] = useState('');
48
+ const [linkType, setLinkType] = useState('external');
49
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
50
+ const [selectedPage, setSelectedPage] = useState('');
51
+ const buttonRef = useRef(null);
52
+ const menuRef = useRef(null);
53
+ const checkLinkSelection = useCallback(() => {
54
+ let isLinkSelected = false;
55
+ editor.update(() => {
56
+ const selection = $getSelection();
57
+ if ($isRangeSelection(selection)) {
58
+ const anchorNode = selection.anchor.getNode();
59
+ const parent = anchorNode.getParent();
60
+ if (parent !== null && $isLinkNode(parent)) {
61
+ const url = parent.getURL();
62
+ if (url.startsWith('tel:')) {
63
+ setLinkType('phone');
64
+ setLinkUrl(url.replace('tel:', ''));
65
+ setIsValidUrl(validateInput(url.replace('tel:', ''), 'phone'));
66
+ }
67
+ else if (url.startsWith('mailto:')) {
68
+ setLinkType('email');
69
+ setLinkUrl(url.replace('mailto:', ''));
70
+ setIsValidUrl(validateInput(url.replace('mailto:', ''), 'email'));
71
+ }
72
+ else if (url.startsWith('/')) {
73
+ setLinkType('internal');
74
+ const [path, anchor] = url.split('#');
75
+ setSelectedPage(path);
76
+ setAnchorLink(anchor || '');
77
+ }
78
+ else if (url.startsWith('https://wa.me/')) {
79
+ setLinkType('whatsapp');
80
+ setLinkUrl(url.replace('https://wa.me/', ''));
81
+ setIsValidUrl(validateInput(url.replace('https://wa.me/', ''), 'whatsapp'));
82
+ }
83
+ else {
84
+ setLinkType('external');
85
+ setLinkUrl(url);
86
+ setIsValidUrl(validateInput(url, 'external'));
87
+ }
88
+ isLinkSelected = true;
89
+ }
90
+ else {
91
+ if (linkType === 'external') {
92
+ setLinkUrl('https://');
93
+ setIsValidUrl(validateInput('https://', 'external'));
94
+ }
95
+ else {
96
+ setLinkUrl('');
97
+ setIsValidUrl(false);
98
+ }
99
+ }
100
+ }
101
+ });
102
+ return isLinkSelected;
103
+ }, [editor, linkType]);
104
+ useEffect(() => {
105
+ return editor.registerUpdateListener(() => { checkLinkSelection(); });
106
+ }, [editor, checkLinkSelection]);
107
+ const handleLinkClick = () => {
108
+ const isLinkSelected = checkLinkSelection();
109
+ editor.update(() => {
110
+ const selection = $getSelection();
111
+ if ($isRangeSelection(selection)) {
112
+ if (!isLinkSelected) {
113
+ setLinkUrl('https://');
114
+ setIsValidUrl(validateInput('https://', 'external'));
115
+ }
116
+ setIsLinkEditorOpen(true);
117
+ }
118
+ });
119
+ };
120
+ const applyLink = () => {
121
+ editor.update(() => {
122
+ let finalUrl = linkUrl;
123
+ if (linkType === 'phone')
124
+ finalUrl = `tel:${linkUrl}`;
125
+ else if (linkType === 'email')
126
+ finalUrl = `mailto:${linkUrl}`;
127
+ else if (linkType === 'internal') {
128
+ finalUrl = selectedPage;
129
+ if (anchorLink?.trim())
130
+ finalUrl += `#${anchorLink.trim()}`;
131
+ }
132
+ else if (linkType === 'whatsapp')
133
+ finalUrl = `https://wa.me/${linkUrl}`;
134
+ else if (linkType === 'external')
135
+ finalUrl = linkUrl.startsWith('http') ? linkUrl : `https://${linkUrl}`;
136
+ $toggleLink(finalUrl);
137
+ });
138
+ setIsLinkEditorOpen(false);
139
+ };
140
+ const calculateMenuPosition = () => {
141
+ if (buttonRef.current && menuRef.current) {
142
+ const buttonRect = buttonRef.current.getBoundingClientRect();
143
+ const menuRect = menuRef.current.getBoundingClientRect();
144
+ let top = buttonRect.bottom;
145
+ let left = buttonRect.left;
146
+ const pageWidth = document.body.clientWidth;
147
+ if (left + menuRect.width > pageWidth)
148
+ left = pageWidth - menuRect.width - 10;
149
+ if (left < 0)
150
+ left = 10;
151
+ setMenuPosition({ top, left });
152
+ }
153
+ };
154
+ useEffect(() => {
155
+ const handleClickOutside = (event) => {
156
+ if (menuRef.current && !menuRef.current.contains(event.target) && !buttonRef.current?.contains(event.target)) {
157
+ setIsLinkEditorOpen(false);
158
+ }
159
+ };
160
+ if (isLinkEditorOpen) {
161
+ document.addEventListener('click', handleClickOutside);
162
+ calculateMenuPosition();
163
+ }
164
+ return () => document.removeEventListener('click', handleClickOutside);
165
+ }, [isLinkEditorOpen]);
166
+ const handleIconClick = () => {
167
+ if (!isValidUrl)
168
+ return;
169
+ if (linkType === 'external')
170
+ window.open(linkUrl.startsWith('http') ? linkUrl : `https://${linkUrl}`, '_blank');
171
+ else if (linkType === 'whatsapp')
172
+ window.open(`https://wa.me/${linkUrl}`, '_blank');
173
+ else if (linkType === 'phone')
174
+ window.location.href = `tel:${linkUrl}`;
175
+ else if (linkType === 'email')
176
+ window.location.href = `mailto:${linkUrl}`;
177
+ };
178
+ return (_jsxs("div", { className: `path-${editorPathKey} toolbarClass`, style: { position: 'relative', display: 'inline-block', textAlign: 'left' }, onPointerDown: e => e.stopPropagation(), children: [_jsxs("button", { ref: buttonRef, type: "button", onClick: handleLinkClick, className: `wr-toolbar-icon-btn ${isValidUrl ? 'wr-toolbar-icon-btn--active' : ''}`, children: [linkType === 'phone' && _jsx(IoCall, { size: 16 }), linkType === 'email' && _jsx(MdAlternateEmail, { size: 16 }), (linkType === 'external' || linkType === 'internal') && _jsx(CiLink, { size: 16 }), linkType === 'whatsapp' && _jsx(FaWhatsapp, { size: 16, color: "rgb(50,200,50)" })] }), isLinkEditorOpen && createPortal(_jsx("div", { ref: menuRef, className: `path-${editorPathKey} toolbarClass wr-toolbar-menu`, style: { ...WR_SURFACE_STYLE, top: menuPosition.top, left: menuPosition.left, width: 256 }, children: _jsxs("div", { style: { padding: '8px 16px' }, children: [_jsx("h3", { style: { color: 'var(--wr-text-2)', fontSize: 14, marginBottom: 8 }, children: t.title }), _jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { className: "wr-label", children: t.linkTypeLabel }), _jsxs("select", { value: linkType, onChange: e => setLinkType(e.target.value), className: "wr-core-input", children: [_jsx("option", { value: "external", children: t.externalLink }), _jsx("option", { value: "internal", children: t.internalLink }), _jsx("option", { value: "phone", children: t.phone }), _jsx("option", { value: "email", children: t.email }), _jsx("option", { value: "whatsapp", children: t.whatsapp })] })] }), linkType === 'external' && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', position: 'relative' }, children: [_jsx("input", { type: "text", placeholder: t.urlPlaceholder, value: linkUrl, onChange: e => { setLinkUrl(e.target.value); setIsValidUrl(validateInput(e.target.value, linkType)); }, className: "wr-core-input" }), _jsx(IoNavigate, { size: 16, style: { position: 'absolute', right: 8, color: 'var(--wr-text-3)', cursor: isValidUrl ? 'pointer' : 'not-allowed', opacity: isValidUrl ? 1 : 0.5 }, onClick: handleIconClick })] })), linkType === 'internal' && (_jsxs("div", { children: [_jsxs("select", { value: selectedPage, onChange: e => { setSelectedPage(e.target.value); setIsValidUrl(true); }, className: "wr-core-input", children: [_jsx("option", { value: "", children: t.selectPage }), availablePages.map((page, idx) => _jsx("option", { value: `/${page}`, children: `/${page}` }, idx))] }), _jsx("label", { className: "wr-label", style: { marginTop: 16, fontWeight: 500 }, children: t.anchorLabel }), _jsx("input", { type: "text", value: anchorLink, maxLength: 30, onChange: e => setAnchorLink(e.target.value.replace(/[^a-zA-Z0-9-_]/g, '')), placeholder: t.anchorPlaceholder, className: "wr-core-input" })] })), (linkType === 'phone' || linkType === 'whatsapp') && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', position: 'relative' }, children: [_jsx("input", { type: "text", placeholder: t.phonePlaceholder, value: linkUrl, onChange: e => { setLinkUrl(e.target.value); setIsValidUrl(validateInput(e.target.value, linkType)); }, className: "wr-core-input" }), linkType === 'phone'
179
+ ? _jsx(IoCall, { size: 16, style: { position: 'absolute', right: 8, color: 'var(--wr-text-3)', cursor: isValidUrl ? 'pointer' : 'not-allowed', opacity: isValidUrl ? 1 : 0.5 }, onClick: handleIconClick })
180
+ : _jsx(FaWhatsapp, { size: 16, style: { position: 'absolute', right: 8, color: 'var(--wr-text-3)', cursor: isValidUrl ? 'pointer' : 'not-allowed', opacity: isValidUrl ? 1 : 0.5 }, onClick: handleIconClick })] })), (linkType === 'phone' || linkType === 'whatsapp') && _jsx("p", { className: "wr-help-text", children: t.phoneHelp }), linkType === 'email' && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', position: 'relative' }, children: [_jsx("input", { type: "text", placeholder: t.emailPlaceholder, value: linkUrl, onChange: e => { setLinkUrl(e.target.value); setIsValidUrl(validateInput(e.target.value, linkType)); }, className: "wr-core-input" }), _jsx(IoNavigate, { size: 16, style: { position: 'absolute', right: 8, color: 'var(--wr-text-3)', cursor: isValidUrl ? 'pointer' : 'not-allowed', opacity: isValidUrl ? 1 : 0.5 }, onClick: handleIconClick })] })), !isValidUrl && linkType !== 'internal' && _jsx("p", { className: "wr-error-text", children: t.invalidValue }), _jsx("div", { style: { marginBottom: 8 }, children: _jsx("button", { onClick: applyLink, disabled: !isValidUrl, className: "wr-primary-btn", children: t.confirm }) })] }) }), document.body)] }));
181
+ }
182
+ export default LinkPlugin;
183
+ //# sourceMappingURL=LinkPlugin.js.map