camox 0.31.4 → 0.32.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.
@@ -1,23 +1,32 @@
1
1
  import { INPUT_BASE_STYLES, INPUT_FOCUS_STYLES, cn } from "../../../lib/utils.js";
2
+ import { FORMAT_FLAGS } from "../../lib/modifierFormats.js";
3
+ import { isHttpTextLinkTarget } from "../../lib/textLinks.js";
2
4
  import { lexicalStateToMarkdown } from "../../lib/lexicalState.js";
3
5
  import { createEditorConfig, normalizeLexicalState } from "./editorConfig.js";
6
+ import { TextLinkPopover } from "./TextLinkPopover.js";
4
7
  import { c } from "react/compiler-runtime";
5
8
  import * as React from "react";
6
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { $createLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
7
11
  import { LexicalComposer } from "@lexical/react/LexicalComposer";
8
12
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
9
13
  import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
14
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
10
15
  import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
11
16
  import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
12
- import { COMMAND_PRIORITY_LOW, INSERT_LINE_BREAK_COMMAND, KEY_ENTER_COMMAND } from "lexical";
17
+ import { $createTextNode, $getSelection, $isRangeSelection, $setSelection, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND, INSERT_LINE_BREAK_COMMAND, KEY_ENTER_COMMAND, PASTE_COMMAND } from "lexical";
18
+ import { Button } from "@camox/ui/button";
19
+ import { Bold, Italic } from "lucide-react";
20
+ import { FloatingToolbar } from "@camox/ui/floating-toolbar";
21
+ import { Toggle } from "@camox/ui/toggle";
13
22
  import { ContentEditable } from "@lexical/react/LexicalContentEditable";
14
23
 
15
24
  //#region src/core/components/lexical/SidebarLexicalEditor.tsx
16
25
  function EnterAsLineBreakHandler() {
17
26
  const $ = c(4);
18
- if ($[0] !== "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e") {
27
+ if ($[0] !== "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2") {
19
28
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
- $[0] = "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e";
29
+ $[0] = "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2";
21
30
  }
22
31
  const [editor] = useLexicalComposerContext();
23
32
  let t0;
@@ -41,9 +50,9 @@ function EnterAsLineBreakHandler() {
41
50
  }
42
51
  function ExternalStateSync(t0) {
43
52
  const $ = c(6);
44
- if ($[0] !== "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e") {
53
+ if ($[0] !== "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2") {
45
54
  for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
46
- $[0] = "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e";
55
+ $[0] = "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2";
47
56
  }
48
57
  const { value, isSyncingRef } = t0;
49
58
  const [editor] = useLexicalComposerContext();
@@ -77,6 +86,428 @@ function ExternalStateSync(t0) {
77
86
  React.useEffect(t1, t2);
78
87
  return null;
79
88
  }
89
+ function SidebarFloatingTextToolbar() {
90
+ const $ = c(66);
91
+ if ($[0] !== "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2") {
92
+ for (let $i = 0; $i < 66; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
93
+ $[0] = "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2";
94
+ }
95
+ const [editor] = useLexicalComposerContext();
96
+ const [open, setOpen] = React.useState(false);
97
+ const [selectedText, setSelectedText] = React.useState("");
98
+ const [linkTarget, setLinkTarget] = React.useState(null);
99
+ const [linkClickAnchor, setLinkClickAnchor] = React.useState(null);
100
+ let t0;
101
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
102
+ t0 = {
103
+ visible: false,
104
+ top: 0,
105
+ left: 0,
106
+ activeFormats: 0
107
+ };
108
+ $[1] = t0;
109
+ } else t0 = $[1];
110
+ const [toolbarState, setToolbarState] = React.useState(t0);
111
+ const lastSelectionRef = React.useRef(null);
112
+ let t1;
113
+ if ($[2] !== editor || $[3] !== open) {
114
+ t1 = () => {
115
+ const root = editor.getRootElement();
116
+ const nativeSelection = window.getSelection();
117
+ if (!root || !nativeSelection || nativeSelection.rangeCount === 0 || nativeSelection.isCollapsed) {
118
+ setToolbarState((state) => ({
119
+ ...state,
120
+ visible: open
121
+ }));
122
+ return;
123
+ }
124
+ if (!root.contains(nativeSelection.anchorNode) || !root.contains(nativeSelection.focusNode)) {
125
+ setToolbarState((state_0) => ({
126
+ ...state_0,
127
+ visible: open
128
+ }));
129
+ return;
130
+ }
131
+ const rect = nativeSelection.getRangeAt(0).getBoundingClientRect();
132
+ if (rect.width === 0 && rect.height === 0) return;
133
+ let activeFormats = 0;
134
+ let currentLinkTarget = null;
135
+ editor.getEditorState().read(() => {
136
+ const selection = $getSelection();
137
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) return;
138
+ lastSelectionRef.current = selection.clone();
139
+ setSelectedText(selection.getTextContent());
140
+ if (selection.hasFormat("bold")) activeFormats = activeFormats | FORMAT_FLAGS.bold;
141
+ if (selection.hasFormat("italic")) activeFormats = activeFormats | FORMAT_FLAGS.italic;
142
+ let node = selection.anchor.getNode();
143
+ while (node) {
144
+ if (node.getType?.() === "link") {
145
+ const url = node.getURL?.();
146
+ currentLinkTarget = typeof url === "string" ? url : null;
147
+ break;
148
+ }
149
+ node = node.getParent?.();
150
+ }
151
+ });
152
+ setLinkTarget(currentLinkTarget);
153
+ setToolbarState({
154
+ visible: true,
155
+ top: rect.top - 52,
156
+ left: rect.left + rect.width / 2,
157
+ activeFormats
158
+ });
159
+ };
160
+ $[2] = editor;
161
+ $[3] = open;
162
+ $[4] = t1;
163
+ } else t1 = $[4];
164
+ const updateToolbar = t1;
165
+ let t2;
166
+ let t3;
167
+ if ($[5] !== editor || $[6] !== updateToolbar) {
168
+ t2 = () => {
169
+ const doc = editor.getRootElement()?.ownerDocument ?? document;
170
+ doc.addEventListener("selectionchange", updateToolbar);
171
+ window.addEventListener("resize", updateToolbar);
172
+ window.addEventListener("scroll", updateToolbar, true);
173
+ return () => {
174
+ doc.removeEventListener("selectionchange", updateToolbar);
175
+ window.removeEventListener("resize", updateToolbar);
176
+ window.removeEventListener("scroll", updateToolbar, true);
177
+ };
178
+ };
179
+ t3 = [editor, updateToolbar];
180
+ $[5] = editor;
181
+ $[6] = updateToolbar;
182
+ $[7] = t2;
183
+ $[8] = t3;
184
+ } else {
185
+ t2 = $[7];
186
+ t3 = $[8];
187
+ }
188
+ React.useEffect(t2, t3);
189
+ let t4;
190
+ let t5;
191
+ if ($[9] !== editor || $[10] !== updateToolbar) {
192
+ t4 = () => editor.registerUpdateListener(() => updateToolbar());
193
+ t5 = [editor, updateToolbar];
194
+ $[9] = editor;
195
+ $[10] = updateToolbar;
196
+ $[11] = t4;
197
+ $[12] = t5;
198
+ } else {
199
+ t4 = $[11];
200
+ t5 = $[12];
201
+ }
202
+ React.useEffect(t4, t5);
203
+ let t6;
204
+ let t7;
205
+ if ($[13] !== editor || $[14] !== updateToolbar) {
206
+ t6 = () => editor.registerRootListener((root_1) => {
207
+ if (!root_1) return;
208
+ const handleLinkClick = (event) => {
209
+ const target = event.target;
210
+ if (!(target instanceof HTMLElement)) return;
211
+ const anchor = target.closest("a");
212
+ if (!anchor || !root_1.contains(anchor)) return;
213
+ const href = anchor.getAttribute("href");
214
+ if (!href) return;
215
+ event.preventDefault();
216
+ event.stopPropagation();
217
+ const rect_0 = anchor.getBoundingClientRect();
218
+ setLinkClickAnchor({
219
+ top: rect_0.bottom + 8,
220
+ left: rect_0.left + rect_0.width / 2
221
+ });
222
+ const doc_0 = root_1.ownerDocument;
223
+ const range_0 = doc_0.createRange();
224
+ range_0.selectNodeContents(anchor);
225
+ const selection_0 = doc_0.getSelection();
226
+ selection_0?.removeAllRanges();
227
+ selection_0?.addRange(range_0);
228
+ root_1.focus();
229
+ setSelectedText(selection_0?.toString() ?? "");
230
+ setLinkTarget(href);
231
+ setOpen(true);
232
+ updateToolbar();
233
+ };
234
+ root_1.addEventListener("click", handleLinkClick);
235
+ return () => root_1.removeEventListener("click", handleLinkClick);
236
+ });
237
+ t7 = [editor, updateToolbar];
238
+ $[13] = editor;
239
+ $[14] = updateToolbar;
240
+ $[15] = t6;
241
+ $[16] = t7;
242
+ } else {
243
+ t6 = $[15];
244
+ t7 = $[16];
245
+ }
246
+ React.useEffect(t6, t7);
247
+ let t8;
248
+ if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
249
+ t8 = () => {
250
+ const selection_1 = $getSelection();
251
+ if ($isRangeSelection(selection_1) && !selection_1.isCollapsed()) return;
252
+ if (!lastSelectionRef.current) return;
253
+ $setSelection(lastSelectionRef.current.clone());
254
+ };
255
+ $[17] = t8;
256
+ } else t8 = $[17];
257
+ const restoreSelection = t8;
258
+ let t9;
259
+ if ($[18] !== editor) {
260
+ t9 = (formatKey) => {
261
+ editor.update(restoreSelection);
262
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, formatKey);
263
+ };
264
+ $[18] = editor;
265
+ $[19] = t9;
266
+ } else t9 = $[19];
267
+ const applyFormat = t9;
268
+ let t10;
269
+ if ($[20] !== editor) {
270
+ t10 = (target_0, text) => {
271
+ editor.update(() => {
272
+ restoreSelection();
273
+ if (target_0 === null) {
274
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
275
+ return;
276
+ }
277
+ if (typeof text !== "string") {
278
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, target_0);
279
+ return;
280
+ }
281
+ const selection_2 = $getSelection();
282
+ if (!$isRangeSelection(selection_2)) return;
283
+ const linkNode = $createLinkNode(target_0);
284
+ linkNode.append($createTextNode(text));
285
+ selection_2.insertNodes([linkNode]);
286
+ });
287
+ setOpen(false);
288
+ setLinkClickAnchor(null);
289
+ };
290
+ $[20] = editor;
291
+ $[21] = t10;
292
+ } else t10 = $[21];
293
+ const applyTarget = t10;
294
+ let t11;
295
+ if ($[22] === Symbol.for("react.memo_cache_sentinel")) {
296
+ t11 = (nextOpen) => {
297
+ setOpen(nextOpen);
298
+ if (!nextOpen) setLinkClickAnchor(null);
299
+ };
300
+ $[22] = t11;
301
+ } else t11 = $[22];
302
+ const handleOpenChange = t11;
303
+ const handleMouseDown = _temp;
304
+ if (linkClickAnchor) {
305
+ let t12;
306
+ if ($[23] !== linkClickAnchor.left || $[24] !== linkClickAnchor.top) {
307
+ t12 = /* @__PURE__ */ jsx(Button, {
308
+ type: "button",
309
+ variant: "ghost",
310
+ size: "sm",
311
+ "aria-hidden": "true",
312
+ tabIndex: -1,
313
+ className: "fixed size-1 min-w-0 p-0 opacity-0",
314
+ style: {
315
+ top: linkClickAnchor.top,
316
+ left: linkClickAnchor.left
317
+ }
318
+ });
319
+ $[23] = linkClickAnchor.left;
320
+ $[24] = linkClickAnchor.top;
321
+ $[25] = t12;
322
+ } else t12 = $[25];
323
+ let t13;
324
+ if ($[26] !== applyTarget) {
325
+ t13 = () => applyTarget(null);
326
+ $[26] = applyTarget;
327
+ $[27] = t13;
328
+ } else t13 = $[27];
329
+ let t14;
330
+ if ($[28] !== applyTarget || $[29] !== linkTarget || $[30] !== open || $[31] !== selectedText || $[32] !== t12 || $[33] !== t13) {
331
+ t14 = /* @__PURE__ */ jsx(TextLinkPopover, {
332
+ open,
333
+ onOpenChange: handleOpenChange,
334
+ trigger: t12,
335
+ text: selectedText,
336
+ target: linkTarget,
337
+ onSave: applyTarget,
338
+ onUnlink: t13
339
+ });
340
+ $[28] = applyTarget;
341
+ $[29] = linkTarget;
342
+ $[30] = open;
343
+ $[31] = selectedText;
344
+ $[32] = t12;
345
+ $[33] = t13;
346
+ $[34] = t14;
347
+ } else t14 = $[34];
348
+ return t14;
349
+ }
350
+ if (!toolbarState.visible) return null;
351
+ let t12;
352
+ if ($[35] !== toolbarState.left || $[36] !== toolbarState.top) {
353
+ t12 = {
354
+ top: toolbarState.top,
355
+ left: toolbarState.left
356
+ };
357
+ $[35] = toolbarState.left;
358
+ $[36] = toolbarState.top;
359
+ $[37] = t12;
360
+ } else t12 = $[37];
361
+ const t13 = toolbarState.activeFormats & FORMAT_FLAGS.bold ? "on" : "off";
362
+ const t14 = !!(toolbarState.activeFormats & FORMAT_FLAGS.bold);
363
+ let t15;
364
+ if ($[38] !== applyFormat) {
365
+ t15 = () => applyFormat("bold");
366
+ $[38] = applyFormat;
367
+ $[39] = t15;
368
+ } else t15 = $[39];
369
+ let t16;
370
+ if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
371
+ t16 = /* @__PURE__ */ jsx(Bold, {});
372
+ $[40] = t16;
373
+ } else t16 = $[40];
374
+ let t17;
375
+ if ($[41] !== t13 || $[42] !== t14 || $[43] !== t15) {
376
+ t17 = /* @__PURE__ */ jsx(Toggle, {
377
+ "data-state": t13,
378
+ pressed: t14,
379
+ variant: "default",
380
+ size: "sm",
381
+ className: "h-7 min-w-7 px-1.5",
382
+ "aria-label": "Bold",
383
+ onPressedChange: t15,
384
+ children: t16
385
+ });
386
+ $[41] = t13;
387
+ $[42] = t14;
388
+ $[43] = t15;
389
+ $[44] = t17;
390
+ } else t17 = $[44];
391
+ const t18 = toolbarState.activeFormats & FORMAT_FLAGS.italic ? "on" : "off";
392
+ const t19 = !!(toolbarState.activeFormats & FORMAT_FLAGS.italic);
393
+ let t20;
394
+ if ($[45] !== applyFormat) {
395
+ t20 = () => applyFormat("italic");
396
+ $[45] = applyFormat;
397
+ $[46] = t20;
398
+ } else t20 = $[46];
399
+ let t21;
400
+ if ($[47] === Symbol.for("react.memo_cache_sentinel")) {
401
+ t21 = /* @__PURE__ */ jsx(Italic, {});
402
+ $[47] = t21;
403
+ } else t21 = $[47];
404
+ let t22;
405
+ if ($[48] !== t18 || $[49] !== t19 || $[50] !== t20) {
406
+ t22 = /* @__PURE__ */ jsx(Toggle, {
407
+ "data-state": t18,
408
+ pressed: t19,
409
+ variant: "default",
410
+ size: "sm",
411
+ className: "h-7 min-w-7 px-1.5",
412
+ "aria-label": "Italic",
413
+ onPressedChange: t20,
414
+ children: t21
415
+ });
416
+ $[48] = t18;
417
+ $[49] = t19;
418
+ $[50] = t20;
419
+ $[51] = t22;
420
+ } else t22 = $[51];
421
+ let t23;
422
+ if ($[52] === Symbol.for("react.memo_cache_sentinel")) {
423
+ t23 = /* @__PURE__ */ jsx(Button, {
424
+ type: "button",
425
+ variant: "ghost",
426
+ size: "sm",
427
+ className: "text-foreground hover:text-foreground h-7 min-w-7 px-1.5"
428
+ });
429
+ $[52] = t23;
430
+ } else t23 = $[52];
431
+ let t24;
432
+ if ($[53] !== applyTarget) {
433
+ t24 = () => applyTarget(null);
434
+ $[53] = applyTarget;
435
+ $[54] = t24;
436
+ } else t24 = $[54];
437
+ let t25;
438
+ if ($[55] !== applyTarget || $[56] !== linkTarget || $[57] !== open || $[58] !== selectedText || $[59] !== t24) {
439
+ t25 = /* @__PURE__ */ jsx(TextLinkPopover, {
440
+ open,
441
+ onOpenChange: handleOpenChange,
442
+ trigger: t23,
443
+ text: selectedText,
444
+ target: linkTarget,
445
+ onSave: applyTarget,
446
+ onUnlink: t24
447
+ });
448
+ $[55] = applyTarget;
449
+ $[56] = linkTarget;
450
+ $[57] = open;
451
+ $[58] = selectedText;
452
+ $[59] = t24;
453
+ $[60] = t25;
454
+ } else t25 = $[60];
455
+ let t26;
456
+ if ($[61] !== t12 || $[62] !== t17 || $[63] !== t22 || $[64] !== t25) {
457
+ t26 = /* @__PURE__ */ jsxs(FloatingToolbar, {
458
+ onMouseDown: handleMouseDown,
459
+ className: "fixed z-50 gap-0.5 rounded-lg p-1 shadow-lg",
460
+ style: t12,
461
+ children: [
462
+ t17,
463
+ t22,
464
+ t25
465
+ ]
466
+ });
467
+ $[61] = t12;
468
+ $[62] = t17;
469
+ $[63] = t22;
470
+ $[64] = t25;
471
+ $[65] = t26;
472
+ } else t26 = $[65];
473
+ return t26;
474
+ }
475
+ function _temp(event_0) {
476
+ const target_1 = event_0.target;
477
+ if (target_1 instanceof HTMLElement && target_1.closest("input, select, textarea")) return;
478
+ event_0.preventDefault();
479
+ }
480
+ function PasteUrlAsLinkHandler() {
481
+ const $ = c(4);
482
+ if ($[0] !== "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2") {
483
+ for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
484
+ $[0] = "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2";
485
+ }
486
+ const [editor] = useLexicalComposerContext();
487
+ let t0;
488
+ let t1;
489
+ if ($[1] !== editor) {
490
+ t0 = () => editor.registerCommand(PASTE_COMMAND, (event) => {
491
+ const selection = $getSelection();
492
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) return false;
493
+ if (!("clipboardData" in event) || !event.clipboardData) return false;
494
+ const url = event.clipboardData.getData("text/plain").trim();
495
+ if (!isHttpTextLinkTarget(url)) return false;
496
+ event.preventDefault();
497
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
498
+ return true;
499
+ }, COMMAND_PRIORITY_LOW);
500
+ t1 = [editor];
501
+ $[1] = editor;
502
+ $[2] = t0;
503
+ $[3] = t1;
504
+ } else {
505
+ t0 = $[2];
506
+ t1 = $[3];
507
+ }
508
+ React.useEffect(t0, t1);
509
+ return null;
510
+ }
80
511
  function SidebarLexicalEditor({ id, value, onChange, onFocus, onBlur }) {
81
512
  const timerRef = React.useRef(null);
82
513
  const onChangeRef = React.useRef(onChange);
@@ -115,19 +546,22 @@ function SidebarLexicalEditor({ id, value, onChange, onFocus, onBlur }) {
115
546
  ignoreSelectionChange: true
116
547
  }),
117
548
  /* @__PURE__ */ jsx(HistoryPlugin, {}),
549
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
118
550
  /* @__PURE__ */ jsx(ExternalStateSync, {
119
551
  value,
120
552
  isSyncingRef
121
553
  }),
122
- /* @__PURE__ */ jsx(EnterAsLineBreakHandler, {})
554
+ /* @__PURE__ */ jsx(EnterAsLineBreakHandler, {}),
555
+ /* @__PURE__ */ jsx(PasteUrlAsLinkHandler, {}),
556
+ /* @__PURE__ */ jsx(SidebarFloatingTextToolbar, {})
123
557
  ]
124
558
  });
125
559
  }
126
560
  function LexicalErrorBoundary(t0) {
127
561
  const $ = c(3);
128
- if ($[0] !== "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e") {
562
+ if ($[0] !== "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2") {
129
563
  for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
130
- $[0] = "007895f09b5a774a44cc27d8ef0328c4985b935c9b126d4ae8b5c8fe1ffd622e";
564
+ $[0] = "487a2055e45cbfcdfeb0dfca78f86f0618f64602e19388a1a5bda9fa6e7197a2";
131
565
  }
132
566
  const { children } = t0;
133
567
  let t1;