@threadlabs/looma 0.28.11 → 0.28.13

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.
Files changed (36) hide show
  1. package/components/ui-editor-chip-editor/ui-editor-chip-editor.html +32 -0
  2. package/components/ui-editor-chip-editor/ui-editor-chip-editor.js +33 -0
  3. package/components/ui-radio-group/ui-radio-group.html +13 -1
  4. package/components/ui-swatch/ui-swatch.html +69 -0
  5. package/dist/index.js +38 -37
  6. package/editor/formatting-selection.d.ts +49 -0
  7. package/package.json +1 -1
  8. package/styles/ui-editor-chip-editor.css +1 -0
  9. package/styles/ui-radio-group.css +9 -0
  10. package/styles/ui-swatch.css +76 -0
  11. package/vanilla/UiEditorChipEditor.d.ts +18 -0
  12. package/vanilla/UiEditorChipEditor.js +45 -0
  13. package/vanilla/UiRadioGroup.d.ts +1 -0
  14. package/vanilla/UiRadioGroup.js +18 -13
  15. package/vanilla/UiSwatch.d.ts +14 -0
  16. package/vanilla/UiSwatch.js +54 -0
  17. package/vanilla/index.js +2 -0
  18. package/vue/UiEditorChipEditor.d.ts +45 -0
  19. package/vue/UiEditorChipEditor.js +2 -0
  20. package/vue/UiEditorChipEditor.vue +163 -0
  21. package/vue/UiRadioGroup.d.ts +9 -0
  22. package/vue/UiRadioGroup.vue +44 -2
  23. package/vue/UiSwatch.d.ts +16 -0
  24. package/vue/UiSwatch.js +2 -0
  25. package/vue/UiSwatch.vue +150 -0
  26. package/vue/chunks/UiEditorChipEditor.js +157 -0
  27. package/vue/chunks/UiEditorImageToolbar.js +2 -2
  28. package/vue/chunks/UiEditorLinkCard.js +1 -1
  29. package/vue/chunks/UiEditorLinkForm.js +2 -2
  30. package/vue/chunks/UiRadioGroup.js +26 -3
  31. package/vue/chunks/UiSwatch.js +78 -0
  32. package/vue/components.css +461 -443
  33. package/vue/editor/index.js +132 -109
  34. package/vue/editor/primitives.d.ts +9 -1
  35. package/vue/index.d.ts +2 -0
  36. package/vue/index.js +6 -4
@@ -1,6 +1,6 @@
1
1
  import { computed, createApp, defineComponent, h, nextTick, onBeforeUnmount, onMounted, reactive, ref, shallowRef, useId, watch } from "vue";
2
- import { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_CHIP_COLORS, LOOMA_ICONS, createLoomaMentionExtension, createLoomaSlashCommandExtension, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getTableOfContentsEntries, getVisualViewportRect, handleTableAction, handleTableOverlayAction, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeLoomaChipColor, normalizeTableOfContentsConfig } from "@threadlabs/looma/editor";
3
- import { Button, Callout, Card, Checkbox, Cluster, Disclosure, EditorBlockActions, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorSurface, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, FormField, Icon, IconButton, Image, Input, List, Menu, MenuItem, Popover, Separator, Stack, Text, Tooltip } from "@threadlabs/looma/vue";
2
+ import { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ICONS, createLoomaMentionExtension, createLoomaSlashCommandExtension, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getTableOfContentsEntries, getVisualViewportRect, handleTableAction, handleTableOverlayAction, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeLoomaChipColor, normalizeTableOfContentsConfig } from "@threadlabs/looma/editor";
3
+ import { Button, Callout, Card, Checkbox, Cluster, Disclosure, EditorBlockActions, EditorChipEditor, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorSurface, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, FormField, Icon, IconButton, Image, Input, List, Menu, MenuItem, Popover, Separator, Stack, Text, Tooltip } from "@threadlabs/looma/vue";
4
4
  import { Editor, Extension, Node as Node$1, getText, getTextSerializersFromSchema, isNodeSelection, mergeAttributes, nodeInputRule, posToDOMRect } from "@tiptap/core";
5
5
  import { Editor as Editor$1 } from "@tiptap/vue-3";
6
6
  import { closeHistory } from "@tiptap/pm/history";
@@ -28,7 +28,8 @@ var EDITOR_ADAPTER_COMPONENT_TAG_MAP = {
28
28
  EditorLinkCard: "ui-editor-link-card",
29
29
  EditorLinkForm: "ui-editor-link-form",
30
30
  EditorBlockActions: "ui-editor-block-actions",
31
- EditorCodeLanguage: "ui-editor-code-language"
31
+ EditorCodeLanguage: "ui-editor-code-language",
32
+ EditorChipEditor: "ui-editor-chip-editor"
32
33
  };
33
34
  //#endregion
34
35
  //#region src/components/shared/overlay.js
@@ -1130,6 +1131,76 @@ function runBlockAction(editor, action) {
1130
1131
  return true;
1131
1132
  }
1132
1133
  //#endregion
