@threadlabs/looma 0.26.9 → 0.26.11
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/components/ui-callout/ui-callout.html +13 -0
- package/components/ui-card/ui-card.html +26 -9
- package/components/ui-list-item/ui-list-item.html +4 -4
- package/components/ui-nav-item/ui-nav-item.html +4 -4
- package/components/ui-search-result-row/ui-search-result-row.html +3 -3
- package/components/ui-section/ui-section.html +12 -0
- package/components/ui-tree-item/ui-tree-item.html +6 -6
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-callout.css +13 -0
- package/styles/ui-card.css +26 -7
- package/styles/ui-list-item.css +4 -4
- package/styles/ui-nav-item.css +4 -4
- package/styles/ui-search-result-row.css +3 -3
- package/styles/ui-section.css +12 -0
- package/styles/ui-tree-item.css +6 -6
- package/tokens.css +2 -2
- package/vue/UiCallout.vue +33 -0
- package/vue/UiCard.vue +29 -8
- package/vue/UiListItem.vue +5 -4
- package/vue/UiNavItem.vue +8 -4
- package/vue/UiSearchResultRow.vue +3 -3
- package/vue/UiSection.vue +12 -0
- package/vue/UiTreeItem.vue +8 -6
- package/vue/chunks/UiCallout.js +1 -1
- package/vue/chunks/UiCard.js +2 -2
- package/vue/chunks/UiListItem.js +1 -1
- package/vue/chunks/UiNavItem.js +1 -1
- package/vue/chunks/UiSearchResultRow.js +1 -1
- package/vue/chunks/UiSection.js +1 -1
- package/vue/chunks/UiTreeItem.js +1 -1
- package/vue/components.css +323 -250
- package/vue/editor/LoomaEditor.d.ts +26 -0
- package/vue/editor/index.d.ts +1 -1
- package/vue/editor/index.js +51 -5
|
@@ -34,6 +34,19 @@ export interface LoomaEditorSelectionAction {
|
|
|
34
34
|
id: string;
|
|
35
35
|
label: string;
|
|
36
36
|
}
|
|
37
|
+
/** Host action available at a collapsed caret inside an ephemeral editor range. */
|
|
38
|
+
export interface LoomaEditorRangeAction extends LoomaEditorSelectionAction {
|
|
39
|
+
from: number;
|
|
40
|
+
to: number;
|
|
41
|
+
}
|
|
42
|
+
/** Matching ranges are grouped into one control for each action ID. */
|
|
43
|
+
export interface LoomaEditorRangeActionDetail {
|
|
44
|
+
id: string;
|
|
45
|
+
ranges: {
|
|
46
|
+
from: number;
|
|
47
|
+
to: number;
|
|
48
|
+
}[];
|
|
49
|
+
}
|
|
37
50
|
/** Exact editor range captured before focus leaves for a host action. */
|
|
38
51
|
export interface LoomaEditorSelectionActionDetail {
|
|
39
52
|
id: string;
|
|
@@ -184,6 +197,11 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
184
197
|
type: PropType<LoomaEditorSelectionAction[]>;
|
|
185
198
|
default: () => never[];
|
|
186
199
|
};
|
|
200
|
+
/** Opt-in caret actions; the host owns durable identities and recalculates positions after edits. */
|
|
201
|
+
rangeActions: {
|
|
202
|
+
type: PropType<LoomaEditorRangeAction[]>;
|
|
203
|
+
default: () => never[];
|
|
204
|
+
};
|
|
187
205
|
toolbarTriggerId: {
|
|
188
206
|
type: StringConstructor;
|
|
189
207
|
default: string;
|
|
@@ -209,6 +227,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
209
227
|
update: (_value: JSONContent) => true;
|
|
210
228
|
ready: (_editor: Editor) => true;
|
|
211
229
|
selectionAction: (_detail: LoomaEditorSelectionActionDetail) => true;
|
|
230
|
+
rangeAction: (_detail: LoomaEditorRangeActionDetail) => true;
|
|
212
231
|
uploadError: (_error: unknown, _file: File) => true;
|
|
213
232
|
imageActivate: (_detail: LoomaImageActivationDetail) => true;
|
|
214
233
|
imageRenditionError: (_detail: LoomaImageRenditionErrorDetail) => true;
|
|
@@ -316,6 +335,11 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
316
335
|
type: PropType<LoomaEditorSelectionAction[]>;
|
|
317
336
|
default: () => never[];
|
|
318
337
|
};
|
|
338
|
+
/** Opt-in caret actions; the host owns durable identities and recalculates positions after edits. */
|
|
339
|
+
rangeActions: {
|
|
340
|
+
type: PropType<LoomaEditorRangeAction[]>;
|
|
341
|
+
default: () => never[];
|
|
342
|
+
};
|
|
319
343
|
toolbarTriggerId: {
|
|
320
344
|
type: StringConstructor;
|
|
321
345
|
default: string;
|
|
@@ -339,6 +363,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
339
363
|
"onUpdate:toolbarOpen"?: ((_value: boolean) => any) | undefined;
|
|
340
364
|
onReady?: ((_editor: Editor) => any) | undefined;
|
|
341
365
|
onSelectionAction?: ((_detail: LoomaEditorSelectionActionDetail) => any) | undefined;
|
|
366
|
+
onRangeAction?: ((_detail: LoomaEditorRangeActionDetail) => any) | undefined;
|
|
342
367
|
onUploadError?: ((_error: unknown, _file: File) => any) | undefined;
|
|
343
368
|
onImageActivate?: ((_detail: LoomaImageActivationDetail) => any) | undefined;
|
|
344
369
|
onImageRenditionError?: ((_detail: LoomaImageRenditionErrorDetail) => any) | undefined;
|
|
@@ -364,6 +389,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
364
389
|
toolbarMode: LoomaEditorToolbarMode;
|
|
365
390
|
compactToolbar: boolean;
|
|
366
391
|
selectionActions: LoomaEditorSelectionAction[];
|
|
392
|
+
rangeActions: LoomaEditorRangeAction[];
|
|
367
393
|
toolbarTriggerId: string;
|
|
368
394
|
toolbarOpen: boolean;
|
|
369
395
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
package/vue/editor/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "@threadlabs/looma/editor";
|
|
2
2
|
export * from "./primitives.js";
|
|
3
|
-
export { LoomaEditor, type LoomaEditorToolbarMode, type LoomaEditorSelectionAction, type LoomaEditorSelectionActionDetail, type LoomaEditorSlashCommands, type LoomaLinkTarget, type LoomaLinkSearch, type LoomaLinkResolve, type LoomaImageUploader, type LoomaImageUploadResult, } from "./LoomaEditor.js";
|
|
3
|
+
export { LoomaEditor, type LoomaEditorToolbarMode, type LoomaEditorSelectionAction, type LoomaEditorSelectionActionDetail, type LoomaEditorRangeAction, type LoomaEditorRangeActionDetail, type LoomaEditorSlashCommands, type LoomaLinkTarget, type LoomaLinkSearch, type LoomaLinkResolve, type LoomaImageUploader, type LoomaImageUploadResult, } from "./LoomaEditor.js";
|
|
4
4
|
export type { LoomaImageActivationDetail, LoomaImageActivationTrigger, LoomaImageAttributeResolver, LoomaImageDescriptor, LoomaImageRenditionErrorDetail, LoomaImageRenditionAttributes, } from "./image-delivery.js";
|
package/vue/editor/index.js
CHANGED
|
@@ -1710,6 +1710,11 @@ var LoomaEditor = defineComponent({
|
|
|
1710
1710
|
type: Array,
|
|
1711
1711
|
default: () => []
|
|
1712
1712
|
},
|
|
1713
|
+
/** Opt-in caret actions; the host owns durable identities and recalculates positions after edits. */
|
|
1714
|
+
rangeActions: {
|
|
1715
|
+
type: Array,
|
|
1716
|
+
default: () => []
|
|
1717
|
+
},
|
|
1713
1718
|
toolbarTriggerId: {
|
|
1714
1719
|
type: String,
|
|
1715
1720
|
default: ""
|
|
@@ -1734,6 +1739,7 @@ var LoomaEditor = defineComponent({
|
|
|
1734
1739
|
update: (_value) => true,
|
|
1735
1740
|
ready: (_editor) => true,
|
|
1736
1741
|
selectionAction: (_detail) => true,
|
|
1742
|
+
rangeAction: (_detail) => true,
|
|
1737
1743
|
uploadError: (_error, _file) => true,
|
|
1738
1744
|
imageActivate: (_detail) => true,
|
|
1739
1745
|
imageRenditionError: (_detail) => true
|
|
@@ -1825,9 +1831,14 @@ var LoomaEditor = defineComponent({
|
|
|
1825
1831
|
heldPressTimer = void 0;
|
|
1826
1832
|
heldPress = null;
|
|
1827
1833
|
};
|
|
1834
|
+
const activeRangeActions = (instance) => {
|
|
1835
|
+
const selection = instance.state.selection;
|
|
1836
|
+
if (!(selection instanceof TextSelection) || !selection.empty) return [];
|
|
1837
|
+
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);
|
|
1838
|
+
};
|
|
1828
1839
|
const formattingSelectionActive = (instance) => {
|
|
1829
1840
|
const selection = instance.state.selection;
|
|
1830
|
-
return (selection instanceof TextSelection || selection instanceof AllSelection && Boolean(instance.state.doc.textContent)) && !dismissedFormattingSelection?.eq(selection) && (!selection.empty || Boolean(heldFormattingSelection?.eq(selection)));
|
|
1841
|
+
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);
|
|
1831
1842
|
};
|
|
1832
1843
|
const editorStateVersion = ref(0);
|
|
1833
1844
|
const mobileToolbarMode = ref("formatting");
|
|
@@ -2501,6 +2512,10 @@ var LoomaEditor = defineComponent({
|
|
|
2501
2512
|
heldFormattingSelection = null;
|
|
2502
2513
|
formattingPopup?.hide();
|
|
2503
2514
|
});
|
|
2515
|
+
watch(() => props.rangeActions, () => {
|
|
2516
|
+
const instance = editor.value;
|
|
2517
|
+
if (instance) instance.view.dispatch(instance.state.tr.setMeta("loomaRangeActionsChanged", true));
|
|
2518
|
+
});
|
|
2504
2519
|
watch(() => props.label, (label) => {
|
|
2505
2520
|
editor.value?.setOptions({ editorProps: { attributes: {
|
|
2506
2521
|
role: "textbox",
|
|
@@ -3054,9 +3069,40 @@ var LoomaEditor = defineComponent({
|
|
|
3054
3069
|
onClick: run
|
|
3055
3070
|
}, () => loomaIcon(icon));
|
|
3056
3071
|
};
|
|
3072
|
+
const renderRangeActions = (instance) => {
|
|
3073
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3074
|
+
for (const action of activeRangeActions(instance)) {
|
|
3075
|
+
const group = groups.get(action.id) ?? {
|
|
3076
|
+
label: action.label,
|
|
3077
|
+
ranges: /* @__PURE__ */ new Map()
|
|
3078
|
+
};
|
|
3079
|
+
group.ranges.set(`${action.from}:${action.to}`, {
|
|
3080
|
+
from: action.from,
|
|
3081
|
+
to: action.to
|
|
3082
|
+
});
|
|
3083
|
+
groups.set(action.id, group);
|
|
3084
|
+
}
|
|
3085
|
+
return [...groups].map(([id, group]) => h(Button, {
|
|
3086
|
+
key: id,
|
|
3087
|
+
"aria-label": group.label,
|
|
3088
|
+
type: "button",
|
|
3089
|
+
variant: "ghost",
|
|
3090
|
+
tone: "neutral",
|
|
3091
|
+
size: "sm",
|
|
3092
|
+
onPointerdown: (event) => event.preventDefault(),
|
|
3093
|
+
onClick: () => {
|
|
3094
|
+
emit("rangeAction", {
|
|
3095
|
+
id,
|
|
3096
|
+
ranges: [...group.ranges.values()]
|
|
3097
|
+
});
|
|
3098
|
+
formattingPopup?.hide();
|
|
3099
|
+
}
|
|
3100
|
+
}, () => group.label));
|
|
3101
|
+
};
|
|
3057
3102
|
const renderSelectionActions = (instance) => {
|
|
3058
3103
|
const selection = instance.state.selection;
|
|
3059
|
-
if (selection.empty
|
|
3104
|
+
if (selection.empty) return renderRangeActions(instance);
|
|
3105
|
+
if (!(selection instanceof TextSelection || selection instanceof AllSelection)) return [];
|
|
3060
3106
|
return props.selectionActions.map((action) => h(Button, {
|
|
3061
3107
|
key: action.id,
|
|
3062
3108
|
"aria-label": action.label,
|
|
@@ -3277,11 +3323,11 @@ var LoomaEditor = defineComponent({
|
|
|
3277
3323
|
placement: "top",
|
|
3278
3324
|
inverse: true
|
|
3279
3325
|
}, () => "Drag to resize column") : null,
|
|
3280
|
-
instance && (props.editable || props.selectionActions.length) && !mobile.value && (props.toolbarMode === "bubble" || props.toolbarMode === "popover" || props.toolbarMode === "contextual") ? h(BubbleMenu, {
|
|
3326
|
+
instance && (props.editable || props.selectionActions.length || props.rangeActions.length) && !mobile.value && (props.toolbarMode === "bubble" || props.toolbarMode === "popover" || props.toolbarMode === "contextual") ? h(BubbleMenu, {
|
|
3281
3327
|
editor: instance,
|
|
3282
3328
|
pluginKey: "looma-text-formatting-menu",
|
|
3283
3329
|
updateDelay: 0,
|
|
3284
|
-
shouldShow: ({ editor: menuEditor }) => (props.toolbarMode !== "popover" || !props.toolbarOpen) && !slash.active && !mention.active && (!props.editable && props.selectionActions.length > 0 || menuEditor.isFocused || formattingPopup?.popper.contains(document.activeElement) || linkContextEditing.value || formattingPickerOpen()) && formattingSelectionActive(menuEditor),
|
|
3330
|
+
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?.popper.contains(document.activeElement) || linkContextEditing.value || formattingPickerOpen()) && formattingSelectionActive(menuEditor),
|
|
3285
3331
|
tippyOptions: {
|
|
3286
3332
|
onCreate: (popup) => {
|
|
3287
3333
|
formattingPopup = popup;
|
|
@@ -3364,7 +3410,7 @@ var LoomaEditor = defineComponent({
|
|
|
3364
3410
|
value: codeUi.language,
|
|
3365
3411
|
onValueChange: (event) => chooseCodeLanguage(event.detail)
|
|
3366
3412
|
}, () => codeLanguageOptions.value.map((option) => h("option", { value: option.value }, option.label)))]) : null,
|
|
3367
|
-
instance && (props.editable || props.selectionActions.length && !instance.state.selection.empty) && mobile.value && (editorFocused.value || linkOpen.value || !props.editable) ? h("div", {
|
|
3413
|
+
instance && (props.editable || props.selectionActions.length && !instance.state.selection.empty || activeRangeActions(instance).length > 0) && mobile.value && (editorFocused.value || linkOpen.value || !props.editable) ? h("div", {
|
|
3368
3414
|
ref: mobileToolbarShell,
|
|
3369
3415
|
class: "looma-editor__mobile-toolbar-shell",
|
|
3370
3416
|
"data-mode": mobileToolbarMode.value,
|