@threadlabs/looma 0.26.23 → 0.26.25
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/package.json +1 -1
- package/vue/editor/index.js +66 -73
package/package.json
CHANGED
package/vue/editor/index.js
CHANGED
|
@@ -1760,6 +1760,7 @@ var LoomaEditor = defineComponent({
|
|
|
1760
1760
|
const linkContextEditing = ref(false);
|
|
1761
1761
|
const linkHref = ref("");
|
|
1762
1762
|
const linkText = ref("");
|
|
1763
|
+
const linkTextEditable = ref(false);
|
|
1763
1764
|
const linkNewTab = ref(true);
|
|
1764
1765
|
const linkError = ref("");
|
|
1765
1766
|
const linkMode = ref("url");
|
|
@@ -2129,6 +2130,8 @@ var LoomaEditor = defineComponent({
|
|
|
2129
2130
|
linkResults.value = [];
|
|
2130
2131
|
linkNewTab.value = false;
|
|
2131
2132
|
linkError.value = "";
|
|
2133
|
+
if (linkTextEditable.value && !linkText.value.trim()) linkText.value = target.label;
|
|
2134
|
+
applyLink();
|
|
2132
2135
|
};
|
|
2133
2136
|
const moveLinkResultFocus = (event, offset) => {
|
|
2134
2137
|
const buttons = [...linkFormRoot.value?.querySelectorAll("[data-link-result]") ?? []];
|
|
@@ -2157,6 +2160,7 @@ var LoomaEditor = defineComponent({
|
|
|
2157
2160
|
to,
|
|
2158
2161
|
existing
|
|
2159
2162
|
};
|
|
2163
|
+
linkTextEditable.value = empty && !existing;
|
|
2160
2164
|
const attrs = existing ? instance.getAttributes("link") : {};
|
|
2161
2165
|
linkHref.value = typeof attrs.href === "string" ? attrs.href : "";
|
|
2162
2166
|
linkNewTab.value = attrs.target !== "_self";
|
|
@@ -2178,38 +2182,45 @@ var LoomaEditor = defineComponent({
|
|
|
2178
2182
|
}
|
|
2179
2183
|
}).catch(() => {});
|
|
2180
2184
|
}
|
|
2181
|
-
nextTick(() =>
|
|
2185
|
+
nextTick(() => {
|
|
2186
|
+
if (context) (props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(instance) ? formattingPopup : linkPopup)?.setProps({ getReferenceClientRect: () => posToDOMRect(instance.view, instance.state.selection.from, instance.state.selection.to) });
|
|
2187
|
+
(linkMode.value === "url" ? linkDestinationInput() : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus();
|
|
2188
|
+
});
|
|
2182
2189
|
};
|
|
2183
|
-
const
|
|
2190
|
+
const applyLink = (reportError = false) => {
|
|
2184
2191
|
const instance = editor.value;
|
|
2185
2192
|
const selection = linkSelection;
|
|
2186
|
-
if (!instance || !selection) return;
|
|
2193
|
+
if (!instance || !selection) return false;
|
|
2187
2194
|
const href = linkMode.value === "target" ? validLinkHref(linkSelectedTarget.value?.href ?? "") : validLinkHref(linkHref.value);
|
|
2188
2195
|
if (!href) {
|
|
2189
|
-
linkError.value = linkMode.value === "target" ? "Choose a destination from the results." : "Enter an http, https, mailto, tel, or relative URL.";
|
|
2190
|
-
|
|
2191
|
-
return;
|
|
2196
|
+
if (reportError) linkError.value = linkMode.value === "target" ? "Choose a destination from the results." : "Enter an http, https, mailto, tel, or relative URL.";
|
|
2197
|
+
return false;
|
|
2192
2198
|
}
|
|
2193
2199
|
if (linkMode.value === "url" && props.linkSearch && !selection.existing && !/^(https?:|mailto:|tel:)/i.test(href)) {
|
|
2194
|
-
linkError.value = "Enter a full website, email, or phone URL, or choose a destination.";
|
|
2195
|
-
|
|
2196
|
-
return;
|
|
2200
|
+
if (reportError) linkError.value = "Enter a full website, email, or phone URL, or choose a destination.";
|
|
2201
|
+
return false;
|
|
2197
2202
|
}
|
|
2198
2203
|
const attrs = {
|
|
2199
2204
|
href: props.linkBaseUrl ? siteRelativeHref(href, props.linkBaseUrl) : href,
|
|
2200
2205
|
target: linkNewTab.value ? "_blank" : "_self",
|
|
2201
2206
|
rel: linkNewTab.value ? "noopener noreferrer" : null
|
|
2202
2207
|
};
|
|
2203
|
-
const
|
|
2208
|
+
const current = instance.state.selection;
|
|
2209
|
+
const chain = instance.chain().setTextSelection({
|
|
2204
2210
|
from: selection.from,
|
|
2205
2211
|
to: selection.to
|
|
2206
2212
|
});
|
|
2207
|
-
if (
|
|
2213
|
+
if (linkTextEditable.value) {
|
|
2208
2214
|
const text = linkText.value.trim();
|
|
2209
2215
|
if (!text) {
|
|
2210
|
-
linkError.value = "Enter link text.";
|
|
2211
|
-
return;
|
|
2216
|
+
if (reportError) linkError.value = "Enter link text.";
|
|
2217
|
+
return false;
|
|
2212
2218
|
}
|
|
2219
|
+
linkSelection = {
|
|
2220
|
+
from: selection.from,
|
|
2221
|
+
to: selection.from + text.length,
|
|
2222
|
+
existing: true
|
|
2223
|
+
};
|
|
2213
2224
|
chain.insertContent({
|
|
2214
2225
|
type: "text",
|
|
2215
2226
|
text,
|
|
@@ -2217,24 +2228,17 @@ var LoomaEditor = defineComponent({
|
|
|
2217
2228
|
type: "link",
|
|
2218
2229
|
attrs
|
|
2219
2230
|
}]
|
|
2220
|
-
}).run();
|
|
2231
|
+
}).setTextSelection(selection.from).run();
|
|
2221
2232
|
} else {
|
|
2222
2233
|
if (selection.existing && selection.from === selection.to) chain.extendMarkRange("link");
|
|
2223
|
-
chain.setLink(attrs).setTextSelection(
|
|
2234
|
+
chain.setLink(attrs).setTextSelection({
|
|
2235
|
+
from: current.from,
|
|
2236
|
+
to: current.to
|
|
2237
|
+
}).run();
|
|
2238
|
+
selection.existing = true;
|
|
2224
2239
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
};
|
|
2228
|
-
const removeLink = () => {
|
|
2229
|
-
const instance = editor.value;
|
|
2230
|
-
const selection = linkSelection;
|
|
2231
|
-
if (!instance || !selection?.existing) return;
|
|
2232
|
-
instance.chain().focus().setTextSelection({
|
|
2233
|
-
from: selection.from,
|
|
2234
|
-
to: selection.to
|
|
2235
|
-
}).extendMarkRange("link").unsetLink().setTextSelection(selection.to).run();
|
|
2236
|
-
linkOpen.value = false;
|
|
2237
|
-
linkContextEditing.value = false;
|
|
2240
|
+
linkError.value = "";
|
|
2241
|
+
return true;
|
|
2238
2242
|
};
|
|
2239
2243
|
const removeCurrentLink = () => {
|
|
2240
2244
|
const instance = editor.value;
|
|
@@ -2242,11 +2246,14 @@ var LoomaEditor = defineComponent({
|
|
|
2242
2246
|
instance.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
2243
2247
|
linkContextEditing.value = false;
|
|
2244
2248
|
};
|
|
2245
|
-
const
|
|
2249
|
+
const dismissLink = (restoreFocus = true) => {
|
|
2246
2250
|
linkOpen.value = false;
|
|
2247
2251
|
linkContextEditing.value = false;
|
|
2248
2252
|
const selection = linkSelection;
|
|
2249
|
-
if (selection) editor.value?.chain().focus().setTextSelection(selection.to).run();
|
|
2253
|
+
if (restoreFocus && selection) editor.value?.chain().focus().setTextSelection(selection.to).run();
|
|
2254
|
+
dismissedFormattingSelection = editor.value?.state.selection ?? null;
|
|
2255
|
+
linkPopup?.hide();
|
|
2256
|
+
formattingPopup?.hide();
|
|
2250
2257
|
};
|
|
2251
2258
|
const renderLinkDestination = (mode) => [h(FormField, { invalid: Boolean(linkError.value) }, {
|
|
2252
2259
|
label: () => h("label", props.linkSearch ? "Link destination" : "URL"),
|
|
@@ -2275,6 +2282,7 @@ var LoomaEditor = defineComponent({
|
|
|
2275
2282
|
}
|
|
2276
2283
|
linkHref.value = value;
|
|
2277
2284
|
linkError.value = "";
|
|
2285
|
+
applyLink();
|
|
2278
2286
|
},
|
|
2279
2287
|
onKeydown: (event) => {
|
|
2280
2288
|
if (mode === "target" && event.key === "ArrowDown") moveLinkResultFocus(event, 1);
|
|
@@ -2348,6 +2356,7 @@ var LoomaEditor = defineComponent({
|
|
|
2348
2356
|
checked: linkNewTab.value,
|
|
2349
2357
|
onChange: (event) => {
|
|
2350
2358
|
linkNewTab.value = event.detail.checked;
|
|
2359
|
+
applyLink();
|
|
2351
2360
|
}
|
|
2352
2361
|
}, () => "Open in new tab")];
|
|
2353
2362
|
const renderLinkForm = () => h("form", {
|
|
@@ -2356,7 +2365,18 @@ var LoomaEditor = defineComponent({
|
|
|
2356
2365
|
"aria-label": "Edit link",
|
|
2357
2366
|
onSubmit: (event) => {
|
|
2358
2367
|
event.preventDefault();
|
|
2359
|
-
|
|
2368
|
+
if (applyLink(true)) dismissLink();
|
|
2369
|
+
},
|
|
2370
|
+
onKeydown: (event) => {
|
|
2371
|
+
if (event.key === "Escape") {
|
|
2372
|
+
event.preventDefault();
|
|
2373
|
+
event.stopPropagation();
|
|
2374
|
+
dismissLink();
|
|
2375
|
+
} else if (event.key === "Enter" && event.target instanceof HTMLInputElement) {
|
|
2376
|
+
event.preventDefault();
|
|
2377
|
+
event.stopPropagation();
|
|
2378
|
+
if (applyLink(true)) dismissLink();
|
|
2379
|
+
}
|
|
2360
2380
|
}
|
|
2361
2381
|
}, [h(Stack, { gap: "s" }, () => [
|
|
2362
2382
|
h(Text, {
|
|
@@ -2365,7 +2385,7 @@ var LoomaEditor = defineComponent({
|
|
|
2365
2385
|
weight: "semibold"
|
|
2366
2386
|
}, () => linkSelection?.existing ? "Edit link" : "Add link"),
|
|
2367
2387
|
h(Stack, { gap: "s" }, () => renderLinkDestination(linkMode.value)),
|
|
2368
|
-
|
|
2388
|
+
linkTextEditable.value ? h(FormField, {}, {
|
|
2369
2389
|
label: () => h("label", "Text"),
|
|
2370
2390
|
default: () => h(Input, {
|
|
2371
2391
|
size: "sm",
|
|
@@ -2373,6 +2393,7 @@ var LoomaEditor = defineComponent({
|
|
|
2373
2393
|
onInput: (event) => {
|
|
2374
2394
|
linkText.value = event.target.value;
|
|
2375
2395
|
linkError.value = "";
|
|
2396
|
+
applyLink();
|
|
2376
2397
|
}
|
|
2377
2398
|
})
|
|
2378
2399
|
}) : null,
|
|
@@ -2380,42 +2401,7 @@ var LoomaEditor = defineComponent({
|
|
|
2380
2401
|
size: "sm",
|
|
2381
2402
|
tone: "danger",
|
|
2382
2403
|
role: "alert"
|
|
2383
|
-
}, () => linkError.value) : null
|
|
2384
|
-
h(Separator),
|
|
2385
|
-
h(Cluster, {
|
|
2386
|
-
gap: "xs",
|
|
2387
|
-
justify: "between",
|
|
2388
|
-
wrap: "nowrap"
|
|
2389
|
-
}, () => [linkSelection?.existing ? h(Button, {
|
|
2390
|
-
type: "button",
|
|
2391
|
-
variant: "ghost",
|
|
2392
|
-
tone: "danger",
|
|
2393
|
-
size: "sm",
|
|
2394
|
-
onClick: removeLink
|
|
2395
|
-
}, () => "Remove link") : null, validLinkHref(linkHref.value) ? h(Button, {
|
|
2396
|
-
as: "a",
|
|
2397
|
-
variant: "outline",
|
|
2398
|
-
tone: "accent",
|
|
2399
|
-
size: "sm",
|
|
2400
|
-
href: validLinkHref(linkHref.value),
|
|
2401
|
-
target: "_blank",
|
|
2402
|
-
rel: "noopener noreferrer"
|
|
2403
|
-
}, () => "Open link") : null]),
|
|
2404
|
-
h(Cluster, {
|
|
2405
|
-
gap: "xs",
|
|
2406
|
-
justify: "end",
|
|
2407
|
-
wrap: "nowrap"
|
|
2408
|
-
}, () => [h(Button, {
|
|
2409
|
-
type: "button",
|
|
2410
|
-
variant: "outline",
|
|
2411
|
-
tone: "accent",
|
|
2412
|
-
size: "sm",
|
|
2413
|
-
onClick: cancelLink
|
|
2414
|
-
}, () => "Cancel"), h(Button, {
|
|
2415
|
-
type: "submit",
|
|
2416
|
-
variant: "solid",
|
|
2417
|
-
size: "sm"
|
|
2418
|
-
}, () => "Save link")])
|
|
2404
|
+
}, () => linkError.value) : null
|
|
2419
2405
|
])]);
|
|
2420
2406
|
const renderLinkContext = (instance) => {
|
|
2421
2407
|
if (linkContextEditing.value) return renderLinkForm();
|
|
@@ -2787,6 +2773,7 @@ var LoomaEditor = defineComponent({
|
|
|
2787
2773
|
const onDocumentPointerDown = (event) => {
|
|
2788
2774
|
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
2789
2775
|
const inElement = (element) => Boolean(element && (path.includes(element) || event.target instanceof Node && element.contains(event.target)));
|
|
2776
|
+
if ((linkOpen.value || linkContextEditing.value) && !inElement(linkFormRoot.value)) dismissLink(false);
|
|
2790
2777
|
const inTableUi = inElement(tableToolbarShell.value) || inElement(tableOverlayShell.value) || inElement(tableMenuShell.value) || inElement(mobileToolbarShell.value);
|
|
2791
2778
|
tableInteractionActive = inTableUi;
|
|
2792
2779
|
if (inTableUi) setTimeout(() => {
|
|
@@ -3378,7 +3365,10 @@ var LoomaEditor = defineComponent({
|
|
|
3378
3365
|
placement: "top",
|
|
3379
3366
|
inverse: true
|
|
3380
3367
|
}, () => "Drag to resize column") : null,
|
|
3381
|
-
|
|
3368
|
+
h(Stack, {
|
|
3369
|
+
key: "looma-formatting-popup-host",
|
|
3370
|
+
gap: "none"
|
|
3371
|
+
}, () => [instance && (props.editable || props.selectionActions.length || props.rangeActions.length) && !mobile.value && (props.toolbarMode === "bubble" || props.toolbarMode === "popover" || props.toolbarMode === "contextual") ? h(BubbleMenu, {
|
|
3382
3372
|
editor: instance,
|
|
3383
3373
|
pluginKey: "looma-text-formatting-menu",
|
|
3384
3374
|
updateDelay: 0,
|
|
@@ -3407,8 +3397,11 @@ var LoomaEditor = defineComponent({
|
|
|
3407
3397
|
}
|
|
3408
3398
|
}] } } : {}
|
|
3409
3399
|
}
|
|
3410
|
-
}, { default: () => props.toolbarMode === "contextual" ? h(Stack, { gap: "xs" }, () => [renderToolbar(instance, true), (linkContextEditing.value || instance.state.selection.empty && instance.isActive("link")) && !linkOpen.value && formattingSelectionActive(instance) ? renderLinkContextCard(instance) : null]) : renderToolbar(instance, true, props.toolbarMode === "popover") }) : null,
|
|
3411
|
-
|
|
3400
|
+
}, { default: () => props.toolbarMode === "contextual" ? h(Stack, { gap: "xs" }, () => [renderToolbar(instance, true), (linkContextEditing.value || instance.state.selection.empty && instance.isActive("link")) && !linkOpen.value && formattingSelectionActive(instance) ? renderLinkContextCard(instance) : null]) : renderToolbar(instance, true, props.toolbarMode === "popover") }) : null]),
|
|
3401
|
+
h(Stack, {
|
|
3402
|
+
key: "looma-link-popup-host",
|
|
3403
|
+
gap: "none"
|
|
3404
|
+
}, () => [instance && props.editable ? h(BubbleMenu, {
|
|
3412
3405
|
editor: instance,
|
|
3413
3406
|
pluginKey: "looma-link-context-menu",
|
|
3414
3407
|
updateDelay: 0,
|
|
@@ -3426,7 +3419,7 @@ var LoomaEditor = defineComponent({
|
|
|
3426
3419
|
maxWidth: Math.min(480, window.innerWidth - 24),
|
|
3427
3420
|
placement: "top"
|
|
3428
3421
|
}
|
|
3429
|
-
}, { default: () => props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(instance) ? null : renderLinkContextCard(instance) }) : null,
|
|
3422
|
+
}, { default: () => props.toolbarMode === "contextual" && floatingFormattingMode() && formattingSelectionActive(instance) ? null : renderLinkContextCard(instance) }) : null]),
|
|
3430
3423
|
instance && props.editable && !mobile.value && props.toolbarMode === "popover" && props.toolbarTriggerId ? h(Popover, {
|
|
3431
3424
|
class: "looma-editor__formatting-popover",
|
|
3432
3425
|
role: "region",
|
|
@@ -3576,7 +3569,7 @@ var LoomaEditor = defineComponent({
|
|
|
3576
3569
|
const current = editor.value?.state.selection;
|
|
3577
3570
|
if (current && !current.empty && linkSelection && current.from === linkSelection.from && current.to === linkSelection.to) editor.value?.commands.setTextSelection(current.to);
|
|
3578
3571
|
}
|
|
3579
|
-
}, renderLinkForm),
|
|
3572
|
+
}, () => linkOpen.value ? renderLinkForm() : null),
|
|
3580
3573
|
props.editable && props.compactToolbar && !mobile.value ? h(Popover, {
|
|
3581
3574
|
class: "looma-editor__compact-formatting",
|
|
3582
3575
|
for: formatAnchorId,
|