1134
+ //#region src/editor/formatting-selection.ts
1135
+ var HOLD_MS = 500;
1136
+ var HOLD_SLOP_PX = 8;
1137
+ function createFormattingSelection(options) {
1138
+ const { editor } = options;
1139
+ let dismissedSelection = null;
1140
+ let heldSelection = null;
1141
+ let held = null;
1142
+ let timer;
1143
+ const cancel = () => {
1144
+ clearTimeout(timer);
1145
+ timer = void 0;
1146
+ held = null;
1147
+ };
1148
+ return {
1149
+ active() {
1150
+ const selection = editor.state.selection;
1151
+ return (selection instanceof TextSelection || selection instanceof AllSelection && Boolean(editor.state.doc.textContent)) && !dismissedSelection?.eq(selection) && (!selection.empty || Boolean(heldSelection?.eq(selection)) || options.shownAtCaret());
1152
+ },
1153
+ dismissed: (selection) => Boolean(dismissedSelection?.eq(selection)),
1154
+ dismiss(selection) {
1155
+ const current = editor.state.selection;
1156
+ dismissedSelection = selection !== void 0 ? selection : current instanceof TextSelection || current instanceof AllSelection ? current : null;
1157
+ heldSelection = null;
1158
+ },
1159
+ undismiss() {
1160
+ dismissedSelection = null;
1161
+ },
1162
+ release() {
1163
+ heldSelection = null;
1164
+ },
1165
+ selectionChanged() {
1166
+ const selection = editor.state.selection;
1167
+ if (!dismissedSelection?.eq(selection)) dismissedSelection = null;
1168
+ if (!heldSelection?.eq(selection)) heldSelection = null;
1169
+ },
1170
+ press: {
1171
+ down(event) {
1172
+ cancel();
1173
+ if (!options.canHold() || editor.isDestroyed || event.button !== 0 || !event.isPrimary || !(event.target instanceof Element) || !editor.view.dom.contains(event.target) || event.target.closest("button, input, select, textarea, [data-looma-image-node]")) return;
1174
+ held = {
1175
+ id: event.pointerId,
1176
+ x: event.clientX,
1177
+ y: event.clientY,
1178
+ time: event.timeStamp
1179
+ };
1180
+ timer = setTimeout(() => {
1181
+ timer = void 0;
1182
+ const selection = editor.state.selection;
1183
+ if (!held || !options.canHold() || !editor.isFocused || options.menuOpen() || !(selection instanceof TextSelection)) return;
1184
+ dismissedSelection = null;
1185
+ heldSelection = selection;
1186
+ options.onHold(selection);
1187
+ }, HOLD_MS);
1188
+ },
1189
+ move(event) {
1190
+ if (held?.id === event.pointerId && Math.hypot(event.clientX - held.x, event.clientY - held.y) > HOLD_SLOP_PX) cancel();
1191
+ },
1192
+ end(event) {
1193
+ if (held?.id === event.pointerId) cancel();
1194
+ },
1195
+ cancel,
1196
+ viewportChanged(event) {
1197
+ if (!event || !held || event.timeStamp >= held.time) cancel();
1198
+ }
1199
+ },
1200
+ destroy: cancel
1201
+ };
1202
+ }
1203
+ //#endregion
1133
1204
  //#region src/editor/table-overlay.ts
