@threadlabs/looma 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,14 @@
1
1
  import {
2
+ IconButton,
2
3
  createAdapterComponent,
3
4
  toHTMLElement
4
- } from "../chunk-P72L3GWE.js";
5
+ } from "../chunk-KDA4RFLQ.js";
5
6
 
6
7
  // src/editor/index.ts
7
8
  import "@threadlabs/looma/editor";
8
9
  export * from "@threadlabs/looma/editor";
10
+
11
+ // src/editor/primitives.ts
9
12
  import {
10
13
  defineComponent,
11
14
  h,
@@ -35,9 +38,7 @@ var EditorSlashMenu = defineComponent({
35
38
  if (element.open !== props.open) element.open = props.open;
36
39
  if (element.query !== props.query) element.query = props.query;
37
40
  if (element.items !== props.items) element.items = props.items;
38
- if (element.selectedIndex !== props.selectedIndex) {
39
- element.selectedIndex = props.selectedIndex;
40
- }
41
+ if (element.selectedIndex !== props.selectedIndex) element.selectedIndex = props.selectedIndex;
41
42
  if (element.anchorRect !== props.anchorRect) element.anchorRect = props.anchorRect;
42
43
  });
43
44
  watchEffect((onCleanup) => {
@@ -81,31 +82,11 @@ var EditorSlashMenu = defineComponent({
81
82
  };
82
83
  }
83
84
  });
84
- var EditorToolbar = createAdapterComponent(
85
- "ui-editor-toolbar",
86
- "EditorToolbar",
87
- EDITOR_EVENT_BINDINGS
88
- );
89
- var EditorTableContextMenu = createAdapterComponent(
90
- "ui-editor-table-context-menu",
91
- "EditorTableContextMenu",
92
- EDITOR_EVENT_BINDINGS
93
- );
94
- var EditorTableToolbar = createAdapterComponent(
95
- "ui-editor-table-toolbar",
96
- "EditorTableToolbar",
97
- EDITOR_EVENT_BINDINGS
98
- );
99
- var EditorInsertTableGrid = createAdapterComponent(
100
- "ui-editor-insert-table-grid",
101
- "EditorInsertTableGrid",
102
- EDITOR_EVENT_BINDINGS
103
- );
104
- var EditorTableOverlay = createAdapterComponent(
105
- "ui-editor-table-overlay",
106
- "EditorTableOverlay",
107
- EDITOR_EVENT_BINDINGS
108
- );
85
+ var EditorToolbar = createAdapterComponent("ui-editor-toolbar", "EditorToolbar", EDITOR_EVENT_BINDINGS);
86
+ var EditorTableContextMenu = createAdapterComponent("ui-editor-table-context-menu", "EditorTableContextMenu", EDITOR_EVENT_BINDINGS);
87
+ var EditorTableToolbar = createAdapterComponent("ui-editor-table-toolbar", "EditorTableToolbar", EDITOR_EVENT_BINDINGS);
88
+ var EditorInsertTableGrid = createAdapterComponent("ui-editor-insert-table-grid", "EditorInsertTableGrid", EDITOR_EVENT_BINDINGS);
89
+ var EditorTableOverlay = createAdapterComponent("ui-editor-table-overlay", "EditorTableOverlay", EDITOR_EVENT_BINDINGS);
109
90
  var EDITOR_ADAPTER_COMPONENT_TAG_MAP = {
110
91
  EditorToolbar: "ui-editor-toolbar",
111
92
  EditorSlashMenu: "ui-editor-slash-menu",
@@ -114,6 +95,480 @@ var EDITOR_ADAPTER_COMPONENT_TAG_MAP = {
114
95
  EditorInsertTableGrid: "ui-editor-insert-table-grid",
115
96
  EditorTableOverlay: "ui-editor-table-overlay"
116
97
  };
98
+
99
+ // src/editor/LoomaEditor.ts
100
+ import { BubbleMenu, EditorContent, useEditor } from "@tiptap/vue-3";
101
+ import {
102
+ defineComponent as defineComponent2,
103
+ h as h2,
104
+ nextTick,
105
+ onBeforeUnmount,
106
+ onMounted,
107
+ reactive,
108
+ ref,
109
+ watch
110
+ } from "vue";
111
+ import {
112
+ createLoomaSlashCommandExtension,
113
+ getActiveTableUiState,
114
+ getDefaultEditorExtensions,
115
+ handleTableAction,
116
+ handleTableOverlayAction,
117
+ normalizeActiveTableColumnWidths,
118
+ shouldShowTextFormattingToolbar
119
+ } from "@threadlabs/looma/editor";
120
+ var EMPTY_DOCUMENT = { type: "doc", content: [] };
121
+ var EMPTY_CAPABILITIES = {
122
+ canAddRowBefore: false,
123
+ canAddRowAfter: false,
124
+ canAddColumnBefore: false,
125
+ canAddColumnAfter: false,
126
+ canDeleteRow: false,
127
+ canDeleteColumn: false,
128
+ canDeleteTable: false,
129
+ canMergeCells: false,
130
+ canSplitCell: false
131
+ };
132
+ function sameDocument(left, right) {
133
+ return JSON.stringify(left) === JSON.stringify(right);
134
+ }
135
+ function selectedTableElement(editor) {
136
+ const { node } = editor.view.domAtPos(editor.state.selection.from);
137
+ const element = node instanceof HTMLElement ? node : node.parentElement;
138
+ return element?.closest("table");
139
+ }
140
+ function selectedTableNode(editor) {
141
+ const { $from } = editor.state.selection;
142
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
143
+ const node = $from.node(depth);
144
+ if (node.type.name === "table") return node;
145
+ }
146
+ return null;
147
+ }
148
+ var LoomaEditor = defineComponent2({
149
+ name: "LoomaEditor",
150
+ inheritAttrs: false,
151
+ props: {
152
+ modelValue: {
153
+ type: Object,
154
+ default: () => ({ ...EMPTY_DOCUMENT })
155
+ },
156
+ editable: { type: Boolean, default: true },
157
+ placeholder: {
158
+ type: String,
159
+ default: "Type \u201C/\u201D for commands, or start writing\u2026"
160
+ },
161
+ extensions: {
162
+ type: Array,
163
+ default: () => []
164
+ },
165
+ uploadImage: {
166
+ type: Function,
167
+ default: void 0
168
+ }
169
+ },
170
+ emits: {
171
+ "update:modelValue": (_value) => true,
172
+ update: (_value) => true,
173
+ ready: (_editor) => true,
174
+ uploadError: (_error, _file) => true
175
+ },
176
+ setup(props, { attrs, emit, expose }) {
177
+ const root = ref(null);
178
+ const fileInput = ref(null);
179
+ const tablePickerAnchor = ref(null);
180
+ const tablePickerPopover = ref(null);
181
+ const tableToolbarShell = ref(null);
182
+ const tableOverlayShell = ref(null);
183
+ const tableMenuShell = ref(null);
184
+ const dragOver = ref(false);
185
+ const uploading = ref(false);
186
+ const tablePickerOpen = ref(false);
187
+ const tablePickerStyle = ref({});
188
+ let dragLeaveTimer = null;
189
+ let tableResizeActive = false;
190
+ let tableInteractionActive = false;
191
+ const slash = reactive({
192
+ active: false,
193
+ items: [],
194
+ selectedIndex: 0,
195
+ query: "",
196
+ rect: null,
197
+ select: null
198
+ });
199
+ const tableUi = reactive({
200
+ toolbarOpen: false,
201
+ overlayOpen: false,
202
+ menuOpen: false,
203
+ alignment: "left",
204
+ background: null,
205
+ rows: 0,
206
+ cols: 0,
207
+ toolbarStyle: {},
208
+ overlayStyle: {},
209
+ menuStyle: {},
210
+ capabilities: { ...EMPTY_CAPABILITIES }
211
+ });
212
+ const slashExtension = createLoomaSlashCommandExtension({
213
+ onOpenImagePicker: () => fileInput.value?.click(),
214
+ onStateChange: (state) => {
215
+ Object.assign(slash, state);
216
+ }
217
+ });
218
+ const editor = useEditor({
219
+ extensions: [
220
+ ...getDefaultEditorExtensions({ placeholder: props.placeholder }),
221
+ slashExtension,
222
+ ...props.extensions
223
+ ],
224
+ content: props.modelValue,
225
+ editable: props.editable,
226
+ onCreate: ({ editor: instance }) => emit("ready", instance),
227
+ onUpdate: ({ editor: instance }) => {
228
+ const value = instance.getJSON();
229
+ emit("update:modelValue", value);
230
+ emit("update", value);
231
+ }
232
+ });
233
+ watch(() => props.editable, (editable) => editor.value?.setEditable(editable));
234
+ watch(() => props.modelValue, (value) => {
235
+ const instance = editor.value;
236
+ if (!instance || sameDocument(instance.getJSON(), value)) return;
237
+ instance.commands.setContent(value, false);
238
+ }, { deep: true });
239
+ const closeTableUi = () => {
240
+ tableUi.toolbarOpen = false;
241
+ tableUi.overlayOpen = false;
242
+ tableUi.menuOpen = false;
243
+ tableUi.alignment = "left";
244
+ tableUi.background = null;
245
+ tableUi.capabilities = { ...EMPTY_CAPABILITIES };
246
+ };
247
+ const updateTableUi = () => {
248
+ const instance = editor.value;
249
+ if (!instance || !props.editable || !instance.isFocused && !tableInteractionActive) {
250
+ closeTableUi();
251
+ return;
252
+ }
253
+ const state = getActiveTableUiState(instance);
254
+ const table = selectedTableElement(instance);
255
+ const tableNode = selectedTableNode(instance);
256
+ if (!state.active || !table || !tableNode) {
257
+ closeTableUi();
258
+ return;
259
+ }
260
+ const rect = table.getBoundingClientRect();
261
+ const maxToolbarWidth = Math.min(420, window.innerWidth - 24);
262
+ const toolbarLeft = Math.min(
263
+ Math.max(12, rect.left + (rect.width - maxToolbarWidth) / 2),
264
+ Math.max(12, window.innerWidth - maxToolbarWidth - 12)
265
+ );
266
+ tableUi.toolbarOpen = state.showToolbar;
267
+ tableUi.overlayOpen = true;
268
+ tableUi.alignment = state.cellAlignment;
269
+ tableUi.background = state.cellBackground;
270
+ tableUi.capabilities = state.capabilities;
271
+ tableUi.rows = tableNode.childCount;
272
+ tableUi.cols = tableNode.childCount ? tableNode.child(0).childCount : 0;
273
+ tableUi.toolbarStyle = {
274
+ top: `${rect.top > 76 ? rect.top - 52 : rect.bottom + 12}px`,
275
+ left: `${toolbarLeft}px`,
276
+ maxWidth: `${maxToolbarWidth}px`
277
+ };
278
+ tableUi.overlayStyle = {
279
+ top: `${rect.top}px`,
280
+ left: `${rect.left}px`,
281
+ width: `${rect.width}px`,
282
+ height: `${rect.height}px`
283
+ };
284
+ };
285
+ const bindEditorUi = (instance) => {
286
+ if (!instance) return;
287
+ instance.on("selectionUpdate", updateTableUi);
288
+ instance.on("transaction", updateTableUi);
289
+ instance.on("focus", updateTableUi);
290
+ instance.on("blur", updateTableUi);
291
+ nextTick(updateTableUi);
292
+ };
293
+ const unbindEditorUi = (instance) => {
294
+ if (!instance) return;
295
+ instance.off("selectionUpdate", updateTableUi);
296
+ instance.off("transaction", updateTableUi);
297
+ instance.off("focus", updateTableUi);
298
+ instance.off("blur", updateTableUi);
299
+ };
300
+ watch(editor, (instance, previous) => {
301
+ unbindEditorUi(previous);
302
+ bindEditorUi(instance);
303
+ }, { immediate: true });
304
+ const updateTablePickerPosition = () => {
305
+ const anchor = tablePickerAnchor.value;
306
+ if (!anchor) return;
307
+ const rect = anchor.getBoundingClientRect();
308
+ const width = 220;
309
+ const left = Math.min(Math.max(12, rect.left), Math.max(12, window.innerWidth - width - 12));
310
+ const popoverHeight = tablePickerPopover.value?.getBoundingClientRect().height || 300;
311
+ const top = Math.min(
312
+ Math.max(12, rect.bottom + 8),
313
+ Math.max(12, window.innerHeight - popoverHeight - 12)
314
+ );
315
+ tablePickerStyle.value = window.innerWidth <= 767 ? { position: "fixed", left: `${left}px`, bottom: `${Math.max(12, window.innerHeight - rect.top + 8)}px` } : { position: "fixed", top: `${top}px`, left: `${left}px` };
316
+ };
317
+ const onViewportChange = () => {
318
+ updateTableUi();
319
+ updateTablePickerPosition();
320
+ tableUi.menuOpen = false;
321
+ };
322
+ const onDocumentPointerDown = (event) => {
323
+ const path = typeof event.composedPath === "function" ? event.composedPath() : [];
324
+ const inElement = (element) => Boolean(
325
+ element && (path.includes(element) || event.target instanceof Node && element.contains(event.target))
326
+ );
327
+ const inTableUi = inElement(tableToolbarShell.value) || inElement(tableOverlayShell.value) || inElement(tableMenuShell.value);
328
+ tableInteractionActive = inTableUi;
329
+ if (inTableUi) setTimeout(() => {
330
+ tableInteractionActive = false;
331
+ }, 0);
332
+ if (tablePickerOpen.value && !inElement(tablePickerAnchor.value) && !inElement(tablePickerPopover.value)) {
333
+ tablePickerOpen.value = false;
334
+ }
335
+ if ((tableUi.toolbarOpen || tableUi.overlayOpen || tableUi.menuOpen) && !inElement(root.value) && !inTableUi) {
336
+ closeTableUi();
337
+ }
338
+ };
339
+ const onResizePointerDown = (event) => {
340
+ tableResizeActive = event.target instanceof HTMLElement && event.target.classList.contains("column-resize-handle");
341
+ };
342
+ const onResizePointerUp = () => {
343
+ if (!tableResizeActive) return;
344
+ tableResizeActive = false;
345
+ requestAnimationFrame(() => {
346
+ const instance = editor.value;
347
+ const table = instance ? selectedTableElement(instance) : null;
348
+ if (instance && table) normalizeActiveTableColumnWidths(instance, table);
349
+ nextTick(updateTableUi);
350
+ });
351
+ };
352
+ onMounted(() => {
353
+ window.addEventListener("resize", onViewportChange);
354
+ window.addEventListener("scroll", onViewportChange, true);
355
+ document.addEventListener("pointerdown", onDocumentPointerDown, true);
356
+ document.addEventListener("pointerdown", onResizePointerDown, true);
357
+ document.addEventListener("pointerup", onResizePointerUp, true);
358
+ });
359
+ onBeforeUnmount(() => {
360
+ window.removeEventListener("resize", onViewportChange);
361
+ window.removeEventListener("scroll", onViewportChange, true);
362
+ document.removeEventListener("pointerdown", onDocumentPointerDown, true);
363
+ document.removeEventListener("pointerdown", onResizePointerDown, true);
364
+ document.removeEventListener("pointerup", onResizePointerUp, true);
365
+ unbindEditorUi(editor.value);
366
+ if (dragLeaveTimer) clearTimeout(dragLeaveTimer);
367
+ });
368
+ const insertImage = async (file) => {
369
+ if (!props.uploadImage || !editor.value) return;
370
+ uploading.value = true;
371
+ try {
372
+ const result = await props.uploadImage(file);
373
+ const image = typeof result === "string" ? { url: result } : result;
374
+ editor.value.chain().focus().setImage({ src: image.url, alt: image.alt ?? file.name }).run();
375
+ } catch (error) {
376
+ emit("uploadError", error, file);
377
+ } finally {
378
+ uploading.value = false;
379
+ }
380
+ };
381
+ const onFileChange = async (event) => {
382
+ const input = event.target;
383
+ const files = Array.from(input.files ?? []);
384
+ input.value = "";
385
+ for (const file of files) await insertImage(file);
386
+ };
387
+ const onDrop = async (event) => {
388
+ event.preventDefault();
389
+ dragOver.value = false;
390
+ if (!props.editable) return;
391
+ const files = Array.from(event.dataTransfer?.files ?? []).filter((file) => file.type.startsWith("image/"));
392
+ for (const file of files) await insertImage(file);
393
+ };
394
+ const onContextMenu = (event) => {
395
+ const instance = editor.value;
396
+ const cell = event.target instanceof HTMLElement ? event.target.closest("td, th") : null;
397
+ if (!props.editable || !instance || !(cell instanceof HTMLElement)) {
398
+ tableUi.menuOpen = false;
399
+ return;
400
+ }
401
+ event.preventDefault();
402
+ instance.chain().focus().setTextSelection(instance.view.posAtDOM(cell, 0) + 1).run();
403
+ updateTableUi();
404
+ tableUi.menuOpen = true;
405
+ tableUi.menuStyle = { top: `${event.clientY}px`, left: `${event.clientX}px` };
406
+ };
407
+ const runTableAction = (detail) => {
408
+ if (!editor.value) return;
409
+ handleTableAction(editor.value, detail);
410
+ tableUi.menuOpen = false;
411
+ nextTick(updateTableUi);
412
+ };
413
+ const runOverlayAction = (detail) => {
414
+ if (!editor.value) return;
415
+ handleTableOverlayAction(editor.value, detail);
416
+ nextTick(updateTableUi);
417
+ };
418
+ const commandButton = (label, icon, active, disabled, run) => h2(IconButton, {
419
+ class: "looma-editor__toolbar-button",
420
+ label,
421
+ title: label,
422
+ size: "sm",
423
+ variant: active ? "solid" : "ghost",
424
+ disabled,
425
+ "data-active": active ? "true" : "false",
426
+ onClick: run
427
+ }, () => h2("span", { class: "looma-editor__toolbar-glyph", "aria-hidden": "true" }, icon));
428
+ const renderToolbar = (instance) => {
429
+ const buttons = [
430
+ commandButton("Bold", "B", instance.isActive("bold"), !instance.can().toggleBold(), () => instance.chain().focus().toggleBold().run()),
431
+ commandButton("Italic", "I", instance.isActive("italic"), !instance.can().toggleItalic(), () => instance.chain().focus().toggleItalic().run()),
432
+ commandButton("Underline", "U", instance.isActive("underline"), !instance.can().toggleUnderline(), () => instance.chain().focus().toggleUnderline().run()),
433
+ commandButton("Strike", "S", instance.isActive("strike"), !instance.can().toggleStrike(), () => instance.chain().focus().toggleStrike().run()),
434
+ commandButton("Highlight", "\u25B0", instance.isActive("highlight"), !instance.can().toggleHighlight(), () => instance.chain().focus().toggleHighlight().run()),
435
+ commandButton("Inline code", "</>", instance.isActive("code"), !instance.can().toggleCode(), () => instance.chain().focus().toggleCode().run()),
436
+ h2("span", { class: "ui-editor-toolbar__divider", "aria-hidden": "true" }),
437
+ commandButton("Heading 1", "H1", instance.isActive("heading", { level: 1 }), false, () => instance.chain().focus().toggleHeading({ level: 1 }).run()),
438
+ commandButton("Heading 2", "H2", instance.isActive("heading", { level: 2 }), false, () => instance.chain().focus().toggleHeading({ level: 2 }).run()),
439
+ commandButton("Heading 3", "H3", instance.isActive("heading", { level: 3 }), false, () => instance.chain().focus().toggleHeading({ level: 3 }).run()),
440
+ commandButton("Bullet list", "\u2022", instance.isActive("bulletList"), false, () => instance.chain().focus().toggleBulletList().run()),
441
+ commandButton("Numbered list", "1.", instance.isActive("orderedList"), false, () => instance.chain().focus().toggleOrderedList().run()),
442
+ commandButton("Checklist", "\u2611", instance.isActive("taskList"), false, () => instance.chain().focus().toggleTaskList().run()),
443
+ commandButton("Blockquote", "\u275D", instance.isActive("blockquote"), !instance.can().toggleBlockquote(), () => instance.chain().focus().toggleBlockquote().run()),
444
+ commandButton("Code block", "{ }", instance.isActive("codeBlock"), !instance.can().toggleCodeBlock(), () => instance.chain().focus().toggleCodeBlock().run()),
445
+ commandButton("Divider", "\u2014", false, !instance.can().setHorizontalRule(), () => instance.chain().focus().setHorizontalRule().run()),
446
+ h2("span", { class: "ui-editor-toolbar__divider", "aria-hidden": "true" }),
447
+ h2("span", { ref: tablePickerAnchor, class: "looma-editor__table-picker-anchor" }, [
448
+ commandButton("Insert table", "\u229E", false, false, () => {
449
+ tablePickerOpen.value = !tablePickerOpen.value;
450
+ nextTick(updateTablePickerPosition);
451
+ })
452
+ ]),
453
+ commandButton(uploading.value ? "Uploading image" : "Insert image", "\u25A7", false, uploading.value || !props.uploadImage, () => fileInput.value?.click()),
454
+ h2("span", { class: "ui-editor-toolbar__divider", "aria-hidden": "true" }),
455
+ commandButton("Undo", "\u21B6", false, !instance.can().undo(), () => instance.chain().focus().undo().run()),
456
+ commandButton("Redo", "\u21B7", false, !instance.can().redo(), () => instance.chain().focus().redo().run())
457
+ ];
458
+ return h2(EditorToolbar, { floating: "" }, () => buttons);
459
+ };
460
+ const focus = (position = "start") => {
461
+ editor.value?.commands.focus(position);
462
+ };
463
+ expose({ editor, focus });
464
+ return () => {
465
+ const instance = editor.value;
466
+ const tableProps = {
467
+ open: true,
468
+ "cell-alignment": tableUi.alignment,
469
+ "cell-background": tableUi.background ?? void 0,
470
+ "can-add-row-before": tableUi.capabilities.canAddRowBefore,
471
+ "can-add-row-after": tableUi.capabilities.canAddRowAfter,
472
+ "can-add-column-before": tableUi.capabilities.canAddColumnBefore,
473
+ "can-add-column-after": tableUi.capabilities.canAddColumnAfter,
474
+ "can-delete-row": tableUi.capabilities.canDeleteRow,
475
+ "can-delete-column": tableUi.capabilities.canDeleteColumn,
476
+ "can-delete-table": tableUi.capabilities.canDeleteTable,
477
+ "can-merge-cells": tableUi.capabilities.canMergeCells,
478
+ "can-split-cell": tableUi.capabilities.canSplitCell,
479
+ onTableAction: runTableAction
480
+ };
481
+ return h2("div", {
482
+ ...attrs,
483
+ ref: root,
484
+ class: ["looma-editor", attrs.class, { "looma-editor--readonly": !props.editable, "looma-editor--drag-over": dragOver.value }],
485
+ onContextmenu: onContextMenu,
486
+ onDragover: (event) => {
487
+ event.preventDefault();
488
+ if (props.editable && Array.from(event.dataTransfer?.types ?? []).includes("Files")) dragOver.value = true;
489
+ },
490
+ onDragleave: () => {
491
+ if (dragLeaveTimer) clearTimeout(dragLeaveTimer);
492
+ dragLeaveTimer = setTimeout(() => {
493
+ dragOver.value = false;
494
+ }, 100);
495
+ },
496
+ onDrop
497
+ }, [
498
+ instance && props.editable ? h2(BubbleMenu, {
499
+ editor: instance,
500
+ pluginKey: "looma-text-formatting-menu",
501
+ shouldShow: ({ editor: menuEditor, from, to }) => shouldShowTextFormattingToolbar(menuEditor, from, to),
502
+ tippyOptions: {
503
+ appendTo: () => root.value ?? document.body,
504
+ duration: 100,
505
+ maxWidth: "none",
506
+ placement: "top"
507
+ }
508
+ }, { default: () => renderToolbar(instance) }) : null,
509
+ instance ? h2(EditorContent, { editor: instance }) : null,
510
+ h2("input", {
511
+ ref: fileInput,
512
+ class: "looma-editor__file-input",
513
+ type: "file",
514
+ accept: "image/jpeg,image/png,image/gif,image/webp,image/svg+xml",
515
+ multiple: true,
516
+ tabindex: -1,
517
+ "aria-hidden": "true",
518
+ onChange: onFileChange
519
+ }),
520
+ dragOver.value ? h2("div", { class: "looma-editor__drop-overlay", "aria-hidden": "true" }, [
521
+ h2("span", { class: "looma-editor__drop-glyph" }, "\u25A7"),
522
+ h2("span", "Drop image to upload")
523
+ ]) : null,
524
+ tablePickerOpen.value ? h2("div", {
525
+ ref: tablePickerPopover,
526
+ class: "looma-editor__table-picker-popover",
527
+ style: tablePickerStyle.value
528
+ }, [h2(EditorInsertTableGrid, {
529
+ open: true,
530
+ onInsertTable: (detail) => {
531
+ instance?.chain().focus().insertTable(detail).run();
532
+ tablePickerOpen.value = false;
533
+ }
534
+ })]) : null,
535
+ slash.active && slash.items.length > 0 ? h2(EditorSlashMenu, {
536
+ open: true,
537
+ query: slash.query,
538
+ items: slash.items,
539
+ selectedIndex: slash.selectedIndex,
540
+ anchorRect: slash.rect,
541
+ onSlashMenuHighlight: ({ index }) => {
542
+ slash.selectedIndex = index;
543
+ },
544
+ onSlashMenuSelect: ({ index }) => {
545
+ slash.select?.(index);
546
+ }
547
+ }) : null,
548
+ tableUi.toolbarOpen ? h2("div", {
549
+ ref: tableToolbarShell,
550
+ class: "looma-editor__table-toolbar-shell",
551
+ style: tableUi.toolbarStyle
552
+ }, [h2(EditorTableToolbar, tableProps)]) : null,
553
+ tableUi.overlayOpen ? h2("div", {
554
+ ref: tableOverlayShell,
555
+ class: "looma-editor__table-overlay-shell",
556
+ style: tableUi.overlayStyle
557
+ }, [h2(EditorTableOverlay, {
558
+ open: true,
559
+ rows: tableUi.rows,
560
+ cols: tableUi.cols,
561
+ onTableOverlayAction: runOverlayAction
562
+ })]) : null,
563
+ tableUi.menuOpen ? h2("div", {
564
+ ref: tableMenuShell,
565
+ class: "looma-editor__table-menu-shell",
566
+ style: tableUi.menuStyle
567
+ }, [h2(EditorTableContextMenu, tableProps)]) : null
568
+ ]);
569
+ };
570
+ }
571
+ });
117
572
  export {
118
573
  EDITOR_ADAPTER_COMPONENT_TAG_MAP,
119
574
  EditorInsertTableGrid,
@@ -121,5 +576,6 @@ export {
121
576
  EditorTableContextMenu,
122
577
  EditorTableOverlay,
123
578
  EditorTableToolbar,
124
- EditorToolbar
579
+ EditorToolbar,
580
+ LoomaEditor
125
581
  };
package/vue/index.js CHANGED
@@ -1,86 +1,42 @@
1
1
  import {
2
- createAdapterComponent
3
- } from "./chunk-P72L3GWE.js";
4
-
5
- // src/index.ts
6
- import "@threadlabs/looma/layout";
7
- import "@threadlabs/looma/core";
8
- var Stack = createAdapterComponent("ui-stack", "Stack");
9
- var Inline = createAdapterComponent("ui-inline", "Inline");
10
- var Cluster = createAdapterComponent("ui-cluster", "Cluster");
11
- var Grid = createAdapterComponent("ui-grid", "Grid");
12
- var Center = createAdapterComponent("ui-center", "Center");
13
- var Switcher = createAdapterComponent("ui-switcher", "Switcher");
14
- var Sidebar = createAdapterComponent("ui-sidebar", "Sidebar");
15
- var Reel = createAdapterComponent("ui-reel", "Reel");
16
- var Separator = createAdapterComponent("ui-separator", "Separator");
17
- var Disclosure = createAdapterComponent("ui-disclosure", "Disclosure");
18
- var Tabs = createAdapterComponent("ui-tabs", "Tabs");
19
- var Dialog = createAdapterComponent("ui-dialog", "Dialog");
20
- var Popover = createAdapterComponent("ui-popover", "Popover");
21
- var Menu = createAdapterComponent("ui-menu", "Menu");
22
- var MenuItem = createAdapterComponent("ui-menu-item", "MenuItem");
23
- var ContextMenu = createAdapterComponent("ui-context-menu", "ContextMenu");
24
- var Button = createAdapterComponent("ui-button", "Button");
25
- var IconButton = createAdapterComponent("ui-icon-button", "IconButton");
26
- var Input = createAdapterComponent("ui-input", "Input");
27
- var Select = createAdapterComponent("ui-select", "Select");
28
- var Textarea = createAdapterComponent("ui-textarea", "Textarea");
29
- var FormField = createAdapterComponent("ui-form-field", "FormField");
30
- var Tooltip = createAdapterComponent("ui-tooltip", "Tooltip");
31
- var ToastRegion = createAdapterComponent("ui-toast-region", "ToastRegion");
32
- var Checkbox = createAdapterComponent("ui-checkbox", "Checkbox");
33
- var Switch = createAdapterComponent("ui-switch", "Switch");
34
- var RadioGroup = createAdapterComponent("ui-radio-group", "RadioGroup");
35
- var Radio = createAdapterComponent("ui-radio", "Radio");
36
- var Badge = createAdapterComponent("ui-badge", "Badge");
37
- var Avatar = createAdapterComponent("ui-avatar", "Avatar");
38
- var AvatarGroup = createAdapterComponent("ui-avatar-group", "AvatarGroup");
39
- var FloatingActionButton = createAdapterComponent(
40
- "ui-floating-action-button",
41
- "FloatingActionButton"
42
- );
43
- var SearchShell = createAdapterComponent("ui-search-shell", "SearchShell");
44
- var SearchResultRow = createAdapterComponent("ui-search-result-row", "SearchResultRow");
45
- var TopBar = createAdapterComponent("ui-top-bar", "TopBar");
46
- var ADAPTER_COMPONENT_TAG_MAP = {
47
- Stack: "ui-stack",
48
- Inline: "ui-inline",
49
- Cluster: "ui-cluster",
50
- Grid: "ui-grid",
51
- Center: "ui-center",
52
- Switcher: "ui-switcher",
53
- Sidebar: "ui-sidebar",
54
- Reel: "ui-reel",
55
- Separator: "ui-separator",
56
- Disclosure: "ui-disclosure",
57
- Tabs: "ui-tabs",
58
- Dialog: "ui-dialog",
59
- Popover: "ui-popover",
60
- Menu: "ui-menu",
61
- MenuItem: "ui-menu-item",
62
- ContextMenu: "ui-context-menu",
63
- Button: "ui-button",
64
- IconButton: "ui-icon-button",
65
- Input: "ui-input",
66
- Select: "ui-select",
67
- Textarea: "ui-textarea",
68
- FormField: "ui-form-field",
69
- Tooltip: "ui-tooltip",
70
- ToastRegion: "ui-toast-region",
71
- Checkbox: "ui-checkbox",
72
- Switch: "ui-switch",
73
- RadioGroup: "ui-radio-group",
74
- Radio: "ui-radio",
75
- Badge: "ui-badge",
76
- Avatar: "ui-avatar",
77
- AvatarGroup: "ui-avatar-group",
78
- FloatingActionButton: "ui-floating-action-button",
79
- SearchShell: "ui-search-shell",
80
- SearchResultRow: "ui-search-result-row",
81
- TopBar: "ui-top-bar"
82
- };
83
- var VUE_ADAPTER_NOTE = "Thin adapter only: attrs and slots pass through to custom elements and DOM events map to typed callbacks.";
2
+ ADAPTER_COMPONENT_TAG_MAP,
3
+ Avatar,
4
+ AvatarGroup,
5
+ Badge,
6
+ Button,
7
+ Center,
8
+ Checkbox,
9
+ Cluster,
10
+ ContextMenu,
11
+ Dialog,
12
+ Disclosure,
13
+ FloatingActionButton,
14
+ FormField,
15
+ Grid,
16
+ IconButton,
17
+ Inline,
18
+ Input,
19
+ Menu,
20
+ MenuItem,
21
+ Popover,
22
+ Radio,
23
+ RadioGroup,
24
+ Reel,
25
+ SearchResultRow,
26
+ SearchShell,
27
+ Select,
28
+ Separator,
29
+ Sidebar,
30
+ Stack,
31
+ Switch,
32
+ Switcher,
33
+ Tabs,
34
+ Textarea,
35
+ ToastRegion,
36
+ Tooltip,
37
+ TopBar,
38
+ VUE_ADAPTER_NOTE
39
+ } from "./chunk-KDA4RFLQ.js";
84
40
  export {
85
41
  ADAPTER_COMPONENT_TAG_MAP,
86
42
  Avatar,
@@ -1,12 +0,0 @@
1
- /**
2
- * ui-editor-table-overlay — table edge controls for row/column insertion.
3
- * Emits looma-editor-table-overlay-action with insertion intent.
4
- * Domain-neutral: no Tiptap dependency.
5
- */
6
- type TableOverlayAction = "add-row-before" | "add-row-after" | "add-column-before" | "add-column-after";
7
- interface TableOverlayActionEventDetail {
8
- action: TableOverlayAction;
9
- boundaryIndex: number;
10
- }
11
-
12
- export type { TableOverlayAction as T, TableOverlayActionEventDetail as a };