@akira-io/ui 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -10
- package/dist/blocks.d.ts +162 -9
- package/dist/blocks.js +1410 -36
- package/dist/blocks.js.map +1 -1
- package/dist/button-Bj6My68b.d.ts +17 -0
- package/dist/chunk-2WIIRDHT.js +83 -0
- package/dist/chunk-2WIIRDHT.js.map +1 -0
- package/dist/{chunk-A2SIZOEA.js → chunk-4SOQAVUP.js} +125 -45
- package/dist/chunk-4SOQAVUP.js.map +1 -0
- package/dist/chunk-5AJFQ6C4.js +35 -0
- package/dist/chunk-5AJFQ6C4.js.map +1 -0
- package/dist/chunk-CFDDAFPJ.js +1243 -0
- package/dist/chunk-CFDDAFPJ.js.map +1 -0
- package/dist/chunk-FWQFB37J.js +45 -0
- package/dist/chunk-FWQFB37J.js.map +1 -0
- package/dist/chunk-IQMRJXEO.js +59 -0
- package/dist/chunk-IQMRJXEO.js.map +1 -0
- package/dist/chunk-L2H5GIF7.js +22 -0
- package/dist/chunk-L2H5GIF7.js.map +1 -0
- package/dist/chunk-ODYCBCKS.js +290 -0
- package/dist/chunk-ODYCBCKS.js.map +1 -0
- package/dist/{chunk-5MQWJVM2.js → chunk-QHZWW55M.js} +383 -326
- package/dist/chunk-QHZWW55M.js.map +1 -0
- package/dist/chunk-RPCL7VFQ.js +123 -0
- package/dist/chunk-RPCL7VFQ.js.map +1 -0
- package/dist/chunk-T2INZOCM.js +165 -0
- package/dist/chunk-T2INZOCM.js.map +1 -0
- package/dist/chunk-TGF54WAO.js +29 -0
- package/dist/chunk-TGF54WAO.js.map +1 -0
- package/dist/chunk-XZCC6MTE.js +26 -0
- package/dist/chunk-XZCC6MTE.js.map +1 -0
- package/dist/{chunk-UXL33CZ4.js → chunk-YOVY3KT6.js} +13 -11
- package/dist/chunk-YOVY3KT6.js.map +1 -0
- package/dist/code.d.ts +9 -0
- package/dist/code.js +650 -0
- package/dist/code.js.map +1 -0
- package/dist/context-YWbxMH0C.d.ts +383 -0
- package/dist/editor.d.ts +128 -0
- package/dist/editor.js +450 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +298 -222
- package/dist/index.js +1300 -670
- package/dist/index.js.map +1 -1
- package/dist/inertia.d.ts +47 -2
- package/dist/inertia.js +138 -7
- package/dist/inertia.js.map +1 -1
- package/dist/input-Dpi7pXeR.d.ts +6 -0
- package/dist/json-viewer-BARA2RCW.d.ts +51 -0
- package/dist/{language-Bft6rBBt.d.ts → language-Dgjr9bm3.d.ts} +1 -1
- package/dist/locales/pt.d.ts +26 -4
- package/dist/locales/pt.js +127 -1
- package/dist/locales/pt.js.map +1 -1
- package/dist/password-input-DcXX7SG9.d.ts +51 -0
- package/dist/shells.d.ts +45 -7
- package/dist/shells.js +96 -60
- package/dist/shells.js.map +1 -1
- package/dist/sidebar-DqkFRUxQ.d.ts +81 -0
- package/dist/types-B-eGPoFD.d.ts +111 -0
- package/dist/types-cmBL8zOc.d.ts +51 -0
- package/package.json +170 -130
- package/theme.css +20 -0
- package/themes/nosferry.css +4 -0
- package/dist/chunk-42BJBH56.js +0 -786
- package/dist/chunk-42BJBH56.js.map +0 -1
- package/dist/chunk-5MQWJVM2.js.map +0 -1
- package/dist/chunk-A2SIZOEA.js.map +0 -1
- package/dist/chunk-POE7OYET.js +0 -96
- package/dist/chunk-POE7OYET.js.map +0 -1
- package/dist/chunk-UXL33CZ4.js.map +0 -1
- package/dist/date-range-filter-QEa8fhUM.d.ts +0 -149
- package/dist/sidebar-BDhG9SUU.d.ts +0 -88
- package/dist/types-Cno1_Ijx.d.ts +0 -40
- package/dist/types-cOieac8G.d.ts +0 -77
package/dist/editor.js
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { Toggle } from './chunk-FWQFB37J.js';
|
|
2
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Label, DialogFooter } from './chunk-T2INZOCM.js';
|
|
3
|
+
import { Separator } from './chunk-TGF54WAO.js';
|
|
4
|
+
import { Input } from './chunk-XZCC6MTE.js';
|
|
5
|
+
import { cn, Button, fieldSurface, focusRing } from './chunk-ODYCBCKS.js';
|
|
6
|
+
import { EditorContent as EditorContent$1, useEditor } from '@tiptap/react';
|
|
7
|
+
import { createContext, useContext, useState, useRef, useMemo, useEffect, useId } from 'react';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { Code, Strikethrough, Italic, Bold, Heading6, Heading5, Heading4, Heading3, Heading2, Heading1, List, ListOrdered, Quote, Undo2, Redo2, Link2 } from 'lucide-react';
|
|
10
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
11
|
+
|
|
12
|
+
var EditorContext = createContext(null);
|
|
13
|
+
var EditorContextProvider = EditorContext.Provider;
|
|
14
|
+
function useEditorContext() {
|
|
15
|
+
const context = useContext(EditorContext);
|
|
16
|
+
if (context === null) {
|
|
17
|
+
throw new Error("Editor parts must be used inside <Editor>.");
|
|
18
|
+
}
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
function EditorContent({
|
|
22
|
+
className,
|
|
23
|
+
slotName = "editor-content"
|
|
24
|
+
}) {
|
|
25
|
+
const { editor, placeholder } = useEditorContext();
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
"data-placeholder": placeholder,
|
|
30
|
+
"data-empty": editor.isEmpty ? "" : void 0,
|
|
31
|
+
className: cn(
|
|
32
|
+
"data-empty:before:top-3 data-empty:before:left-4 data-empty:before:text-sm data-empty:before:font-medium relative w-full data-empty:before:pointer-events-none data-empty:before:absolute data-empty:before:text-muted-foreground data-empty:before:content-[attr(data-placeholder)]",
|
|
33
|
+
className
|
|
34
|
+
),
|
|
35
|
+
"data-slot": slotName,
|
|
36
|
+
children: /* @__PURE__ */ jsx(EditorContent$1, { editor })
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function EditorToolbar({
|
|
41
|
+
children,
|
|
42
|
+
className,
|
|
43
|
+
slotName = "editor-toolbar"
|
|
44
|
+
}) {
|
|
45
|
+
const { labels } = useEditorContext();
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
"div",
|
|
48
|
+
{
|
|
49
|
+
role: "toolbar",
|
|
50
|
+
"aria-label": labels.toolbarLabel,
|
|
51
|
+
"aria-orientation": "horizontal",
|
|
52
|
+
className: cn("gap-1 flex flex-wrap items-center", className),
|
|
53
|
+
"data-slot": slotName,
|
|
54
|
+
children
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function EditorControl({
|
|
59
|
+
label,
|
|
60
|
+
icon: Icon,
|
|
61
|
+
pressed = false,
|
|
62
|
+
disabled = false,
|
|
63
|
+
onActivate,
|
|
64
|
+
className,
|
|
65
|
+
slotName = "editor-control"
|
|
66
|
+
}) {
|
|
67
|
+
const { editable } = useEditorContext();
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
Toggle,
|
|
70
|
+
{
|
|
71
|
+
size: "sm",
|
|
72
|
+
"aria-label": label,
|
|
73
|
+
title: label,
|
|
74
|
+
pressed,
|
|
75
|
+
disabled: disabled || !editable,
|
|
76
|
+
onPressedChange: onActivate,
|
|
77
|
+
className,
|
|
78
|
+
slotName,
|
|
79
|
+
children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
var MARK_ICONS = {
|
|
84
|
+
bold: Bold,
|
|
85
|
+
italic: Italic,
|
|
86
|
+
strike: Strikethrough,
|
|
87
|
+
code: Code
|
|
88
|
+
};
|
|
89
|
+
function EditorMark({ mark, label, className }) {
|
|
90
|
+
const { editor, labels } = useEditorContext();
|
|
91
|
+
const names = {
|
|
92
|
+
bold: labels.boldLabel,
|
|
93
|
+
italic: labels.italicLabel,
|
|
94
|
+
strike: labels.strikeLabel,
|
|
95
|
+
code: labels.codeLabel
|
|
96
|
+
};
|
|
97
|
+
return /* @__PURE__ */ jsx(
|
|
98
|
+
EditorControl,
|
|
99
|
+
{
|
|
100
|
+
label: label ?? names[mark],
|
|
101
|
+
icon: MARK_ICONS[mark],
|
|
102
|
+
pressed: editor.isActive(mark),
|
|
103
|
+
disabled: !editor.can().toggleMark(mark),
|
|
104
|
+
onActivate: () => editor.chain().focus().toggleMark(mark).run(),
|
|
105
|
+
className
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
var HEADING_ICONS = {
|
|
110
|
+
1: Heading1,
|
|
111
|
+
2: Heading2,
|
|
112
|
+
3: Heading3,
|
|
113
|
+
4: Heading4,
|
|
114
|
+
5: Heading5,
|
|
115
|
+
6: Heading6
|
|
116
|
+
};
|
|
117
|
+
function EditorHeading({ level, label, className }) {
|
|
118
|
+
const { editor, labels } = useEditorContext();
|
|
119
|
+
return /* @__PURE__ */ jsx(
|
|
120
|
+
EditorControl,
|
|
121
|
+
{
|
|
122
|
+
label: label ?? labels.headingLabel(level),
|
|
123
|
+
icon: HEADING_ICONS[level],
|
|
124
|
+
pressed: editor.isActive("heading", { level }),
|
|
125
|
+
disabled: !editor.can().toggleNode("heading", "paragraph", { level }),
|
|
126
|
+
onActivate: () => editor.chain().focus().toggleHeading({ level }).run(),
|
|
127
|
+
className
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function EditorList({ variant, label, className }) {
|
|
132
|
+
const { editor, labels } = useEditorContext();
|
|
133
|
+
const name = variant === "bullet" ? "bulletList" : "orderedList";
|
|
134
|
+
return /* @__PURE__ */ jsx(
|
|
135
|
+
EditorControl,
|
|
136
|
+
{
|
|
137
|
+
label: label ?? (variant === "bullet" ? labels.bulletListLabel : labels.orderedListLabel),
|
|
138
|
+
icon: variant === "bullet" ? List : ListOrdered,
|
|
139
|
+
pressed: editor.isActive(name),
|
|
140
|
+
disabled: !editor.can().toggleList(name, "listItem"),
|
|
141
|
+
onActivate: () => variant === "bullet" ? editor.chain().focus().toggleBulletList().run() : editor.chain().focus().toggleOrderedList().run(),
|
|
142
|
+
className
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function EditorBlockquote({ label, className }) {
|
|
147
|
+
const { editor, labels } = useEditorContext();
|
|
148
|
+
return /* @__PURE__ */ jsx(
|
|
149
|
+
EditorControl,
|
|
150
|
+
{
|
|
151
|
+
label: label ?? labels.blockquoteLabel,
|
|
152
|
+
icon: Quote,
|
|
153
|
+
pressed: editor.isActive("blockquote"),
|
|
154
|
+
disabled: !editor.can().toggleWrap("blockquote"),
|
|
155
|
+
onActivate: () => editor.chain().focus().toggleBlockquote().run(),
|
|
156
|
+
className
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function EditorHistory({
|
|
161
|
+
action,
|
|
162
|
+
label,
|
|
163
|
+
className,
|
|
164
|
+
slotName = "editor-history"
|
|
165
|
+
}) {
|
|
166
|
+
const { editor, labels, editable } = useEditorContext();
|
|
167
|
+
const undoing = action === "undo";
|
|
168
|
+
const available = undoing ? editor.can().undo() : editor.can().redo();
|
|
169
|
+
const Icon = undoing ? Undo2 : Redo2;
|
|
170
|
+
return /* @__PURE__ */ jsx(
|
|
171
|
+
Button,
|
|
172
|
+
{
|
|
173
|
+
type: "button",
|
|
174
|
+
variant: "ghost",
|
|
175
|
+
size: "icon-sm",
|
|
176
|
+
"aria-label": label ?? (undoing ? labels.undoLabel : labels.redoLabel),
|
|
177
|
+
title: label ?? (undoing ? labels.undoLabel : labels.redoLabel),
|
|
178
|
+
disabled: !available || !editable,
|
|
179
|
+
onClick: () => undoing ? editor.chain().focus().undo().run() : editor.chain().focus().redo().run(),
|
|
180
|
+
className,
|
|
181
|
+
slotName,
|
|
182
|
+
children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
var SAFE_PROTOCOLS = ["http", "https", "mailto", "tel"];
|
|
187
|
+
function isSafeEditorUrl(url) {
|
|
188
|
+
const protocol = /^([a-z][a-z0-9+.-]*):/i.exec(url.trim())?.[1];
|
|
189
|
+
return protocol === void 0 || SAFE_PROTOCOLS.includes(protocol.toLowerCase());
|
|
190
|
+
}
|
|
191
|
+
function defaultEditorExtensions() {
|
|
192
|
+
return [
|
|
193
|
+
StarterKit.configure({
|
|
194
|
+
link: {
|
|
195
|
+
openOnClick: false,
|
|
196
|
+
autolink: true,
|
|
197
|
+
protocols: SAFE_PROTOCOLS,
|
|
198
|
+
isAllowedUri: (url) => isSafeEditorUrl(url),
|
|
199
|
+
HTMLAttributes: {
|
|
200
|
+
rel: "noopener noreferrer nofollow",
|
|
201
|
+
target: "_blank"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/components/ui/editor/labels.ts
|
|
209
|
+
var editorLabels = {
|
|
210
|
+
toolbarLabel: "Formatting",
|
|
211
|
+
boldLabel: "Bold",
|
|
212
|
+
italicLabel: "Italic",
|
|
213
|
+
strikeLabel: "Strikethrough",
|
|
214
|
+
codeLabel: "Code",
|
|
215
|
+
headingLabel: (level) => `Heading ${level}`,
|
|
216
|
+
bulletListLabel: "Bullet list",
|
|
217
|
+
orderedListLabel: "Numbered list",
|
|
218
|
+
blockquoteLabel: "Quote",
|
|
219
|
+
undoLabel: "Undo",
|
|
220
|
+
redoLabel: "Redo",
|
|
221
|
+
linkLabel: "Link",
|
|
222
|
+
linkDialogTitle: "Link",
|
|
223
|
+
linkDialogDescription: "Point the selected text at an address.",
|
|
224
|
+
linkUrlLabel: "Address",
|
|
225
|
+
linkUrlPlaceholder: "https://example.com",
|
|
226
|
+
linkApplyLabel: "Apply",
|
|
227
|
+
linkRemoveLabel: "Remove",
|
|
228
|
+
linkCancelLabel: "Cancel"
|
|
229
|
+
};
|
|
230
|
+
var editorProse = "text-sm font-medium leading-relaxed [&_p]:my-2 [&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:mt-4 [&_h2]:mb-2 [&_h2]:text-xl [&_h2]:font-bold [&_h3]:mt-3 [&_h3]:mb-2 [&_h3]:text-lg [&_h3]:font-semibold [&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:my-1 [&_blockquote]:my-3 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:text-muted-foreground [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4 [&_code]:rounded-md [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-normal [&_pre]:my-3 [&_pre]:rounded-xl [&_pre]:bg-muted [&_pre]:p-4 [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_hr]:my-4 [&_hr]:border-border";
|
|
231
|
+
var editingSurface = `${fieldSurface} ${focusRing} ${editorProse} min-h-32 w-full px-4 py-3 selection:bg-primary selection:text-primary-foreground`;
|
|
232
|
+
function read(editor, output) {
|
|
233
|
+
return output === "json" ? editor.getJSON() : editor.getHTML();
|
|
234
|
+
}
|
|
235
|
+
function isSameDocument(left, right) {
|
|
236
|
+
if (typeof left === "string" && typeof right === "string") {
|
|
237
|
+
return left === right;
|
|
238
|
+
}
|
|
239
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
240
|
+
}
|
|
241
|
+
function Editor({
|
|
242
|
+
value,
|
|
243
|
+
onChange,
|
|
244
|
+
output = "html",
|
|
245
|
+
extensions,
|
|
246
|
+
labels = editorLabels,
|
|
247
|
+
placeholder,
|
|
248
|
+
label,
|
|
249
|
+
disabled = false,
|
|
250
|
+
readOnly = false,
|
|
251
|
+
className,
|
|
252
|
+
children,
|
|
253
|
+
slotName = "editor"
|
|
254
|
+
}) {
|
|
255
|
+
const [revision, setRevision] = useState(0);
|
|
256
|
+
const editable = !disabled && !readOnly;
|
|
257
|
+
const emit = useRef(onChange);
|
|
258
|
+
emit.current = onChange;
|
|
259
|
+
const emitted = useRef(null);
|
|
260
|
+
const extensionSet = useMemo(
|
|
261
|
+
() => extensions ?? defaultEditorExtensions(),
|
|
262
|
+
[extensions]
|
|
263
|
+
);
|
|
264
|
+
const editor = useEditor(
|
|
265
|
+
{
|
|
266
|
+
extensions: extensionSet,
|
|
267
|
+
content: value,
|
|
268
|
+
editable,
|
|
269
|
+
immediatelyRender: false,
|
|
270
|
+
editorProps: {
|
|
271
|
+
attributes: {
|
|
272
|
+
"data-slot": "editor-surface",
|
|
273
|
+
role: "textbox",
|
|
274
|
+
"aria-multiline": "true",
|
|
275
|
+
...label === void 0 ? {} : { "aria-label": label },
|
|
276
|
+
class: editingSurface
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
onUpdate: ({ editor: instance }) => {
|
|
280
|
+
const next = read(instance, output);
|
|
281
|
+
emitted.current = next;
|
|
282
|
+
emit.current(next);
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
[extensionSet]
|
|
286
|
+
);
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (editor === null) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const rerender = () => setRevision((revision2) => revision2 + 1);
|
|
292
|
+
editor.on("transaction", rerender);
|
|
293
|
+
return () => {
|
|
294
|
+
editor.off("transaction", rerender);
|
|
295
|
+
};
|
|
296
|
+
}, [editor]);
|
|
297
|
+
useEffect(() => {
|
|
298
|
+
editor?.setEditable(editable, false);
|
|
299
|
+
}, [editor, editable]);
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
if (editor === null) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const echoed = emitted.current !== null && isSameDocument(emitted.current, value);
|
|
305
|
+
if (echoed || isSameDocument(read(editor, output), value)) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
emitted.current = null;
|
|
309
|
+
editor.commands.setContent(value, { emitUpdate: false });
|
|
310
|
+
}, [editor, value, output]);
|
|
311
|
+
const context = useMemo(
|
|
312
|
+
() => editor === null ? null : { editor, labels, editable, placeholder },
|
|
313
|
+
[editor, labels, editable, placeholder, revision]
|
|
314
|
+
);
|
|
315
|
+
if (context === null) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
return /* @__PURE__ */ jsx(EditorContextProvider, { value: context, children: /* @__PURE__ */ jsx(
|
|
319
|
+
"div",
|
|
320
|
+
{
|
|
321
|
+
"data-disabled": disabled ? "" : void 0,
|
|
322
|
+
"data-readonly": readOnly ? "" : void 0,
|
|
323
|
+
className: cn(
|
|
324
|
+
"gap-2 flex w-full flex-col data-disabled:opacity-50",
|
|
325
|
+
className
|
|
326
|
+
),
|
|
327
|
+
"data-slot": slotName,
|
|
328
|
+
children
|
|
329
|
+
}
|
|
330
|
+
) });
|
|
331
|
+
}
|
|
332
|
+
function EditorLink({
|
|
333
|
+
label,
|
|
334
|
+
className,
|
|
335
|
+
slotName = "editor-link-dialog"
|
|
336
|
+
}) {
|
|
337
|
+
const { editor, labels, editable } = useEditorContext();
|
|
338
|
+
const [open, setOpen] = useState(false);
|
|
339
|
+
const [href, setHref] = useState("");
|
|
340
|
+
const attached = editor.isActive("link");
|
|
341
|
+
const fieldId = useId();
|
|
342
|
+
function openDialog() {
|
|
343
|
+
setHref(editor.getAttributes("link").href ?? "");
|
|
344
|
+
setOpen(true);
|
|
345
|
+
}
|
|
346
|
+
function apply() {
|
|
347
|
+
if (!isSafeEditorUrl(href)) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
editor.chain().focus().extendMarkRange("link").setLink({ href: href.trim() }).run();
|
|
351
|
+
setOpen(false);
|
|
352
|
+
}
|
|
353
|
+
function remove() {
|
|
354
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
355
|
+
setOpen(false);
|
|
356
|
+
}
|
|
357
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
358
|
+
/* @__PURE__ */ jsx(
|
|
359
|
+
EditorControl,
|
|
360
|
+
{
|
|
361
|
+
label: label ?? labels.linkLabel,
|
|
362
|
+
icon: Link2,
|
|
363
|
+
pressed: attached,
|
|
364
|
+
disabled: !editable,
|
|
365
|
+
onActivate: openDialog,
|
|
366
|
+
className
|
|
367
|
+
}
|
|
368
|
+
),
|
|
369
|
+
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { slotName, children: [
|
|
370
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
371
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: labels.linkDialogTitle }),
|
|
372
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: labels.linkDialogDescription })
|
|
373
|
+
] }),
|
|
374
|
+
/* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-col", children: [
|
|
375
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: labels.linkUrlLabel }),
|
|
376
|
+
/* @__PURE__ */ jsx(
|
|
377
|
+
Input,
|
|
378
|
+
{
|
|
379
|
+
id: fieldId,
|
|
380
|
+
slotName: "editor-link-url",
|
|
381
|
+
value: href,
|
|
382
|
+
placeholder: labels.linkUrlPlaceholder,
|
|
383
|
+
onChange: (event) => setHref(event.target.value),
|
|
384
|
+
onKeyDown: (event) => {
|
|
385
|
+
if (event.key === "Enter") {
|
|
386
|
+
event.preventDefault();
|
|
387
|
+
apply();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
] }),
|
|
393
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
394
|
+
attached && /* @__PURE__ */ jsx(
|
|
395
|
+
Button,
|
|
396
|
+
{
|
|
397
|
+
type: "button",
|
|
398
|
+
variant: "ghost",
|
|
399
|
+
onClick: remove,
|
|
400
|
+
children: labels.linkRemoveLabel
|
|
401
|
+
}
|
|
402
|
+
),
|
|
403
|
+
/* @__PURE__ */ jsx(
|
|
404
|
+
Button,
|
|
405
|
+
{
|
|
406
|
+
type: "button",
|
|
407
|
+
variant: "outline",
|
|
408
|
+
onClick: () => setOpen(false),
|
|
409
|
+
children: labels.linkCancelLabel
|
|
410
|
+
}
|
|
411
|
+
),
|
|
412
|
+
/* @__PURE__ */ jsx(
|
|
413
|
+
Button,
|
|
414
|
+
{
|
|
415
|
+
type: "button",
|
|
416
|
+
onClick: apply,
|
|
417
|
+
disabled: href.trim() === "",
|
|
418
|
+
children: labels.linkApplyLabel
|
|
419
|
+
}
|
|
420
|
+
)
|
|
421
|
+
] })
|
|
422
|
+
] }) })
|
|
423
|
+
] });
|
|
424
|
+
}
|
|
425
|
+
function RichTextEditor(props) {
|
|
426
|
+
return /* @__PURE__ */ jsxs(Editor, { ...props, children: [
|
|
427
|
+
/* @__PURE__ */ jsxs(EditorToolbar, { children: [
|
|
428
|
+
/* @__PURE__ */ jsx(EditorMark, { mark: "bold" }),
|
|
429
|
+
/* @__PURE__ */ jsx(EditorMark, { mark: "italic" }),
|
|
430
|
+
/* @__PURE__ */ jsx(EditorMark, { mark: "strike" }),
|
|
431
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }),
|
|
432
|
+
/* @__PURE__ */ jsx(EditorHeading, { level: 2 }),
|
|
433
|
+
/* @__PURE__ */ jsx(EditorHeading, { level: 3 }),
|
|
434
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }),
|
|
435
|
+
/* @__PURE__ */ jsx(EditorList, { variant: "bullet" }),
|
|
436
|
+
/* @__PURE__ */ jsx(EditorList, { variant: "ordered" }),
|
|
437
|
+
/* @__PURE__ */ jsx(EditorBlockquote, {}),
|
|
438
|
+
/* @__PURE__ */ jsx(EditorMark, { mark: "code" }),
|
|
439
|
+
/* @__PURE__ */ jsx(EditorLink, {}),
|
|
440
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }),
|
|
441
|
+
/* @__PURE__ */ jsx(EditorHistory, { action: "undo" }),
|
|
442
|
+
/* @__PURE__ */ jsx(EditorHistory, { action: "redo" })
|
|
443
|
+
] }),
|
|
444
|
+
/* @__PURE__ */ jsx(EditorContent, {})
|
|
445
|
+
] });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export { Editor, EditorBlockquote, EditorContent, EditorControl, EditorHeading, EditorHistory, EditorLink, EditorList, EditorMark, EditorToolbar, RichTextEditor, defaultEditorExtensions, editorLabels, isSafeEditorUrl, useEditorContext };
|
|
449
|
+
//# sourceMappingURL=editor.js.map
|
|
450
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/editor/context.tsx","../src/components/ui/editor/content.tsx","../src/components/ui/editor/toolbar.tsx","../src/components/ui/editor/controls.tsx","../src/components/ui/editor/extensions.ts","../src/components/ui/editor/labels.ts","../src/components/ui/editor/editor.tsx","../src/components/ui/editor/link.tsx","../src/components/ui/editor/rich-text-editor.tsx"],"names":["TiptapContent","jsx","revision","useState","jsxs"],"mappings":"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","file":"editor.js","sourcesContent":["import type { Editor as TiptapEditor } from '@tiptap/core';\nimport { createContext, useContext } from 'react';\n\nimport type { EditorLabels } from '@/components/ui/editor/labels';\n\nexport interface EditorContextValue {\n editor: TiptapEditor;\n labels: EditorLabels;\n editable: boolean;\n placeholder?: string;\n}\n\nconst EditorContext = createContext<EditorContextValue | null>(null);\n\nexport const EditorContextProvider = EditorContext.Provider;\n\nexport function useEditorContext(): EditorContextValue {\n const context = useContext(EditorContext);\n\n if (context === null) {\n throw new Error('Editor parts must be used inside <Editor>.');\n }\n\n return context;\n}\n","import { EditorContent as TiptapContent } from '@tiptap/react';\n\nimport { useEditorContext } from '@/components/ui/editor/context';\nimport { cn } from '@/lib/utils';\nimport type { SlotNameProps } from '@/types';\n\nexport interface EditorContentProps {\n className?: string;\n}\n\nexport function EditorContent({\n className,\n slotName = 'editor-content',\n}: EditorContentProps & SlotNameProps) {\n const { editor, placeholder } = useEditorContext();\n\n return (\n <div\n data-placeholder={placeholder}\n data-empty={editor.isEmpty ? '' : undefined}\n className={cn(\n 'data-empty:before:top-3 data-empty:before:left-4 data-empty:before:text-sm data-empty:before:font-medium relative w-full data-empty:before:pointer-events-none data-empty:before:absolute data-empty:before:text-muted-foreground data-empty:before:content-[attr(data-placeholder)]',\n className,\n )}\n data-slot={slotName}\n >\n <TiptapContent editor={editor} />\n </div>\n );\n}\n","import type { LucideIcon } from 'lucide-react';\nimport type { ReactNode } from 'react';\n\nimport { useEditorContext } from '@/components/ui/editor/context';\nimport { Toggle } from '@/components/ui/toggle';\nimport { cn } from '@/lib/utils';\nimport type { SlotNameProps } from '@/types';\n\nexport interface EditorToolbarProps {\n children: ReactNode;\n className?: string;\n}\n\nexport function EditorToolbar({\n children,\n className,\n slotName = 'editor-toolbar',\n}: EditorToolbarProps & SlotNameProps) {\n const { labels } = useEditorContext();\n\n return (\n <div\n role=\"toolbar\"\n aria-label={labels.toolbarLabel}\n aria-orientation=\"horizontal\"\n className={cn('gap-1 flex flex-wrap items-center', className)}\n data-slot={slotName}\n >\n {children}\n </div>\n );\n}\n\nexport interface EditorControlProps {\n label: string;\n icon: LucideIcon;\n pressed?: boolean;\n disabled?: boolean;\n onActivate: () => void;\n className?: string;\n}\n\nexport function EditorControl({\n label,\n icon: Icon,\n pressed = false,\n disabled = false,\n onActivate,\n className,\n slotName = 'editor-control',\n}: EditorControlProps & SlotNameProps) {\n const { editable } = useEditorContext();\n\n return (\n <Toggle\n size=\"sm\"\n aria-label={label}\n title={label}\n pressed={pressed}\n disabled={disabled || !editable}\n onPressedChange={onActivate}\n className={className}\n slotName={slotName}\n >\n <Icon aria-hidden=\"true\" />\n </Toggle>\n );\n}\n","import {\n Bold,\n Code,\n Heading1,\n Heading2,\n Heading3,\n Heading4,\n Heading5,\n Heading6,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n type LucideIcon,\n} from 'lucide-react';\n\nimport { Button } from '@/components/ui/button';\nimport { useEditorContext } from '@/components/ui/editor/context';\nimport { EditorControl } from '@/components/ui/editor/toolbar';\nimport type { SlotNameProps } from '@/types';\n\nexport type EditorMarkName = 'bold' | 'italic' | 'strike' | 'code';\n\nconst MARK_ICONS: Record<EditorMarkName, LucideIcon> = {\n bold: Bold,\n italic: Italic,\n strike: Strikethrough,\n code: Code,\n};\n\nexport interface EditorMarkProps {\n mark: EditorMarkName;\n label?: string;\n className?: string;\n}\n\nexport function EditorMark({ mark, label, className }: EditorMarkProps) {\n const { editor, labels } = useEditorContext();\n\n const names: Record<EditorMarkName, string> = {\n bold: labels.boldLabel,\n italic: labels.italicLabel,\n strike: labels.strikeLabel,\n code: labels.codeLabel,\n };\n\n return (\n <EditorControl\n label={label ?? names[mark]}\n icon={MARK_ICONS[mark]}\n pressed={editor.isActive(mark)}\n disabled={!editor.can().toggleMark(mark)}\n onActivate={() => editor.chain().focus().toggleMark(mark).run()}\n className={className}\n />\n );\n}\n\nconst HEADING_ICONS: Record<number, LucideIcon> = {\n 1: Heading1,\n 2: Heading2,\n 3: Heading3,\n 4: Heading4,\n 5: Heading5,\n 6: Heading6,\n};\n\nexport interface EditorHeadingProps {\n level: 1 | 2 | 3 | 4 | 5 | 6;\n label?: string;\n className?: string;\n}\n\nexport function EditorHeading({ level, label, className }: EditorHeadingProps) {\n const { editor, labels } = useEditorContext();\n\n return (\n <EditorControl\n label={label ?? labels.headingLabel(level)}\n icon={HEADING_ICONS[level]}\n pressed={editor.isActive('heading', { level })}\n disabled={\n !editor.can().toggleNode('heading', 'paragraph', { level })\n }\n onActivate={() =>\n editor.chain().focus().toggleHeading({ level }).run()\n }\n className={className}\n />\n );\n}\n\nexport type EditorListVariant = 'bullet' | 'ordered';\n\nexport interface EditorListProps {\n variant: EditorListVariant;\n label?: string;\n className?: string;\n}\n\nexport function EditorList({ variant, label, className }: EditorListProps) {\n const { editor, labels } = useEditorContext();\n const name = variant === 'bullet' ? 'bulletList' : 'orderedList';\n\n return (\n <EditorControl\n label={\n label ??\n (variant === 'bullet'\n ? labels.bulletListLabel\n : labels.orderedListLabel)\n }\n icon={variant === 'bullet' ? List : ListOrdered}\n pressed={editor.isActive(name)}\n disabled={!editor.can().toggleList(name, 'listItem')}\n onActivate={() =>\n variant === 'bullet'\n ? editor.chain().focus().toggleBulletList().run()\n : editor.chain().focus().toggleOrderedList().run()\n }\n className={className}\n />\n );\n}\n\nexport interface EditorBlockquoteProps {\n label?: string;\n className?: string;\n}\n\nexport function EditorBlockquote({ label, className }: EditorBlockquoteProps) {\n const { editor, labels } = useEditorContext();\n\n return (\n <EditorControl\n label={label ?? labels.blockquoteLabel}\n icon={Quote}\n pressed={editor.isActive('blockquote')}\n disabled={!editor.can().toggleWrap('blockquote')}\n onActivate={() => editor.chain().focus().toggleBlockquote().run()}\n className={className}\n />\n );\n}\n\nexport type EditorHistoryAction = 'undo' | 'redo';\n\nexport interface EditorHistoryProps {\n action: EditorHistoryAction;\n label?: string;\n className?: string;\n}\n\nexport function EditorHistory({\n action,\n label,\n className,\n slotName = 'editor-history',\n}: EditorHistoryProps & SlotNameProps) {\n const { editor, labels, editable } = useEditorContext();\n const undoing = action === 'undo';\n const available = undoing ? editor.can().undo() : editor.can().redo();\n const Icon = undoing ? Undo2 : Redo2;\n\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={\n label ?? (undoing ? labels.undoLabel : labels.redoLabel)\n }\n title={label ?? (undoing ? labels.undoLabel : labels.redoLabel)}\n disabled={!available || !editable}\n onClick={() =>\n undoing\n ? editor.chain().focus().undo().run()\n : editor.chain().focus().redo().run()\n }\n className={className}\n slotName={slotName}\n >\n <Icon aria-hidden=\"true\" />\n </Button>\n );\n}\n","import type { Extensions } from '@tiptap/core';\nimport StarterKit from '@tiptap/starter-kit';\n\nconst SAFE_PROTOCOLS = ['http', 'https', 'mailto', 'tel'];\n\nexport function isSafeEditorUrl(url: string): boolean {\n const protocol = /^([a-z][a-z0-9+.-]*):/i.exec(url.trim())?.[1];\n\n return (\n protocol === undefined ||\n SAFE_PROTOCOLS.includes(protocol.toLowerCase())\n );\n}\n\nexport function defaultEditorExtensions(): Extensions {\n return [\n StarterKit.configure({\n link: {\n openOnClick: false,\n autolink: true,\n protocols: SAFE_PROTOCOLS,\n isAllowedUri: (url) => isSafeEditorUrl(url),\n HTMLAttributes: {\n rel: 'noopener noreferrer nofollow',\n target: '_blank',\n },\n },\n }),\n ];\n}\n","export interface EditorLabels {\n toolbarLabel: string;\n boldLabel: string;\n italicLabel: string;\n strikeLabel: string;\n codeLabel: string;\n headingLabel: (level: number) => string;\n bulletListLabel: string;\n orderedListLabel: string;\n blockquoteLabel: string;\n undoLabel: string;\n redoLabel: string;\n linkLabel: string;\n linkDialogTitle: string;\n linkDialogDescription: string;\n linkUrlLabel: string;\n linkUrlPlaceholder: string;\n linkApplyLabel: string;\n linkRemoveLabel: string;\n linkCancelLabel: string;\n}\n\nexport const editorLabels: EditorLabels = {\n toolbarLabel: 'Formatting',\n boldLabel: 'Bold',\n italicLabel: 'Italic',\n strikeLabel: 'Strikethrough',\n codeLabel: 'Code',\n headingLabel: (level) => `Heading ${level}`,\n bulletListLabel: 'Bullet list',\n orderedListLabel: 'Numbered list',\n blockquoteLabel: 'Quote',\n undoLabel: 'Undo',\n redoLabel: 'Redo',\n linkLabel: 'Link',\n linkDialogTitle: 'Link',\n linkDialogDescription: 'Point the selected text at an address.',\n linkUrlLabel: 'Address',\n linkUrlPlaceholder: 'https://example.com',\n linkApplyLabel: 'Apply',\n linkRemoveLabel: 'Remove',\n linkCancelLabel: 'Cancel',\n};\n","import type { Extensions, JSONContent } from '@tiptap/core';\nimport { useEditor } from '@tiptap/react';\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';\n\nimport {\n EditorContextProvider,\n type EditorContextValue,\n} from '@/components/ui/editor/context';\nimport { defaultEditorExtensions } from '@/components/ui/editor/extensions';\nimport { editorLabels, type EditorLabels } from '@/components/ui/editor/labels';\nimport { fieldSurface, focusRing } from '@/lib/language';\nimport { cn } from '@/lib/utils';\nimport type { SlotNameProps } from '@/types';\n\nexport type EditorOutput = 'html' | 'json';\n\nexport type EditorDocument = string | JSONContent;\n\ninterface EditorSharedProps {\n extensions?: Extensions;\n labels?: EditorLabels;\n placeholder?: string;\n label?: string;\n disabled?: boolean;\n readOnly?: boolean;\n className?: string;\n children: ReactNode;\n}\n\nexport type EditorProps = EditorSharedProps &\n (\n | {\n output?: 'html';\n value: string;\n onChange: (value: string) => void;\n }\n | {\n output: 'json';\n value: JSONContent;\n onChange: (value: JSONContent) => void;\n }\n );\n\nconst editorProse =\n 'text-sm font-medium leading-relaxed [&_p]:my-2 [&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:mt-4 [&_h2]:mb-2 [&_h2]:text-xl [&_h2]:font-bold [&_h3]:mt-3 [&_h3]:mb-2 [&_h3]:text-lg [&_h3]:font-semibold [&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:my-1 [&_blockquote]:my-3 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:text-muted-foreground [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4 [&_code]:rounded-md [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-normal [&_pre]:my-3 [&_pre]:rounded-xl [&_pre]:bg-muted [&_pre]:p-4 [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_hr]:my-4 [&_hr]:border-border';\n\nconst editingSurface = `${fieldSurface} ${focusRing} ${editorProse} min-h-32 w-full px-4 py-3 selection:bg-primary selection:text-primary-foreground`;\n\nfunction read(\n editor: { getHTML: () => string; getJSON: () => JSONContent },\n output: EditorOutput,\n): EditorDocument {\n return output === 'json' ? editor.getJSON() : editor.getHTML();\n}\n\nfunction isSameDocument(left: EditorDocument, right: EditorDocument): boolean {\n if (typeof left === 'string' && typeof right === 'string') {\n return left === right;\n }\n\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\nexport function Editor({\n value,\n onChange,\n output = 'html',\n extensions,\n labels = editorLabels,\n placeholder,\n label,\n disabled = false,\n readOnly = false,\n className,\n children,\n slotName = 'editor',\n}: EditorProps & SlotNameProps) {\n const [revision, setRevision] = useState(0);\n const editable = !disabled && !readOnly;\n const emit = useRef(onChange as (next: EditorDocument) => void);\n emit.current = onChange as (next: EditorDocument) => void;\n const emitted = useRef<EditorDocument | null>(null);\n\n const extensionSet = useMemo(\n () => extensions ?? defaultEditorExtensions(),\n [extensions],\n );\n\n const editor = useEditor(\n {\n extensions: extensionSet,\n content: value,\n editable,\n immediatelyRender: false,\n editorProps: {\n attributes: {\n 'data-slot': 'editor-surface',\n role: 'textbox',\n 'aria-multiline': 'true',\n ...(label === undefined ? {} : { 'aria-label': label }),\n class: editingSurface,\n },\n },\n onUpdate: ({ editor: instance }) => {\n const next = read(instance, output);\n\n emitted.current = next;\n emit.current(next);\n },\n },\n [extensionSet],\n );\n\n useEffect(() => {\n if (editor === null) {\n return;\n }\n\n const rerender = () => setRevision((revision) => revision + 1);\n\n editor.on('transaction', rerender);\n\n return () => {\n editor.off('transaction', rerender);\n };\n }, [editor]);\n\n useEffect(() => {\n editor?.setEditable(editable, false);\n }, [editor, editable]);\n\n useEffect(() => {\n if (editor === null) {\n return;\n }\n\n const echoed =\n emitted.current !== null && isSameDocument(emitted.current, value);\n\n if (echoed || isSameDocument(read(editor, output), value)) {\n return;\n }\n\n emitted.current = null;\n editor.commands.setContent(value, { emitUpdate: false });\n }, [editor, value, output]);\n\n const context = useMemo<EditorContextValue | null>(\n () =>\n editor === null ? null : { editor, labels, editable, placeholder },\n [editor, labels, editable, placeholder, revision],\n );\n\n if (context === null) {\n return null;\n }\n\n return (\n <EditorContextProvider value={context}>\n <div\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n className={cn(\n 'gap-2 flex w-full flex-col data-disabled:opacity-50',\n className,\n )}\n data-slot={slotName}\n >\n {children}\n </div>\n </EditorContextProvider>\n );\n}\n","import { Link2 } from 'lucide-react';\nimport { useId, useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog';\nimport { useEditorContext } from '@/components/ui/editor/context';\nimport { isSafeEditorUrl } from '@/components/ui/editor/extensions';\nimport { EditorControl } from '@/components/ui/editor/toolbar';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport type { SlotNameProps } from '@/types';\n\nexport interface EditorLinkProps {\n label?: string;\n className?: string;\n}\n\nexport function EditorLink({\n label,\n className,\n slotName = 'editor-link-dialog',\n}: EditorLinkProps & SlotNameProps) {\n const { editor, labels, editable } = useEditorContext();\n const [open, setOpen] = useState(false);\n const [href, setHref] = useState('');\n const attached = editor.isActive('link');\n const fieldId = useId();\n\n function openDialog() {\n setHref(editor.getAttributes('link').href ?? '');\n setOpen(true);\n }\n\n function apply() {\n if (!isSafeEditorUrl(href)) {\n return;\n }\n\n editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .setLink({ href: href.trim() })\n .run();\n\n setOpen(false);\n }\n\n function remove() {\n editor.chain().focus().extendMarkRange('link').unsetLink().run();\n setOpen(false);\n }\n\n return (\n <>\n <EditorControl\n label={label ?? labels.linkLabel}\n icon={Link2}\n pressed={attached}\n disabled={!editable}\n onActivate={openDialog}\n className={className}\n />\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent slotName={slotName}>\n <DialogHeader>\n <DialogTitle>{labels.linkDialogTitle}</DialogTitle>\n <DialogDescription>\n {labels.linkDialogDescription}\n </DialogDescription>\n </DialogHeader>\n <div className=\"gap-2 flex flex-col\">\n <Label htmlFor={fieldId}>{labels.linkUrlLabel}</Label>\n <Input\n id={fieldId}\n slotName=\"editor-link-url\"\n value={href}\n placeholder={labels.linkUrlPlaceholder}\n onChange={(event) => setHref(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n apply();\n }\n }}\n />\n </div>\n <DialogFooter>\n {attached && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={remove}\n >\n {labels.linkRemoveLabel}\n </Button>\n )}\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setOpen(false)}\n >\n {labels.linkCancelLabel}\n </Button>\n <Button\n type=\"button\"\n onClick={apply}\n disabled={href.trim() === ''}\n >\n {labels.linkApplyLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n","import { EditorContent } from '@/components/ui/editor/content';\nimport {\n EditorBlockquote,\n EditorHeading,\n EditorHistory,\n EditorList,\n EditorMark,\n} from '@/components/ui/editor/controls';\nimport { Editor, type EditorProps } from '@/components/ui/editor/editor';\nimport { EditorLink } from '@/components/ui/editor/link';\nimport { EditorToolbar } from '@/components/ui/editor/toolbar';\nimport { Separator } from '@/components/ui/separator';\n\ntype WithoutChildren<T> = T extends unknown ? Omit<T, 'children'> : never;\n\nexport type RichTextEditorProps = WithoutChildren<EditorProps>;\n\nexport function RichTextEditor(props: RichTextEditorProps) {\n return (\n <Editor {...props}>\n <EditorToolbar>\n <EditorMark mark=\"bold\" />\n <EditorMark mark=\"italic\" />\n <EditorMark mark=\"strike\" />\n <Separator orientation=\"vertical\" className=\"mx-1 h-6\" />\n <EditorHeading level={2} />\n <EditorHeading level={3} />\n <Separator orientation=\"vertical\" className=\"mx-1 h-6\" />\n <EditorList variant=\"bullet\" />\n <EditorList variant=\"ordered\" />\n <EditorBlockquote />\n <EditorMark mark=\"code\" />\n <EditorLink />\n <Separator orientation=\"vertical\" className=\"mx-1 h-6\" />\n <EditorHistory action=\"undo\" />\n <EditorHistory action=\"redo\" />\n </EditorToolbar>\n <EditorContent />\n </Editor>\n );\n}\n"]}
|