1134
1205
  function mapTableCells(table) {
1135
1206
  const rows = Array.from(table.rows);
@@ -2856,7 +2927,12 @@ var LoomaEditor = defineComponent({
2856
2927
  let linkEditing;
2857
2928
  const linkState = shallowRef(LOOMA_LINK_EDITING_IDLE);
2858
2929
  const chipAnchorId = `looma-editor-chip-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
2859
- const chipInput = ref(null);
2930
+ const chipEditorRoot = ref(null);
2931
+ const setChipEditorRoot = (instance) => {
2932
+ const element = instance?.$el;
2933
+ chipEditorRoot.value = element instanceof HTMLElement ? element : null;
2934
+ };
2935
+ const chipInput = () => chipEditorRoot.value?.querySelector("[data-chip-label]");
2860
2936
  const chipOpen = ref(false);
2861
2937
  const chipLabel = ref("");
2862
2938
  const chipColor = ref("neutral");
@@ -2902,29 +2978,18 @@ var LoomaEditor = defineComponent({
2902
2978
  const mobileToolbarStyle = ref({});
2903
2979
  const mobile = ref(typeof window !== "undefined" && window.innerWidth <= 767);
2904
2980
  const editorFocused = ref(false);
2905
- let dismissedFormattingSelection = null;
2906
- let heldFormattingSelection = null;
2981
+ let formatting;
2907
2982
  let formattingPopup = null;
2908
2983
  let linkPopup = null;
2909
2984
  let imagePopup = null;
2910
- let heldPressTimer;
2911
- let heldPress = null;
2912
2985
  const floatingFormattingMode = () => props.toolbarMode !== "sticky" && !(props.toolbarMode === "popover" && props.toolbarOpen) && !mobile.value;
2913
2986
  const formattingPickerOpen = () => linkState.value.surface === "popover" || tablePickerOpen.value || formatOpen.value;
2914
- const cancelHeldPress = () => {
2915
- clearTimeout(heldPressTimer);
2916
- heldPressTimer = void 0;
2917
- heldPress = null;
2918
- };
2919
2987
  const activeRangeActions = (instance) => {
2920
2988
  const selection = instance.state.selection;
2921
2989
  if (!(selection instanceof TextSelection) || !selection.empty) return [];
2922
2990
  return props.rangeActions.filter((action) => Number.isSafeInteger(action.from) && Number.isSafeInteger(action.to) && action.from >= 0 && action.to > action.from && action.to <= instance.state.doc.content.size && action.from <= selection.from && selection.from <= action.to);
2923
2991
  };
2924
- const formattingSelectionActive = (instance) => {
2925
- const selection = instance.state.selection;
2926
- return (selection instanceof TextSelection || selection instanceof AllSelection && Boolean(instance.state.doc.textContent)) && !dismissedFormattingSelection?.eq(selection) && (!selection.empty || Boolean(heldFormattingSelection?.eq(selection)) || activeRangeActions(instance).length > 0);
2927
- };
2992
+ const formattingActive = () => formatting?.active() ?? false;
2928
2993
  const editorStateVersion = ref(0);
2929
2994
  const mobileToolbarMode = ref("formatting");
2930
2995
  let dragLeaveTimer = null;
@@ -2979,7 +3044,7 @@ var LoomaEditor = defineComponent({
2979
3044
  chipLabel.value = String(node.attrs.label ?? "");
2980
3045
  chipColor.value = normalizeLoomaChipColor(node.attrs.color);
2981
3046
  chipOpen.value = true;
2982
- nextTick(() => chipInput.value?.focus());
3047
+ nextTick(() => chipInput()?.focus());
2983
3048
  };
2984
3049
  const changeChip = (changes) => {
2985
3050
  const instance = editor.value;
@@ -3094,7 +3159,7 @@ var LoomaEditor = defineComponent({
3094
3159
  window.open(href, "_blank", "noopener,noreferrer");
3095
3160
  return true;
3096
3161
  }
3097
- dismissedFormattingSelection = null;
3162
+ formatting?.undismiss();
3098
3163
  requestAnimationFrame(() => {
3099
3164
  if (view.isDestroyed) return;
3100
3165
  editorStateVersion.value += 1;
@@ -3117,10 +3182,9 @@ var LoomaEditor = defineComponent({
3117
3182
  onSelectionUpdate: ({ editor: instance }) => {
3118
3183
  rememberSelection(instance);
3119
3184
  if (floatingFormattingMode()) {
3120
- const selection = instance.state.selection;
3121
- if (!dismissedFormattingSelection?.eq(selection)) dismissedFormattingSelection = null;
3122
- if (!heldFormattingSelection?.eq(selection)) heldFormattingSelection = null;
3123
- if (!formattingSelectionActive(instance)) formattingPopup?.hide();
3185
+ instance.state.selection;
3186
+ formatting?.selectionChanged();
3187
+ if (!formattingActive()) formattingPopup?.hide();
3124
3188
  }
3125
3189
  },
3126
3190
  onUpdate: ({ editor: instance }) => {
@@ -3143,6 +3207,17 @@ var LoomaEditor = defineComponent({
3143
3207
  linkState.value = engine.state;
3144
3208
  });
3145
3209
  linkEditing = engine;
3210
+ const instance = session.editor;
3211
+ formatting = createFormattingSelection({
3212
+ editor: instance,
3213
+ canHold: () => floatingFormattingMode() && !tableResizeActive && props.editable,
3214
+ menuOpen: () => slash.active || mention.active,
3215
+ shownAtCaret: () => activeRangeActions(instance).length > 0,
3216
+ onHold: (selection) => {
3217
+ formattingPopup?.setAnchor(() => posToDOMRect(instance.view, selection.from, selection.to));
3218
+ formattingPopup?.show();
3219
+ }
3220
+ });
3146
3221
  });
3147
3222
  const attachDocument = (element) => {
3148
3223
  if (element instanceof HTMLElement) session?.attach(element);
@@ -3164,7 +3239,7 @@ var LoomaEditor = defineComponent({
3164
3239
  const to = selection?.to ?? current.to;
3165
3240
  linkEditing?.open(from, to, context ? "context" : "popover");
3166
3241
  nextTick(() => {
3167
- if (context) (props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(instance) ? formattingPopup : linkPopup)?.setAnchor(() => posToDOMRect(instance.view, instance.state.selection.from, instance.state.selection.to));
3242
+ if (context) (props.toolbarMode === "contextual" && floatingFormattingMode() && formattingActive() ? formattingPopup : linkPopup)?.setAnchor(() => posToDOMRect(instance.view, instance.state.selection.from, instance.state.selection.to));
3168
3243
  (linkState.value.mode === "url" ? linkDestinationInput() : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus();
3169
3244
  });
3170
3245
  };
@@ -3172,8 +3247,7 @@ var LoomaEditor = defineComponent({
3172
3247
  linkEditing?.close();
3173
3248
  const range = linkState.value.range;
3174
3249
  if (restoreFocus && range) editor.value?.chain().focus().setTextSelection(range.to).run();
3175
- dismissedFormattingSelection = editor.value?.state.selection ?? null;
3176
- heldFormattingSelection = null;
3250
+ formatting?.dismiss(editor.value?.state.selection ?? null);
3177
3251
  linkPopup?.hide();
3178
3252
  formattingPopup?.hide();
3179
3253
  };
@@ -3242,8 +3316,8 @@ var LoomaEditor = defineComponent({
3242
3316
  editor.value?.commands.dismissLoomaSlashMenu();
3243
3317
  });
3244
3318
  watch(() => props.editable, (editable) => {
3245
- cancelHeldPress();
3246
- heldFormattingSelection = null;
3319
+ formatting?.press.cancel();
3320
+ formatting?.release();
3247
3321
  editor.value?.setEditable(editable);
3248
3322
  if (!editable) formattingPopup?.hide();
3249
3323
  if (editor.value) imageDelivery.reset(editor.value);
@@ -3258,7 +3332,7 @@ var LoomaEditor = defineComponent({
3258
3332
  formatOpen
3259
3333
  ], ([linking, table, format]) => {
3260
3334
  if (linking || table || format || !floatingFormattingMode() || editor.value?.isFocused || formattingPopup?.element.contains(document.activeElement)) return;
3261
- heldFormattingSelection = null;
3335
+ formatting?.release();
3262
3336
  formattingPopup?.hide();
3263
3337
  });
3264
3338
  watch(() => props.rangeActions, () => {
@@ -3434,12 +3508,12 @@ var LoomaEditor = defineComponent({
3434
3508
  updateMobileViewport();
3435
3509
  };
3436
3510
  const onEditorBlur = () => {
3437
- cancelHeldPress();
3511
+ formatting?.press.cancel();
3438
3512
  queueMicrotask(updateTableUi);
3439
3513
  updateCodeUi();
3440
3514
  setTimeout(() => {
3441
3515
  if (!tableInteractionActive && !editor.value?.isFocused && !mobileToolbarShell.value?.contains(document.activeElement)) editorFocused.value = false;
3442
- if (!editor.value?.isFocused && !formattingPickerOpen() && !formattingPopup?.element.contains(document.activeElement)) heldFormattingSelection = null;
3516
+ if (!editor.value?.isFocused && !formattingPickerOpen() && !formattingPopup?.element.contains(document.activeElement)) formatting?.release();
3443
3517
  }, 0);
3444
3518
  };
3445
3519
  const bindEditorUi = (instance) => {
@@ -3465,15 +3539,15 @@ var LoomaEditor = defineComponent({
3465
3539
  }, { immediate: true });
3466
3540
  const onViewportChange = (event) => {
3467
3541
  if (event?.type === "scroll" && event.target instanceof Node && (tableToolbarShell.value?.contains(event.target) || tableMenuShell.value?.contains(event.target))) return;
3468
- if (!event || !heldPress || event.timeStamp >= heldPress.time) cancelHeldPress();
3542
+ formatting?.press.viewportChanged(event);
3469
3543
  const wasMobile = mobile.value;
3470
3544
  if (slash.active && slash.getRect) slash.rect = slash.getRect();
3471
3545
  if (mention.active && mention.getRect) mention.rect = mention.getRect();
3472
3546
  updateMobileViewport();
3473
- if (wasMobile !== mobile.value) heldFormattingSelection = null;
3547
+ if (wasMobile !== mobile.value) formatting?.release();
3474
3548
  updateTableUi();
3475
3549
  updateCodeUi();
3476
- if (floatingFormattingMode() && editor.value && !formattingSelectionActive(editor.value)) formattingPopup?.hide();
3550
+ if (floatingFormattingMode() && editor.value && !formattingActive()) formattingPopup?.hide();
3477
3551
  tableUi.menuOpen = false;
3478
3552
  };
3479
3553
  let stopViewport;
@@ -3510,42 +3584,17 @@ var LoomaEditor = defineComponent({
3510
3584
  }, 0);
3511
3585
  if ((tableUi.toolbarOpen || tableUi.overlayOpen || tableUi.menuOpen) && !inElement(root.value) && !inTableUi) closeTableUi();
3512
3586
  };
3513
- const onFormattingPointerDown = (event) => {
3514
- cancelHeldPress();
3515
- const instance = editor.value;
3516
- if (!floatingFormattingMode() || tableResizeActive || !props.editable || !instance || event.button !== 0 || !event.isPrimary || !(event.target instanceof Element) || !instance.view.dom.contains(event.target) || event.target.closest("button, input, select, textarea, [data-looma-image-node]")) return;
3517
- heldPress = {
3518
- id: event.pointerId,
3519
- x: event.clientX,
3520
- y: event.clientY,
3521
- time: event.timeStamp
3522
- };
3523
- heldPressTimer = setTimeout(() => {
3524
- heldPressTimer = void 0;
3525
- const selection = instance.state.selection;
3526
- if (!heldPress || !floatingFormattingMode() || tableResizeActive || !props.editable || !instance.isFocused || slash.active || mention.active || !(selection instanceof TextSelection)) return;
3527
- dismissedFormattingSelection = null;
3528
- heldFormattingSelection = selection;
3529
- formattingPopup?.setAnchor(() => posToDOMRect(instance.view, selection.from, selection.to));
3530
- formattingPopup?.show();
3531
- }, 500);
3532
- };
3533
- const onFormattingPointerMove = (event) => {
3534
- if (heldPress?.id === event.pointerId && Math.hypot(event.clientX - heldPress.x, event.clientY - heldPress.y) > 8) cancelHeldPress();
3535
- };
3536
- const onFormattingPointerEnd = (event) => {
3537
- if (heldPress?.id === event.pointerId) cancelHeldPress();
3538
- };
3587
+ const onFormattingPointerDown = (event) => formatting?.press.down(event);
3588
+ const onFormattingPointerMove = (event) => formatting?.press.move(event);
3589
+ const onFormattingPointerEnd = (event) => formatting?.press.end(event);
3539
3590
  const onFormattingKeyDown = (event) => {
3540
- cancelHeldPress();
3591
+ formatting?.press.cancel();
3541
3592
  if (event.key !== "Escape" || !(event.target instanceof Node) || !(root.value?.contains(event.target) || formattingPopup?.element.contains(event.target) || linkPopup?.element.contains(event.target)) || !(formatOpen.value || formattingPopup?.isOpen() || linkPopup?.isOpen())) return false;
3542
3593
  dismissFormatting();
3543
3594
  return true;
3544
3595
  };
3545
3596
  const dismissFormatting = () => {
3546
- const selection = editor.value?.state.selection;
3547
- dismissedFormattingSelection = selection instanceof TextSelection || selection instanceof AllSelection ? selection : null;
3548
- heldFormattingSelection = null;
3597
+ formatting?.dismiss();
3549
3598
  formatOpen.value = false;
3550
3599
  formattingPopup?.hide();
3551
3600
  linkPopup?.hide();
@@ -3568,10 +3617,10 @@ var LoomaEditor = defineComponent({
3568
3617
  editor: instance,
3569
3618
  element,
3570
3619
  pluginKey: "looma-text-formatting-menu",
3571
- shouldShow: ({ editor: menuEditor }) => (props.toolbarMode !== "popover" || !props.toolbarOpen) && !slash.active && !mention.active && (!props.editable && (props.selectionActions.length > 0 || props.rangeActions.length > 0) || menuEditor.isFocused || formattingPopup?.element.contains(document.activeElement) || linkState.value.surface === "context" || formattingPickerOpen()) && formattingSelectionActive(menuEditor),
3620
+ shouldShow: ({ editor: menuEditor }) => (props.toolbarMode !== "popover" || !props.toolbarOpen) && !slash.active && !mention.active && (!props.editable && (props.selectionActions.length > 0 || props.rangeActions.length > 0) || menuEditor.isFocused || formattingPopup?.element.contains(document.activeElement) || linkState.value.surface === "context" || formattingPickerOpen()) && formattingActive(),
3572
3621
  boundary: () => props.toolbarMode === "contextual" ? root.value : null,
3573
3622
  related: editorRoot,
3574
- onHide: () => instance.isEditable && floatingFormattingMode() && formattingPickerOpen() && formattingSelectionActive(instance) ? false : void 0,
3623
+ onHide: () => instance.isEditable && floatingFormattingMode() && formattingPickerOpen() && formattingActive() ? false : void 0,
3575
3624
  onDismiss: (reason) => reason === "escape" ? dismissFormatting() : formattingPopup?.hide()
3576
3625
  }));
3577
3626
  const linkSurfaceRef = surfaceRef((surface) => {
@@ -3580,7 +3629,7 @@ var LoomaEditor = defineComponent({
3580
3629
  editor: instance,
3581
3630
  element,
3582
3631
  pluginKey: "looma-link-context-menu",
3583
- shouldShow: ({ editor: menuEditor, from, to }) => from === to && menuEditor.isActive("link") && linkState.value.surface !== "popover" && !dismissedFormattingSelection?.eq(menuEditor.state.selection) && Boolean(menuEditor.isFocused || linkState.value.surface === "context" || linkPopup?.element.contains(document.activeElement)) && !(props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(menuEditor)),
3632
+ shouldShow: ({ editor: menuEditor, from, to }) => from === to && menuEditor.isActive("link") && linkState.value.surface !== "popover" && !formatting?.dismissed(menuEditor.state.selection) && Boolean(menuEditor.isFocused || linkState.value.surface === "context" || linkPopup?.element.contains(document.activeElement)) && !(props.toolbarMode === "contextual" && floatingFormattingMode() && formattingActive()),
3584
3633
  maxWidth: () => Math.min(480, window.innerWidth - 24),
3585
3634
  related: editorRoot,
3586
3635
  onDismiss: (reason) => reason === "escape" ? dismissFormatting() : linkPopup?.hide()
@@ -3747,7 +3796,7 @@ var LoomaEditor = defineComponent({
3747
3796
  window.removeEventListener("keyup", onLinkNavigationModifier);
3748
3797
  window.removeEventListener("pointermove", onLinkNavigationModifier);
3749
3798
  window.removeEventListener("blur", clearLinkNavigationModifier);
3750
- cancelHeldPress();
3799
+ formatting?.destroy();
3751
3800
  formattingPopup = null;
3752
3801
  document.removeEventListener("pointerdown", onDocumentPointerDown, true);
3753
3802
  document.removeEventListener("pointerdown", onFormattingPointerDown);
@@ -4177,12 +4226,12 @@ var LoomaEditor = defineComponent({
4177
4226
  ref: formattingSurfaceRef,
4178
4227
  class: "looma-editor__floating-shell looma-editor__formatting-shell",
4179
4228
  hidden: true
4180
- }, [props.toolbarMode === "contextual" ? h(Stack, { gap: "xs" }, () => [renderToolbar(instance, true), (linkState.value.surface === "context" || instance.state.selection.empty && instance.isActive("link")) && linkState.value.surface !== "popover" && formattingSelectionActive(instance) ? renderLinkContextCard(instance) : null]) : renderToolbar(instance, true, props.toolbarMode === "popover")])]) : null,
4229
+ }, [props.toolbarMode === "contextual" ? h(Stack, { gap: "xs" }, () => [renderToolbar(instance, true), (linkState.value.surface === "context" || instance.state.selection.empty && instance.isActive("link")) && linkState.value.surface !== "popover" && formattingActive() ? renderLinkContextCard(instance) : null]) : renderToolbar(instance, true, props.toolbarMode === "popover")])]) : null,
4181
4230
  instance && props.editable ? h("div", { key: "looma-link-host" }, [h("div", {
4182
4231
  ref: linkSurfaceRef,
4183
4232
  class: "looma-editor__floating-shell looma-editor__link-shell",
4184
4233
  hidden: true
4185
- }, [props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(instance) ? null : renderLinkContextCard(instance)])]) : null,
4234
+ }, [props.toolbarMode === "contextual" && floatingFormattingMode() && formattingActive() ? null : renderLinkContextCard(instance)])]) : null,
4186
4235
  instance && props.editable && !mobile.value && props.toolbarMode === "popover" && props.toolbarTriggerId ? h(Popover, {
4187
4236
  class: "looma-editor__formatting-popover",
4188
4237
  role: "region",
@@ -4267,46 +4316,20 @@ var LoomaEditor = defineComponent({
4267
4316
  onClose: () => {
4268
4317
  chipOpen.value = false;
4269
4318
  }
4270
- }, () => h("div", {
4271
- class: "looma-editor__chip-form",
4272
- role: "dialog",
4273
- "aria-label": "Edit chip"
4274
- }, [h("input", {
4275
- ref: chipInput,
4276
- type: "text",
4277
- value: chipLabel.value,
4278
- placeholder: "Set a label",
4279
- "aria-label": "Chip text",
4280
- onInput: (event) => {
4281
- chipLabel.value = event.target.value;
4319
+ }, () => h(EditorChipEditor, {
4320
+ ref: setChipEditorRoot,
4321
+ label: chipLabel.value,
4322
+ color: chipColor.value,
4323
+ onLabel: (event) => {
4324
+ chipLabel.value = event.detail.label;
4282
4325
  changeChip({ label: chipLabel.value });
4283
4326
  },
4284
- onKeydown: (event) => {
4285
- if (event.key === "Enter" || event.key === "Escape") {
4286
- event.preventDefault();
4287
- closeChip(true);
4288
- }
4289
- }
4290
- }), h("div", {
4291
- class: "looma-editor__chip-colors",
4292
- role: "group",
4293
- "aria-label": "Chip color"
4294
- }, LOOMA_CHIP_COLORS.map((color) => h("button", {
4295
- type: "button",
4296
- class: "looma-editor__chip-color",
4297
- "data-color": color,
4298
- "aria-label": `${color[0].toUpperCase()}${color.slice(1)} chip`,
4299
- "aria-pressed": chipColor.value === color,
4300
- onClick: () => {
4301
- if (chipColor.value === color) {
4302
- chipInput.value?.focus();
4303
- return;
4304
- }
4305
- chipColor.value = color;
4306
- changeChip({ color });
4307
- chipInput.value?.focus();
4308
- }
4309
- }, chipColor.value === color ? "✓" : "")))])),
4327
+ onColor: (event) => {
4328
+ chipColor.value = event.detail.color;
4329
+ changeChip({ color: event.detail.color });
4330
+ },
4331
+ onDone: () => closeChip(true)
4332
+ })),
4310
4333
  h(Popover, {
4311
4334
  class: "looma-editor__link-popover",
4312
4335
  size: "sm",
@@ -4470,4 +4493,4 @@ var LoomaEditor = defineComponent({
4470
4493
  }
4471
4494
  });
4472
4495
  //#endregion
4473
- export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorBlockActions, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, LoomaEditor };
4496
+ export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorBlockActions, EditorChipEditor, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, LoomaEditor };
@@ -3,7 +3,7 @@
3
3
  * `@threadlabs/looma/vue`, re-exported beside the editor that composes them.
4
4
  */
5
5
  import type { InsertTableEventDetail, MentionMenuHighlightEventDetail, MentionMenuSelectEventDetail, SlashMenuHighlightEventDetail, SlashMenuSelectEventDetail, TableContextMenuActionEventDetail } from "@threadlabs/looma/editor";
6
- export { EditorBlockActions, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, } from "@threadlabs/looma/vue";
6
+ export { EditorBlockActions, EditorChipEditor, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, } from "@threadlabs/looma/vue";
7
7
  /** The editor components' event details, by event name. */
8
8
  export interface VueEditorAdapterEventMap {
9
9
  highlight: MentionMenuHighlightEventDetail | SlashMenuHighlightEventDetail;
@@ -39,6 +39,13 @@ export interface VueEditorAdapterEventMap {
39
39
  language: {
40
40
  value: string;
41
41
  };
42
+ label: {
43
+ label: string;
44
+ };
45
+ color: {
46
+ color: "neutral" | "blue" | "green" | "yellow" | "red" | "purple";
47
+ };
48
+ done: Record<string, never>;
42
49
  "add-row-before": {
43
50
  boundaryIndex: number;
44
51
  };
@@ -107,4 +114,5 @@ export declare const EDITOR_ADAPTER_COMPONENT_TAG_MAP: {
107
114
  readonly EditorLinkForm: "ui-editor-link-form";
108
115
  readonly EditorBlockActions: "ui-editor-block-actions";
109
116
  readonly EditorCodeLanguage: "ui-editor-code-language";
117
+ readonly EditorChipEditor: "ui-editor-chip-editor";
110
118
  };
package/vue/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export { default as Dialog } from "./UiDialog.js";
20
20
  export { default as Disclosure } from "./UiDisclosure.js";
21
21
  export { default as Editable } from "./UiEditable.js";
22
22
  export { default as EditorBlockActions } from "./UiEditorBlockActions.js";
23
+ export { default as EditorChipEditor } from "./UiEditorChipEditor.js";
23
24
  export { default as EditorCodeLanguage } from "./UiEditorCodeLanguage.js";
24
25
  export { default as EditorImageToolbar } from "./UiEditorImageToolbar.js";
25
26
  export { default as EditorInsertTableGrid } from "./UiEditorInsertTableGrid.js";
@@ -66,6 +67,7 @@ export { default as Sidebar } from "./UiSidebar.js";
66
67
  export { default as Spinner } from "./UiSpinner.js";
67
68
  export { default as Stack } from "./UiStack.js";
68
69
  export { default as StatusMessage } from "./UiStatusMessage.js";
70
+ export { default as Swatch } from "./UiSwatch.js";
69
71
  export { default as Switch } from "./UiSwitch.js";
70
72
  export { default as Switcher } from "./UiSwitcher.js";
71
73
  export { default as Table } from "./UiTable.js";
package/vue/index.js CHANGED
@@ -26,13 +26,16 @@ import { t as UiEditable_default } from "./chunks/UiEditable.js";
26
26
  import { t as UiMenu_default } from "./chunks/UiMenu.js";
27
27
  import { t as UiMenuItem_default } from "./chunks/UiMenuItem.js";
28
28
  import { t as UiEditorBlockActions_default } from "./chunks/UiEditorBlockActions.js";
29
+ import { t as UiInput_default } from "./chunks/UiInput.js";
30
+ import { t as UiRadioGroup_default } from "./chunks/UiRadioGroup.js";
31
+ import { t as UiStack_default } from "./chunks/UiStack.js";
32
+ import { t as UiSwatch_default } from "./chunks/UiSwatch.js";
33
+ import { t as UiEditorChipEditor_default } from "./chunks/UiEditorChipEditor.js";
29
34
  import { t as UiEditorCodeLanguage_default } from "./chunks/UiEditorCodeLanguage.js";
30
35
  import { t as UiEditorToolbar_default } from "./chunks/UiEditorToolbar.js";
31
36
  import { t as UiFormField_default } from "./chunks/UiFormField.js";
32
37
  import { t as UiIconButton_default } from "./chunks/UiIconButton.js";
33
- import { t as UiInput_default } from "./chunks/UiInput.js";
34
38
  import { t as UiPopover_default } from "./chunks/UiPopover.js";
35
- import { t as UiStack_default } from "./chunks/UiStack.js";
36
39
  import { t as UiText_default } from "./chunks/UiText.js";
37
40
  import { t as UiEditorImageToolbar_default } from "./chunks/UiEditorImageToolbar.js";
38
41
  import { t as UiEditorInsertTableGrid_default } from "./chunks/UiEditorInsertTableGrid.js";
@@ -60,7 +63,6 @@ import { t as UiMeter_default } from "./chunks/UiMeter.js";
60
63
  import { t as UiNavItem_default } from "./chunks/UiNavItem.js";
61
64
  import { t as UiPageHeader_default } from "./chunks/UiPageHeader.js";
62
65
  import { t as UiRadio_default } from "./chunks/UiRadio.js";
63
- import { t as UiRadioGroup_default } from "./chunks/UiRadioGroup.js";
64
66
  import { t as UiReel_default } from "./chunks/UiReel.js";
65
67
  import { t as UiSearchShell_default } from "./chunks/UiSearchShell.js";
66
68
  import { t as UiSection_default } from "./chunks/UiSection.js";
@@ -79,4 +81,4 @@ import { t as UiTopBar_default } from "./chunks/UiTopBar.js";
79
81
  import { t as UiTree_default } from "./chunks/UiTree.js";
80
82
  import { t as UiTreeItem_default } from "./chunks/UiTreeItem.js";
81
83
  import { trackInputModality } from "@threadlabs/looma/components/shared/input-modality.js";
82
- export { UiActionBar_default as ActionBar, UiAffordanceScope_default as AffordanceScope, UiAvatar_default as Avatar, UiAvatarGroup_default as AvatarGroup, UiBadge_default as Badge, UiBreadcrumbItem_default as BreadcrumbItem, UiBreadcrumbs_default as Breadcrumbs, UiButton_default as Button, UiCallout_default as Callout, UiCard_default as Card, UiCheckbox_default as Checkbox, UiCluster_default as Cluster, UiCombobox_default as Combobox, UiContainer_default as Container, UiContextMenu_default as ContextMenu, UiCover_default as Cover, UiDescriptionItem_default as DescriptionItem, UiDescriptionList_default as DescriptionList, UiDialog_default as Dialog, UiDisclosure_default as Disclosure, UiEditable_default as Editable, UiEditorBlockActions_default as EditorBlockActions, UiEditorCodeLanguage_default as EditorCodeLanguage, UiEditorImageToolbar_default as EditorImageToolbar, UiEditorInsertTableGrid_default as EditorInsertTableGrid, UiEditorLinkCard_default as EditorLinkCard, UiEditorLinkForm_default as EditorLinkForm, UiEditorMentionMenu_default as EditorMentionMenu, UiEditorMentionMenuItem_default as EditorMentionMenuItem, UiEditorSlashMenu_default as EditorSlashMenu, UiEditorSlashMenuGroup_default as EditorSlashMenuGroup, UiEditorSlashMenuItem_default as EditorSlashMenuItem, UiEditorSurface_default as EditorSurface, UiEditorTableContextMenu_default as EditorTableContextMenu, UiEditorTableOverlay_default as EditorTableOverlay, UiEditorTableToolbar_default as EditorTableToolbar, UiEditorToolbar_default as EditorToolbar, UiFloatingActionButton_default as FloatingActionButton, UiFormField_default as FormField, UiGrid_default as Grid, UiIcon_default as Icon, UiIconButton_default as IconButton, UiImage_default as Image, UiInput_default as Input, UiInputGroup_default as InputGroup, UiList_default as List, UiListItem_default as ListItem, UiListbox_default as Listbox, UiMenu_default as Menu, UiMenuGroup_default as MenuGroup, UiMenuItem_default as MenuItem, UiMeter_default as Meter, UiNavItem_default as NavItem, UiPageHeader_default as PageHeader, UiPopover_default as Popover, UiRadio_default as Radio, UiRadioGroup_default as RadioGroup, UiReel_default as Reel, UiScrollArea_default as ScrollArea, UiSearchResultRow_default as SearchResultRow, UiSearchShell_default as SearchShell, UiSection_default as Section, UiSelect_default as Select, UiSeparator_default as Separator, UiSidebar_default as Sidebar, UiSpinner_default as Spinner, UiStack_default as Stack, UiStatusMessage_default as StatusMessage, UiSwitch_default as Switch, UiSwitcher_default as Switcher, UiTable_default as Table, UiTabs_default as Tabs, UiText_default as Text, UiTextarea_default as Textarea, UiToast_default as Toast, UiToastRegion_default as ToastRegion, UiTooltip_default as Tooltip, UiTopBar_default as TopBar, UiTree_default as Tree, UiTreeItem_default as TreeItem, trackInputModality };
84
+ export { UiActionBar_default as ActionBar, UiAffordanceScope_default as AffordanceScope, UiAvatar_default as Avatar, UiAvatarGroup_default as AvatarGroup, UiBadge_default as Badge, UiBreadcrumbItem_default as BreadcrumbItem, UiBreadcrumbs_default as Breadcrumbs, UiButton_default as Button, UiCallout_default as Callout, UiCard_default as Card, UiCheckbox_default as Checkbox, UiCluster_default as Cluster, UiCombobox_default as Combobox, UiContainer_default as Container, UiContextMenu_default as ContextMenu, UiCover_default as Cover, UiDescriptionItem_default as DescriptionItem, UiDescriptionList_default as DescriptionList, UiDialog_default as Dialog, UiDisclosure_default as Disclosure, UiEditable_default as Editable, UiEditorBlockActions_default as EditorBlockActions, UiEditorChipEditor_default as EditorChipEditor, UiEditorCodeLanguage_default as EditorCodeLanguage, UiEditorImageToolbar_default as EditorImageToolbar, UiEditorInsertTableGrid_default as EditorInsertTableGrid, UiEditorLinkCard_default as EditorLinkCard, UiEditorLinkForm_default as EditorLinkForm, UiEditorMentionMenu_default as EditorMentionMenu, UiEditorMentionMenuItem_default as EditorMentionMenuItem, UiEditorSlashMenu_default as EditorSlashMenu, UiEditorSlashMenuGroup_default as EditorSlashMenuGroup, UiEditorSlashMenuItem_default as EditorSlashMenuItem, UiEditorSurface_default as EditorSurface, UiEditorTableContextMenu_default as EditorTableContextMenu, UiEditorTableOverlay_default as EditorTableOverlay, UiEditorTableToolbar_default as EditorTableToolbar, UiEditorToolbar_default as EditorToolbar, UiFloatingActionButton_default as FloatingActionButton, UiFormField_default as FormField, UiGrid_default as Grid, UiIcon_default as Icon, UiIconButton_default as IconButton, UiImage_default as Image, UiInput_default as Input, UiInputGroup_default as InputGroup, UiList_default as List, UiListItem_default as ListItem, UiListbox_default as Listbox, UiMenu_default as Menu, UiMenuGroup_default as MenuGroup, UiMenuItem_default as MenuItem, UiMeter_default as Meter, UiNavItem_default as NavItem, UiPageHeader_default as PageHeader, UiPopover_default as Popover, UiRadio_default as Radio, UiRadioGroup_default as RadioGroup, UiReel_default as Reel, UiScrollArea_default as ScrollArea, UiSearchResultRow_default as SearchResultRow, UiSearchShell_default as SearchShell, UiSection_default as Section, UiSelect_default as Select, UiSeparator_default as Separator, UiSidebar_default as Sidebar, UiSpinner_default as Spinner, UiStack_default as Stack, UiStatusMessage_default as StatusMessage, UiSwatch_default as Swatch, UiSwitch_default as Switch, UiSwitcher_default as Switcher, UiTable_default as Table, UiTabs_default as Tabs, UiText_default as Text, UiTextarea_default as Textarea, UiToast_default as Toast, UiToastRegion_default as ToastRegion, UiTooltip_default as Tooltip, UiTopBar_default as TopBar, UiTree_default as Tree, UiTreeItem_default as TreeItem, trackInputModality };