@elia-ori/editor 0.1.27 → 0.1.29
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.cjs +1627 -533
- package/dist/index.cjs.map +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +14 -21
- package/dist/index.d.ts +14 -21
- package/dist/index.js +1572 -480
- package/dist/index.js.map +1 -1
- package/dist/styles/editor.css +213 -243
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -30,17 +30,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
Callout: () => Callout,
|
|
34
33
|
EliaEditor: () => EliaEditor,
|
|
34
|
+
EliaEditorProvider: () => EliaEditorProvider,
|
|
35
35
|
SimpleEditor: () => EliaEditor,
|
|
36
|
+
TocSidebar: () => TocSidebar,
|
|
36
37
|
contentExtensions: () => contentExtensions,
|
|
37
|
-
generateEditorHTML: () => generateEditorHTML
|
|
38
|
+
generateEditorHTML: () => generateEditorHTML,
|
|
39
|
+
useEliaEditor: () => useEliaEditor
|
|
38
40
|
});
|
|
39
41
|
module.exports = __toCommonJS(index_exports);
|
|
40
42
|
|
|
41
43
|
// src/components/tiptap-templates/simple/simple-editor.tsx
|
|
42
|
-
var
|
|
43
|
-
var
|
|
44
|
+
var import_react101 = require("react");
|
|
45
|
+
var import_react102 = require("@tiptap/react");
|
|
44
46
|
var import_starter_kit = require("@tiptap/starter-kit");
|
|
45
47
|
var import_extension_list = require("@tiptap/extension-list");
|
|
46
48
|
var import_extension_text_align = require("@tiptap/extension-text-align");
|
|
@@ -56,72 +58,9 @@ var import_extension_table = require("@tiptap/extension-table");
|
|
|
56
58
|
var import_extension_table_row = require("@tiptap/extension-table-row");
|
|
57
59
|
var import_extension_table_header = require("@tiptap/extension-table-header");
|
|
58
60
|
var import_extension_table_cell = require("@tiptap/extension-table-cell");
|
|
61
|
+
var import_extension_unique_id = require("@tiptap/extension-unique-id");
|
|
59
62
|
var import_tiptap_extension_resize_image = __toESM(require("tiptap-extension-resize-image"), 1);
|
|
60
63
|
|
|
61
|
-
// src/extensions/callout.ts
|
|
62
|
-
var import_core = require("@tiptap/core");
|
|
63
|
-
var CALLOUT_ARIA_LABELS = {
|
|
64
|
-
success: "\u6210\u529F\u63D0\u793A",
|
|
65
|
-
warning: "\u8B66\u544A\u63D0\u793A",
|
|
66
|
-
error: "\u932F\u8AA4\u63D0\u793A",
|
|
67
|
-
info: "\u8CC7\u8A0A\u63D0\u793A"
|
|
68
|
-
};
|
|
69
|
-
var Callout = import_core.Node.create({
|
|
70
|
-
name: "callout",
|
|
71
|
-
group: "block",
|
|
72
|
-
content: "block+",
|
|
73
|
-
defining: true,
|
|
74
|
-
addOptions() {
|
|
75
|
-
return {
|
|
76
|
-
HTMLAttributes: {}
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
addAttributes() {
|
|
80
|
-
return {
|
|
81
|
-
type: {
|
|
82
|
-
default: "info",
|
|
83
|
-
parseHTML: (element) => element.getAttribute("data-type"),
|
|
84
|
-
renderHTML: (attributes) => ({
|
|
85
|
-
"data-type": attributes.type
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
},
|
|
90
|
-
parseHTML() {
|
|
91
|
-
return [
|
|
92
|
-
{
|
|
93
|
-
tag: "div[data-callout]"
|
|
94
|
-
}
|
|
95
|
-
];
|
|
96
|
-
},
|
|
97
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
98
|
-
const type = node.attrs.type || "info";
|
|
99
|
-
return [
|
|
100
|
-
"div",
|
|
101
|
-
(0, import_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, {
|
|
102
|
-
"data-callout": "",
|
|
103
|
-
"class": `callout callout-${type}`,
|
|
104
|
-
"role": "note",
|
|
105
|
-
"aria-label": CALLOUT_ARIA_LABELS[type]
|
|
106
|
-
}),
|
|
107
|
-
0
|
|
108
|
-
];
|
|
109
|
-
},
|
|
110
|
-
addCommands() {
|
|
111
|
-
return {
|
|
112
|
-
setCallout: (attributes) => ({ commands }) => {
|
|
113
|
-
return commands.wrapIn(this.name, attributes);
|
|
114
|
-
},
|
|
115
|
-
toggleCallout: (attributes) => ({ commands }) => {
|
|
116
|
-
return commands.toggleWrap(this.name, attributes);
|
|
117
|
-
},
|
|
118
|
-
unsetCallout: () => ({ commands }) => {
|
|
119
|
-
return commands.lift(this.name);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
64
|
// src/components/tiptap-ui-primitive/button/button.tsx
|
|
126
65
|
var import_react4 = require("react");
|
|
127
66
|
|
|
@@ -232,7 +171,6 @@ var TooltipTrigger = (0, import_react.forwardRef)(
|
|
|
232
171
|
"button",
|
|
233
172
|
{
|
|
234
173
|
ref,
|
|
235
|
-
type: "button",
|
|
236
174
|
"data-tooltip-state": context.open ? "open" : "closed",
|
|
237
175
|
...context.getReferenceProps(props),
|
|
238
176
|
children
|
|
@@ -437,36 +375,22 @@ function selectionWithinConvertibleTypes(editor, types = []) {
|
|
|
437
375
|
return false;
|
|
438
376
|
}
|
|
439
377
|
var handleImageUpload = async (file, onProgress, abortSignal) => {
|
|
440
|
-
if (!file)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
378
|
+
if (!file) {
|
|
379
|
+
throw new Error("No file provided");
|
|
380
|
+
}
|
|
381
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`File size exceeds maximum allowed (${MAX_FILE_SIZE / (1024 * 1024)}MB)`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
for (let progress = 0; progress <= 100; progress += 10) {
|
|
387
|
+
if (abortSignal?.aborted) {
|
|
388
|
+
throw new Error("Upload cancelled");
|
|
451
389
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
resolve(data.url);
|
|
457
|
-
} catch {
|
|
458
|
-
reject(new Error("Invalid response format"));
|
|
459
|
-
}
|
|
460
|
-
} else {
|
|
461
|
-
reject(new Error(`Upload failed: ${xhr.status}`));
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
xhr.onerror = () => reject(new Error("Network error"));
|
|
465
|
-
xhr.onabort = () => reject(new Error("Upload cancelled"));
|
|
466
|
-
abortSignal?.addEventListener("abort", () => xhr.abort());
|
|
467
|
-
xhr.open("POST", "/api/upload");
|
|
468
|
-
xhr.send(formData);
|
|
469
|
-
});
|
|
390
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
391
|
+
onProgress?.({ progress });
|
|
392
|
+
}
|
|
393
|
+
return "/images/tiptap-ui-placeholder-image.jpg";
|
|
470
394
|
};
|
|
471
395
|
var ATTR_WHITESPACE = (
|
|
472
396
|
// eslint-disable-next-line no-control-regex
|
|
@@ -511,6 +435,34 @@ function sanitizeUrl(inputUrl, baseUrl, protocols) {
|
|
|
511
435
|
}
|
|
512
436
|
return "#";
|
|
513
437
|
}
|
|
438
|
+
function selectCurrentBlockContent(editor) {
|
|
439
|
+
const { selection, doc } = editor.state;
|
|
440
|
+
if (!selection.empty) return;
|
|
441
|
+
const $pos = selection.$from;
|
|
442
|
+
let blockNode = null;
|
|
443
|
+
let blockPos = -1;
|
|
444
|
+
for (let depth = $pos.depth; depth >= 0; depth--) {
|
|
445
|
+
const node = $pos.node(depth);
|
|
446
|
+
const pos = $pos.start(depth);
|
|
447
|
+
if (node.isBlock && node.textContent.trim()) {
|
|
448
|
+
blockNode = node;
|
|
449
|
+
blockPos = pos;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (blockNode && blockPos >= 0) {
|
|
454
|
+
const from = blockPos;
|
|
455
|
+
const to = blockPos + blockNode.nodeSize - 2;
|
|
456
|
+
if (from < to) {
|
|
457
|
+
const $from = doc.resolve(from);
|
|
458
|
+
const $to = doc.resolve(to);
|
|
459
|
+
const newSelection = import_state.TextSelection.between($from, $to, 1);
|
|
460
|
+
if (newSelection && !selection.eq(newSelection)) {
|
|
461
|
+
editor.view.dispatch(editor.state.tr.setSelection(newSelection));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
514
466
|
function getSelectedBlockNodes(editor) {
|
|
515
467
|
const { doc } = editor.state;
|
|
516
468
|
const { from, to } = editor.state.selection;
|
|
@@ -1996,7 +1948,7 @@ function useHeading(config) {
|
|
|
1996
1948
|
isActive,
|
|
1997
1949
|
handleToggle,
|
|
1998
1950
|
canToggle: canToggleState,
|
|
1999
|
-
label:
|
|
1951
|
+
label: `Heading ${level}`,
|
|
2000
1952
|
shortcutKeys: HEADING_SHORTCUT_KEYS[level],
|
|
2001
1953
|
Icon: headingIcons[level]
|
|
2002
1954
|
};
|
|
@@ -2031,7 +1983,7 @@ var DropdownMenuSubContent = (0, import_react27.forwardRef)(({ className, portal
|
|
|
2031
1983
|
return portal ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DropdownMenuPortal, { ...typeof portal === "object" ? portal : {}, children: content }) : content;
|
|
2032
1984
|
});
|
|
2033
1985
|
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
2034
|
-
var DropdownMenuContent = (0, import_react27.forwardRef)(({ className, sideOffset = 4, portal =
|
|
1986
|
+
var DropdownMenuContent = (0, import_react27.forwardRef)(({ className, sideOffset = 4, portal = false, ...props }, ref) => {
|
|
2035
1987
|
const content = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2036
1988
|
DropdownMenuPrimitive.Content,
|
|
2037
1989
|
{
|
|
@@ -2755,9 +2707,9 @@ var listIcons = {
|
|
|
2755
2707
|
taskList: ListTodoIcon
|
|
2756
2708
|
};
|
|
2757
2709
|
var listLabels = {
|
|
2758
|
-
bulletList: "
|
|
2759
|
-
orderedList: "
|
|
2760
|
-
taskList: "
|
|
2710
|
+
bulletList: "Bullet List",
|
|
2711
|
+
orderedList: "Ordered List",
|
|
2712
|
+
taskList: "Task List"
|
|
2761
2713
|
};
|
|
2762
2714
|
var LIST_SHORTCUT_KEYS = {
|
|
2763
2715
|
bulletList: "mod+shift+8",
|
|
@@ -3126,7 +3078,7 @@ var BlockquoteButton = (0, import_react43.forwardRef)(
|
|
|
3126
3078
|
"data-disabled": !canToggle3,
|
|
3127
3079
|
"aria-label": label,
|
|
3128
3080
|
"aria-pressed": isActive,
|
|
3129
|
-
tooltip: "
|
|
3081
|
+
tooltip: "Blockquote",
|
|
3130
3082
|
onClick: handleClick,
|
|
3131
3083
|
...buttonProps,
|
|
3132
3084
|
ref,
|
|
@@ -3312,7 +3264,7 @@ function useBlockquote(config) {
|
|
|
3312
3264
|
isActive,
|
|
3313
3265
|
handleToggle,
|
|
3314
3266
|
canToggle: canToggle3,
|
|
3315
|
-
label: "
|
|
3267
|
+
label: "Blockquote",
|
|
3316
3268
|
shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,
|
|
3317
3269
|
Icon: BlockquoteIcon
|
|
3318
3270
|
};
|
|
@@ -3374,7 +3326,7 @@ var CodeBlockButton = (0, import_react46.forwardRef)(
|
|
|
3374
3326
|
tabIndex: -1,
|
|
3375
3327
|
"aria-label": label,
|
|
3376
3328
|
"aria-pressed": isActive,
|
|
3377
|
-
tooltip: "
|
|
3329
|
+
tooltip: "Code Block",
|
|
3378
3330
|
onClick: handleClick,
|
|
3379
3331
|
...buttonProps,
|
|
3380
3332
|
ref,
|
|
@@ -3551,7 +3503,7 @@ function useCodeBlock(config) {
|
|
|
3551
3503
|
isActive,
|
|
3552
3504
|
handleToggle,
|
|
3553
3505
|
canToggle: canToggleState,
|
|
3554
|
-
label: "
|
|
3506
|
+
label: "Code Block",
|
|
3555
3507
|
shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
|
|
3556
3508
|
Icon: CodeBlockIcon
|
|
3557
3509
|
};
|
|
@@ -3742,52 +3694,52 @@ var import_react_hotkeys_hook2 = require("react-hotkeys-hook");
|
|
|
3742
3694
|
var COLOR_HIGHLIGHT_SHORTCUT_KEY = "mod+shift+h";
|
|
3743
3695
|
var HIGHLIGHT_COLORS = [
|
|
3744
3696
|
{
|
|
3745
|
-
label: "
|
|
3697
|
+
label: "Default background",
|
|
3746
3698
|
value: "var(--tt-bg-color)",
|
|
3747
3699
|
border: "var(--tt-bg-color-contrast)"
|
|
3748
3700
|
},
|
|
3749
3701
|
{
|
|
3750
|
-
label: "
|
|
3702
|
+
label: "Gray background",
|
|
3751
3703
|
value: "var(--tt-color-highlight-gray)",
|
|
3752
3704
|
border: "var(--tt-color-highlight-gray-contrast)"
|
|
3753
3705
|
},
|
|
3754
3706
|
{
|
|
3755
|
-
label: "
|
|
3707
|
+
label: "Brown background",
|
|
3756
3708
|
value: "var(--tt-color-highlight-brown)",
|
|
3757
3709
|
border: "var(--tt-color-highlight-brown-contrast)"
|
|
3758
3710
|
},
|
|
3759
3711
|
{
|
|
3760
|
-
label: "
|
|
3712
|
+
label: "Orange background",
|
|
3761
3713
|
value: "var(--tt-color-highlight-orange)",
|
|
3762
3714
|
border: "var(--tt-color-highlight-orange-contrast)"
|
|
3763
3715
|
},
|
|
3764
3716
|
{
|
|
3765
|
-
label: "
|
|
3717
|
+
label: "Yellow background",
|
|
3766
3718
|
value: "var(--tt-color-highlight-yellow)",
|
|
3767
3719
|
border: "var(--tt-color-highlight-yellow-contrast)"
|
|
3768
3720
|
},
|
|
3769
3721
|
{
|
|
3770
|
-
label: "
|
|
3722
|
+
label: "Green background",
|
|
3771
3723
|
value: "var(--tt-color-highlight-green)",
|
|
3772
3724
|
border: "var(--tt-color-highlight-green-contrast)"
|
|
3773
3725
|
},
|
|
3774
3726
|
{
|
|
3775
|
-
label: "
|
|
3727
|
+
label: "Blue background",
|
|
3776
3728
|
value: "var(--tt-color-highlight-blue)",
|
|
3777
3729
|
border: "var(--tt-color-highlight-blue-contrast)"
|
|
3778
3730
|
},
|
|
3779
3731
|
{
|
|
3780
|
-
label: "
|
|
3732
|
+
label: "Purple background",
|
|
3781
3733
|
value: "var(--tt-color-highlight-purple)",
|
|
3782
3734
|
border: "var(--tt-color-highlight-purple-contrast)"
|
|
3783
3735
|
},
|
|
3784
3736
|
{
|
|
3785
|
-
label: "
|
|
3737
|
+
label: "Pink background",
|
|
3786
3738
|
value: "var(--tt-color-highlight-pink)",
|
|
3787
3739
|
border: "var(--tt-color-highlight-pink-contrast)"
|
|
3788
3740
|
},
|
|
3789
3741
|
{
|
|
3790
|
-
label: "
|
|
3742
|
+
label: "Red background",
|
|
3791
3743
|
value: "var(--tt-color-highlight-red)",
|
|
3792
3744
|
border: "var(--tt-color-highlight-red-contrast)"
|
|
3793
3745
|
}
|
|
@@ -3913,7 +3865,7 @@ function useColorHighlight(config) {
|
|
|
3913
3865
|
const handleRemoveHighlight = (0, import_react52.useCallback)(() => {
|
|
3914
3866
|
const success = removeHighlight(editor, mode);
|
|
3915
3867
|
if (success) {
|
|
3916
|
-
onApplied?.({ color: "", label: "
|
|
3868
|
+
onApplied?.({ color: "", label: "Remove highlight", mode });
|
|
3917
3869
|
}
|
|
3918
3870
|
return success;
|
|
3919
3871
|
}, [editor, onApplied, mode]);
|
|
@@ -3935,7 +3887,7 @@ function useColorHighlight(config) {
|
|
|
3935
3887
|
handleColorHighlight,
|
|
3936
3888
|
handleRemoveHighlight,
|
|
3937
3889
|
canColorHighlight: canColorHighlightState,
|
|
3938
|
-
label: label ||
|
|
3890
|
+
label: label || `Highlight`,
|
|
3939
3891
|
shortcutKeys: COLOR_HIGHLIGHT_SHORTCUT_KEY,
|
|
3940
3892
|
Icon: HighlighterIcon,
|
|
3941
3893
|
mode
|
|
@@ -5538,300 +5490,42 @@ function useUndoRedo(config) {
|
|
|
5538
5490
|
};
|
|
5539
5491
|
}
|
|
5540
5492
|
|
|
5541
|
-
// src/components/tiptap-ui/
|
|
5542
|
-
var
|
|
5493
|
+
// src/components/tiptap-node/table-node/ui/table-trigger-button/table-grid-selector.tsx
|
|
5494
|
+
var import_react81 = require("react");
|
|
5495
|
+
|
|
5496
|
+
// src/components/tiptap-icons/table-column-icon.tsx
|
|
5497
|
+
var import_react79 = require("react");
|
|
5543
5498
|
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
5544
|
-
var
|
|
5545
|
-
{ type: "success", label: "\u6210\u529F\u63D0\u793A", className: "callout-option--success" },
|
|
5546
|
-
{ type: "warning", label: "\u8B66\u544A\u63D0\u793A", className: "callout-option--warning" },
|
|
5547
|
-
{ type: "error", label: "\u932F\u8AA4\u63D0\u793A", className: "callout-option--error" },
|
|
5548
|
-
{ type: "info", label: "\u8CC7\u8A0A\u63D0\u793A", className: "callout-option--info" }
|
|
5549
|
-
];
|
|
5550
|
-
function CalloutButton({ portal = true }) {
|
|
5551
|
-
const { editor } = useTiptapEditor();
|
|
5552
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
|
5553
|
-
const [isVisible, setIsVisible] = React.useState(true);
|
|
5554
|
-
React.useEffect(() => {
|
|
5555
|
-
if (!editor) return;
|
|
5556
|
-
setIsVisible(editor.isEditable);
|
|
5557
|
-
}, [editor]);
|
|
5558
|
-
const handleSelect = React.useCallback(
|
|
5559
|
-
(type) => {
|
|
5560
|
-
if (!editor) return;
|
|
5561
|
-
editor.chain().focus().setCallout({ type }).run();
|
|
5562
|
-
setIsOpen(false);
|
|
5563
|
-
},
|
|
5564
|
-
[editor]
|
|
5565
|
-
);
|
|
5566
|
-
if (!isVisible) return null;
|
|
5567
|
-
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
5568
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
|
|
5569
|
-
Button,
|
|
5570
|
-
{
|
|
5571
|
-
"data-style": "ghost",
|
|
5572
|
-
"data-active-state": isOpen ? "on" : "off",
|
|
5573
|
-
disabled: !editor,
|
|
5574
|
-
tooltip: "\u63D0\u793A\u6846",
|
|
5575
|
-
"aria-label": "\u63D0\u793A\u6846",
|
|
5576
|
-
children: [
|
|
5577
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(CalloutIcon, { className: "tiptap-button-icon" }),
|
|
5578
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(ChevronDownIcon, { className: "tiptap-button-icon" })
|
|
5579
|
-
]
|
|
5580
|
-
}
|
|
5581
|
-
) }),
|
|
5582
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(DropdownMenuContent, { portal, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(CardBody, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "callout-options", children: CALLOUT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
5583
|
-
"button",
|
|
5584
|
-
{
|
|
5585
|
-
type: "button",
|
|
5586
|
-
className: `callout-option ${option.className}`,
|
|
5587
|
-
onClick: () => handleSelect(option.type),
|
|
5588
|
-
title: option.label,
|
|
5589
|
-
"aria-label": option.label
|
|
5590
|
-
},
|
|
5591
|
-
option.type
|
|
5592
|
-
)) }) }) }) })
|
|
5593
|
-
] });
|
|
5594
|
-
}
|
|
5595
|
-
function CalloutIcon({ className }) {
|
|
5499
|
+
var TableColumnIcon = (0, import_react79.memo)(({ className, ...props }) => {
|
|
5596
5500
|
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
5597
5501
|
"svg",
|
|
5598
5502
|
{
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
height: "18",
|
|
5602
|
-
viewBox: "0 0 24 24",
|
|
5603
|
-
fill: "none",
|
|
5604
|
-
stroke: "currentColor",
|
|
5605
|
-
strokeWidth: "2",
|
|
5606
|
-
strokeLinecap: "round",
|
|
5607
|
-
strokeLinejoin: "round",
|
|
5503
|
+
width: "24",
|
|
5504
|
+
height: "24",
|
|
5608
5505
|
className,
|
|
5609
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
|
|
5610
|
-
}
|
|
5611
|
-
);
|
|
5612
|
-
}
|
|
5613
|
-
|
|
5614
|
-
// src/components/tiptap-ui/table-button/table-button.tsx
|
|
5615
|
-
var React2 = __toESM(require("react"), 1);
|
|
5616
|
-
var import_jsx_runtime60 = require("react/jsx-runtime");
|
|
5617
|
-
var GRID_SIZE = 6;
|
|
5618
|
-
var TOTAL_CELLS = GRID_SIZE * GRID_SIZE;
|
|
5619
|
-
function TableButton({ portal = true }) {
|
|
5620
|
-
const { editor } = useTiptapEditor();
|
|
5621
|
-
const [isOpen, setIsOpen] = React2.useState(false);
|
|
5622
|
-
const [isVisible, setIsVisible] = React2.useState(true);
|
|
5623
|
-
const [hoveredCell, setHoveredCell] = React2.useState(null);
|
|
5624
|
-
React2.useEffect(() => {
|
|
5625
|
-
if (!editor) return;
|
|
5626
|
-
setIsVisible(editor.isEditable);
|
|
5627
|
-
}, [editor]);
|
|
5628
|
-
const isInTable = editor?.isActive("table") ?? false;
|
|
5629
|
-
const insertTable = React2.useCallback((rows, cols) => {
|
|
5630
|
-
if (!editor) return;
|
|
5631
|
-
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
5632
|
-
setIsOpen(false);
|
|
5633
|
-
setHoveredCell(null);
|
|
5634
|
-
}, [editor]);
|
|
5635
|
-
const addRowBefore = React2.useCallback(() => {
|
|
5636
|
-
if (!editor) return;
|
|
5637
|
-
editor.chain().focus().addRowBefore().run();
|
|
5638
|
-
setIsOpen(false);
|
|
5639
|
-
}, [editor]);
|
|
5640
|
-
const addRowAfter = React2.useCallback(() => {
|
|
5641
|
-
if (!editor) return;
|
|
5642
|
-
editor.chain().focus().addRowAfter().run();
|
|
5643
|
-
setIsOpen(false);
|
|
5644
|
-
}, [editor]);
|
|
5645
|
-
const addColumnBefore = React2.useCallback(() => {
|
|
5646
|
-
if (!editor) return;
|
|
5647
|
-
editor.chain().focus().addColumnBefore().run();
|
|
5648
|
-
setIsOpen(false);
|
|
5649
|
-
}, [editor]);
|
|
5650
|
-
const addColumnAfter = React2.useCallback(() => {
|
|
5651
|
-
if (!editor) return;
|
|
5652
|
-
editor.chain().focus().addColumnAfter().run();
|
|
5653
|
-
setIsOpen(false);
|
|
5654
|
-
}, [editor]);
|
|
5655
|
-
const deleteRow = React2.useCallback(() => {
|
|
5656
|
-
if (!editor) return;
|
|
5657
|
-
editor.chain().focus().deleteRow().run();
|
|
5658
|
-
setIsOpen(false);
|
|
5659
|
-
}, [editor]);
|
|
5660
|
-
const deleteColumn = React2.useCallback(() => {
|
|
5661
|
-
if (!editor) return;
|
|
5662
|
-
editor.chain().focus().deleteColumn().run();
|
|
5663
|
-
setIsOpen(false);
|
|
5664
|
-
}, [editor]);
|
|
5665
|
-
const deleteTable = React2.useCallback(() => {
|
|
5666
|
-
if (!editor) return;
|
|
5667
|
-
editor.chain().focus().deleteTable().run();
|
|
5668
|
-
setIsOpen(false);
|
|
5669
|
-
}, [editor]);
|
|
5670
|
-
const handleOpenChange = React2.useCallback((open) => {
|
|
5671
|
-
setIsOpen(open);
|
|
5672
|
-
if (!open) {
|
|
5673
|
-
setHoveredCell(null);
|
|
5674
|
-
}
|
|
5675
|
-
}, []);
|
|
5676
|
-
if (!isVisible) return null;
|
|
5677
|
-
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(DropdownMenu, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
5678
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
5679
|
-
Button,
|
|
5680
|
-
{
|
|
5681
|
-
"data-style": "ghost",
|
|
5682
|
-
"data-active-state": isOpen || isInTable ? "on" : "off",
|
|
5683
|
-
disabled: !editor,
|
|
5684
|
-
tooltip: "\u8868\u683C",
|
|
5685
|
-
"aria-label": "\u8868\u683C",
|
|
5686
|
-
children: [
|
|
5687
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(TableIcon, { className: "tiptap-button-icon" }),
|
|
5688
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ChevronDownIcon, { className: "tiptap-button-icon" })
|
|
5689
|
-
]
|
|
5690
|
-
}
|
|
5691
|
-
) }),
|
|
5692
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuContent, { portal, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CardBody, { children: !isInTable ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "table-grid-picker", onMouseLeave: () => setHoveredCell(null), children: [
|
|
5693
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "table-grid", children: Array.from({ length: TOTAL_CELLS }).map((_, index) => {
|
|
5694
|
-
const row = Math.floor(index / GRID_SIZE);
|
|
5695
|
-
const col = index % GRID_SIZE;
|
|
5696
|
-
const isSelected = hoveredCell && row <= hoveredCell.row && col <= hoveredCell.col;
|
|
5697
|
-
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5698
|
-
"div",
|
|
5699
|
-
{
|
|
5700
|
-
className: `table-grid-cell ${isSelected ? "is-selected" : ""}`,
|
|
5701
|
-
onMouseEnter: () => setHoveredCell({ row, col }),
|
|
5702
|
-
onClick: () => insertTable(row + 1, col + 1)
|
|
5703
|
-
},
|
|
5704
|
-
index
|
|
5705
|
-
);
|
|
5706
|
-
}) }),
|
|
5707
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "table-grid-size", children: hoveredCell ? `${hoveredCell.row + 1} x ${hoveredCell.col + 1}` : "" })
|
|
5708
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
|
|
5709
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: addRowBefore, children: "\u4E0A\u65B9\u63D2\u5165\u5217" }),
|
|
5710
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: addRowAfter, children: "\u4E0B\u65B9\u63D2\u5165\u5217" }),
|
|
5711
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: addColumnBefore, children: "\u5DE6\u65B9\u63D2\u5165\u6B04" }),
|
|
5712
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: addColumnAfter, children: "\u53F3\u65B9\u63D2\u5165\u6B04" }),
|
|
5713
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: deleteRow, children: "\u522A\u9664\u5217" }),
|
|
5714
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: deleteColumn, children: "\u522A\u9664\u6B04" }),
|
|
5715
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(DropdownMenuItem, { onSelect: deleteTable, children: "\u522A\u9664\u8868\u683C" })
|
|
5716
|
-
] }) }) }) })
|
|
5717
|
-
] });
|
|
5718
|
-
}
|
|
5719
|
-
TableButton.displayName = "TableButton";
|
|
5720
|
-
function TableIcon({ className }) {
|
|
5721
|
-
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
5722
|
-
"svg",
|
|
5723
|
-
{
|
|
5724
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
5725
|
-
width: "18",
|
|
5726
|
-
height: "18",
|
|
5727
5506
|
viewBox: "0 0 24 24",
|
|
5728
|
-
fill: "
|
|
5729
|
-
stroke: "currentColor",
|
|
5730
|
-
strokeWidth: "2",
|
|
5731
|
-
strokeLinecap: "round",
|
|
5732
|
-
strokeLinejoin: "round",
|
|
5733
|
-
className,
|
|
5734
|
-
children: [
|
|
5735
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("path", { d: "M12 3v18" }),
|
|
5736
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
5737
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("path", { d: "M3 9h18" }),
|
|
5738
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("path", { d: "M3 15h18" })
|
|
5739
|
-
]
|
|
5740
|
-
}
|
|
5741
|
-
);
|
|
5742
|
-
}
|
|
5743
|
-
|
|
5744
|
-
// src/components/tiptap-ui/text-color-button/text-color-button.tsx
|
|
5745
|
-
var React3 = __toESM(require("react"), 1);
|
|
5746
|
-
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
5747
|
-
var TEXT_COLORS = [
|
|
5748
|
-
{ color: "inherit", label: "\u9810\u8A2D", className: "text-color--default" },
|
|
5749
|
-
{ color: "var(--tt-color-text-gray)", label: "\u7070\u8272", className: "text-color--gray" },
|
|
5750
|
-
{ color: "var(--tt-color-text-brown)", label: "\u68D5\u8272", className: "text-color--brown" },
|
|
5751
|
-
{ color: "var(--tt-color-text-orange)", label: "\u6A58\u8272", className: "text-color--orange" },
|
|
5752
|
-
{ color: "var(--tt-color-text-yellow)", label: "\u9EC3\u8272", className: "text-color--yellow" },
|
|
5753
|
-
{ color: "var(--tt-color-text-green)", label: "\u7DA0\u8272", className: "text-color--green" },
|
|
5754
|
-
{ color: "var(--tt-color-text-blue)", label: "\u85CD\u8272", className: "text-color--blue" },
|
|
5755
|
-
{ color: "var(--tt-color-text-purple)", label: "\u7D2B\u8272", className: "text-color--purple" },
|
|
5756
|
-
{ color: "var(--tt-color-text-pink)", label: "\u7C89\u7D05", className: "text-color--pink" },
|
|
5757
|
-
{ color: "var(--tt-color-text-red)", label: "\u7D05\u8272", className: "text-color--red" }
|
|
5758
|
-
];
|
|
5759
|
-
function TextColorButton({ portal = true }) {
|
|
5760
|
-
const { editor } = useTiptapEditor();
|
|
5761
|
-
const [isOpen, setIsOpen] = React3.useState(false);
|
|
5762
|
-
const [isVisible, setIsVisible] = React3.useState(true);
|
|
5763
|
-
React3.useEffect(() => {
|
|
5764
|
-
if (!editor) return;
|
|
5765
|
-
setIsVisible(editor.isEditable);
|
|
5766
|
-
}, [editor]);
|
|
5767
|
-
const handleSelect = React3.useCallback(
|
|
5768
|
-
(color) => {
|
|
5769
|
-
if (!editor) return;
|
|
5770
|
-
if (color === "inherit") {
|
|
5771
|
-
editor.chain().focus().unsetColor().run();
|
|
5772
|
-
} else {
|
|
5773
|
-
editor.chain().focus().setColor(color).run();
|
|
5774
|
-
}
|
|
5775
|
-
setIsOpen(false);
|
|
5776
|
-
},
|
|
5777
|
-
[editor]
|
|
5778
|
-
);
|
|
5779
|
-
if (!isVisible) return null;
|
|
5780
|
-
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
5781
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
|
|
5782
|
-
Button,
|
|
5783
|
-
{
|
|
5784
|
-
"data-style": "ghost",
|
|
5785
|
-
"data-active-state": isOpen ? "on" : "off",
|
|
5786
|
-
disabled: !editor,
|
|
5787
|
-
children: [
|
|
5788
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(TextColorIcon, { className: "tiptap-button-icon" }),
|
|
5789
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ChevronDownIcon, { className: "tiptap-button-icon" })
|
|
5790
|
-
]
|
|
5791
|
-
}
|
|
5792
|
-
) }),
|
|
5793
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(DropdownMenuContent, { portal, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(CardBody, { children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "text-color-options", children: TEXT_COLORS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
5794
|
-
"button",
|
|
5795
|
-
{
|
|
5796
|
-
type: "button",
|
|
5797
|
-
className: `text-color-option ${option.className}`,
|
|
5798
|
-
onClick: () => handleSelect(option.color),
|
|
5799
|
-
title: option.label,
|
|
5800
|
-
"aria-label": option.label,
|
|
5801
|
-
children: "A"
|
|
5802
|
-
},
|
|
5803
|
-
option.color
|
|
5804
|
-
)) }) }) }) })
|
|
5805
|
-
] });
|
|
5806
|
-
}
|
|
5807
|
-
function TextColorIcon({ className }) {
|
|
5808
|
-
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
|
|
5809
|
-
"svg",
|
|
5810
|
-
{
|
|
5507
|
+
fill: "currentColor",
|
|
5811
5508
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("path", { d: "M4 20h16" }),
|
|
5823
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("path", { d: "m6 16 6-12 6 12" }),
|
|
5824
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("path", { d: "M8 12h8" })
|
|
5825
|
-
]
|
|
5509
|
+
...props,
|
|
5510
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
5511
|
+
"path",
|
|
5512
|
+
{
|
|
5513
|
+
fillRule: "evenodd",
|
|
5514
|
+
clipRule: "evenodd",
|
|
5515
|
+
d: "M4 5C4 4.44772 4.44772 4 5 4H8V20H5C4.44772 20 4 19.5523 4 19V5ZM9 22H5C3.34315 22 2 20.6569 2 19V5C2 3.34315 3.34315 2 5 2H9H15H19C20.6569 2 22 3.34315 22 5V19C22 20.6569 20.6569 22 19 22H15H9ZM14 20H10V4H14V20ZM16 20H19C19.5523 20 20 19.5523 20 19V5C20 4.44772 19.5523 4 19 4H16V20Z",
|
|
5516
|
+
fill: "currentColor"
|
|
5517
|
+
}
|
|
5518
|
+
)
|
|
5826
5519
|
}
|
|
5827
5520
|
);
|
|
5828
|
-
}
|
|
5521
|
+
});
|
|
5522
|
+
TableColumnIcon.displayName = "TableColumnIcon";
|
|
5829
5523
|
|
|
5830
|
-
// src/components/tiptap-icons/
|
|
5831
|
-
var
|
|
5832
|
-
var
|
|
5833
|
-
var
|
|
5834
|
-
return /* @__PURE__ */ (0,
|
|
5524
|
+
// src/components/tiptap-icons/table-row-icon.tsx
|
|
5525
|
+
var import_react80 = require("react");
|
|
5526
|
+
var import_jsx_runtime60 = require("react/jsx-runtime");
|
|
5527
|
+
var TableRowIcon = (0, import_react80.memo)(({ className, ...props }) => {
|
|
5528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5835
5529
|
"svg",
|
|
5836
5530
|
{
|
|
5837
5531
|
width: "24",
|
|
@@ -5841,69 +5535,1322 @@ var ArrowLeftIcon = (0, import_react79.memo)(({ className, ...props }) => {
|
|
|
5841
5535
|
fill: "currentColor",
|
|
5842
5536
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5843
5537
|
...props,
|
|
5844
|
-
children: /* @__PURE__ */ (0,
|
|
5538
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5845
5539
|
"path",
|
|
5846
5540
|
{
|
|
5847
|
-
|
|
5541
|
+
fillRule: "evenodd",
|
|
5542
|
+
clipRule: "evenodd",
|
|
5543
|
+
d: "M5 4C4.44772 4 4 4.44772 4 5V8H20V5C20 4.44772 19.5523 4 19 4H5ZM2 5V9V15V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V15V9V5C22 3.34315 20.6569 2 19 2H5C3.34315 2 2 3.34315 2 5ZM20 14V10H4V14H20ZM4 16H20V19C20 19.5523 19.5523 20 19 20H5C4.44772 20 4 19.5523 4 19V16Z",
|
|
5848
5544
|
fill: "currentColor"
|
|
5849
5545
|
}
|
|
5850
5546
|
)
|
|
5851
5547
|
}
|
|
5852
5548
|
);
|
|
5853
5549
|
});
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
// src/hooks/use-window-size.ts
|
|
5857
|
-
var import_react82 = require("react");
|
|
5550
|
+
TableRowIcon.displayName = "TableRowIcon";
|
|
5858
5551
|
|
|
5859
|
-
// src/
|
|
5860
|
-
var
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
var useUnmount = (callback) => {
|
|
5865
|
-
const ref = (0, import_react80.useRef)(callback);
|
|
5866
|
-
ref.current = callback;
|
|
5867
|
-
(0, import_react80.useEffect)(
|
|
5868
|
-
() => () => {
|
|
5869
|
-
ref.current();
|
|
5870
|
-
},
|
|
5871
|
-
[]
|
|
5872
|
-
);
|
|
5552
|
+
// src/components/tiptap-node/table-node/ui/table-trigger-button/table-grid-selector.tsx
|
|
5553
|
+
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
5554
|
+
var isCellSelected = (cell, hoveredCell) => {
|
|
5555
|
+
if (!hoveredCell) return false;
|
|
5556
|
+
return cell.row <= hoveredCell.row && cell.col <= hoveredCell.col;
|
|
5873
5557
|
};
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5558
|
+
var generateGridCells = (rows, cols) => {
|
|
5559
|
+
const totalCells = rows * cols;
|
|
5560
|
+
return Array.from({ length: totalCells }, (_, index) => ({
|
|
5561
|
+
row: Math.floor(index / cols),
|
|
5562
|
+
col: index % cols
|
|
5563
|
+
}));
|
|
5880
5564
|
};
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5565
|
+
var GridCell = ({
|
|
5566
|
+
row,
|
|
5567
|
+
col,
|
|
5568
|
+
isSelected,
|
|
5569
|
+
disabled,
|
|
5570
|
+
onMouseEnter,
|
|
5571
|
+
onClick
|
|
5572
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
5573
|
+
Button,
|
|
5574
|
+
{
|
|
5575
|
+
"data-size": "small",
|
|
5576
|
+
type: "button",
|
|
5577
|
+
className: cn("tiptap-table-grid-cell", isSelected && "selected"),
|
|
5578
|
+
disabled,
|
|
5579
|
+
onMouseEnter,
|
|
5580
|
+
onClick,
|
|
5581
|
+
"aria-label": `Select ${row + 1}x${col + 1} table`
|
|
5582
|
+
}
|
|
5583
|
+
);
|
|
5584
|
+
var SizeIndicator = ({
|
|
5585
|
+
hoveredCell
|
|
5586
|
+
}) => {
|
|
5587
|
+
const columns = hoveredCell ? hoveredCell.col + 1 : 1;
|
|
5588
|
+
const rows = hoveredCell ? hoveredCell.row + 1 : 1;
|
|
5589
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "tiptap-table-size-indicator", children: [
|
|
5590
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "tiptap-table-size-indicator-item", children: [
|
|
5591
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(TableColumnIcon, { className: "tiptap-table-column-icon" }),
|
|
5592
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "tiptap-table-size-indicator-text", children: columns })
|
|
5593
|
+
] }),
|
|
5594
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "tiptap-table-size-indicator-delimiter", children: "x" }),
|
|
5595
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "tiptap-table-size-indicator-item", children: [
|
|
5596
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(TableRowIcon, { className: "tiptap-table-row-icon" }),
|
|
5597
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "tiptap-table-size-indicator-text", children: rows })
|
|
5598
|
+
] })
|
|
5599
|
+
] });
|
|
5600
|
+
};
|
|
5601
|
+
var TableGridSelector = (0, import_react81.forwardRef)(
|
|
5602
|
+
({
|
|
5603
|
+
maxRows = 8,
|
|
5604
|
+
maxCols = 8,
|
|
5605
|
+
hoveredCell,
|
|
5606
|
+
onCellHover,
|
|
5607
|
+
onCellClick,
|
|
5608
|
+
onMouseLeave,
|
|
5609
|
+
disabled = false,
|
|
5610
|
+
className,
|
|
5611
|
+
showSizeIndicator = true
|
|
5612
|
+
}, ref) => {
|
|
5613
|
+
const gridCells = (0, import_react81.useMemo)(
|
|
5614
|
+
() => generateGridCells(maxRows, maxCols),
|
|
5615
|
+
[maxRows, maxCols]
|
|
5616
|
+
);
|
|
5617
|
+
const gridStyle = (0, import_react81.useMemo)(
|
|
5618
|
+
() => ({
|
|
5619
|
+
"--tt-table-columns": maxCols,
|
|
5620
|
+
"--tt-table-rows": maxRows
|
|
5621
|
+
}),
|
|
5622
|
+
[maxCols, maxRows]
|
|
5623
|
+
);
|
|
5624
|
+
const handleCellHover = (0, import_react81.useCallback)(
|
|
5625
|
+
(row, col) => () => onCellHover(row, col),
|
|
5626
|
+
[onCellHover]
|
|
5627
|
+
);
|
|
5628
|
+
const handleCellClick = (0, import_react81.useCallback)(
|
|
5629
|
+
(row, col) => () => onCellClick(row, col),
|
|
5630
|
+
[onCellClick]
|
|
5631
|
+
);
|
|
5632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_jsx_runtime61.Fragment, { children: [
|
|
5633
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
5634
|
+
"div",
|
|
5635
|
+
{
|
|
5636
|
+
ref,
|
|
5637
|
+
className: cn("tiptap-table-grid", className),
|
|
5638
|
+
onMouseLeave,
|
|
5639
|
+
style: gridStyle,
|
|
5640
|
+
children: gridCells.map((cell, index) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
5641
|
+
GridCell,
|
|
5642
|
+
{
|
|
5643
|
+
row: cell.row,
|
|
5644
|
+
col: cell.col,
|
|
5645
|
+
isSelected: isCellSelected(cell, hoveredCell),
|
|
5646
|
+
disabled,
|
|
5647
|
+
onMouseEnter: handleCellHover(cell.row, cell.col),
|
|
5648
|
+
onClick: handleCellClick(cell.row, cell.col)
|
|
5649
|
+
},
|
|
5650
|
+
index
|
|
5651
|
+
))
|
|
5652
|
+
}
|
|
5653
|
+
),
|
|
5654
|
+
showSizeIndicator && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(SizeIndicator, { hoveredCell })
|
|
5655
|
+
] });
|
|
5656
|
+
}
|
|
5657
|
+
);
|
|
5658
|
+
TableGridSelector.displayName = "TableGridSelector";
|
|
5892
5659
|
|
|
5893
|
-
// src/
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5660
|
+
// src/components/tiptap-node/table-node/ui/table-trigger-button/table-trigger-button.tsx
|
|
5661
|
+
var import_react82 = require("react");
|
|
5662
|
+
var import_jsx_runtime62 = require("react/jsx-runtime");
|
|
5663
|
+
var TableTriggerButton = (0, import_react82.forwardRef)(
|
|
5664
|
+
({
|
|
5665
|
+
editor: providedEditor,
|
|
5666
|
+
hideWhenUnavailable = false,
|
|
5667
|
+
maxRows = 8,
|
|
5668
|
+
maxCols = 8,
|
|
5669
|
+
onInserted,
|
|
5670
|
+
text,
|
|
5671
|
+
children,
|
|
5672
|
+
...buttonProps
|
|
5673
|
+
}, ref) => {
|
|
5674
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
5675
|
+
const {
|
|
5676
|
+
isVisible,
|
|
5677
|
+
canInsert,
|
|
5678
|
+
isOpen,
|
|
5679
|
+
setIsOpen,
|
|
5680
|
+
hoveredCell,
|
|
5681
|
+
handleCellHover,
|
|
5682
|
+
handleCellClick,
|
|
5683
|
+
resetHoveredCell,
|
|
5684
|
+
label,
|
|
5685
|
+
Icon
|
|
5686
|
+
} = useTableTriggerButton({
|
|
5687
|
+
editor,
|
|
5688
|
+
hideWhenUnavailable,
|
|
5689
|
+
maxRows,
|
|
5690
|
+
maxCols,
|
|
5691
|
+
onInserted
|
|
5692
|
+
});
|
|
5693
|
+
if (!isVisible) {
|
|
5694
|
+
return null;
|
|
5695
|
+
}
|
|
5696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
5697
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
5698
|
+
Button,
|
|
5699
|
+
{
|
|
5700
|
+
ref,
|
|
5701
|
+
type: "button",
|
|
5702
|
+
"data-style": "ghost",
|
|
5703
|
+
disabled: !canInsert,
|
|
5704
|
+
"data-disabled": !canInsert,
|
|
5705
|
+
"aria-label": label,
|
|
5706
|
+
tooltip: label,
|
|
5707
|
+
...buttonProps,
|
|
5708
|
+
children: children ?? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(import_jsx_runtime62.Fragment, { children: [
|
|
5709
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Icon, { className: "tiptap-button-icon" }),
|
|
5710
|
+
text && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "tiptap-button-text", children: text })
|
|
5711
|
+
] })
|
|
5712
|
+
}
|
|
5713
|
+
) }),
|
|
5714
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(PopoverContent, { align: "start", side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CardBody, { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
5715
|
+
TableGridSelector,
|
|
5716
|
+
{
|
|
5717
|
+
maxRows,
|
|
5718
|
+
maxCols,
|
|
5719
|
+
hoveredCell,
|
|
5720
|
+
onCellHover: handleCellHover,
|
|
5721
|
+
onCellClick: handleCellClick,
|
|
5722
|
+
onMouseLeave: resetHoveredCell,
|
|
5723
|
+
disabled: !canInsert
|
|
5724
|
+
}
|
|
5725
|
+
) }) }) })
|
|
5726
|
+
] });
|
|
5727
|
+
}
|
|
5728
|
+
);
|
|
5729
|
+
TableTriggerButton.displayName = "TableTriggerButton";
|
|
5730
|
+
|
|
5731
|
+
// src/components/tiptap-node/table-node/ui/table-trigger-button/use-table-trigger.ts
|
|
5732
|
+
var import_react84 = require("react");
|
|
5733
|
+
|
|
5734
|
+
// src/components/tiptap-icons/table-icon.tsx
|
|
5735
|
+
var import_react83 = require("react");
|
|
5736
|
+
var import_jsx_runtime63 = require("react/jsx-runtime");
|
|
5737
|
+
var TableIcon = (0, import_react83.memo)(({ className, ...props }) => {
|
|
5738
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5739
|
+
"svg",
|
|
5740
|
+
{
|
|
5741
|
+
width: "24",
|
|
5742
|
+
height: "24",
|
|
5743
|
+
className,
|
|
5744
|
+
viewBox: "0 0 24 24",
|
|
5745
|
+
fill: "currentColor",
|
|
5746
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5747
|
+
...props,
|
|
5748
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
5749
|
+
"path",
|
|
5750
|
+
{
|
|
5751
|
+
fillRule: "evenodd",
|
|
5752
|
+
clipRule: "evenodd",
|
|
5753
|
+
d: "M2 5C2 3.34315 3.34315 2 5 2H19C20.6569 2 22 3.34315 22 5V19C22 20.6569 20.6569 22 19 22H5C3.34315 22 2 20.6569 2 19V5ZM4 5C4 4.44772 4.44772 4 5 4H11V8H4V5ZM4 10H11V14H4V10ZM20 14V10H13V14H20ZM13 16H20V19C20 19.5523 19.5523 20 19 20H13V16ZM11 16V20H5C4.44772 20 4 19.5523 4 19V16H11ZM13 8H20V5C20 4.44772 19.5523 4 19 4H13V8Z",
|
|
5754
|
+
fill: "currentColor"
|
|
5755
|
+
}
|
|
5756
|
+
)
|
|
5757
|
+
}
|
|
5758
|
+
);
|
|
5759
|
+
});
|
|
5760
|
+
TableIcon.displayName = "TableIcon";
|
|
5761
|
+
|
|
5762
|
+
// src/components/tiptap-node/table-node/ui/table-trigger-button/use-table-trigger.ts
|
|
5763
|
+
var REQUIRED_EXTENSIONS = ["table"];
|
|
5764
|
+
function canInsertTable(editor) {
|
|
5765
|
+
if (!editor || !editor.isEditable) return false;
|
|
5766
|
+
return isExtensionAvailable(editor, REQUIRED_EXTENSIONS);
|
|
5767
|
+
}
|
|
5768
|
+
function insertTable(editor, rows, cols) {
|
|
5769
|
+
if (!editor || !canInsertTable(editor)) return false;
|
|
5770
|
+
try {
|
|
5771
|
+
return editor.chain().focus().insertTable({
|
|
5772
|
+
rows,
|
|
5773
|
+
cols,
|
|
5774
|
+
withHeaderRow: false
|
|
5775
|
+
}).run();
|
|
5776
|
+
} catch (error) {
|
|
5777
|
+
console.error("Error inserting table:", error);
|
|
5778
|
+
return false;
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
function shouldShowButton10(editor, hideWhenUnavailable) {
|
|
5782
|
+
if (!editor || !editor.isEditable) return false;
|
|
5783
|
+
const hasExtension = isExtensionAvailable(editor, REQUIRED_EXTENSIONS);
|
|
5784
|
+
if (!hasExtension) return false;
|
|
5785
|
+
return !hideWhenUnavailable || canInsertTable(editor);
|
|
5786
|
+
}
|
|
5787
|
+
function useTableTriggerButton(config) {
|
|
5788
|
+
const {
|
|
5789
|
+
editor: providedEditor,
|
|
5790
|
+
hideWhenUnavailable = false,
|
|
5791
|
+
onInserted
|
|
5792
|
+
} = config || {};
|
|
5793
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
5794
|
+
const [isOpen, setIsOpen] = (0, import_react84.useState)(false);
|
|
5795
|
+
const [hoveredCell, setHoveredCell] = (0, import_react84.useState)(null);
|
|
5796
|
+
const isVisible = shouldShowButton10(editor, hideWhenUnavailable);
|
|
5797
|
+
const canInsert = canInsertTable(editor);
|
|
5798
|
+
const handleCellHover = (0, import_react84.useCallback)((row, col) => {
|
|
5799
|
+
setHoveredCell({ row, col });
|
|
5800
|
+
}, []);
|
|
5801
|
+
const handleCellClick = (0, import_react84.useCallback)(
|
|
5802
|
+
(row, col) => {
|
|
5803
|
+
const success = insertTable(editor, row + 1, col + 1);
|
|
5804
|
+
if (success) {
|
|
5805
|
+
setIsOpen(false);
|
|
5806
|
+
onInserted?.(row + 1, col + 1);
|
|
5807
|
+
}
|
|
5808
|
+
},
|
|
5809
|
+
[editor, onInserted]
|
|
5810
|
+
);
|
|
5811
|
+
const resetHoveredCell = (0, import_react84.useCallback)(() => {
|
|
5812
|
+
setHoveredCell(null);
|
|
5813
|
+
}, []);
|
|
5814
|
+
return {
|
|
5815
|
+
isVisible,
|
|
5816
|
+
canInsert,
|
|
5817
|
+
isOpen,
|
|
5818
|
+
setIsOpen,
|
|
5819
|
+
hoveredCell,
|
|
5820
|
+
handleCellHover,
|
|
5821
|
+
handleCellClick,
|
|
5822
|
+
resetHoveredCell,
|
|
5823
|
+
label: "Insert table",
|
|
5824
|
+
Icon: TableIcon
|
|
5825
|
+
};
|
|
5826
|
+
}
|
|
5827
|
+
|
|
5828
|
+
// src/components/tiptap-ui/color-text-popover/color-text-popover.tsx
|
|
5829
|
+
var import_react88 = require("react");
|
|
5830
|
+
|
|
5831
|
+
// src/components/tiptap-ui/color-text-button/color-text-button.tsx
|
|
5832
|
+
var import_react85 = require("react");
|
|
5833
|
+
var import_jsx_runtime64 = require("react/jsx-runtime");
|
|
5834
|
+
function ColorTextShortcutBadge({
|
|
5835
|
+
shortcutKeys = COLOR_TEXT_SHORTCUT_KEY
|
|
5836
|
+
}) {
|
|
5837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(Badge, { children: parseShortcutKeys({ shortcutKeys }) });
|
|
5838
|
+
}
|
|
5839
|
+
var ColorTextButton = (0, import_react85.forwardRef)(
|
|
5840
|
+
({
|
|
5841
|
+
editor: providedEditor,
|
|
5842
|
+
textColor,
|
|
5843
|
+
text,
|
|
5844
|
+
hideWhenUnavailable = false,
|
|
5845
|
+
onApplied,
|
|
5846
|
+
showShortcut = false,
|
|
5847
|
+
onClick,
|
|
5848
|
+
children,
|
|
5849
|
+
style,
|
|
5850
|
+
...buttonProps
|
|
5851
|
+
}, ref) => {
|
|
5852
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
5853
|
+
const {
|
|
5854
|
+
isVisible,
|
|
5855
|
+
canColorText: canColorText2,
|
|
5856
|
+
isActive,
|
|
5857
|
+
handleColorText,
|
|
5858
|
+
label,
|
|
5859
|
+
shortcutKeys,
|
|
5860
|
+
Icon
|
|
5861
|
+
} = useColorText({
|
|
5862
|
+
editor,
|
|
5863
|
+
textColor,
|
|
5864
|
+
label: text || `Color text to ${textColor}`,
|
|
5865
|
+
hideWhenUnavailable,
|
|
5866
|
+
onApplied
|
|
5867
|
+
});
|
|
5868
|
+
const handleClick = (0, import_react85.useCallback)(
|
|
5869
|
+
(event) => {
|
|
5870
|
+
onClick?.(event);
|
|
5871
|
+
if (event.defaultPrevented) return;
|
|
5872
|
+
handleColorText();
|
|
5873
|
+
},
|
|
5874
|
+
[handleColorText, onClick]
|
|
5875
|
+
);
|
|
5876
|
+
const buttonStyle = (0, import_react85.useMemo)(
|
|
5877
|
+
() => ({
|
|
5878
|
+
...style,
|
|
5879
|
+
"--color-text-button-color": textColor
|
|
5880
|
+
}),
|
|
5881
|
+
[textColor, style]
|
|
5882
|
+
);
|
|
5883
|
+
if (!isVisible) {
|
|
5884
|
+
return null;
|
|
5885
|
+
}
|
|
5886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
5887
|
+
Button,
|
|
5888
|
+
{
|
|
5889
|
+
type: "button",
|
|
5890
|
+
"data-style": "ghost",
|
|
5891
|
+
"data-active-state": isActive ? "on" : "off",
|
|
5892
|
+
role: "button",
|
|
5893
|
+
tabIndex: -1,
|
|
5894
|
+
disabled: !canColorText2,
|
|
5895
|
+
"data-disabled": !canColorText2,
|
|
5896
|
+
"aria-label": label,
|
|
5897
|
+
"aria-pressed": isActive,
|
|
5898
|
+
tooltip: label,
|
|
5899
|
+
onClick: handleClick,
|
|
5900
|
+
style: buttonStyle,
|
|
5901
|
+
...buttonProps,
|
|
5902
|
+
ref,
|
|
5903
|
+
children: children ?? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(import_jsx_runtime64.Fragment, { children: [
|
|
5904
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
5905
|
+
"span",
|
|
5906
|
+
{
|
|
5907
|
+
className: "tiptap-button-color-text",
|
|
5908
|
+
style: { color: textColor },
|
|
5909
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
5910
|
+
Icon,
|
|
5911
|
+
{
|
|
5912
|
+
className: "tiptap-button-icon",
|
|
5913
|
+
style: { color: textColor, flexGrow: 1 }
|
|
5914
|
+
}
|
|
5915
|
+
)
|
|
5916
|
+
}
|
|
5917
|
+
),
|
|
5918
|
+
text && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "tiptap-button-text", children: text }),
|
|
5919
|
+
showShortcut && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(ColorTextShortcutBadge, { shortcutKeys })
|
|
5920
|
+
] })
|
|
5921
|
+
}
|
|
5922
|
+
);
|
|
5923
|
+
}
|
|
5924
|
+
);
|
|
5925
|
+
ColorTextButton.displayName = "ColorTextButton";
|
|
5926
|
+
|
|
5927
|
+
// src/components/tiptap-ui/color-text-button/use-color-text.ts
|
|
5928
|
+
var import_react87 = require("react");
|
|
5929
|
+
var import_react_hotkeys_hook3 = require("react-hotkeys-hook");
|
|
5930
|
+
|
|
5931
|
+
// src/components/tiptap-icons/text-color-small-icon.tsx
|
|
5932
|
+
var import_react86 = require("react");
|
|
5933
|
+
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
5934
|
+
var TextColorSmallIcon = (0, import_react86.memo)(({ className, ...props }) => {
|
|
5935
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
5936
|
+
"svg",
|
|
5937
|
+
{
|
|
5938
|
+
width: "24",
|
|
5939
|
+
height: "24",
|
|
5940
|
+
className,
|
|
5941
|
+
viewBox: "0 0 24 24",
|
|
5942
|
+
fill: "currentColor",
|
|
5943
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5944
|
+
...props,
|
|
5945
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
5946
|
+
"path",
|
|
5947
|
+
{
|
|
5948
|
+
fillRule: "evenodd",
|
|
5949
|
+
clipRule: "evenodd",
|
|
5950
|
+
d: "M12.8944 5.55279C12.725 5.214 12.3787 5 12 5C11.6212 5 11.2749 5.214 11.1055 5.55279L5.10555 17.5528C4.85856 18.0468 5.05878 18.6474 5.55276 18.8944C6.04674 19.1414 6.64741 18.9412 6.8944 18.4472L8.64957 14.9369C8.75862 14.9777 8.87671 15 9 15H15C15.1233 15 15.2413 14.9777 15.3504 14.9369L17.1055 18.4472C17.3525 18.9412 17.9532 19.1414 18.4472 18.8944C18.9412 18.6474 19.1414 18.0468 18.8944 17.5528L12.8944 5.55279ZM14.3819 13L12 8.23607L9.61801 13H14.3819Z",
|
|
5951
|
+
fill: "currentColor"
|
|
5952
|
+
}
|
|
5953
|
+
)
|
|
5954
|
+
}
|
|
5955
|
+
);
|
|
5956
|
+
});
|
|
5957
|
+
TextColorSmallIcon.displayName = "TextColorSmallIcon";
|
|
5958
|
+
|
|
5959
|
+
// src/components/tiptap-ui/color-text-button/use-color-text.ts
|
|
5960
|
+
var COLOR_TEXT_SHORTCUT_KEY = "mod+shift+t";
|
|
5961
|
+
var TEXT_COLORS = [
|
|
5962
|
+
{
|
|
5963
|
+
label: "Default text",
|
|
5964
|
+
value: "var(--tt-color-text)",
|
|
5965
|
+
border: "var(--tt-color-text-contrast)"
|
|
5966
|
+
},
|
|
5967
|
+
{
|
|
5968
|
+
label: "Gray text",
|
|
5969
|
+
value: "var(--tt-color-text-gray)",
|
|
5970
|
+
border: "var(--tt-color-text-gray-contrast)"
|
|
5971
|
+
},
|
|
5972
|
+
{
|
|
5973
|
+
label: "Brown text",
|
|
5974
|
+
value: "var(--tt-color-text-brown)",
|
|
5975
|
+
border: "var(--tt-color-text-brown-contrast)"
|
|
5976
|
+
},
|
|
5977
|
+
{
|
|
5978
|
+
label: "Orange text",
|
|
5979
|
+
value: "var(--tt-color-text-orange)",
|
|
5980
|
+
border: "var(--tt-color-text-orange-contrast)"
|
|
5981
|
+
},
|
|
5982
|
+
{
|
|
5983
|
+
label: "Yellow text",
|
|
5984
|
+
value: "var(--tt-color-text-yellow)",
|
|
5985
|
+
border: "var(--tt-color-text-yellow-contrast)"
|
|
5986
|
+
},
|
|
5987
|
+
{
|
|
5988
|
+
label: "Green text",
|
|
5989
|
+
value: "var(--tt-color-text-green)",
|
|
5990
|
+
border: "var(--tt-color-text-green-contrast)"
|
|
5991
|
+
},
|
|
5992
|
+
{
|
|
5993
|
+
label: "Blue text",
|
|
5994
|
+
value: "var(--tt-color-text-blue)",
|
|
5995
|
+
border: "var(--tt-color-text-blue-contrast)"
|
|
5996
|
+
},
|
|
5997
|
+
{
|
|
5998
|
+
label: "Purple text",
|
|
5999
|
+
value: "var(--tt-color-text-purple)",
|
|
6000
|
+
border: "var(--tt-color-text-purple-contrast)"
|
|
6001
|
+
},
|
|
6002
|
+
{
|
|
6003
|
+
label: "Pink text",
|
|
6004
|
+
value: "var(--tt-color-text-pink)",
|
|
6005
|
+
border: "var(--tt-color-text-pink-contrast)"
|
|
6006
|
+
},
|
|
6007
|
+
{
|
|
6008
|
+
label: "Red text",
|
|
6009
|
+
value: "var(--tt-color-text-red)",
|
|
6010
|
+
border: "var(--tt-color-text-red-contrast)"
|
|
6011
|
+
}
|
|
6012
|
+
];
|
|
6013
|
+
function canColorText(editor) {
|
|
6014
|
+
if (!editor || !editor.isEditable) return false;
|
|
6015
|
+
if (!isMarkInSchema("textStyle", editor) || isNodeTypeSelected(editor, ["image"]))
|
|
6016
|
+
return false;
|
|
6017
|
+
try {
|
|
6018
|
+
return editor.can().setMark("textStyle", { color: "currentColor" });
|
|
6019
|
+
} catch {
|
|
6020
|
+
return false;
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
function isColorTextActive(editor, textColor) {
|
|
6024
|
+
if (!editor || !editor.isEditable) return false;
|
|
6025
|
+
return editor.isActive("textStyle", { color: textColor });
|
|
6026
|
+
}
|
|
6027
|
+
function shouldShowButton11(props) {
|
|
6028
|
+
const { editor, hideWhenUnavailable } = props;
|
|
6029
|
+
if (!editor || !editor.isEditable) return false;
|
|
6030
|
+
if (!isMarkInSchema("textStyle", editor)) return false;
|
|
6031
|
+
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
6032
|
+
return canColorText(editor);
|
|
6033
|
+
}
|
|
6034
|
+
return true;
|
|
6035
|
+
}
|
|
6036
|
+
function useColorText(config) {
|
|
6037
|
+
const {
|
|
6038
|
+
editor: providedEditor,
|
|
6039
|
+
label,
|
|
6040
|
+
textColor,
|
|
6041
|
+
hideWhenUnavailable = false,
|
|
6042
|
+
onApplied
|
|
6043
|
+
} = config;
|
|
6044
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
6045
|
+
const isMobile = useIsBreakpoint();
|
|
6046
|
+
const [isVisible, setIsVisible] = (0, import_react87.useState)(true);
|
|
6047
|
+
const canColorTextState = canColorText(editor);
|
|
6048
|
+
const isActive = isColorTextActive(editor, textColor);
|
|
6049
|
+
(0, import_react87.useEffect)(() => {
|
|
6050
|
+
if (!editor) return;
|
|
6051
|
+
const handleSelectionUpdate = () => {
|
|
6052
|
+
setIsVisible(shouldShowButton11({ editor, hideWhenUnavailable }));
|
|
6053
|
+
};
|
|
6054
|
+
handleSelectionUpdate();
|
|
6055
|
+
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
6056
|
+
return () => {
|
|
6057
|
+
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
6058
|
+
};
|
|
6059
|
+
}, [editor, hideWhenUnavailable]);
|
|
6060
|
+
const handleColorText = (0, import_react87.useCallback)(() => {
|
|
6061
|
+
if (!editor || !canColorTextState) return false;
|
|
6062
|
+
if (editor.state.storedMarks) {
|
|
6063
|
+
const textStyleMarkType = editor.schema.marks.textStyle;
|
|
6064
|
+
if (textStyleMarkType) {
|
|
6065
|
+
editor.view.dispatch(
|
|
6066
|
+
editor.state.tr.removeStoredMark(textStyleMarkType)
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
setTimeout(() => {
|
|
6071
|
+
selectCurrentBlockContent(editor);
|
|
6072
|
+
const success = editor.chain().focus().toggleMark("textStyle", { color: textColor }).run();
|
|
6073
|
+
if (success) {
|
|
6074
|
+
onApplied?.({ color: textColor, label });
|
|
6075
|
+
}
|
|
6076
|
+
return success;
|
|
6077
|
+
}, 0);
|
|
6078
|
+
}, [editor, canColorTextState, textColor, onApplied, label]);
|
|
6079
|
+
(0, import_react_hotkeys_hook3.useHotkeys)(
|
|
6080
|
+
COLOR_TEXT_SHORTCUT_KEY,
|
|
6081
|
+
(event) => {
|
|
6082
|
+
event.preventDefault();
|
|
6083
|
+
handleColorText();
|
|
6084
|
+
},
|
|
6085
|
+
{
|
|
6086
|
+
enabled: isVisible && canColorTextState,
|
|
6087
|
+
enableOnContentEditable: !isMobile,
|
|
6088
|
+
enableOnFormTags: true
|
|
6089
|
+
}
|
|
6090
|
+
);
|
|
6091
|
+
return {
|
|
6092
|
+
isVisible,
|
|
6093
|
+
isActive,
|
|
6094
|
+
handleColorText,
|
|
6095
|
+
canColorText: canColorTextState,
|
|
6096
|
+
label: label || `Color text to ${textColor}`,
|
|
6097
|
+
shortcutKeys: COLOR_TEXT_SHORTCUT_KEY,
|
|
6098
|
+
Icon: TextColorSmallIcon
|
|
6099
|
+
};
|
|
6100
|
+
}
|
|
6101
|
+
|
|
6102
|
+
// src/lib/tiptap-advanced-utils.ts
|
|
6103
|
+
var import_state6 = require("@tiptap/pm/state");
|
|
6104
|
+
function chunkArray(array, size) {
|
|
6105
|
+
return Array.from(
|
|
6106
|
+
{ length: Math.ceil(array.length / size) },
|
|
6107
|
+
(_, index) => array.slice(index * size, index * size + size)
|
|
6108
|
+
);
|
|
6109
|
+
}
|
|
6110
|
+
function getActiveMarkAttrs(editor, markName) {
|
|
6111
|
+
if (!editor) return null;
|
|
6112
|
+
const { state } = editor;
|
|
6113
|
+
const { from, to, empty, $from } = state.selection;
|
|
6114
|
+
if (empty) {
|
|
6115
|
+
const mark = $from.marks().find((m) => m.type.name === markName);
|
|
6116
|
+
return mark?.attrs ?? null;
|
|
6117
|
+
}
|
|
6118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6119
|
+
let foundAttrs = null;
|
|
6120
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
6121
|
+
if (!node.isText) return;
|
|
6122
|
+
for (const mark of node.marks) {
|
|
6123
|
+
if (mark.type.name === markName && !seen.has(mark.type.name)) {
|
|
6124
|
+
seen.add(mark.type.name);
|
|
6125
|
+
foundAttrs = mark.attrs;
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
});
|
|
6129
|
+
return foundAttrs;
|
|
6130
|
+
}
|
|
6131
|
+
|
|
6132
|
+
// src/components/tiptap-ui/color-text-popover/color-text-popover.tsx
|
|
6133
|
+
var import_jsx_runtime66 = require("react/jsx-runtime");
|
|
6134
|
+
var RecentColorButton = ({
|
|
6135
|
+
colorObj,
|
|
6136
|
+
withLabel = false,
|
|
6137
|
+
onColorChanged,
|
|
6138
|
+
...props
|
|
6139
|
+
}) => {
|
|
6140
|
+
const colorSet = colorObj.type === "text" ? TEXT_COLORS : HIGHLIGHT_COLORS;
|
|
6141
|
+
const color = getColorByValue(colorObj.value, colorSet);
|
|
6142
|
+
const commonProps = {
|
|
6143
|
+
tooltip: color.label,
|
|
6144
|
+
text: withLabel ? color.label : void 0,
|
|
6145
|
+
onApplied: () => onColorChanged?.({
|
|
6146
|
+
type: colorObj.type,
|
|
6147
|
+
label: color.label,
|
|
6148
|
+
value: color.value
|
|
6149
|
+
}),
|
|
6150
|
+
...props
|
|
6151
|
+
};
|
|
6152
|
+
return colorObj.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6153
|
+
ColorTextButton,
|
|
6154
|
+
{
|
|
6155
|
+
textColor: color.value,
|
|
6156
|
+
label: color.label,
|
|
6157
|
+
...commonProps
|
|
6158
|
+
}
|
|
6159
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(ColorHighlightButton, { highlightColor: color.value, ...commonProps });
|
|
6160
|
+
};
|
|
6161
|
+
var ColorGroup = ({
|
|
6162
|
+
type,
|
|
6163
|
+
colors,
|
|
6164
|
+
onColorSelected,
|
|
6165
|
+
selectedIndex,
|
|
6166
|
+
startIndexOffset
|
|
6167
|
+
}) => {
|
|
6168
|
+
return colors.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(ButtonGroup, { orientation: "horizontal", children: group.map((color, colorIndex) => {
|
|
6169
|
+
const itemIndex = startIndexOffset + colors.slice(0, groupIndex).reduce((acc, g) => acc + g.length, 0) + colorIndex;
|
|
6170
|
+
const isHighlighted = selectedIndex === itemIndex;
|
|
6171
|
+
const commonProps = {
|
|
6172
|
+
tooltip: color.label,
|
|
6173
|
+
onApplied: () => onColorSelected({ type, label: color.label, value: color.value }),
|
|
6174
|
+
tabIndex: isHighlighted ? 0 : -1,
|
|
6175
|
+
"data-highlighted": isHighlighted,
|
|
6176
|
+
"aria-label": `${color.label} ${type === "text" ? "text" : "highlight"} color`
|
|
6177
|
+
};
|
|
6178
|
+
return type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6179
|
+
ColorTextButton,
|
|
6180
|
+
{
|
|
6181
|
+
textColor: color.value,
|
|
6182
|
+
label: color.label,
|
|
6183
|
+
...commonProps
|
|
6184
|
+
},
|
|
6185
|
+
`${type}-${color.value}-${colorIndex}`
|
|
6186
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6187
|
+
ColorHighlightButton,
|
|
6188
|
+
{
|
|
6189
|
+
highlightColor: color.value,
|
|
6190
|
+
...commonProps
|
|
6191
|
+
},
|
|
6192
|
+
`${type}-${color.value}-${colorIndex}`
|
|
6193
|
+
);
|
|
6194
|
+
}) }, `${type}-group-${groupIndex}`));
|
|
6195
|
+
};
|
|
6196
|
+
var RecentColorsSection = ({
|
|
6197
|
+
recentColors,
|
|
6198
|
+
onColorSelected,
|
|
6199
|
+
selectedIndex
|
|
6200
|
+
}) => {
|
|
6201
|
+
if (recentColors.length === 0) return null;
|
|
6202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(CardItemGroup, { children: [
|
|
6203
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(CardGroupLabel, { children: "Recently used" }),
|
|
6204
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(ButtonGroup, { orientation: "horizontal", children: recentColors.map((colorObj, index) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6205
|
+
RecentColorButton,
|
|
6206
|
+
{
|
|
6207
|
+
colorObj,
|
|
6208
|
+
onColorChanged: onColorSelected,
|
|
6209
|
+
tabIndex: selectedIndex === index ? 0 : -1,
|
|
6210
|
+
"data-highlighted": selectedIndex === index
|
|
6211
|
+
},
|
|
6212
|
+
`recent-${colorObj.type}-${colorObj.value}`
|
|
6213
|
+
)) })
|
|
6214
|
+
] });
|
|
6215
|
+
};
|
|
6216
|
+
var TextStyleColorPanel = ({
|
|
6217
|
+
maxColorsPerGroup = 5,
|
|
6218
|
+
maxRecentColors = 3,
|
|
6219
|
+
onColorChanged
|
|
6220
|
+
}) => {
|
|
6221
|
+
const { recentColors, addRecentColor, isInitialized } = useRecentColors(maxRecentColors);
|
|
6222
|
+
const containerRef = (0, import_react88.useRef)(null);
|
|
6223
|
+
const textColorGroups = (0, import_react88.useMemo)(
|
|
6224
|
+
() => chunkArray(TEXT_COLORS, maxColorsPerGroup),
|
|
6225
|
+
[maxColorsPerGroup]
|
|
6226
|
+
);
|
|
6227
|
+
const highlightColorGroups = (0, import_react88.useMemo)(
|
|
6228
|
+
() => chunkArray(HIGHLIGHT_COLORS, maxColorsPerGroup),
|
|
6229
|
+
[maxColorsPerGroup]
|
|
6230
|
+
);
|
|
6231
|
+
const allTextColors = (0, import_react88.useMemo)(() => textColorGroups.flat(), [textColorGroups]);
|
|
6232
|
+
const allHighlightColors = (0, import_react88.useMemo)(
|
|
6233
|
+
() => highlightColorGroups.flat(),
|
|
6234
|
+
[highlightColorGroups]
|
|
6235
|
+
);
|
|
6236
|
+
const textColorStartIndex = (0, import_react88.useMemo)(
|
|
6237
|
+
() => isInitialized ? recentColors.length : 0,
|
|
6238
|
+
[isInitialized, recentColors.length]
|
|
6239
|
+
);
|
|
6240
|
+
const highlightColorStartIndex = (0, import_react88.useMemo)(
|
|
6241
|
+
() => textColorStartIndex + allTextColors.length,
|
|
6242
|
+
[textColorStartIndex, allTextColors.length]
|
|
6243
|
+
);
|
|
6244
|
+
const menuItems = (0, import_react88.useMemo)(() => {
|
|
6245
|
+
const items = [];
|
|
6246
|
+
if (isInitialized && recentColors.length > 0) {
|
|
6247
|
+
items.push(
|
|
6248
|
+
...recentColors.map((color) => ({
|
|
6249
|
+
type: color.type,
|
|
6250
|
+
value: color.value,
|
|
6251
|
+
label: `Recent ${color.type === "text" ? "text" : "highlight"} color`,
|
|
6252
|
+
group: "recent"
|
|
6253
|
+
}))
|
|
6254
|
+
);
|
|
6255
|
+
}
|
|
6256
|
+
items.push(
|
|
6257
|
+
...allTextColors.map((color) => ({
|
|
6258
|
+
type: "text",
|
|
6259
|
+
value: color.value,
|
|
6260
|
+
label: color.label,
|
|
6261
|
+
group: "text"
|
|
6262
|
+
}))
|
|
6263
|
+
);
|
|
6264
|
+
items.push(
|
|
6265
|
+
...allHighlightColors.map((color) => ({
|
|
6266
|
+
type: "highlight",
|
|
6267
|
+
value: color.value,
|
|
6268
|
+
label: color.label,
|
|
6269
|
+
group: "highlight"
|
|
6270
|
+
}))
|
|
6271
|
+
);
|
|
6272
|
+
return items;
|
|
6273
|
+
}, [isInitialized, recentColors, allTextColors, allHighlightColors]);
|
|
6274
|
+
const handleColorSelected = (0, import_react88.useCallback)(
|
|
6275
|
+
({
|
|
6276
|
+
type,
|
|
6277
|
+
label,
|
|
6278
|
+
value
|
|
6279
|
+
}) => {
|
|
6280
|
+
if (!containerRef.current) return false;
|
|
6281
|
+
const highlightedElement = containerRef.current.querySelector(
|
|
6282
|
+
'[data-highlighted="true"]'
|
|
6283
|
+
);
|
|
6284
|
+
if (highlightedElement) {
|
|
6285
|
+
highlightedElement.click();
|
|
6286
|
+
}
|
|
6287
|
+
addRecentColor({ type, label, value });
|
|
6288
|
+
onColorChanged?.({ type, label, value });
|
|
6289
|
+
},
|
|
6290
|
+
[addRecentColor, onColorChanged]
|
|
6291
|
+
);
|
|
6292
|
+
const { selectedIndex } = useMenuNavigation({
|
|
6293
|
+
containerRef,
|
|
6294
|
+
items: menuItems,
|
|
6295
|
+
onSelect: (item) => {
|
|
6296
|
+
if (item) {
|
|
6297
|
+
handleColorSelected({
|
|
6298
|
+
type: item.type,
|
|
6299
|
+
label: item.label,
|
|
6300
|
+
value: item.value
|
|
6301
|
+
});
|
|
6302
|
+
}
|
|
6303
|
+
},
|
|
6304
|
+
orientation: "both",
|
|
6305
|
+
autoSelectFirstItem: false
|
|
6306
|
+
});
|
|
6307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(Card, { ref: containerRef, tabIndex: 0, role: "menu", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(CardBody, { children: [
|
|
6308
|
+
isInitialized && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6309
|
+
RecentColorsSection,
|
|
6310
|
+
{
|
|
6311
|
+
recentColors,
|
|
6312
|
+
onColorSelected: handleColorSelected,
|
|
6313
|
+
selectedIndex
|
|
6314
|
+
}
|
|
6315
|
+
),
|
|
6316
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(CardItemGroup, { children: [
|
|
6317
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(CardGroupLabel, { children: "Text color" }),
|
|
6318
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6319
|
+
ColorGroup,
|
|
6320
|
+
{
|
|
6321
|
+
type: "text",
|
|
6322
|
+
colors: textColorGroups,
|
|
6323
|
+
onColorSelected: handleColorSelected,
|
|
6324
|
+
selectedIndex,
|
|
6325
|
+
startIndexOffset: textColorStartIndex
|
|
6326
|
+
}
|
|
6327
|
+
)
|
|
6328
|
+
] }),
|
|
6329
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(CardItemGroup, { children: [
|
|
6330
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(CardGroupLabel, { children: "Highlight color" }),
|
|
6331
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6332
|
+
ColorGroup,
|
|
6333
|
+
{
|
|
6334
|
+
type: "highlight",
|
|
6335
|
+
colors: highlightColorGroups,
|
|
6336
|
+
onColorSelected: handleColorSelected,
|
|
6337
|
+
selectedIndex,
|
|
6338
|
+
startIndexOffset: highlightColorStartIndex
|
|
6339
|
+
}
|
|
6340
|
+
)
|
|
6341
|
+
] })
|
|
6342
|
+
] }) });
|
|
6343
|
+
};
|
|
6344
|
+
var ColorTextPopover = (0, import_react88.forwardRef)(
|
|
6345
|
+
({
|
|
6346
|
+
editor: providedEditor,
|
|
6347
|
+
hideWhenUnavailable = false,
|
|
6348
|
+
onColorChanged,
|
|
6349
|
+
onClick,
|
|
6350
|
+
children,
|
|
6351
|
+
...buttonProps
|
|
6352
|
+
}, ref) => {
|
|
6353
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
6354
|
+
const [isOpen, setIsOpen] = (0, import_react88.useState)(false);
|
|
6355
|
+
const {
|
|
6356
|
+
isVisible,
|
|
6357
|
+
canToggle: canToggle3,
|
|
6358
|
+
activeTextStyle,
|
|
6359
|
+
activeHighlight,
|
|
6360
|
+
handleColorChanged,
|
|
6361
|
+
label,
|
|
6362
|
+
Icon
|
|
6363
|
+
} = useColorTextPopover({
|
|
6364
|
+
editor,
|
|
6365
|
+
hideWhenUnavailable,
|
|
6366
|
+
onColorChanged
|
|
6367
|
+
});
|
|
6368
|
+
const handleClick = (0, import_react88.useCallback)(
|
|
6369
|
+
(event) => {
|
|
6370
|
+
onClick?.(event);
|
|
6371
|
+
if (event.defaultPrevented) return;
|
|
6372
|
+
setIsOpen(!isOpen);
|
|
6373
|
+
},
|
|
6374
|
+
[onClick, isOpen, setIsOpen]
|
|
6375
|
+
);
|
|
6376
|
+
if (!isVisible) {
|
|
6377
|
+
return null;
|
|
6378
|
+
}
|
|
6379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
6380
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6381
|
+
Button,
|
|
6382
|
+
{
|
|
6383
|
+
type: "button",
|
|
6384
|
+
"data-style": "ghost",
|
|
6385
|
+
"data-appearance": "default",
|
|
6386
|
+
role: "button",
|
|
6387
|
+
"aria-label": label,
|
|
6388
|
+
tooltip: label,
|
|
6389
|
+
disabled: !canToggle3,
|
|
6390
|
+
"data-disabled": !canToggle3,
|
|
6391
|
+
onClick: handleClick,
|
|
6392
|
+
...buttonProps,
|
|
6393
|
+
ref,
|
|
6394
|
+
children: children ?? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(import_jsx_runtime66.Fragment, { children: [
|
|
6395
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6396
|
+
"span",
|
|
6397
|
+
{
|
|
6398
|
+
className: "tiptap-button-color-text-popover",
|
|
6399
|
+
style: activeHighlight.color ? {
|
|
6400
|
+
"--active-highlight-color": activeHighlight.color
|
|
6401
|
+
} : {},
|
|
6402
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6403
|
+
Icon,
|
|
6404
|
+
{
|
|
6405
|
+
className: "tiptap-button-icon",
|
|
6406
|
+
style: {
|
|
6407
|
+
color: activeTextStyle.color || void 0
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
)
|
|
6411
|
+
}
|
|
6412
|
+
),
|
|
6413
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(ChevronDownIcon, { className: "tiptap-button-dropdown-small" })
|
|
6414
|
+
] })
|
|
6415
|
+
}
|
|
6416
|
+
) }),
|
|
6417
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
6418
|
+
PopoverContent,
|
|
6419
|
+
{
|
|
6420
|
+
"aria-label": "Text color options",
|
|
6421
|
+
side: "bottom",
|
|
6422
|
+
align: "start",
|
|
6423
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(TextStyleColorPanel, { onColorChanged: handleColorChanged })
|
|
6424
|
+
}
|
|
6425
|
+
)
|
|
6426
|
+
] });
|
|
6427
|
+
}
|
|
6428
|
+
);
|
|
6429
|
+
ColorTextPopover.displayName = "ColorTextPopover";
|
|
6430
|
+
|
|
6431
|
+
// src/components/tiptap-ui/color-text-popover/use-color-text-popover.ts
|
|
6432
|
+
var import_react89 = require("react");
|
|
6433
|
+
function getColorByValue(value, colorArray) {
|
|
6434
|
+
return colorArray.find((color) => color.value === value) ?? {
|
|
6435
|
+
value,
|
|
6436
|
+
label: value
|
|
6437
|
+
};
|
|
6438
|
+
}
|
|
6439
|
+
function shouldShowColorTextPopover(params) {
|
|
6440
|
+
const { editor, hideWhenUnavailable } = params;
|
|
6441
|
+
if (!editor || !editor.isEditable) return false;
|
|
6442
|
+
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
6443
|
+
return canColorText(editor) || canColorHighlight(editor);
|
|
6444
|
+
}
|
|
6445
|
+
return true;
|
|
6446
|
+
}
|
|
6447
|
+
function useRecentColors(maxColors = 3) {
|
|
6448
|
+
const [recentColors, setRecentColors] = (0, import_react89.useState)([]);
|
|
6449
|
+
const [isInitialized, setIsInitialized] = (0, import_react89.useState)(false);
|
|
6450
|
+
(0, import_react89.useEffect)(() => {
|
|
6451
|
+
try {
|
|
6452
|
+
const storedColors = localStorage.getItem("tiptapRecentlyUsedColors");
|
|
6453
|
+
if (storedColors) {
|
|
6454
|
+
const colors = JSON.parse(storedColors);
|
|
6455
|
+
setRecentColors(colors.slice(0, maxColors));
|
|
6456
|
+
}
|
|
6457
|
+
} catch (e) {
|
|
6458
|
+
console.error("Failed to load stored colors:", e);
|
|
6459
|
+
} finally {
|
|
6460
|
+
setIsInitialized(true);
|
|
6461
|
+
}
|
|
6462
|
+
}, [maxColors]);
|
|
6463
|
+
const addRecentColor = (0, import_react89.useCallback)(
|
|
6464
|
+
({
|
|
6465
|
+
type,
|
|
6466
|
+
label,
|
|
6467
|
+
value
|
|
6468
|
+
}) => {
|
|
6469
|
+
setRecentColors((prevColors) => {
|
|
6470
|
+
const filtered = prevColors.filter(
|
|
6471
|
+
(c) => !(c.type === type && c.value === value)
|
|
6472
|
+
);
|
|
6473
|
+
const updated = [{ type, label, value }, ...filtered].slice(
|
|
6474
|
+
0,
|
|
6475
|
+
maxColors
|
|
6476
|
+
);
|
|
6477
|
+
try {
|
|
6478
|
+
localStorage.setItem(
|
|
6479
|
+
"tiptapRecentlyUsedColors",
|
|
6480
|
+
JSON.stringify(updated)
|
|
6481
|
+
);
|
|
6482
|
+
} catch (e) {
|
|
6483
|
+
console.error("Failed to store colors:", e);
|
|
6484
|
+
}
|
|
6485
|
+
return updated;
|
|
6486
|
+
});
|
|
6487
|
+
},
|
|
6488
|
+
[maxColors]
|
|
6489
|
+
);
|
|
6490
|
+
return { recentColors, addRecentColor, isInitialized };
|
|
6491
|
+
}
|
|
6492
|
+
function useColorTextPopover(config) {
|
|
6493
|
+
const {
|
|
6494
|
+
editor: providedEditor,
|
|
6495
|
+
hideWhenUnavailable = false,
|
|
6496
|
+
onColorChanged
|
|
6497
|
+
} = config || {};
|
|
6498
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
6499
|
+
const [isVisible, setIsVisible] = (0, import_react89.useState)(true);
|
|
6500
|
+
const textStyleInSchema = isMarkInSchema("textStyle", editor);
|
|
6501
|
+
const highlightInSchema = isMarkInSchema("highlight", editor);
|
|
6502
|
+
const activeTextStyle = getActiveMarkAttrs(editor, "textStyle") || {};
|
|
6503
|
+
const activeHighlight = getActiveMarkAttrs(editor, "highlight") || {};
|
|
6504
|
+
const canToggle3 = canColorText(editor) || canColorHighlight(editor);
|
|
6505
|
+
(0, import_react89.useEffect)(() => {
|
|
6506
|
+
if (!editor) return;
|
|
6507
|
+
const updateVisibility = () => {
|
|
6508
|
+
setIsVisible(
|
|
6509
|
+
shouldShowColorTextPopover({
|
|
6510
|
+
editor,
|
|
6511
|
+
hideWhenUnavailable
|
|
6512
|
+
})
|
|
6513
|
+
);
|
|
6514
|
+
};
|
|
6515
|
+
updateVisibility();
|
|
6516
|
+
editor.on("selectionUpdate", updateVisibility);
|
|
6517
|
+
return () => {
|
|
6518
|
+
editor.off("selectionUpdate", updateVisibility);
|
|
6519
|
+
};
|
|
6520
|
+
}, [editor, hideWhenUnavailable, highlightInSchema, textStyleInSchema]);
|
|
6521
|
+
const handleColorChanged = (0, import_react89.useCallback)(
|
|
6522
|
+
({
|
|
6523
|
+
type,
|
|
6524
|
+
label,
|
|
6525
|
+
value
|
|
6526
|
+
}) => {
|
|
6527
|
+
onColorChanged?.({ type, label, value });
|
|
6528
|
+
},
|
|
6529
|
+
[onColorChanged]
|
|
6530
|
+
);
|
|
6531
|
+
return {
|
|
6532
|
+
isVisible,
|
|
6533
|
+
canToggle: canToggle3,
|
|
6534
|
+
activeTextStyle,
|
|
6535
|
+
activeHighlight,
|
|
6536
|
+
handleColorChanged,
|
|
6537
|
+
label: "Text color",
|
|
6538
|
+
Icon: TextColorSmallIcon
|
|
6539
|
+
};
|
|
6540
|
+
}
|
|
6541
|
+
|
|
6542
|
+
// src/components/tiptap-ui/text-button/text-button.tsx
|
|
6543
|
+
var import_react90 = require("react");
|
|
6544
|
+
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
6545
|
+
function TextShortcutBadge({
|
|
6546
|
+
shortcutKeys = TEXT_SHORTCUT_KEY
|
|
6547
|
+
}) {
|
|
6548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(Badge, { children: parseShortcutKeys({ shortcutKeys }) });
|
|
6549
|
+
}
|
|
6550
|
+
var TextButton = (0, import_react90.forwardRef)(
|
|
6551
|
+
({
|
|
6552
|
+
editor: providedEditor,
|
|
6553
|
+
text,
|
|
6554
|
+
hideWhenUnavailable = false,
|
|
6555
|
+
onToggled,
|
|
6556
|
+
showShortcut = false,
|
|
6557
|
+
onClick,
|
|
6558
|
+
children,
|
|
6559
|
+
...buttonProps
|
|
6560
|
+
}, ref) => {
|
|
6561
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
6562
|
+
const {
|
|
6563
|
+
isVisible,
|
|
6564
|
+
canToggle: canToggle3,
|
|
6565
|
+
isActive,
|
|
6566
|
+
handleToggle,
|
|
6567
|
+
label,
|
|
6568
|
+
shortcutKeys,
|
|
6569
|
+
Icon
|
|
6570
|
+
} = useText({
|
|
6571
|
+
editor,
|
|
6572
|
+
hideWhenUnavailable,
|
|
6573
|
+
onToggled
|
|
6574
|
+
});
|
|
6575
|
+
const handleClick = (0, import_react90.useCallback)(
|
|
6576
|
+
(event) => {
|
|
6577
|
+
onClick?.(event);
|
|
6578
|
+
if (event.defaultPrevented) return;
|
|
6579
|
+
handleToggle();
|
|
6580
|
+
},
|
|
6581
|
+
[handleToggle, onClick]
|
|
6582
|
+
);
|
|
6583
|
+
if (!isVisible) {
|
|
6584
|
+
return null;
|
|
6585
|
+
}
|
|
6586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
|
|
6587
|
+
Button,
|
|
6588
|
+
{
|
|
6589
|
+
type: "button",
|
|
6590
|
+
"data-style": "ghost",
|
|
6591
|
+
"data-active-state": isActive ? "on" : "off",
|
|
6592
|
+
role: "button",
|
|
6593
|
+
tabIndex: -1,
|
|
6594
|
+
disabled: !canToggle3,
|
|
6595
|
+
"data-disabled": !canToggle3,
|
|
6596
|
+
"aria-label": label,
|
|
6597
|
+
"aria-pressed": isActive,
|
|
6598
|
+
tooltip: "Text",
|
|
6599
|
+
onClick: handleClick,
|
|
6600
|
+
...buttonProps,
|
|
6601
|
+
ref,
|
|
6602
|
+
children: children ?? /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_jsx_runtime67.Fragment, { children: [
|
|
6603
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)(Icon, { className: "tiptap-button-icon" }),
|
|
6604
|
+
text && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "tiptap-button-text", children: text }),
|
|
6605
|
+
showShortcut && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(TextShortcutBadge, { shortcutKeys })
|
|
6606
|
+
] })
|
|
6607
|
+
}
|
|
6608
|
+
);
|
|
6609
|
+
}
|
|
6610
|
+
);
|
|
6611
|
+
TextButton.displayName = "TextButton";
|
|
6612
|
+
|
|
6613
|
+
// src/components/tiptap-ui/text-button/use-text.ts
|
|
6614
|
+
var import_react92 = require("react");
|
|
6615
|
+
var import_react_hotkeys_hook4 = require("react-hotkeys-hook");
|
|
6616
|
+
var import_state7 = require("@tiptap/pm/state");
|
|
6617
|
+
|
|
6618
|
+
// src/components/tiptap-icons/type-icon.tsx
|
|
6619
|
+
var import_react91 = require("react");
|
|
6620
|
+
var import_jsx_runtime68 = require("react/jsx-runtime");
|
|
6621
|
+
var TypeIcon = (0, import_react91.memo)(({ className, ...props }) => {
|
|
6622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
6623
|
+
"svg",
|
|
6624
|
+
{
|
|
6625
|
+
width: "24",
|
|
6626
|
+
height: "24",
|
|
6627
|
+
className,
|
|
6628
|
+
viewBox: "0 0 24 24",
|
|
6629
|
+
fill: "currentColor",
|
|
6630
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
6631
|
+
...props,
|
|
6632
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
6633
|
+
"path",
|
|
6634
|
+
{
|
|
6635
|
+
d: "M3 4C3 3.44772 3.44772 3 4 3H20C20.5523 3 21 3.44772 21 4V7C21 7.55228 20.5523 8 20 8C19.4477 8 19 7.55228 19 7V5H13V19H15C15.5523 19 16 19.4477 16 20C16 20.5523 15.5523 21 15 21H9C8.44772 21 8 20.5523 8 20C8 19.4477 8.44772 19 9 19H11V5H5V7C5 7.55228 4.55228 8 4 8C3.44772 8 3 7.55228 3 7V4Z",
|
|
6636
|
+
fill: "currentColor"
|
|
6637
|
+
}
|
|
6638
|
+
)
|
|
6639
|
+
}
|
|
6640
|
+
);
|
|
6641
|
+
});
|
|
6642
|
+
TypeIcon.displayName = "TypeIcon";
|
|
6643
|
+
|
|
6644
|
+
// src/components/tiptap-ui/text-button/use-text.ts
|
|
6645
|
+
var TEXT_SHORTCUT_KEY = "mod+alt+0";
|
|
6646
|
+
function canToggleText(editor, turnInto = true) {
|
|
6647
|
+
if (!editor) return false;
|
|
6648
|
+
if (!editor.schema.nodes.paragraph) return false;
|
|
6649
|
+
if (!turnInto) {
|
|
6650
|
+
return editor.can().setNode("paragraph");
|
|
6651
|
+
}
|
|
6652
|
+
if (!selectionWithinConvertibleTypes(editor, [
|
|
6653
|
+
"paragraph",
|
|
6654
|
+
"heading",
|
|
6655
|
+
"bulletList",
|
|
6656
|
+
"orderedList",
|
|
6657
|
+
"taskList",
|
|
6658
|
+
"blockquote",
|
|
6659
|
+
"codeBlock"
|
|
6660
|
+
]))
|
|
6661
|
+
return false;
|
|
6662
|
+
return editor.can().setNode("paragraph") || editor.can().clearNodes();
|
|
6663
|
+
}
|
|
6664
|
+
function isParagraphActive(editor) {
|
|
6665
|
+
if (!editor) return false;
|
|
6666
|
+
return editor.isActive("paragraph");
|
|
6667
|
+
}
|
|
6668
|
+
function toggleParagraph(editor) {
|
|
6669
|
+
if (!editor || !editor.isEditable) return false;
|
|
6670
|
+
if (!canToggleText(editor)) return false;
|
|
6671
|
+
try {
|
|
6672
|
+
const view = editor.view;
|
|
6673
|
+
let state = view.state;
|
|
6674
|
+
let tr = state.tr;
|
|
6675
|
+
const blocks = getSelectedBlockNodes(editor);
|
|
6676
|
+
const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
|
|
6677
|
+
"paragraph",
|
|
6678
|
+
"heading",
|
|
6679
|
+
"bulletList",
|
|
6680
|
+
"orderedList",
|
|
6681
|
+
"taskList",
|
|
6682
|
+
"blockquote",
|
|
6683
|
+
"codeBlock"
|
|
6684
|
+
]) && blocks.length === 1;
|
|
6685
|
+
if ((state.selection.empty || state.selection instanceof import_state7.TextSelection) && isPossibleToTurnInto) {
|
|
6686
|
+
const pos = findNodePosition({
|
|
6687
|
+
editor,
|
|
6688
|
+
node: state.selection.$anchor.node(1)
|
|
6689
|
+
})?.pos;
|
|
6690
|
+
if (!isValidPosition(pos)) return false;
|
|
6691
|
+
tr = tr.setSelection(import_state7.NodeSelection.create(state.doc, pos));
|
|
6692
|
+
view.dispatch(tr);
|
|
6693
|
+
state = view.state;
|
|
6694
|
+
}
|
|
6695
|
+
const selection = state.selection;
|
|
6696
|
+
let chain = editor.chain().focus();
|
|
6697
|
+
if (selection instanceof import_state7.NodeSelection) {
|
|
6698
|
+
const firstChild = selection.node.firstChild?.firstChild;
|
|
6699
|
+
const lastChild = selection.node.lastChild?.lastChild;
|
|
6700
|
+
const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
|
|
6701
|
+
const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
|
|
6702
|
+
const resolvedFrom = state.doc.resolve(from);
|
|
6703
|
+
const resolvedTo = state.doc.resolve(to);
|
|
6704
|
+
chain = chain.setTextSelection(import_state7.TextSelection.between(resolvedFrom, resolvedTo)).clearNodes();
|
|
6705
|
+
}
|
|
6706
|
+
if (!editor.isActive("paragraph")) {
|
|
6707
|
+
chain.setNode("paragraph").run();
|
|
6708
|
+
}
|
|
6709
|
+
editor.chain().focus().selectTextblockEnd().run();
|
|
6710
|
+
return true;
|
|
6711
|
+
} catch {
|
|
6712
|
+
return false;
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
function shouldShowButton12(props) {
|
|
6716
|
+
const { editor, hideWhenUnavailable } = props;
|
|
6717
|
+
if (!editor || !editor.isEditable) return false;
|
|
6718
|
+
if (!isNodeInSchema("paragraph", editor)) return false;
|
|
6719
|
+
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
6720
|
+
return canToggleText(editor);
|
|
6721
|
+
}
|
|
6722
|
+
return true;
|
|
6723
|
+
}
|
|
6724
|
+
function useText(config) {
|
|
6725
|
+
const {
|
|
6726
|
+
editor: providedEditor,
|
|
6727
|
+
hideWhenUnavailable = false,
|
|
6728
|
+
onToggled
|
|
6729
|
+
} = config || {};
|
|
6730
|
+
const { editor } = useTiptapEditor(providedEditor);
|
|
6731
|
+
const isMobile = useIsBreakpoint();
|
|
6732
|
+
const [isVisible, setIsVisible] = (0, import_react92.useState)(true);
|
|
6733
|
+
const canToggle3 = canToggleText(editor);
|
|
6734
|
+
const isActive = isParagraphActive(editor);
|
|
6735
|
+
(0, import_react92.useEffect)(() => {
|
|
6736
|
+
if (!editor) return;
|
|
6737
|
+
const handleSelectionUpdate = () => {
|
|
6738
|
+
setIsVisible(shouldShowButton12({ editor, hideWhenUnavailable }));
|
|
6739
|
+
};
|
|
6740
|
+
handleSelectionUpdate();
|
|
6741
|
+
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
6742
|
+
return () => {
|
|
6743
|
+
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
6744
|
+
};
|
|
6745
|
+
}, [editor, hideWhenUnavailable]);
|
|
6746
|
+
const handleToggle = (0, import_react92.useCallback)(() => {
|
|
6747
|
+
if (!editor) return false;
|
|
6748
|
+
const success = toggleParagraph(editor);
|
|
6749
|
+
if (success) {
|
|
6750
|
+
onToggled?.();
|
|
6751
|
+
}
|
|
6752
|
+
return success;
|
|
6753
|
+
}, [editor, onToggled]);
|
|
6754
|
+
(0, import_react_hotkeys_hook4.useHotkeys)(
|
|
6755
|
+
TEXT_SHORTCUT_KEY,
|
|
6756
|
+
(event) => {
|
|
6757
|
+
event.preventDefault();
|
|
6758
|
+
handleToggle();
|
|
6759
|
+
},
|
|
6760
|
+
{
|
|
6761
|
+
enabled: isVisible && canToggle3,
|
|
6762
|
+
enableOnContentEditable: !isMobile,
|
|
6763
|
+
enableOnFormTags: true
|
|
6764
|
+
}
|
|
6765
|
+
);
|
|
6766
|
+
return {
|
|
6767
|
+
isVisible,
|
|
6768
|
+
isActive,
|
|
6769
|
+
handleToggle,
|
|
6770
|
+
canToggle: canToggle3,
|
|
6771
|
+
label: "Text",
|
|
6772
|
+
shortcutKeys: TEXT_SHORTCUT_KEY,
|
|
6773
|
+
Icon: TypeIcon
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
// src/components/tiptap-icons/arrow-left-icon.tsx
|
|
6778
|
+
var import_react93 = require("react");
|
|
6779
|
+
var import_jsx_runtime69 = require("react/jsx-runtime");
|
|
6780
|
+
var ArrowLeftIcon = (0, import_react93.memo)(({ className, ...props }) => {
|
|
6781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
6782
|
+
"svg",
|
|
6783
|
+
{
|
|
6784
|
+
width: "24",
|
|
6785
|
+
height: "24",
|
|
6786
|
+
className,
|
|
6787
|
+
viewBox: "0 0 24 24",
|
|
6788
|
+
fill: "currentColor",
|
|
6789
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
6790
|
+
...props,
|
|
6791
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
6792
|
+
"path",
|
|
6793
|
+
{
|
|
6794
|
+
d: "M12.7071 5.70711C13.0976 5.31658 13.0976 4.68342 12.7071 4.29289C12.3166 3.90237 11.6834 3.90237 11.2929 4.29289L4.29289 11.2929C3.90237 11.6834 3.90237 12.3166 4.29289 12.7071L11.2929 19.7071C11.6834 20.0976 12.3166 20.0976 12.7071 19.7071C13.0976 19.3166 13.0976 18.6834 12.7071 18.2929L7.41421 13L19 13C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11L7.41421 11L12.7071 5.70711Z",
|
|
6795
|
+
fill: "currentColor"
|
|
6796
|
+
}
|
|
6797
|
+
)
|
|
6798
|
+
}
|
|
6799
|
+
);
|
|
6800
|
+
});
|
|
6801
|
+
ArrowLeftIcon.displayName = "ArrowLeftIcon";
|
|
6802
|
+
|
|
6803
|
+
// src/hooks/use-window-size.ts
|
|
6804
|
+
var import_react96 = require("react");
|
|
6805
|
+
|
|
6806
|
+
// src/hooks/use-throttled-callback.ts
|
|
6807
|
+
var import_lodash = __toESM(require("lodash.throttle"), 1);
|
|
6808
|
+
|
|
6809
|
+
// src/hooks/use-unmount.ts
|
|
6810
|
+
var import_react94 = require("react");
|
|
6811
|
+
var useUnmount = (callback) => {
|
|
6812
|
+
const ref = (0, import_react94.useRef)(callback);
|
|
6813
|
+
ref.current = callback;
|
|
6814
|
+
(0, import_react94.useEffect)(
|
|
6815
|
+
() => () => {
|
|
6816
|
+
ref.current();
|
|
6817
|
+
},
|
|
6818
|
+
[]
|
|
6819
|
+
);
|
|
6820
|
+
};
|
|
6821
|
+
|
|
6822
|
+
// src/hooks/use-throttled-callback.ts
|
|
6823
|
+
var import_react95 = require("react");
|
|
6824
|
+
var defaultOptions = {
|
|
6825
|
+
leading: false,
|
|
6826
|
+
trailing: true
|
|
6827
|
+
};
|
|
6828
|
+
function useThrottledCallback(fn, wait = 250, dependencies = [], options = defaultOptions) {
|
|
6829
|
+
const handler = (0, import_react95.useMemo)(
|
|
6830
|
+
() => (0, import_lodash.default)(fn, wait, options),
|
|
6831
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6832
|
+
dependencies
|
|
6833
|
+
);
|
|
6834
|
+
useUnmount(() => {
|
|
6835
|
+
handler.cancel();
|
|
6836
|
+
});
|
|
6837
|
+
return handler;
|
|
6838
|
+
}
|
|
6839
|
+
|
|
6840
|
+
// src/hooks/use-window-size.ts
|
|
6841
|
+
function useWindowSize() {
|
|
6842
|
+
const [windowSize, setWindowSize] = (0, import_react96.useState)({
|
|
6843
|
+
width: 0,
|
|
6844
|
+
height: 0,
|
|
6845
|
+
offsetTop: 0,
|
|
6846
|
+
offsetLeft: 0,
|
|
6847
|
+
scale: 0
|
|
6848
|
+
});
|
|
6849
|
+
const handleViewportChange = useThrottledCallback(() => {
|
|
6850
|
+
if (typeof window === "undefined") return;
|
|
6851
|
+
const vp = window.visualViewport;
|
|
6852
|
+
if (!vp) return;
|
|
6853
|
+
const {
|
|
5907
6854
|
width = 0,
|
|
5908
6855
|
height = 0,
|
|
5909
6856
|
offsetTop = 0,
|
|
@@ -5917,7 +6864,7 @@ function useWindowSize() {
|
|
|
5917
6864
|
return { width, height, offsetTop, offsetLeft, scale };
|
|
5918
6865
|
});
|
|
5919
6866
|
}, 200);
|
|
5920
|
-
(0,
|
|
6867
|
+
(0, import_react96.useEffect)(() => {
|
|
5921
6868
|
const visualViewport = window.visualViewport;
|
|
5922
6869
|
if (!visualViewport) return;
|
|
5923
6870
|
visualViewport.addEventListener("resize", handleViewportChange);
|
|
@@ -5930,7 +6877,7 @@ function useWindowSize() {
|
|
|
5930
6877
|
}
|
|
5931
6878
|
|
|
5932
6879
|
// src/hooks/use-element-rect.ts
|
|
5933
|
-
var
|
|
6880
|
+
var import_react97 = require("react");
|
|
5934
6881
|
var initialRect = {
|
|
5935
6882
|
x: 0,
|
|
5936
6883
|
y: 0,
|
|
@@ -5950,8 +6897,8 @@ function useElementRect({
|
|
|
5950
6897
|
throttleMs = 100,
|
|
5951
6898
|
useResizeObserver = true
|
|
5952
6899
|
} = {}) {
|
|
5953
|
-
const [rect, setRect] = (0,
|
|
5954
|
-
const getTargetElement = (0,
|
|
6900
|
+
const [rect, setRect] = (0, import_react97.useState)(initialRect);
|
|
6901
|
+
const getTargetElement = (0, import_react97.useCallback)(() => {
|
|
5955
6902
|
if (!enabled || !isClientSide()) return null;
|
|
5956
6903
|
if (!element) {
|
|
5957
6904
|
return document.body;
|
|
@@ -5988,7 +6935,7 @@ function useElementRect({
|
|
|
5988
6935
|
[enabled, getTargetElement],
|
|
5989
6936
|
{ leading: true, trailing: true }
|
|
5990
6937
|
);
|
|
5991
|
-
(0,
|
|
6938
|
+
(0, import_react97.useEffect)(() => {
|
|
5992
6939
|
if (!enabled || !isClientSide()) {
|
|
5993
6940
|
setRect(initialRect);
|
|
5994
6941
|
return;
|
|
@@ -6026,7 +6973,7 @@ function useBodyRect(options = {}) {
|
|
|
6026
6973
|
}
|
|
6027
6974
|
|
|
6028
6975
|
// src/hooks/use-cursor-visibility.ts
|
|
6029
|
-
var
|
|
6976
|
+
var import_react98 = require("react");
|
|
6030
6977
|
function useCursorVisibility({
|
|
6031
6978
|
editor,
|
|
6032
6979
|
overlayHeight = 0
|
|
@@ -6037,7 +6984,7 @@ function useCursorVisibility({
|
|
|
6037
6984
|
throttleMs: 100,
|
|
6038
6985
|
useResizeObserver: true
|
|
6039
6986
|
});
|
|
6040
|
-
(0,
|
|
6987
|
+
(0, import_react98.useEffect)(() => {
|
|
6041
6988
|
const ensureCursorVisibility = () => {
|
|
6042
6989
|
if (!editor) return;
|
|
6043
6990
|
const { state, view } = editor;
|
|
@@ -6064,11 +7011,11 @@ function useCursorVisibility({
|
|
|
6064
7011
|
}
|
|
6065
7012
|
|
|
6066
7013
|
// src/hooks/use-auto-height.ts
|
|
6067
|
-
var
|
|
7014
|
+
var import_react99 = require("react");
|
|
6068
7015
|
function useAutoHeight(containerRef, options) {
|
|
6069
7016
|
const { enabled, bottomOffset = 24, minHeight = 200 } = options;
|
|
6070
|
-
const [height, setHeight] = (0,
|
|
6071
|
-
const calculateHeight = (0,
|
|
7017
|
+
const [height, setHeight] = (0, import_react99.useState)(void 0);
|
|
7018
|
+
const calculateHeight = (0, import_react99.useCallback)(() => {
|
|
6072
7019
|
if (!enabled || !containerRef.current) {
|
|
6073
7020
|
setHeight(void 0);
|
|
6074
7021
|
return;
|
|
@@ -6078,7 +7025,7 @@ function useAutoHeight(containerRef, options) {
|
|
|
6078
7025
|
const finalHeight = Math.max(availableHeight, minHeight);
|
|
6079
7026
|
setHeight(finalHeight);
|
|
6080
7027
|
}, [enabled, bottomOffset, minHeight, containerRef]);
|
|
6081
|
-
(0,
|
|
7028
|
+
(0, import_react99.useEffect)(() => {
|
|
6082
7029
|
if (!enabled) {
|
|
6083
7030
|
setHeight(void 0);
|
|
6084
7031
|
return;
|
|
@@ -6092,15 +7039,32 @@ function useAutoHeight(containerRef, options) {
|
|
|
6092
7039
|
return height;
|
|
6093
7040
|
}
|
|
6094
7041
|
|
|
7042
|
+
// src/context/elia-editor-context.tsx
|
|
7043
|
+
var import_react100 = require("react");
|
|
7044
|
+
var import_jsx_runtime70 = require("react/jsx-runtime");
|
|
7045
|
+
var EliaEditorContext = (0, import_react100.createContext)(null);
|
|
7046
|
+
function EliaEditorProvider({ children }) {
|
|
7047
|
+
const [editor, setEditor] = (0, import_react100.useState)(null);
|
|
7048
|
+
const value = (0, import_react100.useMemo)(() => ({ editor, setEditor }), [editor]);
|
|
7049
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(EliaEditorContext.Provider, { value, children });
|
|
7050
|
+
}
|
|
7051
|
+
function useEliaEditor() {
|
|
7052
|
+
const context = (0, import_react100.useContext)(EliaEditorContext);
|
|
7053
|
+
return context?.editor ?? null;
|
|
7054
|
+
}
|
|
7055
|
+
function useEliaEditorContext() {
|
|
7056
|
+
return (0, import_react100.useContext)(EliaEditorContext);
|
|
7057
|
+
}
|
|
7058
|
+
|
|
6095
7059
|
// src/components/tiptap-templates/simple/simple-editor.tsx
|
|
6096
|
-
var
|
|
7060
|
+
var import_jsx_runtime71 = require("react/jsx-runtime");
|
|
6097
7061
|
var DEFAULT_TOOLBAR = [
|
|
6098
7062
|
"undo-redo",
|
|
7063
|
+
"text",
|
|
6099
7064
|
"heading",
|
|
6100
7065
|
"list",
|
|
6101
7066
|
"blockquote",
|
|
6102
7067
|
"code-block",
|
|
6103
|
-
"callout",
|
|
6104
7068
|
"table",
|
|
6105
7069
|
"format",
|
|
6106
7070
|
"text-color",
|
|
@@ -6118,66 +7082,66 @@ var MainToolbarContent = ({
|
|
|
6118
7082
|
toolbar
|
|
6119
7083
|
}) => {
|
|
6120
7084
|
const has = (item) => toolbar.includes(item);
|
|
6121
|
-
return /* @__PURE__ */ (0,
|
|
6122
|
-
/* @__PURE__ */ (0,
|
|
6123
|
-
has("undo-redo") && /* @__PURE__ */ (0,
|
|
6124
|
-
/* @__PURE__ */ (0,
|
|
6125
|
-
/* @__PURE__ */ (0,
|
|
7085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_jsx_runtime71.Fragment, { children: [
|
|
7086
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Spacer, {}),
|
|
7087
|
+
has("undo-redo") && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(ToolbarGroup, { children: [
|
|
7088
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(UndoRedoButton, { action: "undo" }),
|
|
7089
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(UndoRedoButton, { action: "redo" })
|
|
6126
7090
|
] }),
|
|
6127
|
-
has("undo-redo") && (has("
|
|
6128
|
-
(has("
|
|
6129
|
-
has("
|
|
6130
|
-
has("
|
|
7091
|
+
has("undo-redo") && (has("text") || has("heading") || has("list") || has("blockquote") || has("code-block") || has("table")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7092
|
+
(has("text") || has("heading") || has("list") || has("blockquote") || has("code-block") || has("table")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(ToolbarGroup, { children: [
|
|
7093
|
+
has("text") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TextButton, {}),
|
|
7094
|
+
has("heading") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(HeadingDropdownMenu, { levels: [1, 2, 3, 4] }),
|
|
7095
|
+
has("list") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
6131
7096
|
ListDropdownMenu,
|
|
6132
7097
|
{
|
|
6133
7098
|
types: ["bulletList", "orderedList", "taskList"]
|
|
6134
7099
|
}
|
|
6135
7100
|
),
|
|
6136
|
-
has("blockquote") && /* @__PURE__ */ (0,
|
|
6137
|
-
has("code-block") && /* @__PURE__ */ (0,
|
|
6138
|
-
has("
|
|
6139
|
-
has("table") && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(TableButton, {})
|
|
7101
|
+
has("blockquote") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(BlockquoteButton, {}),
|
|
7102
|
+
has("code-block") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(CodeBlockButton, {}),
|
|
7103
|
+
has("table") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TableTriggerButton, {})
|
|
6140
7104
|
] }),
|
|
6141
|
-
(has("
|
|
6142
|
-
(has("format") || has("text-color") || has("highlight") || has("link")) && /* @__PURE__ */ (0,
|
|
6143
|
-
has("format") && /* @__PURE__ */ (0,
|
|
6144
|
-
/* @__PURE__ */ (0,
|
|
6145
|
-
/* @__PURE__ */ (0,
|
|
6146
|
-
/* @__PURE__ */ (0,
|
|
6147
|
-
/* @__PURE__ */ (0,
|
|
6148
|
-
/* @__PURE__ */ (0,
|
|
7105
|
+
(has("text") || has("heading") || has("list") || has("blockquote") || has("code-block") || has("table")) && (has("format") || has("text-color") || has("highlight") || has("link")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7106
|
+
(has("format") || has("text-color") || has("highlight") || has("link")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(ToolbarGroup, { children: [
|
|
7107
|
+
has("format") && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_jsx_runtime71.Fragment, { children: [
|
|
7108
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "bold" }),
|
|
7109
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "italic" }),
|
|
7110
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "strike" }),
|
|
7111
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "code" }),
|
|
7112
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "underline" })
|
|
6149
7113
|
] }),
|
|
6150
|
-
has("text-color") && /* @__PURE__ */ (0,
|
|
6151
|
-
has("highlight") && (!isMobile ? /* @__PURE__ */ (0,
|
|
6152
|
-
has("link") && (!isMobile ? /* @__PURE__ */ (0,
|
|
7114
|
+
has("text-color") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorTextPopover, {}),
|
|
7115
|
+
has("highlight") && (!isMobile ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorHighlightPopover, {}) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorHighlightPopoverButton, { onClick: onHighlighterClick })),
|
|
7116
|
+
has("link") && (!isMobile ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(LinkPopover, {}) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(LinkButton, { onClick: onLinkClick }))
|
|
6153
7117
|
] }),
|
|
6154
|
-
(has("format") || has("text-color") || has("highlight") || has("link")) && (has("superscript") || has("subscript")) && /* @__PURE__ */ (0,
|
|
6155
|
-
(has("superscript") || has("subscript")) && /* @__PURE__ */ (0,
|
|
6156
|
-
has("superscript") && /* @__PURE__ */ (0,
|
|
6157
|
-
has("subscript") && /* @__PURE__ */ (0,
|
|
7118
|
+
(has("format") || has("text-color") || has("highlight") || has("link")) && (has("superscript") || has("subscript")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7119
|
+
(has("superscript") || has("subscript")) && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(ToolbarGroup, { children: [
|
|
7120
|
+
has("superscript") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "superscript" }),
|
|
7121
|
+
has("subscript") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(MarkButton, { type: "subscript" })
|
|
6158
7122
|
] }),
|
|
6159
|
-
(has("superscript") || has("subscript")) && has("align") && /* @__PURE__ */ (0,
|
|
6160
|
-
has("align") && /* @__PURE__ */ (0,
|
|
6161
|
-
/* @__PURE__ */ (0,
|
|
6162
|
-
/* @__PURE__ */ (0,
|
|
6163
|
-
/* @__PURE__ */ (0,
|
|
6164
|
-
/* @__PURE__ */ (0,
|
|
7123
|
+
(has("superscript") || has("subscript")) && has("align") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7124
|
+
has("align") && /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(ToolbarGroup, { children: [
|
|
7125
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TextAlignButton, { align: "left" }),
|
|
7126
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TextAlignButton, { align: "center" }),
|
|
7127
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TextAlignButton, { align: "right" }),
|
|
7128
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(TextAlignButton, { align: "justify" })
|
|
6165
7129
|
] }),
|
|
6166
|
-
has("align") && has("image") && /* @__PURE__ */ (0,
|
|
6167
|
-
has("image") && /* @__PURE__ */ (0,
|
|
6168
|
-
/* @__PURE__ */ (0,
|
|
7130
|
+
has("align") && has("image") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7131
|
+
has("image") && /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ImageUploadButton, {}) }),
|
|
7132
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Spacer, {})
|
|
6169
7133
|
] });
|
|
6170
7134
|
};
|
|
6171
7135
|
var MobileToolbarContent = ({
|
|
6172
7136
|
type,
|
|
6173
7137
|
onBack
|
|
6174
|
-
}) => /* @__PURE__ */ (0,
|
|
6175
|
-
/* @__PURE__ */ (0,
|
|
6176
|
-
/* @__PURE__ */ (0,
|
|
6177
|
-
type === "highlighter" ? /* @__PURE__ */ (0,
|
|
7138
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_jsx_runtime71.Fragment, { children: [
|
|
7139
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Button, { "data-style": "ghost", onClick: onBack, children: [
|
|
7140
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ArrowLeftIcon, { className: "tiptap-button-icon" }),
|
|
7141
|
+
type === "highlighter" ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(HighlighterIcon, { className: "tiptap-button-icon" }) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(LinkIcon, { className: "tiptap-button-icon" })
|
|
6178
7142
|
] }) }),
|
|
6179
|
-
/* @__PURE__ */ (0,
|
|
6180
|
-
type === "highlighter" ? /* @__PURE__ */ (0,
|
|
7143
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ToolbarSeparator, {}),
|
|
7144
|
+
type === "highlighter" ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(ColorHighlightPopoverContent, {}) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(LinkContent, {})
|
|
6181
7145
|
] });
|
|
6182
7146
|
function EliaEditor({
|
|
6183
7147
|
content = "",
|
|
@@ -6197,26 +7161,26 @@ function EliaEditor({
|
|
|
6197
7161
|
}) {
|
|
6198
7162
|
const isMobile = useIsBreakpoint();
|
|
6199
7163
|
const { height } = useWindowSize();
|
|
6200
|
-
const [mobileView, setMobileView] = (0,
|
|
7164
|
+
const [mobileView, setMobileView] = (0, import_react101.useState)(
|
|
6201
7165
|
"main"
|
|
6202
7166
|
);
|
|
6203
|
-
const toolbarRef = (0,
|
|
6204
|
-
const containerRef = (0,
|
|
6205
|
-
const isInternalUpdate = (0,
|
|
7167
|
+
const toolbarRef = (0, import_react101.useRef)(null);
|
|
7168
|
+
const containerRef = (0, import_react101.useRef)(null);
|
|
7169
|
+
const isInternalUpdate = (0, import_react101.useRef)(false);
|
|
6206
7170
|
const computedHeight = useAutoHeight(containerRef, {
|
|
6207
7171
|
enabled: embedded && autoHeight,
|
|
6208
7172
|
bottomOffset,
|
|
6209
7173
|
minHeight
|
|
6210
7174
|
});
|
|
6211
7175
|
const uploadFn = onImageUpload || handleImageUpload;
|
|
6212
|
-
const handleUpdate = (0,
|
|
7176
|
+
const handleUpdate = (0, import_react101.useCallback)(
|
|
6213
7177
|
({ editor: editor2 }) => {
|
|
6214
7178
|
isInternalUpdate.current = true;
|
|
6215
7179
|
onChange?.(editor2.getHTML(), editor2.getJSON());
|
|
6216
7180
|
},
|
|
6217
7181
|
[onChange]
|
|
6218
7182
|
);
|
|
6219
|
-
const editor = (0,
|
|
7183
|
+
const editor = (0, import_react102.useEditor)({
|
|
6220
7184
|
immediatelyRender: false,
|
|
6221
7185
|
editorProps: {
|
|
6222
7186
|
attributes: {
|
|
@@ -6258,7 +7222,6 @@ function EliaEditor({
|
|
|
6258
7222
|
import_tiptap_extension_resize_image.default.configure({
|
|
6259
7223
|
allowBase64: true
|
|
6260
7224
|
}),
|
|
6261
|
-
Callout,
|
|
6262
7225
|
ImageUploadNode2.configure({
|
|
6263
7226
|
type: "imageResize",
|
|
6264
7227
|
accept: "image/*",
|
|
@@ -6266,6 +7229,10 @@ function EliaEditor({
|
|
|
6266
7229
|
limit: 3,
|
|
6267
7230
|
upload: uploadFn,
|
|
6268
7231
|
onError: (error) => console.error("Upload failed:", error)
|
|
7232
|
+
}),
|
|
7233
|
+
import_extension_unique_id.UniqueID.configure({
|
|
7234
|
+
types: ["heading"],
|
|
7235
|
+
attributeName: "id"
|
|
6269
7236
|
})
|
|
6270
7237
|
],
|
|
6271
7238
|
content,
|
|
@@ -6273,27 +7240,38 @@ function EliaEditor({
|
|
|
6273
7240
|
editable: !readOnly,
|
|
6274
7241
|
onUpdate: handleUpdate
|
|
6275
7242
|
});
|
|
7243
|
+
const eliaContext = useEliaEditorContext();
|
|
7244
|
+
(0, import_react101.useEffect)(() => {
|
|
7245
|
+
if (eliaContext && editor) {
|
|
7246
|
+
eliaContext.setEditor(editor);
|
|
7247
|
+
}
|
|
7248
|
+
return () => {
|
|
7249
|
+
if (eliaContext) {
|
|
7250
|
+
eliaContext.setEditor(null);
|
|
7251
|
+
}
|
|
7252
|
+
};
|
|
7253
|
+
}, [editor]);
|
|
6276
7254
|
const rect = useCursorVisibility({
|
|
6277
7255
|
editor,
|
|
6278
7256
|
overlayHeight: toolbarRef.current?.getBoundingClientRect().height ?? 0
|
|
6279
7257
|
});
|
|
6280
|
-
(0,
|
|
7258
|
+
(0, import_react101.useEffect)(() => {
|
|
6281
7259
|
if (!isMobile && mobileView !== "main") {
|
|
6282
7260
|
setMobileView("main");
|
|
6283
7261
|
}
|
|
6284
7262
|
}, [isMobile, mobileView]);
|
|
6285
|
-
(0,
|
|
7263
|
+
(0, import_react101.useEffect)(() => {
|
|
6286
7264
|
if (editor && !isInternalUpdate.current && content !== editor.getHTML()) {
|
|
6287
7265
|
editor.commands.setContent(content);
|
|
6288
7266
|
}
|
|
6289
7267
|
isInternalUpdate.current = false;
|
|
6290
7268
|
}, [content, editor]);
|
|
6291
|
-
(0,
|
|
7269
|
+
(0, import_react101.useEffect)(() => {
|
|
6292
7270
|
if (editor) {
|
|
6293
7271
|
editor.setEditable(!readOnly);
|
|
6294
7272
|
}
|
|
6295
7273
|
}, [readOnly, editor]);
|
|
6296
|
-
return /* @__PURE__ */ (0,
|
|
7274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
6297
7275
|
"div",
|
|
6298
7276
|
{
|
|
6299
7277
|
ref: containerRef,
|
|
@@ -6304,8 +7282,8 @@ function EliaEditor({
|
|
|
6304
7282
|
className
|
|
6305
7283
|
),
|
|
6306
7284
|
style: computedHeight ? { height: computedHeight } : void 0,
|
|
6307
|
-
children: /* @__PURE__ */ (0,
|
|
6308
|
-
/* @__PURE__ */ (0,
|
|
7285
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_react102.EditorContext.Provider, { value: { editor }, children: [
|
|
7286
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
6309
7287
|
Toolbar,
|
|
6310
7288
|
{
|
|
6311
7289
|
ref: toolbarRef,
|
|
@@ -6314,7 +7292,7 @@ function EliaEditor({
|
|
|
6314
7292
|
bottom: `calc(100% - ${height - rect.y}px)`
|
|
6315
7293
|
} : {}
|
|
6316
7294
|
},
|
|
6317
|
-
children: mobileView === "main" ? /* @__PURE__ */ (0,
|
|
7295
|
+
children: mobileView === "main" ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
6318
7296
|
MainToolbarContent,
|
|
6319
7297
|
{
|
|
6320
7298
|
onHighlighterClick: () => setMobileView("highlighter"),
|
|
@@ -6322,7 +7300,7 @@ function EliaEditor({
|
|
|
6322
7300
|
isMobile,
|
|
6323
7301
|
toolbar
|
|
6324
7302
|
}
|
|
6325
|
-
) : /* @__PURE__ */ (0,
|
|
7303
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
6326
7304
|
MobileToolbarContent,
|
|
6327
7305
|
{
|
|
6328
7306
|
type: mobileView === "highlighter" ? "highlighter" : "link",
|
|
@@ -6331,8 +7309,8 @@ function EliaEditor({
|
|
|
6331
7309
|
)
|
|
6332
7310
|
}
|
|
6333
7311
|
),
|
|
6334
|
-
/* @__PURE__ */ (0,
|
|
6335
|
-
|
|
7312
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
7313
|
+
import_react102.EditorContent,
|
|
6336
7314
|
{
|
|
6337
7315
|
editor,
|
|
6338
7316
|
role: "presentation",
|
|
@@ -6344,8 +7322,123 @@ function EliaEditor({
|
|
|
6344
7322
|
);
|
|
6345
7323
|
}
|
|
6346
7324
|
|
|
7325
|
+
// src/components/toc-sidebar/toc-sidebar.tsx
|
|
7326
|
+
var import_react105 = require("react");
|
|
7327
|
+
|
|
7328
|
+
// src/hooks/use-toc-items.ts
|
|
7329
|
+
var import_react103 = require("react");
|
|
7330
|
+
function useTocItems(editor) {
|
|
7331
|
+
const [items, setItems] = (0, import_react103.useState)([]);
|
|
7332
|
+
const updateToc = (0, import_react103.useCallback)(() => {
|
|
7333
|
+
if (!editor) {
|
|
7334
|
+
setItems([]);
|
|
7335
|
+
return;
|
|
7336
|
+
}
|
|
7337
|
+
const headings = [];
|
|
7338
|
+
const doc = editor.state.doc;
|
|
7339
|
+
doc.descendants((node, pos) => {
|
|
7340
|
+
if (node.type.name === "heading") {
|
|
7341
|
+
const level = node.attrs.level;
|
|
7342
|
+
if (level >= 1 && level <= 4) {
|
|
7343
|
+
const id = node.attrs.id || `heading-${pos}`;
|
|
7344
|
+
headings.push({
|
|
7345
|
+
id,
|
|
7346
|
+
level,
|
|
7347
|
+
text: node.textContent,
|
|
7348
|
+
pos
|
|
7349
|
+
});
|
|
7350
|
+
}
|
|
7351
|
+
}
|
|
7352
|
+
});
|
|
7353
|
+
setItems(headings);
|
|
7354
|
+
}, [editor]);
|
|
7355
|
+
(0, import_react103.useEffect)(() => {
|
|
7356
|
+
if (!editor) return;
|
|
7357
|
+
updateToc();
|
|
7358
|
+
editor.on("update", updateToc);
|
|
7359
|
+
return () => {
|
|
7360
|
+
editor.off("update", updateToc);
|
|
7361
|
+
};
|
|
7362
|
+
}, [editor, updateToc]);
|
|
7363
|
+
return items;
|
|
7364
|
+
}
|
|
7365
|
+
|
|
7366
|
+
// src/hooks/use-active-heading.ts
|
|
7367
|
+
var import_react104 = require("react");
|
|
7368
|
+
function useActiveHeading(items) {
|
|
7369
|
+
const [activeId, setActiveId] = (0, import_react104.useState)(items[0]?.id ?? null);
|
|
7370
|
+
const handleScroll = (0, import_react104.useCallback)(() => {
|
|
7371
|
+
if (items.length === 0) {
|
|
7372
|
+
setActiveId(null);
|
|
7373
|
+
return;
|
|
7374
|
+
}
|
|
7375
|
+
const headingElements = items.map((item) => ({
|
|
7376
|
+
id: item.id,
|
|
7377
|
+
element: document.getElementById(item.id)
|
|
7378
|
+
})).filter((item) => item.element !== null);
|
|
7379
|
+
if (headingElements.length === 0) {
|
|
7380
|
+
setActiveId(items[0]?.id ?? null);
|
|
7381
|
+
return;
|
|
7382
|
+
}
|
|
7383
|
+
const offset2 = 100;
|
|
7384
|
+
let currentId = null;
|
|
7385
|
+
for (const { id, element } of headingElements) {
|
|
7386
|
+
if (element) {
|
|
7387
|
+
const rect = element.getBoundingClientRect();
|
|
7388
|
+
if (rect.top <= offset2) {
|
|
7389
|
+
currentId = id;
|
|
7390
|
+
}
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
setActiveId(currentId ?? items[0]?.id ?? null);
|
|
7394
|
+
}, [items]);
|
|
7395
|
+
(0, import_react104.useEffect)(() => {
|
|
7396
|
+
handleScroll();
|
|
7397
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
7398
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
7399
|
+
}, [handleScroll]);
|
|
7400
|
+
return activeId;
|
|
7401
|
+
}
|
|
7402
|
+
|
|
7403
|
+
// src/components/toc-sidebar/toc-sidebar.tsx
|
|
7404
|
+
var import_jsx_runtime72 = require("react/jsx-runtime");
|
|
7405
|
+
function TocSidebar({ className }) {
|
|
7406
|
+
const editor = useEliaEditor();
|
|
7407
|
+
const items = useTocItems(editor);
|
|
7408
|
+
const activeId = useActiveHeading(items);
|
|
7409
|
+
const handleClick = (0, import_react105.useCallback)((id) => {
|
|
7410
|
+
const element = document.getElementById(id);
|
|
7411
|
+
if (element) {
|
|
7412
|
+
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
7413
|
+
}
|
|
7414
|
+
}, []);
|
|
7415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("nav", { className: cn("toc-sidebar", className), children: [
|
|
7416
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { className: "toc-sidebar-title", children: "\u76EE\u9304" }),
|
|
7417
|
+
items.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("ul", { className: "toc-sidebar-list", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
|
|
7418
|
+
"li",
|
|
7419
|
+
{
|
|
7420
|
+
className: cn(
|
|
7421
|
+
"toc-sidebar-item",
|
|
7422
|
+
`toc-sidebar-item--level-${item.level}`,
|
|
7423
|
+
activeId === item.id && "toc-sidebar-item--active"
|
|
7424
|
+
),
|
|
7425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
|
|
7426
|
+
"button",
|
|
7427
|
+
{
|
|
7428
|
+
type: "button",
|
|
7429
|
+
onClick: () => handleClick(item.id),
|
|
7430
|
+
className: "toc-sidebar-link",
|
|
7431
|
+
children: item.text
|
|
7432
|
+
}
|
|
7433
|
+
)
|
|
7434
|
+
},
|
|
7435
|
+
item.id
|
|
7436
|
+
)) })
|
|
7437
|
+
] });
|
|
7438
|
+
}
|
|
7439
|
+
|
|
6347
7440
|
// src/utils/content-renderer.ts
|
|
6348
|
-
var
|
|
7441
|
+
var import_core = require("@tiptap/core");
|
|
6349
7442
|
var import_starter_kit2 = __toESM(require("@tiptap/starter-kit"), 1);
|
|
6350
7443
|
var import_extension_text_align2 = require("@tiptap/extension-text-align");
|
|
6351
7444
|
var import_extension_list2 = require("@tiptap/extension-list");
|
|
@@ -6378,18 +7471,19 @@ var contentExtensions = [
|
|
|
6378
7471
|
import_extension_table_row2.TableRow,
|
|
6379
7472
|
import_extension_table_header2.TableHeader,
|
|
6380
7473
|
import_extension_table_cell2.TableCell,
|
|
6381
|
-
import_tiptap_extension_resize_image2.default.configure({ allowBase64: true })
|
|
6382
|
-
Callout
|
|
7474
|
+
import_tiptap_extension_resize_image2.default.configure({ allowBase64: true })
|
|
6383
7475
|
];
|
|
6384
7476
|
function generateEditorHTML(content) {
|
|
6385
|
-
return (0,
|
|
7477
|
+
return (0, import_core.generateHTML)(content, contentExtensions);
|
|
6386
7478
|
}
|
|
6387
7479
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6388
7480
|
0 && (module.exports = {
|
|
6389
|
-
Callout,
|
|
6390
7481
|
EliaEditor,
|
|
7482
|
+
EliaEditorProvider,
|
|
6391
7483
|
SimpleEditor,
|
|
7484
|
+
TocSidebar,
|
|
6392
7485
|
contentExtensions,
|
|
6393
|
-
generateEditorHTML
|
|
7486
|
+
generateEditorHTML,
|
|
7487
|
+
useEliaEditor
|
|
6394
7488
|
});
|
|
6395
7489
|
//# sourceMappingURL=index.cjs.map
|