@pilotiq/tiptap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/Block.d.ts +47 -0
- package/dist/Block.d.ts.map +1 -0
- package/dist/Block.js +56 -0
- package/dist/Block.js.map +1 -0
- package/dist/MentionProvider.d.ts +97 -0
- package/dist/MentionProvider.d.ts.map +1 -0
- package/dist/MentionProvider.js +104 -0
- package/dist/MentionProvider.js.map +1 -0
- package/dist/RichTextField.d.ts +286 -0
- package/dist/RichTextField.d.ts.map +1 -0
- package/dist/RichTextField.js +369 -0
- package/dist/RichTextField.js.map +1 -0
- package/dist/extensions/BlockNodeExtension.d.ts +41 -0
- package/dist/extensions/BlockNodeExtension.d.ts.map +1 -0
- package/dist/extensions/BlockNodeExtension.js +103 -0
- package/dist/extensions/BlockNodeExtension.js.map +1 -0
- package/dist/extensions/DragHandleExtension.d.ts +19 -0
- package/dist/extensions/DragHandleExtension.d.ts.map +1 -0
- package/dist/extensions/DragHandleExtension.js +166 -0
- package/dist/extensions/DragHandleExtension.js.map +1 -0
- package/dist/extensions/GridExtension.d.ts +49 -0
- package/dist/extensions/GridExtension.d.ts.map +1 -0
- package/dist/extensions/GridExtension.js +105 -0
- package/dist/extensions/GridExtension.js.map +1 -0
- package/dist/extensions/MentionExtension.d.ts +71 -0
- package/dist/extensions/MentionExtension.d.ts.map +1 -0
- package/dist/extensions/MentionExtension.js +165 -0
- package/dist/extensions/MentionExtension.js.map +1 -0
- package/dist/extensions/MergeTagExtension.d.ts +24 -0
- package/dist/extensions/MergeTagExtension.d.ts.map +1 -0
- package/dist/extensions/MergeTagExtension.js +57 -0
- package/dist/extensions/MergeTagExtension.js.map +1 -0
- package/dist/extensions/SlashCommandExtension.d.ts +71 -0
- package/dist/extensions/SlashCommandExtension.d.ts.map +1 -0
- package/dist/extensions/SlashCommandExtension.js +244 -0
- package/dist/extensions/SlashCommandExtension.js.map +1 -0
- package/dist/extensions/TextSizeMarks.d.ts +33 -0
- package/dist/extensions/TextSizeMarks.d.ts.map +1 -0
- package/dist/extensions/TextSizeMarks.js +47 -0
- package/dist/extensions/TextSizeMarks.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +25 -0
- package/dist/plugin.js.map +1 -0
- package/dist/react/BlockNodeView.d.ts +19 -0
- package/dist/react/BlockNodeView.d.ts.map +1 -0
- package/dist/react/BlockNodeView.js +60 -0
- package/dist/react/BlockNodeView.js.map +1 -0
- package/dist/react/BlockSidePanel.d.ts +105 -0
- package/dist/react/BlockSidePanel.d.ts.map +1 -0
- package/dist/react/BlockSidePanel.js +339 -0
- package/dist/react/BlockSidePanel.js.map +1 -0
- package/dist/react/FloatingToolbar.d.ts +13 -0
- package/dist/react/FloatingToolbar.d.ts.map +1 -0
- package/dist/react/FloatingToolbar.js +113 -0
- package/dist/react/FloatingToolbar.js.map +1 -0
- package/dist/react/MentionMenu.d.ts +26 -0
- package/dist/react/MentionMenu.d.ts.map +1 -0
- package/dist/react/MentionMenu.js +64 -0
- package/dist/react/MentionMenu.js.map +1 -0
- package/dist/react/Palette.d.ts +26 -0
- package/dist/react/Palette.d.ts.map +1 -0
- package/dist/react/Palette.js +21 -0
- package/dist/react/Palette.js.map +1 -0
- package/dist/react/SlashMenu.d.ts +24 -0
- package/dist/react/SlashMenu.d.ts.map +1 -0
- package/dist/react/SlashMenu.js +74 -0
- package/dist/react/SlashMenu.js.map +1 -0
- package/dist/react/TableFloatingToolbar.d.ts +7 -0
- package/dist/react/TableFloatingToolbar.d.ts.map +1 -0
- package/dist/react/TableFloatingToolbar.js +108 -0
- package/dist/react/TableFloatingToolbar.js.map +1 -0
- package/dist/react/TiptapEditor.d.ts +20 -0
- package/dist/react/TiptapEditor.d.ts.map +1 -0
- package/dist/react/TiptapEditor.js +398 -0
- package/dist/react/TiptapEditor.js.map +1 -0
- package/dist/react/Toolbar.d.ts +45 -0
- package/dist/react/Toolbar.d.ts.map +1 -0
- package/dist/react/Toolbar.js +204 -0
- package/dist/react/Toolbar.js.map +1 -0
- package/dist/react/toolbarButtons.d.ts +36 -0
- package/dist/react/toolbarButtons.d.ts.map +1 -0
- package/dist/react/toolbarButtons.js +300 -0
- package/dist/react/toolbarButtons.js.map +1 -0
- package/dist/register.d.ts +20 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +27 -0
- package/dist/register.js.map +1 -0
- package/dist/render.d.ts +89 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +439 -0
- package/dist/render.js.map +1 -0
- package/package.json +92 -0
- package/src/Block.ts +75 -0
- package/src/MentionProvider.ts +153 -0
- package/src/RichTextField.test.ts +447 -0
- package/src/RichTextField.ts +508 -0
- package/src/extensions/BlockNodeExtension.ts +134 -0
- package/src/extensions/DragHandleExtension.ts +184 -0
- package/src/extensions/GridExtension.test.ts +31 -0
- package/src/extensions/GridExtension.ts +138 -0
- package/src/extensions/MentionExtension.ts +248 -0
- package/src/extensions/MergeTagExtension.ts +75 -0
- package/src/extensions/SlashCommandExtension.test.ts +147 -0
- package/src/extensions/SlashCommandExtension.ts +332 -0
- package/src/extensions/TextSizeMarks.ts +73 -0
- package/src/index.ts +28 -0
- package/src/plugin.test.ts +19 -0
- package/src/plugin.ts +26 -0
- package/src/react/BlockNodeView.tsx +99 -0
- package/src/react/BlockSidePanel.test.ts +412 -0
- package/src/react/BlockSidePanel.tsx +451 -0
- package/src/react/FloatingToolbar.tsx +304 -0
- package/src/react/MentionMenu.tsx +120 -0
- package/src/react/Palette.tsx +86 -0
- package/src/react/SlashMenu.tsx +129 -0
- package/src/react/TableFloatingToolbar.tsx +154 -0
- package/src/react/TiptapEditor.tsx +535 -0
- package/src/react/Toolbar.tsx +438 -0
- package/src/react/toolbarButtons.tsx +579 -0
- package/src/register.test.ts +14 -0
- package/src/register.ts +27 -0
- package/src/render.test.ts +745 -0
- package/src/render.ts +480 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Tooltip } from '@base-ui/react/tooltip';
|
|
4
|
+
import { Dialog } from '@base-ui/react/dialog';
|
|
5
|
+
import { TOOLBAR_BUTTONS } from './toolbarButtons.js';
|
|
6
|
+
import { Palette } from './Palette.js';
|
|
7
|
+
/**
|
|
8
|
+
* Always-on toolbar rendered above the editor. Groups are joined with thin
|
|
9
|
+
* dividers; unknown / unavailable button ids are silently dropped so config
|
|
10
|
+
* can target later-phase buttons today without crashing.
|
|
11
|
+
*
|
|
12
|
+
* Inline link button opens a Base UI dialog with a URL input — kept here
|
|
13
|
+
* (not in the FloatingToolbar's dialog) because each toolbar instance owns
|
|
14
|
+
* its own React state for the modal.
|
|
15
|
+
*/
|
|
16
|
+
export function Toolbar({ editor, groups, tick, textColors, customTextColors, highlightColors, onAttachOpenChange, }) {
|
|
17
|
+
const [linkOpen, setLinkOpen] = useState(false);
|
|
18
|
+
const [linkUrl, setLinkUrl] = useState('');
|
|
19
|
+
const filteredGroups = groups
|
|
20
|
+
.map((g) => g.map((id) => TOOLBAR_BUTTONS[id]).filter((b) => Boolean(b?.available)))
|
|
21
|
+
.filter((g) => g.length > 0);
|
|
22
|
+
if (filteredGroups.length === 0)
|
|
23
|
+
return null;
|
|
24
|
+
const openLinkDialog = () => {
|
|
25
|
+
const previousUrl = editor.getAttributes('link')['href'];
|
|
26
|
+
setLinkUrl(previousUrl ?? '');
|
|
27
|
+
setLinkOpen(true);
|
|
28
|
+
};
|
|
29
|
+
const applyLink = () => {
|
|
30
|
+
setLinkOpen(false);
|
|
31
|
+
const trimmed = linkUrl.trim();
|
|
32
|
+
if (trimmed === '') {
|
|
33
|
+
editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
editor.chain().focus().extendMarkRange('link').setLink({ href: trimmed }).run();
|
|
37
|
+
};
|
|
38
|
+
const removeLink = () => {
|
|
39
|
+
setLinkOpen(false);
|
|
40
|
+
editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
41
|
+
};
|
|
42
|
+
return (_jsxs(_Fragment, { children: [_jsx(Tooltip.Provider, { delay: 400, children: _jsx("div", {
|
|
43
|
+
// The toolbar sits visually attached to the top of the editor body —
|
|
44
|
+
// shared border-radius + a `border-b` on the toolbar gives a single
|
|
45
|
+
// grouped look. Editor body's own border-radius is reset top-side
|
|
46
|
+
// via global classnames in the editor.
|
|
47
|
+
className: "flex flex-wrap items-center gap-0.5 rounded-t-md border border-input bg-muted/30 px-1 py-1 text-foreground",
|
|
48
|
+
// Mousedown anywhere in the toolbar should not steal focus from the
|
|
49
|
+
// editor — `preventDefault` keeps the selection alive while the
|
|
50
|
+
// command runs.
|
|
51
|
+
onMouseDown: (e) => { e.preventDefault(); }, "data-tick": tick, children: filteredGroups.map((group, gi) => (_jsxs("div", { className: "flex items-center gap-0.5", children: [gi > 0 && _jsx("span", { "aria-hidden": true, className: "mx-1 h-5 w-px shrink-0 bg-border" }), group.map((btn) => {
|
|
52
|
+
if (btn.custom === 'textColor') {
|
|
53
|
+
return (_jsx(Palette, { swatches: textColors, custom: customTextColors, activeColor: editor.getAttributes('textStyle')['color'], onPick: (value) => editor.chain().focus().setColor(value).run(), onClear: () => editor.chain().focus().unsetColor().run(), clearLabel: "Reset color", trigger: _jsx(ToolbarButton, { def: btn, editor: editor }) }, btn.id));
|
|
54
|
+
}
|
|
55
|
+
if (btn.custom === 'highlight') {
|
|
56
|
+
return (_jsx(Palette, { swatches: highlightColors, custom: false, activeColor: editor.getAttributes('highlight')['color'], onPick: (value) => editor.chain().focus().toggleHighlight({ color: value }).run(), onClear: () => editor.chain().focus().unsetHighlight().run(), clearLabel: "Remove highlight", trigger: _jsx(ToolbarButton, { def: btn, editor: editor }) }, btn.id));
|
|
57
|
+
}
|
|
58
|
+
const customClick = btn.custom === 'link' ? openLinkDialog :
|
|
59
|
+
btn.custom === 'attachFiles' ? () => onAttachOpenChange(true) :
|
|
60
|
+
undefined;
|
|
61
|
+
return (_jsx(ToolbarButton, { def: btn, editor: editor, onCustomClick: customClick }, btn.id));
|
|
62
|
+
})] }, gi))) }) }), _jsx(LinkDialog, { open: linkOpen, onOpenChange: setLinkOpen, url: linkUrl, onUrlChange: setLinkUrl, onApply: applyLink, onRemove: editor.isActive('link') ? removeLink : null, isEdit: editor.isActive('link') })] }));
|
|
63
|
+
}
|
|
64
|
+
function ToolbarButton({ def, editor, onCustomClick }) {
|
|
65
|
+
const active = def.isActive?.(editor) ?? false;
|
|
66
|
+
const disabled = def.isDisabled?.(editor) ?? false;
|
|
67
|
+
const mod = isMac() ? '⌘' : 'Ctrl+';
|
|
68
|
+
const tooltip = def.shortcut ? `${def.label} (${mod}${def.shortcut})` : def.label;
|
|
69
|
+
return (_jsxs(Tooltip.Root, { children: [_jsx(Tooltip.Trigger, { render: (props) => (_jsx("button", { ...props, type: "button", disabled: disabled, onClick: () => {
|
|
70
|
+
if (onCustomClick) {
|
|
71
|
+
onCustomClick();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
def.command(editor);
|
|
75
|
+
}, className: `inline-flex h-7 w-7 items-center justify-center rounded text-foreground transition-colors disabled:opacity-50 disabled:pointer-events-none ${active ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/60'}`, "aria-label": def.label, "aria-pressed": active, children: def.icon })) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Positioner, { side: "bottom", sideOffset: 6, className: "isolate z-50", children: _jsx(Tooltip.Popup, { className: "rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-md data-[side=bottom]:slide-in-from-top-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", children: tooltip }) }) })] }));
|
|
76
|
+
}
|
|
77
|
+
export function AttachFilesDialog({ open, onOpenChange, editor, uploadUrl, fieldName, acceptedFileTypes, maxFileSize, directory, visibility, }) {
|
|
78
|
+
const [file, setFile] = useState(null);
|
|
79
|
+
const [alt, setAlt] = useState('');
|
|
80
|
+
const [busy, setBusy] = useState(false);
|
|
81
|
+
const [error, setError] = useState(null);
|
|
82
|
+
// Reset transient state every time the dialog opens — otherwise the
|
|
83
|
+
// previous upload's filename / alt text would leak into the next one.
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (open) {
|
|
86
|
+
setFile(null);
|
|
87
|
+
setAlt('');
|
|
88
|
+
setError(null);
|
|
89
|
+
setBusy(false);
|
|
90
|
+
}
|
|
91
|
+
}, [open]);
|
|
92
|
+
const accept = acceptedFileTypes?.join(',') ?? 'image/*';
|
|
93
|
+
const onPickFile = (f) => {
|
|
94
|
+
setError(null);
|
|
95
|
+
if (!f) {
|
|
96
|
+
setFile(null);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (maxFileSize !== undefined && f.size > maxFileSize) {
|
|
100
|
+
setError(`File exceeds the maximum size of ${formatBytes(maxFileSize)}.`);
|
|
101
|
+
setFile(null);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
setFile(f);
|
|
105
|
+
if (alt === '')
|
|
106
|
+
setAlt(stripExtension(f.name));
|
|
107
|
+
};
|
|
108
|
+
const onUpload = async () => {
|
|
109
|
+
if (!file)
|
|
110
|
+
return;
|
|
111
|
+
if (!uploadUrl) {
|
|
112
|
+
setError('No upload route configured for this panel.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
setBusy(true);
|
|
116
|
+
setError(null);
|
|
117
|
+
try {
|
|
118
|
+
const fd = new FormData();
|
|
119
|
+
fd.append('file', file);
|
|
120
|
+
fd.append('fieldName', fieldName);
|
|
121
|
+
if (directory)
|
|
122
|
+
fd.append('directory', directory);
|
|
123
|
+
if (visibility)
|
|
124
|
+
fd.append('visibility', visibility);
|
|
125
|
+
const res = await fetch(uploadUrl, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
body: fd,
|
|
128
|
+
headers: { Accept: 'application/json' },
|
|
129
|
+
});
|
|
130
|
+
const data = await res.json().catch(() => ({}));
|
|
131
|
+
if (!res.ok || !data.ok || !data.url) {
|
|
132
|
+
setError(data.error ?? `Upload failed (status ${res.status}).`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Image vs non-image file. Tiptap's StarterKit doesn't ship a generic
|
|
136
|
+
// file/attachment node, so for non-images we fall back to inserting
|
|
137
|
+
// a link mark on the filename — same shape MarkdownField uses.
|
|
138
|
+
if (file.type.startsWith('image/')) {
|
|
139
|
+
editor.chain().focus().setImage({ src: data.url, alt: alt || file.name }).run();
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
editor.chain().focus()
|
|
143
|
+
.insertContent({
|
|
144
|
+
type: 'text',
|
|
145
|
+
text: alt || file.name,
|
|
146
|
+
marks: [{ type: 'link', attrs: { href: data.url } }],
|
|
147
|
+
})
|
|
148
|
+
.run();
|
|
149
|
+
}
|
|
150
|
+
onOpenChange(false);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
setBusy(false);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-150 data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(Dialog.Popup, { className: "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] sm:max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg transition-[opacity,transform] duration-150 data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[ending-style]:scale-95 data-[ending-style]:opacity-0", children: [_jsx(Dialog.Title, { className: "text-lg leading-none font-semibold", children: "Attach file" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("input", { type: "file", accept: accept, disabled: busy, onChange: (e) => onPickFile(e.target.files?.[0] ?? null), className: "block w-full text-sm file:me-3 file:rounded-md file:border-0 file:bg-muted file:px-3 file:py-1.5 file:text-sm file:font-medium hover:file:bg-muted/80" }), file && (_jsx("input", { type: "text", value: alt, onChange: (e) => setAlt(e.target.value), placeholder: "Alt text (described for screen readers)", disabled: busy, className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })), maxFileSize !== undefined && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Maximum size: ", formatBytes(maxFileSize), "."] })), error && (_jsx("p", { className: "text-xs text-destructive", children: error }))] }), _jsxs("div", { className: "flex flex-row-reverse items-center gap-2", children: [_jsx("button", { type: "button", onClick: onUpload, disabled: !file || busy, className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:pointer-events-none", children: busy ? 'Uploading…' : 'Insert' }), _jsx("button", { type: "button", onClick: () => onOpenChange(false), disabled: busy, className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground disabled:opacity-50", children: "Cancel" })] })] })] }) }));
|
|
160
|
+
}
|
|
161
|
+
function stripExtension(name) {
|
|
162
|
+
const i = name.lastIndexOf('.');
|
|
163
|
+
return i > 0 ? name.slice(0, i) : name;
|
|
164
|
+
}
|
|
165
|
+
function formatBytes(bytes) {
|
|
166
|
+
if (bytes < 1024)
|
|
167
|
+
return `${bytes} B`;
|
|
168
|
+
if (bytes < 1024 * 1024)
|
|
169
|
+
return `${(bytes / 1024).toFixed(0)} KB`;
|
|
170
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
171
|
+
}
|
|
172
|
+
function LinkDialog({ open, onOpenChange, url, onUrlChange, onApply, onRemove, isEdit, }) {
|
|
173
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-150 data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(Dialog.Popup, { className: "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] sm:max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg transition-[opacity,transform] duration-150 data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[ending-style]:scale-95 data-[ending-style]:opacity-0", children: [_jsx(Dialog.Title, { className: "text-lg leading-none font-semibold", children: isEdit ? 'Edit link' : 'Add link' }), _jsx("input", { type: "url", value: url, onChange: (e) => onUrlChange(e.target.value), onKeyDown: (e) => {
|
|
174
|
+
if (e.key === 'Enter') {
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
onApply();
|
|
177
|
+
}
|
|
178
|
+
}, placeholder: "https://example.com", autoFocus: true, className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" }), _jsxs("div", { className: "flex flex-row-reverse items-center gap-2", children: [_jsx("button", { type: "button", onClick: onApply, className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90", children: isEdit ? 'Update' : 'Add' }), _jsx("button", { type: "button", onClick: () => onOpenChange(false), className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground", children: "Cancel" }), onRemove && (_jsx("button", { type: "button", onClick: onRemove, className: "me-auto inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-destructive hover:bg-destructive/10", children: "Remove link" }))] })] })] }) }));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Hook returning a tick that bumps on any selection / transaction change.
|
|
182
|
+
* The Toolbar renders dependent on this so active-state booleans stay fresh.
|
|
183
|
+
*/
|
|
184
|
+
export function useEditorTick(editor) {
|
|
185
|
+
const [tick, setTick] = useState(0);
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (!editor)
|
|
188
|
+
return;
|
|
189
|
+
const bump = () => setTick((t) => t + 1);
|
|
190
|
+
editor.on('selectionUpdate', bump);
|
|
191
|
+
editor.on('transaction', bump);
|
|
192
|
+
return () => {
|
|
193
|
+
editor.off('selectionUpdate', bump);
|
|
194
|
+
editor.off('transaction', bump);
|
|
195
|
+
};
|
|
196
|
+
}, [editor]);
|
|
197
|
+
return tick;
|
|
198
|
+
}
|
|
199
|
+
function isMac() {
|
|
200
|
+
if (typeof navigator === 'undefined')
|
|
201
|
+
return false;
|
|
202
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=Toolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toolbar.js","sourceRoot":"","sources":["../../src/react/Toolbar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Editor } from '@tiptap/core';
|
|
3
|
+
import type { ToolbarButtonId } from '../RichTextField.js';
|
|
4
|
+
/**
|
|
5
|
+
* One toolbar button's behavior. The renderer wires `<button>` chrome — this
|
|
6
|
+
* descriptor only knows the editor command, the active-state predicate, and
|
|
7
|
+
* the icon.
|
|
8
|
+
*
|
|
9
|
+
* Buttons that depend on extensions registered in later phases (textColor,
|
|
10
|
+
* highlight, attachFiles, table*) are registered with `available: false`
|
|
11
|
+
* until that phase ships, so config that targets them today is silently
|
|
12
|
+
* dropped instead of crashing.
|
|
13
|
+
*/
|
|
14
|
+
export interface ToolbarButtonDef {
|
|
15
|
+
id: ToolbarButtonId;
|
|
16
|
+
label: string;
|
|
17
|
+
shortcut?: string;
|
|
18
|
+
icon: ReactNode;
|
|
19
|
+
/** Phase-A buttons set this to `true`; later-phase placeholders set `false`. */
|
|
20
|
+
available: boolean;
|
|
21
|
+
/** Whether the button reflects the editor's "currently active" state. */
|
|
22
|
+
isActive?: (editor: Editor) => boolean;
|
|
23
|
+
/** Whether the button is disabled given the current selection. */
|
|
24
|
+
isDisabled?: (editor: Editor) => boolean;
|
|
25
|
+
/** Run on click. */
|
|
26
|
+
command: (editor: Editor) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Optional opt-in for buttons whose behavior is "open a UI" rather than
|
|
29
|
+
* fire a chain — the editor surface keeps its own state for these and the
|
|
30
|
+
* default click handler doesn't run a chain.
|
|
31
|
+
*/
|
|
32
|
+
custom?: 'link' | 'textColor' | 'highlight' | 'attachFiles';
|
|
33
|
+
}
|
|
34
|
+
/** Lookup table for every recognized button id. */
|
|
35
|
+
export declare const TOOLBAR_BUTTONS: Record<ToolbarButtonId, ToolbarButtonDef>;
|
|
36
|
+
//# sourceMappingURL=toolbarButtons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbarButtons.d.ts","sourceRoot":"","sources":["../../src/react/toolbarButtons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAS,eAAe,CAAA;IAC1B,KAAK,EAAM,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAO,SAAS,CAAA;IACpB,gFAAgF;IAChF,SAAS,EAAE,OAAO,CAAA;IAClB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACtC,kEAAkE;IAClE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACxC,oBAAoB;IACpB,OAAO,EAAI,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,CAAA;CAC5D;AAiTD,mDAAmD;AACnD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAyNrE,CAAA"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const ICON_PROPS = {
|
|
3
|
+
width: 14, height: 14, viewBox: '0 0 24 24',
|
|
4
|
+
fill: 'none', stroke: 'currentColor',
|
|
5
|
+
strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
|
|
6
|
+
'aria-hidden': 'true',
|
|
7
|
+
};
|
|
8
|
+
// Inline SVGs (lucide.dev paths). Kept inline so the package doesn't pull
|
|
9
|
+
// `lucide-react` as a peer dep.
|
|
10
|
+
const Icons = {
|
|
11
|
+
bold: (_jsxs("svg", { ...ICON_PROPS, strokeWidth: 2.25, children: [_jsx("path", { d: "M6 12h9a4 4 0 0 1 0 8H6Z" }), _jsx("path", { d: "M6 4h7a4 4 0 0 1 0 8H6Z" })] })),
|
|
12
|
+
italic: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "19", y1: "4", x2: "10", y2: "4" }), _jsx("line", { x1: "14", y1: "20", x2: "5", y2: "20" }), _jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" })] })),
|
|
13
|
+
underline: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M6 4v6a6 6 0 0 0 12 0V4" }), _jsx("line", { x1: "4", y1: "20", x2: "20", y2: "20" })] })),
|
|
14
|
+
strike: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M16 4H9a3 3 0 0 0-2.83 4" }), _jsx("path", { d: "M14 12a4 4 0 0 1 0 8H6" }), _jsx("line", { x1: "4", y1: "12", x2: "20", y2: "12" })] })),
|
|
15
|
+
subscript: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m4 5 8 10" }), _jsx("path", { d: "m12 5-8 10" }), _jsx("path", { d: "M20 21h-4c0-1.5.44-2 1.5-2.5S20 17.33 20 16c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })] })),
|
|
16
|
+
superscript: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m4 19 8-10" }), _jsx("path", { d: "m12 19-8-10" }), _jsx("path", { d: "M20 11h-4c0-1.5.44-2 1.5-2.5S20 7.33 20 6c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })] })),
|
|
17
|
+
code: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("polyline", { points: "16 18 22 12 16 6" }), _jsx("polyline", { points: "8 6 2 12 8 18" })] })),
|
|
18
|
+
paragraph: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M13 4v16" }), _jsx("path", { d: "M17 4v16" }), _jsx("path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" })] })),
|
|
19
|
+
h1: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H1" }),
|
|
20
|
+
h2: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H2" }),
|
|
21
|
+
h3: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H3" }),
|
|
22
|
+
h4: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H4" }),
|
|
23
|
+
h5: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H5" }),
|
|
24
|
+
h6: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H6" }),
|
|
25
|
+
// Sized text-string glyphs match the visual contrast of the marks they
|
|
26
|
+
// toggle — lead is the larger of the pair, small is the slightly smaller.
|
|
27
|
+
lead: _jsx("span", { className: "text-sm font-semibold leading-none tracking-tight", children: "P+" }),
|
|
28
|
+
small: _jsx("span", { className: "text-[10px] font-semibold leading-none tracking-tight", children: "P-" }),
|
|
29
|
+
alignStart: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "3", y1: "12", x2: "15", y2: "12" }), _jsx("line", { x1: "3", y1: "18", x2: "18", y2: "18" })] })),
|
|
30
|
+
alignCenter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "6", y1: "12", x2: "18", y2: "12" }), _jsx("line", { x1: "4", y1: "18", x2: "20", y2: "18" })] })),
|
|
31
|
+
alignEnd: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "9", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "6", y1: "18", x2: "21", y2: "18" })] })),
|
|
32
|
+
alignJustify: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })] })),
|
|
33
|
+
blockquote: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" }), _jsx("path", { d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" })] })),
|
|
34
|
+
codeBlock: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("polyline", { points: "10 10 7 13 10 16" }), _jsx("polyline", { points: "14 10 17 13 14 16" })] })),
|
|
35
|
+
bulletList: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), _jsx("circle", { cx: "4", cy: "6", r: "1", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "12", r: "1", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "18", r: "1", fill: "currentColor" })] })),
|
|
36
|
+
orderedList: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "10", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "10", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "10", y1: "18", x2: "21", y2: "18" }), _jsx("path", { d: "M4 6h1v4" }), _jsx("path", { d: "M4 10h2" }), _jsx("path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })] })),
|
|
37
|
+
horizontalRule: (_jsx("svg", { ...ICON_PROPS, children: _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }) })),
|
|
38
|
+
clearFormatting: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M4 7V4h16v3" }), _jsx("line", { x1: "5", y1: "20", x2: "21", y2: "20" }), _jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" }), _jsx("line", { x1: "3", y1: "3", x2: "21", y2: "21" })] })),
|
|
39
|
+
link: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }), _jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })] })),
|
|
40
|
+
textColor: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M5 21h14" }), _jsx("path", { d: "m6 18 6-13 6 13" }), _jsx("path", { d: "M9 13h6" })] })),
|
|
41
|
+
highlight: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m9 11-6 6v3h3l6-6" }), _jsx("path", { d: "M22 12 12 2l-2 2 10 10z" }), _jsx("path", { d: "m14 6 6 6" })] })),
|
|
42
|
+
attachFiles: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("circle", { cx: "9", cy: "9", r: "2" }), _jsx("path", { d: "m21 15-5-5L5 21" })] })),
|
|
43
|
+
table: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }), _jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" })] })),
|
|
44
|
+
tableAddColumnBefore: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "11", y: "3", width: "10", height: "18", rx: "1" }), _jsx("line", { x1: "6", y1: "12", x2: "2", y2: "12" }), _jsx("line", { x1: "4", y1: "10", x2: "4", y2: "14" })] })),
|
|
45
|
+
tableAddColumnAfter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "10", height: "18", rx: "1" }), _jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), _jsx("line", { x1: "20", y1: "10", x2: "20", y2: "14" })] })),
|
|
46
|
+
tableDeleteColumn: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }), _jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" }), _jsx("line", { x1: "10", y1: "9", x2: "14", y2: "13" }), _jsx("line", { x1: "14", y1: "9", x2: "10", y2: "13" })] })),
|
|
47
|
+
tableAddRowBefore: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "1" }), _jsx("line", { x1: "12", y1: "6", x2: "12", y2: "2" }), _jsx("line", { x1: "10", y1: "4", x2: "14", y2: "4" })] })),
|
|
48
|
+
tableAddRowAfter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "10", rx: "1" }), _jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), _jsx("line", { x1: "10", y1: "20", x2: "14", y2: "20" })] })),
|
|
49
|
+
tableDeleteRow: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "9", y1: "11", x2: "13", y2: "13" }), _jsx("line", { x1: "13", y1: "11", x2: "9", y2: "13" })] })),
|
|
50
|
+
tableMergeCells: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "12", x2: "9", y2: "12" }), _jsx("line", { x1: "15", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "9" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "21" })] })),
|
|
51
|
+
tableSplitCell: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "21" })] })),
|
|
52
|
+
tableToggleHeaderRow: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("rect", { x: "3", y: "3", width: "18", height: "6", fill: "currentColor", opacity: "0.25", stroke: "none" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" })] })),
|
|
53
|
+
tableToggleHeaderCell: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("rect", { x: "3", y: "3", width: "9", height: "6", fill: "currentColor", opacity: "0.25", stroke: "none" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "21" })] })),
|
|
54
|
+
tableDelete: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }), _jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })] })),
|
|
55
|
+
undo: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M3 7v6h6" }), _jsx("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" })] })),
|
|
56
|
+
redo: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M21 7v6h-6" }), _jsx("path", { d: "M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13" })] })),
|
|
57
|
+
details: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("polyline", { points: "6 9 12 15 18 9" }), _jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }), _jsx("line", { x1: "3", y1: "20", x2: "21", y2: "20" })] })),
|
|
58
|
+
grid: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "4", width: "8", height: "16", rx: "1" }), _jsx("rect", { x: "13", y: "4", width: "8", height: "16", rx: "1" })] })),
|
|
59
|
+
gridDelete: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "4", width: "8", height: "16", rx: "1" }), _jsx("rect", { x: "13", y: "4", width: "8", height: "16", rx: "1" }), _jsx("line", { x1: "2", y1: "22", x2: "22", y2: "2" })] })),
|
|
60
|
+
};
|
|
61
|
+
/** Lookup table for every recognized button id. */
|
|
62
|
+
export const TOOLBAR_BUTTONS = {
|
|
63
|
+
bold: {
|
|
64
|
+
id: 'bold', label: 'Bold', shortcut: 'B', available: true, icon: Icons.bold,
|
|
65
|
+
isActive: (ed) => ed.isActive('bold'),
|
|
66
|
+
command: (ed) => { ed.chain().focus().toggleBold().run(); },
|
|
67
|
+
},
|
|
68
|
+
italic: {
|
|
69
|
+
id: 'italic', label: 'Italic', shortcut: 'I', available: true, icon: Icons.italic,
|
|
70
|
+
isActive: (ed) => ed.isActive('italic'),
|
|
71
|
+
command: (ed) => { ed.chain().focus().toggleItalic().run(); },
|
|
72
|
+
},
|
|
73
|
+
underline: {
|
|
74
|
+
id: 'underline', label: 'Underline', shortcut: 'U', available: true, icon: Icons.underline,
|
|
75
|
+
isActive: (ed) => ed.isActive('underline'),
|
|
76
|
+
command: (ed) => { ed.chain().focus().toggleUnderline().run(); },
|
|
77
|
+
},
|
|
78
|
+
strike: {
|
|
79
|
+
id: 'strike', label: 'Strikethrough', shortcut: '⇧X', available: true, icon: Icons.strike,
|
|
80
|
+
isActive: (ed) => ed.isActive('strike'),
|
|
81
|
+
command: (ed) => { ed.chain().focus().toggleStrike().run(); },
|
|
82
|
+
},
|
|
83
|
+
subscript: {
|
|
84
|
+
id: 'subscript', label: 'Subscript', shortcut: ',', available: true, icon: Icons.subscript,
|
|
85
|
+
isActive: (ed) => ed.isActive('subscript'),
|
|
86
|
+
command: (ed) => { ed.chain().focus().toggleSubscript().run(); },
|
|
87
|
+
},
|
|
88
|
+
superscript: {
|
|
89
|
+
id: 'superscript', label: 'Superscript', shortcut: '.', available: true, icon: Icons.superscript,
|
|
90
|
+
isActive: (ed) => ed.isActive('superscript'),
|
|
91
|
+
command: (ed) => { ed.chain().focus().toggleSuperscript().run(); },
|
|
92
|
+
},
|
|
93
|
+
code: {
|
|
94
|
+
id: 'code', label: 'Inline code', shortcut: 'E', available: true, icon: Icons.code,
|
|
95
|
+
isActive: (ed) => ed.isActive('code'),
|
|
96
|
+
command: (ed) => { ed.chain().focus().toggleCode().run(); },
|
|
97
|
+
},
|
|
98
|
+
lead: {
|
|
99
|
+
id: 'lead', label: 'Lead', available: true, icon: Icons.lead,
|
|
100
|
+
isActive: (ed) => ed.isActive('lead'),
|
|
101
|
+
// `toggleMark` is the safest call — `chain().toggleLead()` would require
|
|
102
|
+
// the command type to be visible to the chain at compile time, and the
|
|
103
|
+
// marks ship from a sibling extension file the toolbar can't see typewise.
|
|
104
|
+
command: (ed) => { ed.chain().focus().toggleMark('lead').run(); },
|
|
105
|
+
},
|
|
106
|
+
small: {
|
|
107
|
+
id: 'small', label: 'Small', available: true, icon: Icons.small,
|
|
108
|
+
isActive: (ed) => ed.isActive('small'),
|
|
109
|
+
command: (ed) => { ed.chain().focus().toggleMark('small').run(); },
|
|
110
|
+
},
|
|
111
|
+
paragraph: {
|
|
112
|
+
id: 'paragraph', label: 'Paragraph', available: true, icon: Icons.paragraph,
|
|
113
|
+
isActive: (ed) => ed.isActive('paragraph'),
|
|
114
|
+
command: (ed) => { ed.chain().focus().setParagraph().run(); },
|
|
115
|
+
},
|
|
116
|
+
h1: makeHeading(1, Icons.h1),
|
|
117
|
+
h2: makeHeading(2, Icons.h2),
|
|
118
|
+
h3: makeHeading(3, Icons.h3),
|
|
119
|
+
h4: makeHeading(4, Icons.h4),
|
|
120
|
+
h5: makeHeading(5, Icons.h5),
|
|
121
|
+
h6: makeHeading(6, Icons.h6),
|
|
122
|
+
alignStart: makeAlign('left', 'Align left', Icons.alignStart),
|
|
123
|
+
alignCenter: makeAlign('center', 'Align center', Icons.alignCenter),
|
|
124
|
+
alignEnd: makeAlign('right', 'Align right', Icons.alignEnd),
|
|
125
|
+
alignJustify: makeAlign('justify', 'Justify', Icons.alignJustify),
|
|
126
|
+
blockquote: {
|
|
127
|
+
id: 'blockquote', label: 'Quote', available: true, icon: Icons.blockquote,
|
|
128
|
+
isActive: (ed) => ed.isActive('blockquote'),
|
|
129
|
+
command: (ed) => { ed.chain().focus().toggleBlockquote().run(); },
|
|
130
|
+
},
|
|
131
|
+
codeBlock: {
|
|
132
|
+
id: 'codeBlock', label: 'Code block', available: true, icon: Icons.codeBlock,
|
|
133
|
+
isActive: (ed) => ed.isActive('codeBlock'),
|
|
134
|
+
command: (ed) => { ed.chain().focus().toggleCodeBlock().run(); },
|
|
135
|
+
},
|
|
136
|
+
bulletList: {
|
|
137
|
+
id: 'bulletList', label: 'Bullet list', available: true, icon: Icons.bulletList,
|
|
138
|
+
isActive: (ed) => ed.isActive('bulletList'),
|
|
139
|
+
command: (ed) => { ed.chain().focus().toggleBulletList().run(); },
|
|
140
|
+
},
|
|
141
|
+
orderedList: {
|
|
142
|
+
id: 'orderedList', label: 'Numbered list', available: true, icon: Icons.orderedList,
|
|
143
|
+
isActive: (ed) => ed.isActive('orderedList'),
|
|
144
|
+
command: (ed) => { ed.chain().focus().toggleOrderedList().run(); },
|
|
145
|
+
},
|
|
146
|
+
horizontalRule: {
|
|
147
|
+
id: 'horizontalRule', label: 'Divider', available: true, icon: Icons.horizontalRule,
|
|
148
|
+
command: (ed) => { ed.chain().focus().setHorizontalRule().run(); },
|
|
149
|
+
},
|
|
150
|
+
clearFormatting: {
|
|
151
|
+
id: 'clearFormatting', label: 'Clear formatting', available: true, icon: Icons.clearFormatting,
|
|
152
|
+
command: (ed) => { ed.chain().focus().clearNodes().unsetAllMarks().run(); },
|
|
153
|
+
},
|
|
154
|
+
link: {
|
|
155
|
+
id: 'link', label: 'Link', shortcut: 'K', available: true, icon: Icons.link, custom: 'link',
|
|
156
|
+
isActive: (ed) => ed.isActive('link'),
|
|
157
|
+
// Click-handling lives in the toolbar itself — the dialog needs React state.
|
|
158
|
+
command: () => { },
|
|
159
|
+
},
|
|
160
|
+
undo: {
|
|
161
|
+
id: 'undo', label: 'Undo', shortcut: 'Z', available: true, icon: Icons.undo,
|
|
162
|
+
isDisabled: (ed) => !ed.can().undo(),
|
|
163
|
+
command: (ed) => { ed.chain().focus().undo().run(); },
|
|
164
|
+
},
|
|
165
|
+
redo: {
|
|
166
|
+
id: 'redo', label: 'Redo', shortcut: '⇧Z', available: true, icon: Icons.redo,
|
|
167
|
+
isDisabled: (ed) => !ed.can().redo(),
|
|
168
|
+
command: (ed) => { ed.chain().focus().redo().run(); },
|
|
169
|
+
},
|
|
170
|
+
textColor: {
|
|
171
|
+
id: 'textColor', label: 'Text color', available: true, icon: Icons.textColor, custom: 'textColor',
|
|
172
|
+
isActive: (ed) => Boolean(ed.getAttributes('textStyle')['color']),
|
|
173
|
+
command: () => { },
|
|
174
|
+
},
|
|
175
|
+
highlight: {
|
|
176
|
+
id: 'highlight', label: 'Highlight', available: true, icon: Icons.highlight, custom: 'highlight',
|
|
177
|
+
isActive: (ed) => ed.isActive('highlight'),
|
|
178
|
+
command: () => { },
|
|
179
|
+
},
|
|
180
|
+
attachFiles: {
|
|
181
|
+
id: 'attachFiles', label: 'Attach files', available: true, icon: Icons.attachFiles, custom: 'attachFiles',
|
|
182
|
+
// Click-handling lives in the toolbar — opens a Base UI dialog driven
|
|
183
|
+
// by React state (file picker + alt text + upload progress).
|
|
184
|
+
command: () => { },
|
|
185
|
+
},
|
|
186
|
+
// ---- Tables (Phase F). ----------------------------------------------------
|
|
187
|
+
// The "insert table" button is always enabled (creates a table at the cursor);
|
|
188
|
+
// every cell-action button is gated on `inTable` so the user can't run a
|
|
189
|
+
// no-op command outside a table. Tiptap returns `false` from these commands
|
|
190
|
+
// when the cursor is elsewhere — `can()` is the canonical check.
|
|
191
|
+
table: {
|
|
192
|
+
id: 'table', label: 'Insert table', available: true, icon: Icons.table,
|
|
193
|
+
command: (ed) => {
|
|
194
|
+
ed.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
tableAddColumnBefore: {
|
|
198
|
+
id: 'tableAddColumnBefore', label: 'Add column before', available: true, icon: Icons.tableAddColumnBefore,
|
|
199
|
+
isDisabled: (ed) => !ed.can().addColumnBefore(),
|
|
200
|
+
command: (ed) => { ed.chain().focus().addColumnBefore().run(); },
|
|
201
|
+
},
|
|
202
|
+
tableAddColumnAfter: {
|
|
203
|
+
id: 'tableAddColumnAfter', label: 'Add column after', available: true, icon: Icons.tableAddColumnAfter,
|
|
204
|
+
isDisabled: (ed) => !ed.can().addColumnAfter(),
|
|
205
|
+
command: (ed) => { ed.chain().focus().addColumnAfter().run(); },
|
|
206
|
+
},
|
|
207
|
+
tableDeleteColumn: {
|
|
208
|
+
id: 'tableDeleteColumn', label: 'Delete column', available: true, icon: Icons.tableDeleteColumn,
|
|
209
|
+
isDisabled: (ed) => !ed.can().deleteColumn(),
|
|
210
|
+
command: (ed) => { ed.chain().focus().deleteColumn().run(); },
|
|
211
|
+
},
|
|
212
|
+
tableAddRowBefore: {
|
|
213
|
+
id: 'tableAddRowBefore', label: 'Add row before', available: true, icon: Icons.tableAddRowBefore,
|
|
214
|
+
isDisabled: (ed) => !ed.can().addRowBefore(),
|
|
215
|
+
command: (ed) => { ed.chain().focus().addRowBefore().run(); },
|
|
216
|
+
},
|
|
217
|
+
tableAddRowAfter: {
|
|
218
|
+
id: 'tableAddRowAfter', label: 'Add row after', available: true, icon: Icons.tableAddRowAfter,
|
|
219
|
+
isDisabled: (ed) => !ed.can().addRowAfter(),
|
|
220
|
+
command: (ed) => { ed.chain().focus().addRowAfter().run(); },
|
|
221
|
+
},
|
|
222
|
+
tableDeleteRow: {
|
|
223
|
+
id: 'tableDeleteRow', label: 'Delete row', available: true, icon: Icons.tableDeleteRow,
|
|
224
|
+
isDisabled: (ed) => !ed.can().deleteRow(),
|
|
225
|
+
command: (ed) => { ed.chain().focus().deleteRow().run(); },
|
|
226
|
+
},
|
|
227
|
+
tableMergeCells: {
|
|
228
|
+
id: 'tableMergeCells', label: 'Merge cells', available: true, icon: Icons.tableMergeCells,
|
|
229
|
+
isDisabled: (ed) => !ed.can().mergeCells(),
|
|
230
|
+
command: (ed) => { ed.chain().focus().mergeCells().run(); },
|
|
231
|
+
},
|
|
232
|
+
tableSplitCell: {
|
|
233
|
+
id: 'tableSplitCell', label: 'Split cell', available: true, icon: Icons.tableSplitCell,
|
|
234
|
+
isDisabled: (ed) => !ed.can().splitCell(),
|
|
235
|
+
command: (ed) => { ed.chain().focus().splitCell().run(); },
|
|
236
|
+
},
|
|
237
|
+
tableToggleHeaderRow: {
|
|
238
|
+
id: 'tableToggleHeaderRow', label: 'Toggle header row', available: true, icon: Icons.tableToggleHeaderRow,
|
|
239
|
+
isDisabled: (ed) => !ed.can().toggleHeaderRow(),
|
|
240
|
+
command: (ed) => { ed.chain().focus().toggleHeaderRow().run(); },
|
|
241
|
+
},
|
|
242
|
+
tableToggleHeaderCell: {
|
|
243
|
+
id: 'tableToggleHeaderCell', label: 'Toggle header cell', available: true, icon: Icons.tableToggleHeaderCell,
|
|
244
|
+
isDisabled: (ed) => !ed.can().toggleHeaderCell(),
|
|
245
|
+
isActive: (ed) => ed.isActive('tableHeader'),
|
|
246
|
+
command: (ed) => { ed.chain().focus().toggleHeaderCell().run(); },
|
|
247
|
+
},
|
|
248
|
+
tableDelete: {
|
|
249
|
+
id: 'tableDelete', label: 'Delete table', available: true, icon: Icons.tableDelete,
|
|
250
|
+
isDisabled: (ed) => !ed.can().deleteTable(),
|
|
251
|
+
command: (ed) => { ed.chain().focus().deleteTable().run(); },
|
|
252
|
+
},
|
|
253
|
+
// Wraps the current paragraph in a `<details>` node with an empty summary
|
|
254
|
+
// and the existing content as the body. `setDetails` is shipped by the
|
|
255
|
+
// `@tiptap/extension-details` package; `unsetDetails` (toggle off) sits
|
|
256
|
+
// behind a separate slash entry to keep the toolbar surface compact.
|
|
257
|
+
details: {
|
|
258
|
+
id: 'details', label: 'Collapsible block', available: true, icon: Icons.details,
|
|
259
|
+
isActive: (ed) => ed.isActive('details'),
|
|
260
|
+
isDisabled: (ed) => !ed.can().setDetails(),
|
|
261
|
+
command: (ed) => { ed.chain().focus().setDetails().run(); },
|
|
262
|
+
},
|
|
263
|
+
// Multi-column grid layout. Toolbar button defaults to 2 columns — most-
|
|
264
|
+
// common-case UX. Users wanting 3 columns reach for the slash menu's
|
|
265
|
+
// dedicated entry. `gridDelete` unwraps the enclosing grid; greyed out
|
|
266
|
+
// when the cursor isn't inside one.
|
|
267
|
+
grid: {
|
|
268
|
+
id: 'grid', label: '2-column grid', available: true, icon: Icons.grid,
|
|
269
|
+
isActive: () => false,
|
|
270
|
+
isDisabled: (ed) => !ed.can().setGrid({ columns: 2 }),
|
|
271
|
+
command: (ed) => { ed.chain().focus().setGrid({ columns: 2 }).run(); },
|
|
272
|
+
},
|
|
273
|
+
gridDelete: {
|
|
274
|
+
id: 'gridDelete', label: 'Remove grid', available: true, icon: Icons.gridDelete,
|
|
275
|
+
isActive: (ed) => ed.isActive('grid'),
|
|
276
|
+
isDisabled: (ed) => !ed.can().unsetGrid(),
|
|
277
|
+
command: (ed) => { ed.chain().focus().unsetGrid().run(); },
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
function makeHeading(level, icon) {
|
|
281
|
+
return {
|
|
282
|
+
id: `h${level}`,
|
|
283
|
+
label: `Heading ${level}`,
|
|
284
|
+
available: true,
|
|
285
|
+
icon,
|
|
286
|
+
isActive: (ed) => ed.isActive('heading', { level }),
|
|
287
|
+
command: (ed) => { ed.chain().focus().toggleHeading({ level }).run(); },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function makeAlign(value, label, icon) {
|
|
291
|
+
return {
|
|
292
|
+
id: `align${value === 'left' ? 'Start' : value === 'right' ? 'End' : value.charAt(0).toUpperCase() + value.slice(1)}`,
|
|
293
|
+
label,
|
|
294
|
+
available: true,
|
|
295
|
+
icon,
|
|
296
|
+
isActive: (ed) => ed.isActive({ textAlign: value }),
|
|
297
|
+
command: (ed) => { ed.chain().focus().setTextAlign(value).run(); },
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=toolbarButtons.js.map
|