@threadlabs/looma 0.22.10 → 0.22.12
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/shared/overlay.js +33 -1
- package/components/ui-dialog/ui-dialog.html +6 -0
- package/components/ui-dialog/ui-dialog.js +2 -2
- package/components/ui-input-group/ui-input-group.html +14 -0
- package/components/ui-search-shell/ui-search-shell.html +8 -0
- package/components/ui-search-shell/ui-search-shell.js +11 -1
- package/components/ui-sidebar/ui-sidebar.js +3 -0
- package/components/ui-toast-region/ui-toast-region.js +4 -0
- package/components/ui-tooltip/ui-tooltip.js +13 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-dialog.css +6 -0
- package/styles/ui-input-group.css +16 -0
- package/styles/ui-search-shell.css +8 -0
- package/vue/UiDialog.vue +6 -0
- package/vue/UiInputGroup.vue +14 -0
- package/vue/UiSearchShell.vue +8 -0
- package/vue/chunks/UiDialog.js +1 -1
- package/vue/chunks/UiInputGroup.js +1 -1
- package/vue/chunks/UiSearchShell.js +1 -1
- package/vue/components.css +145 -63
- package/vue/editor/LoomaEditor.d.ts +48 -0
- package/vue/editor/index.d.ts +1 -1
- package/vue/editor/index.js +174 -9
|
@@ -26,6 +26,25 @@ export type LoomaImageUploader = (file: File) => Promise<string | LoomaImageUplo
|
|
|
26
26
|
* bubble for selections); it does not alter document commands or stored content.
|
|
27
27
|
*/
|
|
28
28
|
export type LoomaEditorToolbarMode = "bubble" | "sticky" | "popover";
|
|
29
|
+
/** A destination supplied by the host application, such as a page or record. */
|
|
30
|
+
export interface LoomaLinkTarget {
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
33
|
+
detail?: string;
|
|
34
|
+
href: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Finds host records for a link without teaching the editor how they are stored.
|
|
38
|
+
* @ownership The host checks access and returns durable, openable href values.
|
|
39
|
+
* @failure A rejection leaves the existing document untouched and shows a retryable search error.
|
|
40
|
+
*/
|
|
41
|
+
export type LoomaLinkSearch = (query: string) => Promise<LoomaLinkTarget[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Describes a saved host link so a renamed record can show its current label.
|
|
44
|
+
* @ownership The host resolves identity and access; Looma keeps the saved href as is.
|
|
45
|
+
* @failure A rejection leaves the original href visible and editable.
|
|
46
|
+
*/
|
|
47
|
+
export type LoomaLinkResolve = (href: string) => Promise<LoomaLinkTarget | null>;
|
|
29
48
|
/**
|
|
30
49
|
* Turnkey Vue editor that composes Looma's framework-neutral Tiptap extensions
|
|
31
50
|
* with declarative menus, image delivery, and table interaction chrome.
|
|
@@ -88,6 +107,19 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
88
107
|
type: NumberConstructor;
|
|
89
108
|
default: number;
|
|
90
109
|
};
|
|
110
|
+
/** Host-owned search and identity lookup for links to application records. */
|
|
111
|
+
linkSearch: {
|
|
112
|
+
type: PropType<LoomaLinkSearch | undefined>;
|
|
113
|
+
default: undefined;
|
|
114
|
+
};
|
|
115
|
+
linkResolve: {
|
|
116
|
+
type: PropType<LoomaLinkResolve | undefined>;
|
|
117
|
+
default: undefined;
|
|
118
|
+
};
|
|
119
|
+
linkTargetLabel: {
|
|
120
|
+
type: StringConstructor;
|
|
121
|
+
default: string;
|
|
122
|
+
};
|
|
91
123
|
uploadImage: {
|
|
92
124
|
type: PropType<LoomaImageUploader | undefined>;
|
|
93
125
|
default: undefined;
|
|
@@ -181,6 +213,19 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
181
213
|
type: NumberConstructor;
|
|
182
214
|
default: number;
|
|
183
215
|
};
|
|
216
|
+
/** Host-owned search and identity lookup for links to application records. */
|
|
217
|
+
linkSearch: {
|
|
218
|
+
type: PropType<LoomaLinkSearch | undefined>;
|
|
219
|
+
default: undefined;
|
|
220
|
+
};
|
|
221
|
+
linkResolve: {
|
|
222
|
+
type: PropType<LoomaLinkResolve | undefined>;
|
|
223
|
+
default: undefined;
|
|
224
|
+
};
|
|
225
|
+
linkTargetLabel: {
|
|
226
|
+
type: StringConstructor;
|
|
227
|
+
default: string;
|
|
228
|
+
};
|
|
184
229
|
uploadImage: {
|
|
185
230
|
type: PropType<LoomaImageUploader | undefined>;
|
|
186
231
|
default: undefined;
|
|
@@ -233,6 +278,9 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
233
278
|
mentionItems: LoomaMentionItem[];
|
|
234
279
|
mentionProvider: LoomaMentionProvider | undefined;
|
|
235
280
|
mentionLimit: number;
|
|
281
|
+
linkSearch: LoomaLinkSearch | undefined;
|
|
282
|
+
linkResolve: LoomaLinkResolve | undefined;
|
|
283
|
+
linkTargetLabel: string;
|
|
236
284
|
uploadImage: LoomaImageUploader | undefined;
|
|
237
285
|
resolveImageAttributes: LoomaImageAttributeResolver | undefined;
|
|
238
286
|
toolbarMode: LoomaEditorToolbarMode;
|
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 LoomaImageUploader, type LoomaImageUploadResult, } from "./LoomaEditor.js";
|
|
3
|
+
export { LoomaEditor, type LoomaEditorToolbarMode, 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
|
@@ -23,6 +23,11 @@ var EDITOR_ADAPTER_COMPONENT_TAG_MAP = {
|
|
|
23
23
|
EditorTableOverlay: "ui-editor-table-overlay"
|
|
24
24
|
};
|
|
25
25
|
//#endregion
|
|
26
|
+
//#region src/components/shared/overlay.js
|
|
27
|
+
function announceOverlayOpen(document, id) {
|
|
28
|
+
document.dispatchEvent(new CustomEvent("ui-overlay-open", { detail: { id } }));
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
26
31
|
//#region src/vue/editor/image-delivery.ts
|
|
27
32
|
function optionalString(value) {
|
|
28
33
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -557,6 +562,19 @@ var LoomaEditor = defineComponent({
|
|
|
557
562
|
type: Number,
|
|
558
563
|
default: DEFAULT_MENTION_RESULT_LIMIT
|
|
559
564
|
},
|
|
565
|
+
/** Host-owned search and identity lookup for links to application records. */
|
|
566
|
+
linkSearch: {
|
|
567
|
+
type: Function,
|
|
568
|
+
default: void 0
|
|
569
|
+
},
|
|
570
|
+
linkResolve: {
|
|
571
|
+
type: Function,
|
|
572
|
+
default: void 0
|
|
573
|
+
},
|
|
574
|
+
linkTargetLabel: {
|
|
575
|
+
type: String,
|
|
576
|
+
default: "Item"
|
|
577
|
+
},
|
|
560
578
|
uploadImage: {
|
|
561
579
|
type: Function,
|
|
562
580
|
default: void 0
|
|
@@ -608,12 +626,22 @@ var LoomaEditor = defineComponent({
|
|
|
608
626
|
let blockActionTarget = null;
|
|
609
627
|
const linkAnchorId = `looma-editor-link-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
610
628
|
const linkUrlInput = ref(null);
|
|
629
|
+
const linkFormRoot = ref(null);
|
|
611
630
|
const linkOpen = ref(false);
|
|
612
631
|
const linkContextEditing = ref(false);
|
|
613
632
|
const linkHref = ref("");
|
|
614
633
|
const linkText = ref("");
|
|
615
634
|
const linkNewTab = ref(true);
|
|
616
635
|
const linkError = ref("");
|
|
636
|
+
const linkMode = ref("url");
|
|
637
|
+
const linkQuery = ref("");
|
|
638
|
+
const linkResults = ref([]);
|
|
639
|
+
const linkSearching = ref(false);
|
|
640
|
+
const linkSelectedTarget = ref(null);
|
|
641
|
+
const linkContextTarget = ref(null);
|
|
642
|
+
const linkTargetCache = /* @__PURE__ */ new Map();
|
|
643
|
+
let linkSearchSequence = 0;
|
|
644
|
+
let linkResolveSequence = 0;
|
|
617
645
|
const chipAnchorId = `looma-editor-chip-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
618
646
|
const chipInput = ref(null);
|
|
619
647
|
const chipOpen = ref(false);
|
|
@@ -857,6 +885,63 @@ var LoomaEditor = defineComponent({
|
|
|
857
885
|
emit("update", value);
|
|
858
886
|
}
|
|
859
887
|
});
|
|
888
|
+
watch(() => {
|
|
889
|
+
editorStateVersion.value;
|
|
890
|
+
return editor.value?.isActive("link") ? editor.value.getAttributes("link").href : null;
|
|
891
|
+
}, async (href) => {
|
|
892
|
+
const sequence = ++linkResolveSequence;
|
|
893
|
+
linkContextTarget.value = null;
|
|
894
|
+
if (!href || !props.linkResolve) return;
|
|
895
|
+
if (linkTargetCache.has(href)) {
|
|
896
|
+
linkContextTarget.value = linkTargetCache.get(href) ?? null;
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
try {
|
|
900
|
+
const target = await props.linkResolve(href);
|
|
901
|
+
linkTargetCache.set(href, target);
|
|
902
|
+
if (sequence === linkResolveSequence) linkContextTarget.value = target;
|
|
903
|
+
} catch {}
|
|
904
|
+
});
|
|
905
|
+
watch([
|
|
906
|
+
linkQuery,
|
|
907
|
+
linkMode,
|
|
908
|
+
linkOpen,
|
|
909
|
+
linkContextEditing
|
|
910
|
+
], async ([query, mode, open, contextEditing]) => {
|
|
911
|
+
const sequence = ++linkSearchSequence;
|
|
912
|
+
linkResults.value = [];
|
|
913
|
+
linkSearching.value = false;
|
|
914
|
+
if (!props.linkSearch || mode !== "target" || !open && !contextEditing || query.trim().length < 2 || linkSelectedTarget.value && query === linkSelectedTarget.value.label) return;
|
|
915
|
+
linkSearching.value = true;
|
|
916
|
+
try {
|
|
917
|
+
const results = await props.linkSearch(query.trim());
|
|
918
|
+
if (sequence === linkSearchSequence) linkResults.value = results.filter((target) => validLinkHref(target.href));
|
|
919
|
+
} catch {
|
|
920
|
+
if (sequence === linkSearchSequence) linkError.value = "Search is unavailable. Try again.";
|
|
921
|
+
} finally {
|
|
922
|
+
if (sequence === linkSearchSequence) linkSearching.value = false;
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
const selectLinkTarget = (target) => {
|
|
926
|
+
linkSelectedTarget.value = target;
|
|
927
|
+
linkTargetCache.set(target.href, target);
|
|
928
|
+
linkHref.value = target.href;
|
|
929
|
+
linkQuery.value = target.label;
|
|
930
|
+
linkResults.value = [];
|
|
931
|
+
linkNewTab.value = false;
|
|
932
|
+
linkError.value = "";
|
|
933
|
+
};
|
|
934
|
+
const moveLinkResultFocus = (event, offset) => {
|
|
935
|
+
const buttons = [...linkFormRoot.value?.querySelectorAll("[data-link-result]") ?? []];
|
|
936
|
+
const next = buttons[buttons.indexOf(event.target) + offset];
|
|
937
|
+
if (next) {
|
|
938
|
+
event.preventDefault();
|
|
939
|
+
next.focus();
|
|
940
|
+
} else if (offset < 0) {
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
linkFormRoot.value?.querySelector("[data-link-search]")?.focus();
|
|
943
|
+
}
|
|
944
|
+
};
|
|
860
945
|
const openLinkEditor = (context = false) => {
|
|
861
946
|
const instance = editor.value;
|
|
862
947
|
if (!instance || !props.editable) return;
|
|
@@ -878,17 +963,36 @@ var LoomaEditor = defineComponent({
|
|
|
878
963
|
linkNewTab.value = attrs.target !== "_self";
|
|
879
964
|
linkText.value = empty && !existing ? "" : instance.state.doc.textBetween(from, to);
|
|
880
965
|
linkError.value = "";
|
|
966
|
+
linkSelectedTarget.value = linkTargetCache.get(linkHref.value) ?? null;
|
|
967
|
+
linkMode.value = props.linkSearch && (!existing || linkSelectedTarget.value) ? "target" : "url";
|
|
968
|
+
linkQuery.value = linkSelectedTarget.value?.label ?? "";
|
|
881
969
|
linkContextEditing.value = context;
|
|
882
970
|
linkOpen.value = !context;
|
|
883
|
-
|
|
971
|
+
if (existing && props.linkResolve && !linkTargetCache.has(linkHref.value)) {
|
|
972
|
+
const href = linkHref.value;
|
|
973
|
+
props.linkResolve(href).then((target) => {
|
|
974
|
+
linkTargetCache.set(href, target);
|
|
975
|
+
if (linkHref.value === href && target) {
|
|
976
|
+
linkSelectedTarget.value = target;
|
|
977
|
+
linkMode.value = "target";
|
|
978
|
+
linkQuery.value = target.label;
|
|
979
|
+
}
|
|
980
|
+
}).catch(() => {});
|
|
981
|
+
}
|
|
982
|
+
nextTick(() => (linkMode.value === "url" ? linkUrlInput.value : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus());
|
|
884
983
|
};
|
|
885
984
|
const saveLink = () => {
|
|
886
985
|
const instance = editor.value;
|
|
887
986
|
const selection = linkSelection;
|
|
888
987
|
if (!instance || !selection) return;
|
|
889
|
-
const href = validLinkHref(linkHref.value);
|
|
988
|
+
const href = linkMode.value === "target" ? validLinkHref(linkSelectedTarget.value?.href ?? "") : validLinkHref(linkHref.value);
|
|
890
989
|
if (!href) {
|
|
891
|
-
linkError.value = "Enter an http, https, mailto, tel, or relative URL.";
|
|
990
|
+
linkError.value = linkMode.value === "target" ? "Choose a destination from the results." : "Enter an http, https, mailto, tel, or relative URL.";
|
|
991
|
+
(linkMode.value === "url" ? linkUrlInput.value : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus();
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if (linkMode.value === "url" && props.linkSearch && !selection.existing && !/^(https?:|mailto:|tel:)/i.test(href)) {
|
|
995
|
+
linkError.value = "Enter a full website, email, or phone URL, or choose a destination.";
|
|
892
996
|
linkUrlInput.value?.focus();
|
|
893
997
|
return;
|
|
894
998
|
}
|
|
@@ -940,6 +1044,7 @@ var LoomaEditor = defineComponent({
|
|
|
940
1044
|
linkContextEditing.value = false;
|
|
941
1045
|
};
|
|
942
1046
|
const renderLinkForm = () => h("form", {
|
|
1047
|
+
ref: linkFormRoot,
|
|
943
1048
|
class: "looma-editor__link-form",
|
|
944
1049
|
"aria-label": "Edit link",
|
|
945
1050
|
onSubmit: (event) => {
|
|
@@ -954,7 +1059,47 @@ var LoomaEditor = defineComponent({
|
|
|
954
1059
|
linkError.value = "";
|
|
955
1060
|
}
|
|
956
1061
|
})]) : null,
|
|
957
|
-
|
|
1062
|
+
props.linkSearch ? h("div", {
|
|
1063
|
+
class: "looma-editor__link-modes",
|
|
1064
|
+
role: "group",
|
|
1065
|
+
"aria-label": "Link destination"
|
|
1066
|
+
}, [h("button", {
|
|
1067
|
+
type: "button",
|
|
1068
|
+
"aria-pressed": linkMode.value === "target",
|
|
1069
|
+
onClick: () => {
|
|
1070
|
+
linkMode.value = "target";
|
|
1071
|
+
linkHref.value = "";
|
|
1072
|
+
linkSelectedTarget.value = null;
|
|
1073
|
+
linkQuery.value = "";
|
|
1074
|
+
linkError.value = "";
|
|
1075
|
+
nextTick(() => linkFormRoot.value?.querySelector("[data-link-search]")?.focus());
|
|
1076
|
+
}
|
|
1077
|
+
}, props.linkTargetLabel), h("button", {
|
|
1078
|
+
type: "button",
|
|
1079
|
+
"aria-pressed": linkMode.value === "url",
|
|
1080
|
+
onClick: () => {
|
|
1081
|
+
linkMode.value = "url";
|
|
1082
|
+
linkHref.value = "";
|
|
1083
|
+
linkSelectedTarget.value = null;
|
|
1084
|
+
linkError.value = "";
|
|
1085
|
+
nextTick(() => linkUrlInput.value?.focus());
|
|
1086
|
+
}
|
|
1087
|
+
}, "URL")]) : null,
|
|
1088
|
+
linkMode.value === "target" && props.linkSearch ? h("label", [h("span", `Find a ${props.linkTargetLabel.toLowerCase()}`), h("input", {
|
|
1089
|
+
"data-link-search": "",
|
|
1090
|
+
type: "search",
|
|
1091
|
+
value: linkQuery.value,
|
|
1092
|
+
autocomplete: "off",
|
|
1093
|
+
"aria-invalid": linkError.value ? "true" : void 0,
|
|
1094
|
+
onInput: (event) => {
|
|
1095
|
+
linkQuery.value = event.target.value;
|
|
1096
|
+
linkSelectedTarget.value = null;
|
|
1097
|
+
linkError.value = "";
|
|
1098
|
+
},
|
|
1099
|
+
onKeydown: (event) => {
|
|
1100
|
+
if (event.key === "ArrowDown") moveLinkResultFocus(event, 1);
|
|
1101
|
+
}
|
|
1102
|
+
})]) : h("label", [h("span", "URL"), h("input", {
|
|
958
1103
|
ref: linkUrlInput,
|
|
959
1104
|
type: "text",
|
|
960
1105
|
inputmode: "url",
|
|
@@ -965,7 +1110,25 @@ var LoomaEditor = defineComponent({
|
|
|
965
1110
|
linkError.value = "";
|
|
966
1111
|
}
|
|
967
1112
|
})]),
|
|
968
|
-
|
|
1113
|
+
linkMode.value === "target" && props.linkSearch ? h("div", {
|
|
1114
|
+
class: "looma-editor__link-results",
|
|
1115
|
+
"aria-label": "Destination search results"
|
|
1116
|
+
}, [
|
|
1117
|
+
linkSearching.value ? h("p", { role: "status" }, "Searching…") : null,
|
|
1118
|
+
linkSelectedTarget.value ? h("p", { class: "looma-editor__link-selected" }, `Selected: ${linkSelectedTarget.value.label}${linkSelectedTarget.value.detail ? ` · ${linkSelectedTarget.value.detail}` : ""}`) : null,
|
|
1119
|
+
!linkSearching.value && !linkSelectedTarget.value && linkQuery.value.trim().length < 2 ? h("p", "Type at least two letters to search.") : null,
|
|
1120
|
+
!linkSearching.value && !linkSelectedTarget.value && linkQuery.value.trim().length >= 2 && !linkResults.value.length && !linkError.value ? h("p", "No results found.") : null,
|
|
1121
|
+
...linkResults.value.map((target) => h("button", {
|
|
1122
|
+
key: target.id,
|
|
1123
|
+
type: "button",
|
|
1124
|
+
"data-link-result": "",
|
|
1125
|
+
onClick: () => selectLinkTarget(target),
|
|
1126
|
+
onKeydown: (event) => {
|
|
1127
|
+
if (event.key === "ArrowDown") moveLinkResultFocus(event, 1);
|
|
1128
|
+
if (event.key === "ArrowUp") moveLinkResultFocus(event, -1);
|
|
1129
|
+
}
|
|
1130
|
+
}, [h("strong", target.label), target.detail ? h("small", target.detail) : null]))
|
|
1131
|
+
]) : h("label", { class: "looma-editor__link-new-tab" }, [h("input", {
|
|
969
1132
|
type: "checkbox",
|
|
970
1133
|
checked: linkNewTab.value,
|
|
971
1134
|
onChange: (event) => {
|
|
@@ -976,7 +1139,7 @@ var LoomaEditor = defineComponent({
|
|
|
976
1139
|
class: "looma-editor__link-error",
|
|
977
1140
|
role: "alert"
|
|
978
1141
|
}, linkError.value) : null,
|
|
979
|
-
validLinkHref(linkHref.value) ? h("a", {
|
|
1142
|
+
linkMode.value === "url" && validLinkHref(linkHref.value) ? h("a", {
|
|
980
1143
|
class: "looma-editor__link-preview",
|
|
981
1144
|
href: validLinkHref(linkHref.value),
|
|
982
1145
|
target: "_blank",
|
|
@@ -1000,7 +1163,7 @@ var LoomaEditor = defineComponent({
|
|
|
1000
1163
|
h("span", {
|
|
1001
1164
|
class: "looma-editor__link-context-url",
|
|
1002
1165
|
title: href
|
|
1003
|
-
}, href),
|
|
1166
|
+
}, linkContextTarget.value ? [h("strong", linkContextTarget.value.label), linkContextTarget.value.detail ? h("small", linkContextTarget.value.detail) : null] : href),
|
|
1004
1167
|
h("button", {
|
|
1005
1168
|
type: "button",
|
|
1006
1169
|
onPointerdown: (event) => event.preventDefault(),
|
|
@@ -1722,7 +1885,8 @@ var LoomaEditor = defineComponent({
|
|
|
1722
1885
|
pluginKey: "looma-text-formatting-menu",
|
|
1723
1886
|
shouldShow: ({ editor: menuEditor, from, to }) => (props.toolbarMode !== "popover" || !props.toolbarOpen) && shouldShowTextFormattingToolbar(menuEditor, from, to),
|
|
1724
1887
|
tippyOptions: {
|
|
1725
|
-
appendTo: () => root.value ?? document.body,
|
|
1888
|
+
appendTo: () => root.value?.closest("dialog[open], [popover]") ?? document.body,
|
|
1889
|
+
onShow: () => announceOverlayOpen(root.value?.ownerDocument ?? document, root.value),
|
|
1726
1890
|
duration: 100,
|
|
1727
1891
|
maxWidth: "none",
|
|
1728
1892
|
placement: "top"
|
|
@@ -1733,7 +1897,8 @@ var LoomaEditor = defineComponent({
|
|
|
1733
1897
|
pluginKey: "looma-link-context-menu",
|
|
1734
1898
|
shouldShow: ({ editor: menuEditor, from, to }) => from === to && menuEditor.isActive("link") && !linkOpen.value,
|
|
1735
1899
|
tippyOptions: {
|
|
1736
|
-
appendTo: () => root.value ?? document.body,
|
|
1900
|
+
appendTo: () => root.value?.closest("dialog[open], [popover]") ?? document.body,
|
|
1901
|
+
onShow: () => announceOverlayOpen(root.value?.ownerDocument ?? document, root.value),
|
|
1737
1902
|
duration: 100,
|
|
1738
1903
|
maxWidth: "none",
|
|
1739
1904
|
placement: "bottom"
|