@juxtui/ui 0.5.0 → 0.6.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/dist/editor.js ADDED
@@ -0,0 +1,1115 @@
1
+ import { _ as useLayer, a as JDropdownMenuItem_default, h as matchText, i as JFieldShell_default, l as useFloating, m as JPortal_default, n as JKbd_default, o as JDropdownMenu_default, r as useField, t as JTooltip_default } from "./JTooltip-CAL9lsPF.js";
2
+ import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, h, mergeModels, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, reactive, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, unref, useModel, vModelText, watch, withCtx, withDirectives, withModifiers } from "vue";
3
+ import { Extension } from "@tiptap/core";
4
+ import { Highlight } from "@tiptap/extension-highlight";
5
+ import { TaskItem, TaskList } from "@tiptap/extension-list";
6
+ import { Typography } from "@tiptap/extension-typography";
7
+ import { CharacterCount, Placeholder } from "@tiptap/extensions";
8
+ import { StarterKit } from "@tiptap/starter-kit";
9
+ import { EditorContent, useEditor } from "@tiptap/vue-3";
10
+ import { BubbleMenu } from "@tiptap/vue-3/menus";
11
+ import { PluginKey } from "@tiptap/pm/state";
12
+ import Suggestion from "@tiptap/suggestion";
13
+ //#region src/editor/icons.ts
14
+ function icon(name, children) {
15
+ const component = (props) => h("svg", {
16
+ "xmlns": "http://www.w3.org/2000/svg",
17
+ "viewBox": "0 0 16 16",
18
+ "width": 16,
19
+ "height": 16,
20
+ "fill": "none",
21
+ "stroke": "currentColor",
22
+ "stroke-width": 1.5,
23
+ "stroke-linecap": "round",
24
+ "stroke-linejoin": "round",
25
+ "aria-hidden": "true",
26
+ "focusable": "false",
27
+ ...props
28
+ }, children());
29
+ component.displayName = name;
30
+ return component;
31
+ }
32
+ var p = (d) => h("path", { d });
33
+ var IconBold = icon("IconBold", () => [p("M4.5 3h4.25a2.5 2.5 0 0 1 0 5H4.5zM4.5 8h5a2.5 2.5 0 0 1 0 5h-5z")]);
34
+ var IconItalic = icon("IconItalic", () => [p("M10 3H6.5M9.5 13H6M9 3 7 13")]);
35
+ var IconUnderline = icon("IconUnderline", () => [p("M4.5 2.75V7a3.5 3.5 0 0 0 7 0V2.75M3.5 13.5h9")]);
36
+ var IconStrike = icon("IconStrike", () => [p("M11.5 4.25C11 3.2 9.8 2.75 8.2 2.75 6.1 2.75 4.75 3.8 4.75 5.25c0 1 .6 1.7 1.7 2.1M3 8.25h10M11.25 10.25c.1.3.15.6.15.9 0 1.5-1.45 2.6-3.6 2.6-1.8 0-3.1-.6-3.65-1.75")]);
37
+ var IconCode = icon("IconCode", () => [p("M6 4.5 2.75 8 6 11.5M10 4.5 13.25 8 10 11.5")]);
38
+ var IconLink = icon("IconLink", () => [p("M6.75 9.25a2.75 2.75 0 0 0 3.9 0l2-2a2.75 2.75 0 0 0-3.9-3.9l-.6.6M9.25 6.75a2.75 2.75 0 0 0-3.9 0l-2 2a2.75 2.75 0 0 0 3.9 3.9l.6-.6")]);
39
+ var IconUnlink = icon("IconUnlink", () => [p("M9.8 3.95l.6-.6a2.75 2.75 0 0 1 3.9 3.9l-1.2 1.2M6.2 12.05l-.6.6a2.75 2.75 0 0 1-3.9-3.9l1.2-1.2M2.5 2.5l11 11")]);
40
+ var IconExternal = icon("IconExternal", () => [p("M9.5 2.75h3.75V6.5M13.25 2.75 7.5 8.5M11.5 9.5v2.75a1 1 0 0 1-1 1h-6.75a1 1 0 0 1-1-1V5.5a1 1 0 0 1 1-1H6.5")]);
41
+ var IconHighlight = icon("IconHighlight", () => [p("M9.75 3.25l3 3-5.5 5.5H4.25v-3zM2.75 13.5h10.5")]);
42
+ var IconBulletList = icon("IconBulletList", () => [
43
+ p("M6.5 4h7M6.5 8h7M6.5 12h7"),
44
+ h("circle", {
45
+ "cx": 3,
46
+ "cy": 4,
47
+ "r": .6,
48
+ "fill": "currentColor",
49
+ "stroke-width": 1
50
+ }),
51
+ h("circle", {
52
+ "cx": 3,
53
+ "cy": 8,
54
+ "r": .6,
55
+ "fill": "currentColor",
56
+ "stroke-width": 1
57
+ }),
58
+ h("circle", {
59
+ "cx": 3,
60
+ "cy": 12,
61
+ "r": .6,
62
+ "fill": "currentColor",
63
+ "stroke-width": 1
64
+ })
65
+ ]);
66
+ var IconOrderedList = icon("IconOrderedList", () => [p("M6.75 4h6.75M6.75 8h6.75M6.75 12h6.75M2.5 3l1-.5v3M2.5 5.5h2M2.5 9.25c0-.5.45-.9 1-.9s1 .35 1 .85c0 .9-2 1.3-2 2.3h2")]);
67
+ var IconTaskList = icon("IconTaskList", () => [p("M8 4.25h5.5M8 11.75h5.5M2.5 4.25l1.25 1.25L6 3.25"), h("rect", {
68
+ x: 2.5,
69
+ y: 9.75,
70
+ width: 3.5,
71
+ height: 3.5,
72
+ rx: .75
73
+ })]);
74
+ var IconQuote = icon("IconQuote", () => [p("M3 4v8M6 5.5h7M6 8h7M6 10.5h4.5")]);
75
+ var IconCodeBlock = icon("IconCodeBlock", () => [h("rect", {
76
+ x: 2,
77
+ y: 2.75,
78
+ width: 12,
79
+ height: 10.5,
80
+ rx: 2
81
+ }), p("M6.25 6.5 4.75 8l1.5 1.5M9.75 6.5l1.5 1.5-1.5 1.5")]);
82
+ var IconDivider = icon("IconDivider", () => [p("M2.5 8h11M5 4.5h6M5 11.5h6")]);
83
+ var IconUndo = icon("IconUndo", () => [p("M5.5 3.5 2.75 6.25 5.5 9M3 6.25h6.25a3.75 3.75 0 0 1 0 7.5H7")]);
84
+ var IconRedo = icon("IconRedo", () => [p("M10.5 3.5l2.75 2.75L10.5 9M13 6.25H6.75a3.75 3.75 0 0 0 0 7.5H9")]);
85
+ var IconText = icon("IconText", () => [p("M3.5 4V3h9v1M8 3v10M6.25 13h3.5")]);
86
+ var IconHeading1 = icon("IconHeading1", () => [p("M2.5 3.5v9M8 3.5v9M2.5 8H8M11 6.25l1.75-1.25v7.5")]);
87
+ var IconHeading2 = icon("IconHeading2", () => [p("M2.5 3.5v9M7.5 3.5v9M2.5 8h5M10.25 6.5c0-.95.75-1.6 1.6-1.6s1.65.6 1.65 1.55c0 1.95-3.25 2.8-3.25 5.05h3.3")]);
88
+ var IconHeading3 = icon("IconHeading3", () => [p("M2.5 3.5v9M7.5 3.5v9M2.5 8h5M10.25 5.25h3.25L11.75 7.7c1 0 1.75.7 1.75 1.75 0 1.2-.9 1.95-1.9 1.95-.7 0-1.2-.3-1.5-.8")]);
89
+ var IconChevronDown = icon("IconChevronDown", () => [p("M4.5 6.25 8 9.75l3.5-3.5")]);
90
+ var IconCornerDownLeft = icon("IconCornerDownLeft", () => [p("M12.5 3.5v4.25a2 2 0 0 1-2 2h-7M6 7l-2.5 2.75L6 12.5")]);
91
+ //#endregion
92
+ //#region src/editor/commands.ts
93
+ var chain = (editor, range) => range ? editor.chain().focus().deleteRange(range) : editor.chain().focus();
94
+ var blockCommands = [
95
+ {
96
+ id: "text",
97
+ label: "Text",
98
+ description: "Plain paragraph",
99
+ icon: IconText,
100
+ keywords: [
101
+ "paragraph",
102
+ "p",
103
+ "body"
104
+ ],
105
+ convertible: true,
106
+ isActive: (editor) => editor.isActive("paragraph") && !editor.isActive("bulletList") && !editor.isActive("orderedList") && !editor.isActive("taskList") && !editor.isActive("blockquote"),
107
+ run: (editor, range) => chain(editor, range).setParagraph().run()
108
+ },
109
+ {
110
+ id: "h1",
111
+ label: "Heading 1",
112
+ description: "Large section title",
113
+ icon: IconHeading1,
114
+ markdown: "#",
115
+ keywords: [
116
+ "title",
117
+ "h1",
118
+ "big"
119
+ ],
120
+ convertible: true,
121
+ isActive: (editor) => editor.isActive("heading", { level: 1 }),
122
+ run: (editor, range) => chain(editor, range).setHeading({ level: 1 }).run()
123
+ },
124
+ {
125
+ id: "h2",
126
+ label: "Heading 2",
127
+ description: "Medium section title",
128
+ icon: IconHeading2,
129
+ markdown: "##",
130
+ keywords: ["subtitle", "h2"],
131
+ convertible: true,
132
+ isActive: (editor) => editor.isActive("heading", { level: 2 }),
133
+ run: (editor, range) => chain(editor, range).setHeading({ level: 2 }).run()
134
+ },
135
+ {
136
+ id: "h3",
137
+ label: "Heading 3",
138
+ description: "Small section title",
139
+ icon: IconHeading3,
140
+ markdown: "###",
141
+ keywords: ["h3"],
142
+ convertible: true,
143
+ isActive: (editor) => editor.isActive("heading", { level: 3 }),
144
+ run: (editor, range) => chain(editor, range).setHeading({ level: 3 }).run()
145
+ },
146
+ {
147
+ id: "bullet",
148
+ label: "Bulleted list",
149
+ description: "A simple list",
150
+ icon: IconBulletList,
151
+ markdown: "-",
152
+ keywords: [
153
+ "ul",
154
+ "unordered",
155
+ "bullets"
156
+ ],
157
+ convertible: true,
158
+ isActive: (editor) => editor.isActive("bulletList"),
159
+ run: (editor, range) => chain(editor, range).toggleBulletList().run()
160
+ },
161
+ {
162
+ id: "ordered",
163
+ label: "Numbered list",
164
+ description: "A list with numbers",
165
+ icon: IconOrderedList,
166
+ markdown: "1.",
167
+ keywords: [
168
+ "ol",
169
+ "ordered",
170
+ "numbers"
171
+ ],
172
+ convertible: true,
173
+ isActive: (editor) => editor.isActive("orderedList"),
174
+ run: (editor, range) => chain(editor, range).toggleOrderedList().run()
175
+ },
176
+ {
177
+ id: "task",
178
+ label: "To-do list",
179
+ description: "Checkboxes you can tick off",
180
+ icon: IconTaskList,
181
+ markdown: "[ ]",
182
+ keywords: [
183
+ "todo",
184
+ "task",
185
+ "checkbox",
186
+ "checklist"
187
+ ],
188
+ convertible: true,
189
+ isActive: (editor) => editor.isActive("taskList"),
190
+ run: (editor, range) => chain(editor, range).toggleTaskList().run()
191
+ },
192
+ {
193
+ id: "quote",
194
+ label: "Quote",
195
+ description: "Set a passage apart",
196
+ icon: IconQuote,
197
+ markdown: ">",
198
+ keywords: ["blockquote", "citation"],
199
+ convertible: true,
200
+ isActive: (editor) => editor.isActive("blockquote"),
201
+ run: (editor, range) => chain(editor, range).toggleBlockquote().run()
202
+ },
203
+ {
204
+ id: "code",
205
+ label: "Code block",
206
+ description: "Monospaced, whitespace kept",
207
+ icon: IconCodeBlock,
208
+ markdown: "```",
209
+ keywords: [
210
+ "pre",
211
+ "snippet",
212
+ "code"
213
+ ],
214
+ convertible: true,
215
+ isActive: (editor) => editor.isActive("codeBlock"),
216
+ run: (editor, range) => chain(editor, range).toggleCodeBlock().run()
217
+ },
218
+ {
219
+ id: "divider",
220
+ label: "Divider",
221
+ description: "A line between sections",
222
+ icon: IconDivider,
223
+ markdown: "---",
224
+ keywords: [
225
+ "hr",
226
+ "rule",
227
+ "separator",
228
+ "line"
229
+ ],
230
+ convertible: false,
231
+ run: (editor, range) => chain(editor, range).setHorizontalRule().run()
232
+ }
233
+ ];
234
+ //#endregion
235
+ //#region src/editor/JEditorButton.vue?vue&type=script&setup=true&lang.ts
236
+ var _hoisted_1$1 = [
237
+ "aria-label",
238
+ "aria-pressed",
239
+ "disabled"
240
+ ];
241
+ //#endregion
242
+ //#region src/editor/JEditorButton.vue
243
+ var JEditorButton_default = /* @__PURE__ */ defineComponent({
244
+ __name: "JEditorButton",
245
+ props: {
246
+ label: {},
247
+ shortcut: {},
248
+ active: { type: Boolean },
249
+ disabled: { type: Boolean },
250
+ side: {}
251
+ },
252
+ emits: ["click"],
253
+ setup(__props, { emit: __emit }) {
254
+ /** Internal: a toolbar / bubble-menu button with a tooltip that shows its shortcut. */
255
+ const emit = __emit;
256
+ return (_ctx, _cache) => {
257
+ return openBlock(), createBlock(JTooltip_default, {
258
+ side: __props.side ?? "top",
259
+ delay: 500,
260
+ disabled: __props.disabled
261
+ }, {
262
+ content: withCtx(() => [createTextVNode(toDisplayString(__props.label), 1), __props.shortcut ? (openBlock(), createBlock(JKbd_default, {
263
+ key: 0,
264
+ keys: __props.shortcut,
265
+ size: "sm",
266
+ class: "j-editor-button__keys"
267
+ }, null, 8, ["keys"])) : createCommentVNode("", true)]),
268
+ default: withCtx(() => [createElementVNode("button", {
269
+ type: "button",
270
+ class: normalizeClass(["j-editor-button j-focusable", { "is-active": __props.active }]),
271
+ "aria-label": __props.label,
272
+ "aria-pressed": __props.active === void 0 ? void 0 : __props.active,
273
+ disabled: __props.disabled,
274
+ "data-editor-control": "",
275
+ onPointerdown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"])),
276
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("click", $event))
277
+ }, [renderSlot(_ctx.$slots, "default")], 42, _hoisted_1$1)]),
278
+ _: 3
279
+ }, 8, ["side", "disabled"]);
280
+ };
281
+ }
282
+ });
283
+ //#endregion
284
+ //#region src/editor/slash.ts
285
+ function filterCommands(query) {
286
+ const q = query.trim();
287
+ if (!q) return blockCommands;
288
+ return blockCommands.map((command) => {
289
+ return {
290
+ command,
291
+ score: Math.max(matchText(q, command.label).score, ...command.keywords.map((keyword) => matchText(q, keyword).score * .9))
292
+ };
293
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score).map((entry) => entry.command);
294
+ }
295
+ var slashPluginKey = new PluginKey("juxtSlash");
296
+ /**
297
+ * Typing `/` opens a menu of blocks at the caret. The menu itself is rendered
298
+ * by JEditor from `state`; this extension only feeds it and handles keys.
299
+ */
300
+ function createSlashCommands(state) {
301
+ return Extension.create({
302
+ name: "juxtSlashCommands",
303
+ addProseMirrorPlugins() {
304
+ return [Suggestion({
305
+ editor: this.editor,
306
+ pluginKey: slashPluginKey,
307
+ char: "/",
308
+ allow: ({ state: editorState, range }) => {
309
+ const $from = editorState.doc.resolve(range.from);
310
+ return $from.parent.type.name === "paragraph" && !$from.parent.type.spec.code;
311
+ },
312
+ items: ({ query }) => filterCommands(query),
313
+ command: ({ editor, range, props }) => props.run(editor, range),
314
+ render: () => {
315
+ let select = null;
316
+ return {
317
+ onStart: (props) => {
318
+ select = (item) => props.command(item);
319
+ Object.assign(state, {
320
+ open: true,
321
+ query: props.query,
322
+ items: props.items,
323
+ index: 0,
324
+ rect: props.clientRect?.() ?? null,
325
+ select
326
+ });
327
+ },
328
+ onUpdate: (props) => {
329
+ select = (item) => props.command(item);
330
+ const index = props.query !== state.query ? 0 : Math.min(state.index, Math.max(0, props.items.length - 1));
331
+ Object.assign(state, {
332
+ query: props.query,
333
+ items: props.items,
334
+ index,
335
+ rect: props.clientRect?.() ?? null,
336
+ select
337
+ });
338
+ },
339
+ onKeyDown: ({ event }) => {
340
+ if (!state.open) return false;
341
+ const count = state.items.length;
342
+ if (event.key === "ArrowDown") {
343
+ if (count) state.index = (state.index + 1) % count;
344
+ return true;
345
+ }
346
+ if (event.key === "ArrowUp") {
347
+ if (count) state.index = (state.index - 1 + count) % count;
348
+ return true;
349
+ }
350
+ if (event.key === "Enter" || event.key === "Tab") {
351
+ const item = state.items[state.index];
352
+ if (!item) return false;
353
+ select?.(item);
354
+ return true;
355
+ }
356
+ if (event.key === "Escape") {
357
+ state.open = false;
358
+ return true;
359
+ }
360
+ return false;
361
+ },
362
+ onExit: () => {
363
+ Object.assign(state, {
364
+ open: false,
365
+ items: [],
366
+ query: "",
367
+ rect: null,
368
+ select: null
369
+ });
370
+ }
371
+ };
372
+ }
373
+ })];
374
+ }
375
+ });
376
+ }
377
+ //#endregion
378
+ //#region src/editor/JEditor.vue?vue&type=script&setup=true&lang.ts
379
+ var _hoisted_1 = ["disabled"];
380
+ var _hoisted_2 = { class: "j-editor__block-label" };
381
+ var _hoisted_3 = {
382
+ key: 1,
383
+ class: "j-editor__prose j-editor__ssr",
384
+ "aria-hidden": "true"
385
+ };
386
+ var _hoisted_4 = {
387
+ key: 1,
388
+ class: "j-editor__footer",
389
+ "aria-live": "polite"
390
+ };
391
+ var _hoisted_5 = [
392
+ "id",
393
+ "aria-selected",
394
+ "onPointermove",
395
+ "onClick"
396
+ ];
397
+ var _hoisted_6 = { class: "j-editor__slash-icon" };
398
+ var _hoisted_7 = { class: "j-editor__slash-text" };
399
+ var _hoisted_8 = { class: "j-editor__slash-title" };
400
+ var _hoisted_9 = { class: "j-editor__slash-description" };
401
+ var _hoisted_10 = {
402
+ key: 0,
403
+ class: "j-editor__slash-md"
404
+ };
405
+ var _hoisted_11 = {
406
+ key: 1,
407
+ class: "j-editor__slash-empty"
408
+ };
409
+ var _hoisted_12 = {
410
+ type: "submit",
411
+ class: "j-editor-button j-focusable",
412
+ "aria-label": "Apply link"
413
+ };
414
+ var _hoisted_13 = ["href"];
415
+ //#endregion
416
+ //#region src/editor/JEditor.vue
417
+ var JEditor_default = /* @__PURE__ */ defineComponent({
418
+ inheritAttrs: false,
419
+ __name: "JEditor",
420
+ props: /*@__PURE__*/ mergeModels({
421
+ placeholder: { default: "Write something, or type / for blocks…" },
422
+ format: { default: "html" },
423
+ toolbar: {
424
+ type: [String, Boolean],
425
+ default: "full"
426
+ },
427
+ bubbleMenu: {
428
+ type: Boolean,
429
+ default: true
430
+ },
431
+ slashCommands: {
432
+ type: Boolean,
433
+ default: true
434
+ },
435
+ limit: {},
436
+ showCount: {
437
+ type: Boolean,
438
+ default: false
439
+ },
440
+ minHeight: { default: "10rem" },
441
+ maxHeight: {},
442
+ readonly: { type: Boolean },
443
+ autofocus: { type: [Boolean, String] },
444
+ label: {},
445
+ description: {},
446
+ error: {
447
+ type: [String, Boolean],
448
+ default: void 0
449
+ },
450
+ required: { type: Boolean },
451
+ disabled: { type: Boolean },
452
+ id: {}
453
+ }, {
454
+ "modelValue": { default: "" },
455
+ "modelModifiers": {}
456
+ }),
457
+ emits: /*@__PURE__*/ mergeModels([
458
+ "ready",
459
+ "focus",
460
+ "blur"
461
+ ], ["update:modelValue"]),
462
+ setup(__props, { expose: __expose, emit: __emit }) {
463
+ const props = __props;
464
+ const model = useModel(__props, "modelValue");
465
+ const emit = __emit;
466
+ const field = useField(props);
467
+ const root = ref(null);
468
+ const slashId = `${field.id.value}-blocks`;
469
+ const slash = reactive({
470
+ open: false,
471
+ query: "",
472
+ items: [],
473
+ index: 0,
474
+ rect: null,
475
+ select: null
476
+ });
477
+ let lastEmitted = null;
478
+ const serialize = (value) => typeof value === "string" ? value : JSON.stringify(value ?? "");
479
+ function contentFromModel() {
480
+ const value = model.value;
481
+ if (!value) return "";
482
+ return value;
483
+ }
484
+ const LinkShortcut = Extension.create({
485
+ name: "juxtLinkShortcut",
486
+ addKeyboardShortcuts() {
487
+ return { "Mod-k": () => {
488
+ openLink();
489
+ return true;
490
+ } };
491
+ }
492
+ });
493
+ const editor = useEditor({
494
+ content: contentFromModel(),
495
+ editable: !props.disabled && !props.readonly,
496
+ autofocus: props.autofocus ?? false,
497
+ extensions: [
498
+ StarterKit.configure({
499
+ heading: { levels: [
500
+ 1,
501
+ 2,
502
+ 3
503
+ ] },
504
+ link: {
505
+ openOnClick: false,
506
+ autolink: true,
507
+ defaultProtocol: "https",
508
+ HTMLAttributes: {
509
+ rel: "noopener noreferrer nofollow",
510
+ target: null
511
+ }
512
+ },
513
+ codeBlock: { HTMLAttributes: { spellcheck: "false" } },
514
+ dropcursor: {
515
+ color: "var(--juxt-accent)",
516
+ width: 2
517
+ }
518
+ }),
519
+ TaskList,
520
+ TaskItem.configure({ nested: true }),
521
+ Highlight,
522
+ Typography,
523
+ Placeholder.configure({ placeholder: ({ editor: instance, node }) => {
524
+ if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
525
+ if (instance.isEmpty) return props.placeholder;
526
+ return props.slashCommands ? "Type / for blocks" : "";
527
+ } }),
528
+ CharacterCount.configure({ limit: props.limit ?? null }),
529
+ LinkShortcut,
530
+ ...props.slashCommands ? [createSlashCommands(slash)] : []
531
+ ],
532
+ editorProps: { attributes: editorAttributes() },
533
+ onCreate: ({ editor: instance }) => emit("ready", instance),
534
+ onFocus: () => emit("focus"),
535
+ onBlur: () => emit("blur"),
536
+ onUpdate: ({ editor: instance }) => {
537
+ const value = props.format === "json" ? instance.getJSON() : instance.isEmpty ? "" : instance.getHTML();
538
+ lastEmitted = serialize(value);
539
+ model.value = value;
540
+ }
541
+ });
542
+ function editorAttributes() {
543
+ const attrs = {
544
+ "role": "textbox",
545
+ "aria-multiline": "true",
546
+ "class": "j-editor__prose"
547
+ };
548
+ if (props.label || props.id) attrs["aria-labelledby"] = field.labelId.value;
549
+ if (field.describedBy.value) attrs["aria-describedby"] = field.describedBy.value;
550
+ if (field.invalid.value) attrs["aria-invalid"] = "true";
551
+ if (props.required) attrs["aria-required"] = "true";
552
+ if (props.disabled || props.readonly) attrs["aria-readonly"] = "true";
553
+ if (slash.open) {
554
+ attrs["aria-controls"] = slashId;
555
+ attrs["aria-expanded"] = "true";
556
+ const active = slash.items[slash.index];
557
+ if (active) attrs["aria-activedescendant"] = `${slashId}-${active.id}`;
558
+ }
559
+ return attrs;
560
+ }
561
+ watch([
562
+ () => field.describedBy.value,
563
+ () => field.invalid.value,
564
+ () => props.disabled,
565
+ () => props.readonly,
566
+ () => slash.open,
567
+ () => slash.index,
568
+ () => slash.items
569
+ ], () => editor.value?.setOptions({ editorProps: { attributes: editorAttributes() } }));
570
+ watch(model, (value) => {
571
+ const instance = editor.value;
572
+ if (!instance || serialize(value) === lastEmitted) return;
573
+ instance.commands.setContent(value || "", { emitUpdate: false });
574
+ lastEmitted = serialize(value);
575
+ });
576
+ watch(() => [props.disabled, props.readonly], () => editor.value?.setEditable(!props.disabled && !props.readonly));
577
+ const currentBlock = computed(() => {
578
+ const instance = editor.value;
579
+ const fallback = blockCommands[0];
580
+ if (!instance) return fallback;
581
+ return blockCommands.find((command) => command.convertible && command.id !== "text" && command.isActive?.(instance)) ?? fallback;
582
+ });
583
+ const can = computed(() => ({
584
+ undo: !!editor.value?.can().undo(),
585
+ redo: !!editor.value?.can().redo()
586
+ }));
587
+ const editable = computed(() => !!editor.value && !props.disabled && !props.readonly);
588
+ function run(fn) {
589
+ const instance = editor.value;
590
+ if (!instance) return;
591
+ fn(instance.chain().focus()).run();
592
+ }
593
+ function onToolbarKeydown(event) {
594
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft" && event.key !== "Home" && event.key !== "End") return;
595
+ const toolbar = event.currentTarget;
596
+ const items = Array.from(toolbar.querySelectorAll("[data-editor-control]:not(:disabled)"));
597
+ const index = items.indexOf(document.activeElement);
598
+ if (index === -1) return;
599
+ event.preventDefault();
600
+ let next = index;
601
+ if (event.key === "ArrowRight") next = (index + 1) % items.length;
602
+ else if (event.key === "ArrowLeft") next = (index - 1 + items.length) % items.length;
603
+ else if (event.key === "Home") next = 0;
604
+ else next = items.length - 1;
605
+ items.forEach((item, i) => item.tabIndex = i === next ? 0 : -1);
606
+ items[next].focus();
607
+ }
608
+ function initRovingTabindex(el) {
609
+ const toolbar = el;
610
+ if (!toolbar) return;
611
+ nextTick(() => {
612
+ const items = Array.from(toolbar.querySelectorAll("[data-editor-control]"));
613
+ if (items.some((item) => item.tabIndex === 0)) return;
614
+ items.forEach((item, i) => item.tabIndex = i === 0 ? 0 : -1);
615
+ });
616
+ }
617
+ function virtualAt(getRect) {
618
+ return {
619
+ getBoundingClientRect: () => getRect() ?? new DOMRect(),
620
+ get contextElement() {
621
+ return editor.value?.view.dom;
622
+ }
623
+ };
624
+ }
625
+ const slashEl = ref(null);
626
+ const slashRef = shallowRef(virtualAt(() => slash.rect));
627
+ const { styles: slashStyles, update: updateSlash } = useFloating(slashRef, slashEl, {
628
+ placement: "bottom-start",
629
+ offset: 6
630
+ });
631
+ watch(() => slash.rect, () => nextTick(updateSlash));
632
+ watch(() => slash.index, () => nextTick(() => document.getElementById(`${slashId}-${slash.items[slash.index]?.id}`)?.scrollIntoView({ block: "nearest" })));
633
+ const link = reactive({
634
+ open: false,
635
+ url: "",
636
+ rect: null,
637
+ active: false
638
+ });
639
+ const linkEl = ref(null);
640
+ const linkInput = ref(null);
641
+ const linkRef = shallowRef(virtualAt(() => link.rect));
642
+ const { styles: linkStyles, update: updateLink } = useFloating(linkRef, linkEl, {
643
+ placement: "bottom-start",
644
+ offset: 8
645
+ });
646
+ function selectionRect(instance) {
647
+ const { from, to } = instance.state.selection;
648
+ const start = instance.view.coordsAtPos(from);
649
+ const end = instance.view.coordsAtPos(to);
650
+ const left = Math.min(start.left, end.left);
651
+ const right = Math.max(start.right, end.right);
652
+ return new DOMRect(left, start.top, Math.max(1, right - left), end.bottom - start.top);
653
+ }
654
+ function openLink() {
655
+ const instance = editor.value;
656
+ if (!instance || !editable.value) return;
657
+ if (instance.isActive("link")) instance.chain().extendMarkRange("link").run();
658
+ link.active = instance.isActive("link");
659
+ link.url = instance.getAttributes("link").href ?? "";
660
+ link.rect = selectionRect(instance);
661
+ link.open = true;
662
+ nextTick(() => {
663
+ updateLink();
664
+ linkInput.value?.focus();
665
+ linkInput.value?.select();
666
+ });
667
+ }
668
+ function closeLink(refocus = true) {
669
+ link.open = false;
670
+ if (refocus) editor.value?.commands.focus();
671
+ }
672
+ function normalizeUrl(raw) {
673
+ const value = raw.trim();
674
+ if (!value) return null;
675
+ if (/^(?:javascript|data|vbscript):/i.test(value)) return null;
676
+ if (/^(?:https?:|mailto:|tel:|\/|#)/i.test(value)) return value;
677
+ if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return `mailto:${value}`;
678
+ return `https://${value}`;
679
+ }
680
+ function applyLink() {
681
+ const instance = editor.value;
682
+ if (!instance) return;
683
+ const href = normalizeUrl(link.url);
684
+ if (!href) {
685
+ removeLink();
686
+ return;
687
+ }
688
+ if (instance.state.selection.empty && !instance.isActive("link")) instance.chain().focus().insertContent({
689
+ type: "text",
690
+ text: link.url.trim(),
691
+ marks: [{
692
+ type: "link",
693
+ attrs: { href }
694
+ }]
695
+ }).run();
696
+ else instance.chain().focus().extendMarkRange("link").setLink({ href }).run();
697
+ closeLink(false);
698
+ }
699
+ function removeLink() {
700
+ editor.value?.chain().focus().extendMarkRange("link").unsetLink().run();
701
+ closeLink(false);
702
+ }
703
+ useLayer({
704
+ active: computed(() => link.open),
705
+ elements: () => [linkEl.value],
706
+ onEscape: () => closeLink(),
707
+ onPointerDownOutside: () => closeLink(false)
708
+ });
709
+ function shouldShowBubble({ editor: instance, from, to }) {
710
+ if (!props.bubbleMenu || !instance.isEditable || link.open || slash.open) return false;
711
+ if (from === to || instance.isActive("codeBlock")) return false;
712
+ return instance.state.doc.textBetween(from, to).trim().length > 0;
713
+ }
714
+ const counts = computed(() => {
715
+ const storage = editor.value?.storage.characterCount;
716
+ return {
717
+ characters: storage?.characters() ?? 0,
718
+ words: storage?.words() ?? 0
719
+ };
720
+ });
721
+ const nearLimit = computed(() => !!props.limit && counts.value.characters >= props.limit * .9);
722
+ const atLimit = computed(() => !!props.limit && counts.value.characters >= props.limit);
723
+ function focusEditor(event) {
724
+ if (event.target === event.currentTarget && editable.value) editor.value?.commands.focus("end");
725
+ }
726
+ onBeforeUnmount(() => editor.value?.destroy());
727
+ __expose({
728
+ /** The underlying Tiptap editor. */
729
+ editor,
730
+ focus: (position) => editor.value?.commands.focus(position ?? null),
731
+ clear: () => editor.value?.commands.clearContent(true)
732
+ });
733
+ return (_ctx, _cache) => {
734
+ return openBlock(), createBlock(JFieldShell_default, {
735
+ id: unref(field).id.value,
736
+ class: normalizeClass(_ctx.$attrs.class),
737
+ style: normalizeStyle(_ctx.$attrs.style),
738
+ "label-id": unref(field).labelId.value,
739
+ "description-id": unref(field).descriptionId.value,
740
+ "error-id": unref(field).errorId.value,
741
+ label: __props.label,
742
+ description: __props.description,
743
+ "error-message": unref(field).errorMessage.value,
744
+ "show-description": unref(field).showDescription.value,
745
+ required: __props.required,
746
+ disabled: __props.disabled,
747
+ "label-for": null
748
+ }, createSlots({
749
+ default: withCtx(() => [
750
+ createElementVNode("div", {
751
+ ref_key: "root",
752
+ ref: root,
753
+ class: normalizeClass(["j-editor", {
754
+ "is-invalid": unref(field).invalid.value,
755
+ "is-disabled": __props.disabled,
756
+ "is-readonly": __props.readonly
757
+ }]),
758
+ style: normalizeStyle({
759
+ "--j-editor-min": __props.minHeight,
760
+ "--j-editor-max": __props.maxHeight ?? "none"
761
+ })
762
+ }, [
763
+ __props.toolbar ? (openBlock(), createElementBlock("div", {
764
+ key: 0,
765
+ ref: initRovingTabindex,
766
+ role: "toolbar",
767
+ "aria-label": "Formatting",
768
+ class: "j-editor__toolbar",
769
+ onKeydown: onToolbarKeydown
770
+ }, [
771
+ __props.toolbar === "full" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(JDropdownMenu_default, { disabled: !editable.value }, {
772
+ trigger: withCtx(() => [createElementVNode("button", {
773
+ type: "button",
774
+ class: "j-editor__block j-focusable",
775
+ disabled: !editable.value,
776
+ "aria-label": "Block type",
777
+ "data-editor-control": "",
778
+ onPointerdown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
779
+ }, [
780
+ (openBlock(), createBlock(resolveDynamicComponent(currentBlock.value.icon))),
781
+ createElementVNode("span", _hoisted_2, toDisplayString(currentBlock.value.label), 1),
782
+ createVNode(unref(IconChevronDown), { class: "j-editor__block-chevron" })
783
+ ], 40, _hoisted_1)]),
784
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(blockCommands).filter((c) => c.convertible), (command) => {
785
+ return openBlock(), createBlock(JDropdownMenuItem_default, {
786
+ key: command.id,
787
+ checked: command.id === currentBlock.value.id,
788
+ onSelect: ($event) => unref(editor) && command.run(unref(editor))
789
+ }, {
790
+ leading: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(command.icon)))]),
791
+ default: withCtx(() => [createTextVNode(" " + toDisplayString(command.label), 1)]),
792
+ _: 2
793
+ }, 1032, ["checked", "onSelect"]);
794
+ }), 128))]),
795
+ _: 1
796
+ }, 8, ["disabled"]), _cache[16] || (_cache[16] = createElementVNode("span", {
797
+ class: "j-editor__divider",
798
+ "aria-hidden": "true"
799
+ }, null, -1))], 64)) : createCommentVNode("", true),
800
+ createVNode(JEditorButton_default, {
801
+ label: "Bold",
802
+ shortcut: "mod+b",
803
+ active: !!unref(editor)?.isActive("bold"),
804
+ disabled: !editable.value,
805
+ onClick: _cache[1] || (_cache[1] = ($event) => run((c) => c.toggleBold()))
806
+ }, {
807
+ default: withCtx(() => [createVNode(unref(IconBold))]),
808
+ _: 1
809
+ }, 8, ["active", "disabled"]),
810
+ createVNode(JEditorButton_default, {
811
+ label: "Italic",
812
+ shortcut: "mod+i",
813
+ active: !!unref(editor)?.isActive("italic"),
814
+ disabled: !editable.value,
815
+ onClick: _cache[2] || (_cache[2] = ($event) => run((c) => c.toggleItalic()))
816
+ }, {
817
+ default: withCtx(() => [createVNode(unref(IconItalic))]),
818
+ _: 1
819
+ }, 8, ["active", "disabled"]),
820
+ createVNode(JEditorButton_default, {
821
+ label: "Underline",
822
+ shortcut: "mod+u",
823
+ active: !!unref(editor)?.isActive("underline"),
824
+ disabled: !editable.value,
825
+ onClick: _cache[3] || (_cache[3] = ($event) => run((c) => c.toggleUnderline()))
826
+ }, {
827
+ default: withCtx(() => [createVNode(unref(IconUnderline))]),
828
+ _: 1
829
+ }, 8, ["active", "disabled"]),
830
+ __props.toolbar === "full" ? (openBlock(), createBlock(JEditorButton_default, {
831
+ key: 1,
832
+ label: "Strikethrough",
833
+ shortcut: "mod+shift+s",
834
+ active: !!unref(editor)?.isActive("strike"),
835
+ disabled: !editable.value,
836
+ onClick: _cache[4] || (_cache[4] = ($event) => run((c) => c.toggleStrike()))
837
+ }, {
838
+ default: withCtx(() => [createVNode(unref(IconStrike))]),
839
+ _: 1
840
+ }, 8, ["active", "disabled"])) : createCommentVNode("", true),
841
+ __props.toolbar === "full" ? (openBlock(), createBlock(JEditorButton_default, {
842
+ key: 2,
843
+ label: "Inline code",
844
+ shortcut: "mod+e",
845
+ active: !!unref(editor)?.isActive("code"),
846
+ disabled: !editable.value,
847
+ onClick: _cache[5] || (_cache[5] = ($event) => run((c) => c.toggleCode()))
848
+ }, {
849
+ default: withCtx(() => [createVNode(unref(IconCode))]),
850
+ _: 1
851
+ }, 8, ["active", "disabled"])) : createCommentVNode("", true),
852
+ createVNode(JEditorButton_default, {
853
+ label: "Link",
854
+ shortcut: "mod+k",
855
+ active: !!unref(editor)?.isActive("link"),
856
+ disabled: !editable.value,
857
+ onClick: openLink
858
+ }, {
859
+ default: withCtx(() => [createVNode(unref(IconLink))]),
860
+ _: 1
861
+ }, 8, ["active", "disabled"]),
862
+ _cache[17] || (_cache[17] = createElementVNode("span", {
863
+ class: "j-editor__divider",
864
+ "aria-hidden": "true"
865
+ }, null, -1)),
866
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(blockCommands).filter((c) => (__props.toolbar === "full" ? [
867
+ "bullet",
868
+ "ordered",
869
+ "task",
870
+ "quote",
871
+ "code",
872
+ "divider"
873
+ ] : ["bullet", "ordered"]).includes(c.id)), (command) => {
874
+ return openBlock(), createBlock(JEditorButton_default, {
875
+ key: command.id,
876
+ label: command.label,
877
+ active: command.isActive ? !!(unref(editor) && command.isActive(unref(editor))) : void 0,
878
+ disabled: !editable.value,
879
+ onClick: ($event) => unref(editor) && command.run(unref(editor))
880
+ }, {
881
+ default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(command.icon)))]),
882
+ _: 2
883
+ }, 1032, [
884
+ "label",
885
+ "active",
886
+ "disabled",
887
+ "onClick"
888
+ ]);
889
+ }), 128)),
890
+ _cache[18] || (_cache[18] = createElementVNode("span", { class: "j-editor__spacer" }, null, -1)),
891
+ unref(editor) ? renderSlot(_ctx.$slots, "toolbar-end", { editor: unref(editor) }, void 0, void 0, 3) : createCommentVNode("", true),
892
+ createVNode(JEditorButton_default, {
893
+ label: "Undo",
894
+ shortcut: "mod+z",
895
+ disabled: !editable.value || !can.value.undo,
896
+ onClick: _cache[6] || (_cache[6] = ($event) => run((c) => c.undo()))
897
+ }, {
898
+ default: withCtx(() => [createVNode(unref(IconUndo))]),
899
+ _: 1
900
+ }, 8, ["disabled"]),
901
+ createVNode(JEditorButton_default, {
902
+ label: "Redo",
903
+ shortcut: "mod+shift+z",
904
+ disabled: !editable.value || !can.value.redo,
905
+ onClick: _cache[7] || (_cache[7] = ($event) => run((c) => c.redo()))
906
+ }, {
907
+ default: withCtx(() => [createVNode(unref(IconRedo))]),
908
+ _: 1
909
+ }, 8, ["disabled"])
910
+ ], 544)) : createCommentVNode("", true),
911
+ createElementVNode("div", {
912
+ class: "j-editor__content",
913
+ onClick: focusEditor
914
+ }, [unref(editor) ? (openBlock(), createBlock(unref(EditorContent), {
915
+ key: 0,
916
+ editor: unref(editor)
917
+ }, null, 8, ["editor"])) : (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString(__props.placeholder), 1))]),
918
+ __props.showCount || __props.limit ? (openBlock(), createElementBlock("div", _hoisted_4, [createElementVNode("span", null, toDisplayString(counts.value.words) + " " + toDisplayString(counts.value.words === 1 ? "word" : "words"), 1), createElementVNode("span", { class: normalizeClass({
919
+ "is-near": nearLimit.value,
920
+ "is-at": atLimit.value
921
+ }) }, [
922
+ createTextVNode(toDisplayString(counts.value.characters), 1),
923
+ __props.limit ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" / " + toDisplayString(__props.limit), 1)], 64)) : createCommentVNode("", true),
924
+ _cache[19] || (_cache[19] = createTextVNode(" characters ", -1))
925
+ ], 2)])) : createCommentVNode("", true)
926
+ ], 6),
927
+ unref(editor) && __props.bubbleMenu ? (openBlock(), createBlock(unref(BubbleMenu), {
928
+ key: 0,
929
+ editor: unref(editor),
930
+ "should-show": shouldShowBubble,
931
+ options: {
932
+ placement: "top",
933
+ offset: 8
934
+ },
935
+ class: "j-editor__bubble"
936
+ }, {
937
+ default: withCtx(() => [
938
+ createVNode(JEditorButton_default, {
939
+ label: "Bold",
940
+ shortcut: "mod+b",
941
+ side: "bottom",
942
+ active: unref(editor).isActive("bold"),
943
+ onClick: _cache[8] || (_cache[8] = ($event) => run((c) => c.toggleBold()))
944
+ }, {
945
+ default: withCtx(() => [createVNode(unref(IconBold))]),
946
+ _: 1
947
+ }, 8, ["active"]),
948
+ createVNode(JEditorButton_default, {
949
+ label: "Italic",
950
+ shortcut: "mod+i",
951
+ side: "bottom",
952
+ active: unref(editor).isActive("italic"),
953
+ onClick: _cache[9] || (_cache[9] = ($event) => run((c) => c.toggleItalic()))
954
+ }, {
955
+ default: withCtx(() => [createVNode(unref(IconItalic))]),
956
+ _: 1
957
+ }, 8, ["active"]),
958
+ createVNode(JEditorButton_default, {
959
+ label: "Underline",
960
+ shortcut: "mod+u",
961
+ side: "bottom",
962
+ active: unref(editor).isActive("underline"),
963
+ onClick: _cache[10] || (_cache[10] = ($event) => run((c) => c.toggleUnderline()))
964
+ }, {
965
+ default: withCtx(() => [createVNode(unref(IconUnderline))]),
966
+ _: 1
967
+ }, 8, ["active"]),
968
+ createVNode(JEditorButton_default, {
969
+ label: "Strikethrough",
970
+ shortcut: "mod+shift+s",
971
+ side: "bottom",
972
+ active: unref(editor).isActive("strike"),
973
+ onClick: _cache[11] || (_cache[11] = ($event) => run((c) => c.toggleStrike()))
974
+ }, {
975
+ default: withCtx(() => [createVNode(unref(IconStrike))]),
976
+ _: 1
977
+ }, 8, ["active"]),
978
+ createVNode(JEditorButton_default, {
979
+ label: "Inline code",
980
+ shortcut: "mod+e",
981
+ side: "bottom",
982
+ active: unref(editor).isActive("code"),
983
+ onClick: _cache[12] || (_cache[12] = ($event) => run((c) => c.toggleCode()))
984
+ }, {
985
+ default: withCtx(() => [createVNode(unref(IconCode))]),
986
+ _: 1
987
+ }, 8, ["active"]),
988
+ createVNode(JEditorButton_default, {
989
+ label: "Highlight",
990
+ shortcut: "mod+shift+h",
991
+ side: "bottom",
992
+ active: unref(editor).isActive("highlight"),
993
+ onClick: _cache[13] || (_cache[13] = ($event) => run((c) => c.toggleHighlight()))
994
+ }, {
995
+ default: withCtx(() => [createVNode(unref(IconHighlight))]),
996
+ _: 1
997
+ }, 8, ["active"]),
998
+ _cache[20] || (_cache[20] = createElementVNode("span", {
999
+ class: "j-editor__divider",
1000
+ "aria-hidden": "true"
1001
+ }, null, -1)),
1002
+ createVNode(JEditorButton_default, {
1003
+ label: "Link",
1004
+ shortcut: "mod+k",
1005
+ side: "bottom",
1006
+ active: unref(editor).isActive("link"),
1007
+ onClick: openLink
1008
+ }, {
1009
+ default: withCtx(() => [createVNode(unref(IconLink))]),
1010
+ _: 1
1011
+ }, 8, ["active"])
1012
+ ]),
1013
+ _: 1
1014
+ }, 8, ["editor"])) : createCommentVNode("", true),
1015
+ createVNode(JPortal_default, null, {
1016
+ default: withCtx(() => [createVNode(Transition, { name: "j-pop" }, {
1017
+ default: withCtx(() => [slash.open ? (openBlock(), createElementBlock("div", {
1018
+ key: 0,
1019
+ id: slashId,
1020
+ ref_key: "slashEl",
1021
+ ref: slashEl,
1022
+ role: "listbox",
1023
+ "aria-label": "Blocks",
1024
+ class: "j-editor__slash j-popover-surface",
1025
+ style: normalizeStyle(unref(slashStyles)),
1026
+ onPointerdown: _cache[14] || (_cache[14] = withModifiers(() => {}, ["prevent"]))
1027
+ }, [slash.items.length ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[21] || (_cache[21] = createElementVNode("div", {
1028
+ class: "j-editor__slash-label",
1029
+ role: "presentation"
1030
+ }, " Blocks ", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(slash.items, (item, i) => {
1031
+ return openBlock(), createElementBlock("div", {
1032
+ id: `${slashId}-${item.id}`,
1033
+ key: item.id,
1034
+ role: "option",
1035
+ class: normalizeClass(["j-editor__slash-item", { "is-active": i === slash.index }]),
1036
+ "aria-selected": i === slash.index,
1037
+ onPointermove: ($event) => slash.index = i,
1038
+ onClick: ($event) => slash.select?.(item)
1039
+ }, [
1040
+ createElementVNode("span", _hoisted_6, [(openBlock(), createBlock(resolveDynamicComponent(item.icon)))]),
1041
+ createElementVNode("span", _hoisted_7, [createElementVNode("span", _hoisted_8, toDisplayString(item.label), 1), createElementVNode("span", _hoisted_9, toDisplayString(item.description), 1)]),
1042
+ item.markdown ? (openBlock(), createElementBlock("kbd", _hoisted_10, toDisplayString(item.markdown), 1)) : createCommentVNode("", true)
1043
+ ], 42, _hoisted_5);
1044
+ }), 128))], 64)) : (openBlock(), createElementBlock("div", _hoisted_11, " No blocks match “" + toDisplayString(slash.query) + "” ", 1))], 36)) : createCommentVNode("", true)]),
1045
+ _: 1
1046
+ }), createVNode(Transition, { name: "j-pop" }, {
1047
+ default: withCtx(() => [link.open ? (openBlock(), createElementBlock("form", {
1048
+ key: 0,
1049
+ ref_key: "linkEl",
1050
+ ref: linkEl,
1051
+ class: "j-editor__link j-popover-surface",
1052
+ style: normalizeStyle(unref(linkStyles)),
1053
+ onSubmit: withModifiers(applyLink, ["prevent"])
1054
+ }, [
1055
+ createVNode(unref(IconLink), { class: "j-editor__link-icon" }),
1056
+ withDirectives(createElementVNode("input", {
1057
+ ref_key: "linkInput",
1058
+ ref: linkInput,
1059
+ "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => link.url = $event),
1060
+ class: "j-editor__link-input",
1061
+ type: "text",
1062
+ inputmode: "url",
1063
+ placeholder: "Paste or type a link",
1064
+ "aria-label": "Link address",
1065
+ autocomplete: "off",
1066
+ spellcheck: "false"
1067
+ }, null, 512), [[vModelText, link.url]]),
1068
+ createElementVNode("button", _hoisted_12, [createVNode(unref(IconCornerDownLeft))]),
1069
+ link.active && normalizeUrl(link.url) ? (openBlock(), createElementBlock("a", {
1070
+ key: 0,
1071
+ href: normalizeUrl(link.url),
1072
+ target: "_blank",
1073
+ rel: "noopener noreferrer",
1074
+ class: "j-editor-button j-focusable",
1075
+ "aria-label": "Open link in a new tab"
1076
+ }, [createVNode(unref(IconExternal))], 8, _hoisted_13)) : createCommentVNode("", true),
1077
+ link.active ? (openBlock(), createElementBlock("button", {
1078
+ key: 1,
1079
+ type: "button",
1080
+ class: "j-editor-button j-focusable",
1081
+ "aria-label": "Remove link",
1082
+ onClick: removeLink
1083
+ }, [createVNode(unref(IconUnlink))])) : createCommentVNode("", true)
1084
+ ], 36)) : createCommentVNode("", true)]),
1085
+ _: 1
1086
+ })]),
1087
+ _: 1
1088
+ })
1089
+ ]),
1090
+ _: 2
1091
+ }, [_ctx.$slots.label ? {
1092
+ name: "label",
1093
+ fn: withCtx(() => [renderSlot(_ctx.$slots, "label")]),
1094
+ key: "0"
1095
+ } : void 0]), 1032, [
1096
+ "id",
1097
+ "class",
1098
+ "style",
1099
+ "label-id",
1100
+ "description-id",
1101
+ "error-id",
1102
+ "label",
1103
+ "description",
1104
+ "error-message",
1105
+ "show-description",
1106
+ "required",
1107
+ "disabled"
1108
+ ]);
1109
+ };
1110
+ }
1111
+ });
1112
+ //#endregion
1113
+ export { JEditor_default as JEditor, blockCommands };
1114
+
1115
+ //# sourceMappingURL=editor.js.map