@singapore-editor/plugin-ui 0.1.0
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/README.md +27 -0
- package/dist/anchoredSurface.d.ts +48 -0
- package/dist/anchoredSurface.d.ts.map +1 -0
- package/dist/anchoredSurface.js +140 -0
- package/dist/anchoredSurface.js.map +1 -0
- package/dist/codeTokens.d.ts +24 -0
- package/dist/codeTokens.d.ts.map +1 -0
- package/dist/codeTokens.js +78 -0
- package/dist/codeTokens.js.map +1 -0
- package/dist/hoverController.d.ts +30 -0
- package/dist/hoverController.d.ts.map +1 -0
- package/dist/hoverController.js +277 -0
- package/dist/hoverController.js.map +1 -0
- package/dist/hoverParticipant.d.ts +48 -0
- package/dist/hoverParticipant.d.ts.map +1 -0
- package/dist/hoverParticipant.js +2 -0
- package/dist/hoverPlugin.d.ts +14 -0
- package/dist/hoverPlugin.d.ts.map +1 -0
- package/dist/hoverPlugin.js +49 -0
- package/dist/hoverPlugin.js.map +1 -0
- package/dist/hoverRegistry.d.ts +7 -0
- package/dist/hoverRegistry.d.ts.map +1 -0
- package/dist/hoverRegistry.js +24 -0
- package/dist/hoverRegistry.js.map +1 -0
- package/dist/hoverToken.d.ts +9 -0
- package/dist/hoverToken.d.ts.map +1 -0
- package/dist/hoverToken.js +17 -0
- package/dist/hoverToken.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/markdownTooltip.d.ts +12 -0
- package/dist/markdownTooltip.d.ts.map +1 -0
- package/dist/markdownTooltip.js +282 -0
- package/dist/markdownTooltip.js.map +1 -0
- package/dist/offsetRange.d.ts +24 -0
- package/dist/offsetRange.d.ts.map +1 -0
- package/dist/offsetRange.js +81 -0
- package/dist/offsetRange.js.map +1 -0
- package/dist/styles.d.ts +13 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +39 -0
- package/dist/styles.js.map +1 -0
- package/dist/tooltip.d.ts +71 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +786 -0
- package/dist/tooltip.js.map +1 -0
- package/package.json +100 -0
package/dist/tooltip.js
ADDED
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import { createAnchoredSurface } from "./anchoredSurface.js";
|
|
2
|
+
import { renderTooltipMarkdown } from "./markdownTooltip.js";
|
|
3
|
+
import { HOVER_COLORS, HOVER_THEME_VARIABLES } from "./styles.js";
|
|
4
|
+
//#region src/tooltip.ts
|
|
5
|
+
/** The full pointer-settle delay. Semantic requests begin halfway through it. */
|
|
6
|
+
var HOVER_REQUEST_DEBOUNCE_MS = 300;
|
|
7
|
+
var HOVER_ASYNC_DISPATCH_DELAY_MS = 150;
|
|
8
|
+
var HOVER_LOADING_DELAY_MS = 900;
|
|
9
|
+
var TOOLTIP_HIDE_DELAY_MS = 300;
|
|
10
|
+
var COPY_BUTTON_RESET_DELAY_MS = 1200;
|
|
11
|
+
var TOOLTIP_GAP_PX = 8;
|
|
12
|
+
var TOOLTIP_VIEWPORT_MARGIN_PX = 12;
|
|
13
|
+
var TOOLTIP_MIN_WIDTH_PX = 150;
|
|
14
|
+
var TOOLTIP_MIN_HEIGHT_PX = 32;
|
|
15
|
+
var TOOLTIP_MIN_MAX_HEIGHT_PX = 250;
|
|
16
|
+
var TOOLTIP_MAX_WIDTH_FRACTION = .66;
|
|
17
|
+
var TOOLTIP_POINTER_INTENT_TOLERANCE_PX = 4;
|
|
18
|
+
var TOOLTIP_RESIZE_HANDLE_PX = 5;
|
|
19
|
+
var TOOLTIP_SCROLL_STEP_PX = 30;
|
|
20
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
21
|
+
var TOOLTIP_THEME_VARIABLES = [
|
|
22
|
+
"--editor-background",
|
|
23
|
+
"--editor-popup-background",
|
|
24
|
+
"--editor-foreground",
|
|
25
|
+
"--editor-caret-color",
|
|
26
|
+
"--editor-font-family",
|
|
27
|
+
"--editor-font-size",
|
|
28
|
+
"--editor-row-height",
|
|
29
|
+
...HOVER_THEME_VARIABLES,
|
|
30
|
+
"--editor-syntax-bracket",
|
|
31
|
+
"--editor-syntax-comment",
|
|
32
|
+
"--editor-syntax-keyword",
|
|
33
|
+
"--editor-syntax-number",
|
|
34
|
+
"--editor-syntax-string",
|
|
35
|
+
"--editor-syntax-type"
|
|
36
|
+
];
|
|
37
|
+
var lastTooltipDimensions = /* @__PURE__ */ new WeakMap();
|
|
38
|
+
function createTooltipController(options) {
|
|
39
|
+
const { document, themeSource, reentryElement } = options;
|
|
40
|
+
const classNamespace = options.classNamespace ?? "plugin";
|
|
41
|
+
const tooltip = createTooltipElement(document, classNamespace);
|
|
42
|
+
document.body.append(tooltip);
|
|
43
|
+
let actionRows = /* @__PURE__ */ new WeakMap();
|
|
44
|
+
let hideTimer = null;
|
|
45
|
+
let pointerDown = false;
|
|
46
|
+
let keyboardFocusOwned = false;
|
|
47
|
+
let resize = null;
|
|
48
|
+
let disposed = false;
|
|
49
|
+
let anchorRect = null;
|
|
50
|
+
let placement = "top";
|
|
51
|
+
let closestPointerDistance = null;
|
|
52
|
+
const surface = createAnchoredSurface({
|
|
53
|
+
element: tooltip,
|
|
54
|
+
anchorClassName: tooltipClassName(classNamespace, "anchor"),
|
|
55
|
+
preferredPlacement: "top",
|
|
56
|
+
gapPx: TOOLTIP_GAP_PX,
|
|
57
|
+
viewportMarginPx: TOOLTIP_VIEWPORT_MARGIN_PX,
|
|
58
|
+
maxHeightPx: () => tooltipMaximumHeight(reentryElement, document),
|
|
59
|
+
onPlaced: (maxHeightPx, nextPlacement) => {
|
|
60
|
+
placement = nextPlacement;
|
|
61
|
+
setTooltipBodyMaxHeight(tooltip, maxHeightPx);
|
|
62
|
+
updateResizeHandles(tooltip, nextPlacement, classNamespace);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const cancelHide = () => {
|
|
66
|
+
if (!hideTimer) return;
|
|
67
|
+
clearTimeout(hideTimer);
|
|
68
|
+
hideTimer = null;
|
|
69
|
+
};
|
|
70
|
+
const hide = () => {
|
|
71
|
+
const restoreFocus = keyboardFocusOwned && tooltip.contains(document.activeElement);
|
|
72
|
+
cancelHide();
|
|
73
|
+
pointerDown = false;
|
|
74
|
+
keyboardFocusOwned = false;
|
|
75
|
+
resize = null;
|
|
76
|
+
closestPointerDistance = null;
|
|
77
|
+
tooltip.hidden = true;
|
|
78
|
+
anchorRect = null;
|
|
79
|
+
surface.release();
|
|
80
|
+
tooltip.replaceChildren();
|
|
81
|
+
actionRows = /* @__PURE__ */ new WeakMap();
|
|
82
|
+
if (restoreFocus) options.onRequestEditorFocus?.();
|
|
83
|
+
options.onDidHide?.();
|
|
84
|
+
};
|
|
85
|
+
const scheduleHide = () => {
|
|
86
|
+
if (pointerDown || resize) return;
|
|
87
|
+
if (hideTimer) clearTimeout(hideTimer);
|
|
88
|
+
hideTimer = setTimeout(() => {
|
|
89
|
+
hideTimer = null;
|
|
90
|
+
hide();
|
|
91
|
+
}, 300);
|
|
92
|
+
};
|
|
93
|
+
const show = (showOptions) => {
|
|
94
|
+
const focusState = tooltipFocusState(tooltip, document.activeElement);
|
|
95
|
+
const scrollState = tooltipScrollState(tooltip);
|
|
96
|
+
if (!anchorRect || !sameRect(anchorRect, showOptions.anchor)) closestPointerDistance = null;
|
|
97
|
+
anchorRect = showOptions.anchor;
|
|
98
|
+
const parts = showOptions.parts ?? [];
|
|
99
|
+
const hasNotes = parts.some((part) => part.notes && part.notes.length > 0);
|
|
100
|
+
placement = showOptions.preferredPlacement ?? (hasNotes ? "bottom" : "top");
|
|
101
|
+
syncEditorThemeVariables(tooltip, themeSource);
|
|
102
|
+
applyTooltipDimensions(tooltip, reentryElement, tooltip.hidden !== false);
|
|
103
|
+
renderTooltip(tooltip, {
|
|
104
|
+
actionRows,
|
|
105
|
+
hoverText: showOptions.hoverText,
|
|
106
|
+
parts,
|
|
107
|
+
theme: showOptions.theme,
|
|
108
|
+
loading: showOptions.loading ?? false,
|
|
109
|
+
markdownCodeBackground: options.markdownCodeBackground ?? false,
|
|
110
|
+
classNamespace,
|
|
111
|
+
codeTokenizer: options.codeTokenizer ?? null
|
|
112
|
+
});
|
|
113
|
+
surface.place(showOptions.anchor, placement);
|
|
114
|
+
restoreTooltipScroll(tooltip, scrollState);
|
|
115
|
+
restoreTooltipFocus(tooltip, focusState, showOptions.focus ?? false);
|
|
116
|
+
if (showOptions.focus) keyboardFocusOwned = true;
|
|
117
|
+
};
|
|
118
|
+
const reanchor = (nextAnchor) => {
|
|
119
|
+
if (tooltip.hidden) return;
|
|
120
|
+
anchorRect = nextAnchor;
|
|
121
|
+
closestPointerDistance = null;
|
|
122
|
+
surface.place(nextAnchor, placement);
|
|
123
|
+
};
|
|
124
|
+
const containsTarget = (target) => {
|
|
125
|
+
if (!(target instanceof Node)) return false;
|
|
126
|
+
return tooltip.contains(target);
|
|
127
|
+
};
|
|
128
|
+
const shouldKeepForPointer = (clientX, clientY) => {
|
|
129
|
+
if (tooltip.hidden || !anchorRect) return false;
|
|
130
|
+
if (pointerDown || resize) return true;
|
|
131
|
+
if (tooltip.contains(document.activeElement)) return true;
|
|
132
|
+
if (selectionInsideTooltip(document, tooltip)) return true;
|
|
133
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
134
|
+
const distance = pointToRectDistance(tooltipRect, clientX, clientY);
|
|
135
|
+
if (rectContainsPoint(tooltipRect, clientX, clientY)) {
|
|
136
|
+
closestPointerDistance = 0;
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (rectContainsPoint(anchorRect, clientX, clientY)) {
|
|
140
|
+
closestPointerDistance = distance;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
if (closestPointerDistance === null) return false;
|
|
144
|
+
if (distance > closestPointerDistance + TOOLTIP_POINTER_INTENT_TOLERANCE_PX) return false;
|
|
145
|
+
closestPointerDistance = Math.min(closestPointerDistance, distance);
|
|
146
|
+
return true;
|
|
147
|
+
};
|
|
148
|
+
const handleTooltipPointerEnter = () => cancelHide();
|
|
149
|
+
const handleTooltipPointerLeave = (event) => {
|
|
150
|
+
if (pointerDown || resize) return;
|
|
151
|
+
if (targetInsideElement(reentryElement, event.relatedTarget)) return;
|
|
152
|
+
scheduleHide();
|
|
153
|
+
};
|
|
154
|
+
const handleTooltipPointerDown = (event) => {
|
|
155
|
+
if (event.button !== 0) return;
|
|
156
|
+
pointerDown = true;
|
|
157
|
+
cancelHide();
|
|
158
|
+
const edge = resizeEdgeFromTarget(event.target);
|
|
159
|
+
if (!edge) return;
|
|
160
|
+
resize = beginTooltipResize(tooltip, reentryElement, event, edge);
|
|
161
|
+
event.preventDefault();
|
|
162
|
+
event.stopPropagation();
|
|
163
|
+
};
|
|
164
|
+
const handleDocumentPointerMove = (event) => {
|
|
165
|
+
if (!resize) return;
|
|
166
|
+
applyTooltipResize(tooltip, resize, event);
|
|
167
|
+
if (anchorRect) surface.place(anchorRect, placement);
|
|
168
|
+
event.preventDefault();
|
|
169
|
+
};
|
|
170
|
+
const handleDocumentPointerUp = () => {
|
|
171
|
+
if (resize) rememberTooltipDimensions(tooltip);
|
|
172
|
+
resize = null;
|
|
173
|
+
pointerDown = false;
|
|
174
|
+
};
|
|
175
|
+
const handleTooltipKeyDown = (event) => {
|
|
176
|
+
if (event.key === "Escape") {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
event.stopPropagation();
|
|
179
|
+
hide();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (isInteractiveKeyboardTarget(event.target)) return;
|
|
183
|
+
const body = tooltipBody(tooltip);
|
|
184
|
+
if (!body) return;
|
|
185
|
+
if (!scrollTooltipBody(body, event.key)) return;
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
event.stopPropagation();
|
|
188
|
+
};
|
|
189
|
+
tooltip.addEventListener("pointerenter", handleTooltipPointerEnter);
|
|
190
|
+
tooltip.addEventListener("pointerleave", handleTooltipPointerLeave);
|
|
191
|
+
tooltip.addEventListener("pointerdown", handleTooltipPointerDown);
|
|
192
|
+
tooltip.addEventListener("keydown", handleTooltipKeyDown);
|
|
193
|
+
document.addEventListener("pointermove", handleDocumentPointerMove);
|
|
194
|
+
document.addEventListener("pointerup", handleDocumentPointerUp);
|
|
195
|
+
document.addEventListener("pointercancel", handleDocumentPointerUp);
|
|
196
|
+
const dispose = () => {
|
|
197
|
+
if (disposed) return;
|
|
198
|
+
disposed = true;
|
|
199
|
+
cancelHide();
|
|
200
|
+
tooltip.removeEventListener("pointerenter", handleTooltipPointerEnter);
|
|
201
|
+
tooltip.removeEventListener("pointerleave", handleTooltipPointerLeave);
|
|
202
|
+
tooltip.removeEventListener("pointerdown", handleTooltipPointerDown);
|
|
203
|
+
tooltip.removeEventListener("keydown", handleTooltipKeyDown);
|
|
204
|
+
document.removeEventListener("pointermove", handleDocumentPointerMove);
|
|
205
|
+
document.removeEventListener("pointerup", handleDocumentPointerUp);
|
|
206
|
+
document.removeEventListener("pointercancel", handleDocumentPointerUp);
|
|
207
|
+
surface.dispose();
|
|
208
|
+
tooltip.remove();
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
show,
|
|
212
|
+
reanchor,
|
|
213
|
+
hide,
|
|
214
|
+
scheduleHide,
|
|
215
|
+
cancelHide,
|
|
216
|
+
containsTarget,
|
|
217
|
+
shouldKeepForPointer,
|
|
218
|
+
dispose
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function createTooltipElement(document, classNamespace) {
|
|
222
|
+
const element = document.createElement("div");
|
|
223
|
+
element.className = tooltipClassName(classNamespace);
|
|
224
|
+
element.hidden = true;
|
|
225
|
+
element.tabIndex = -1;
|
|
226
|
+
element.setAttribute("role", "dialog");
|
|
227
|
+
element.setAttribute("aria-label", "Editor hover");
|
|
228
|
+
Object.assign(element.style, {
|
|
229
|
+
zIndex: "1000",
|
|
230
|
+
width: "max-content",
|
|
231
|
+
minWidth: `${TOOLTIP_MIN_WIDTH_PX}px`,
|
|
232
|
+
overflow: "hidden",
|
|
233
|
+
padding: "0",
|
|
234
|
+
border: `1px solid ${HOVER_COLORS.border}`,
|
|
235
|
+
borderRadius: "2px",
|
|
236
|
+
boxSizing: "border-box",
|
|
237
|
+
background: `var(--editor-popup-background, ${HOVER_COLORS.background})`,
|
|
238
|
+
color: HOVER_COLORS.foreground,
|
|
239
|
+
boxShadow: `0 8px 28px ${HOVER_COLORS.shadow}`,
|
|
240
|
+
fontFamily: "var(--editor-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace)",
|
|
241
|
+
fontSize: "var(--editor-font-size, 13px)",
|
|
242
|
+
lineHeight: "1.45",
|
|
243
|
+
whiteSpace: "normal",
|
|
244
|
+
pointerEvents: "auto",
|
|
245
|
+
userSelect: "text",
|
|
246
|
+
cursor: "default"
|
|
247
|
+
});
|
|
248
|
+
return element;
|
|
249
|
+
}
|
|
250
|
+
function renderTooltip(element, content) {
|
|
251
|
+
element.replaceChildren();
|
|
252
|
+
element.setAttribute("aria-busy", String(content.loading));
|
|
253
|
+
const body = createTooltipBody(element.ownerDocument, content.classNamespace);
|
|
254
|
+
const parts = content.hoverText ? [{ markdown: content.hoverText }, ...content.parts] : content.parts;
|
|
255
|
+
let markdownIndex = 0;
|
|
256
|
+
for (const part of parts) {
|
|
257
|
+
if (part.markdown) {
|
|
258
|
+
body.append(hoverPart(content, element.ownerDocument, part.markdown, markdownIndex));
|
|
259
|
+
markdownIndex += 1;
|
|
260
|
+
}
|
|
261
|
+
for (const note of part.notes ?? []) body.append(noteSection(content, element.ownerDocument, note));
|
|
262
|
+
for (const action of part.actions ?? []) body.append(tooltipAction(element.ownerDocument, content, action));
|
|
263
|
+
}
|
|
264
|
+
if (content.loading) body.append(loadingSection(content, element.ownerDocument));
|
|
265
|
+
const firstRow = body.firstElementChild;
|
|
266
|
+
if (firstRow) firstRow.style.borderTop = "0";
|
|
267
|
+
element.append(body, createResizeHandle(element.ownerDocument, content.classNamespace, "right"), createResizeHandle(element.ownerDocument, content.classNamespace, "top"), createResizeHandle(element.ownerDocument, content.classNamespace, "bottom"));
|
|
268
|
+
element.hidden = false;
|
|
269
|
+
}
|
|
270
|
+
function createTooltipBody(document, classNamespace) {
|
|
271
|
+
const body = document.createElement("div");
|
|
272
|
+
body.className = tooltipClassName(classNamespace, "body");
|
|
273
|
+
Object.assign(body.style, {
|
|
274
|
+
width: "100%",
|
|
275
|
+
height: "100%",
|
|
276
|
+
minWidth: "0",
|
|
277
|
+
minHeight: "0",
|
|
278
|
+
overflowX: "auto",
|
|
279
|
+
overflowY: "auto",
|
|
280
|
+
scrollbarGutter: "stable"
|
|
281
|
+
});
|
|
282
|
+
return body;
|
|
283
|
+
}
|
|
284
|
+
function hoverPart(content, document, markdown, index) {
|
|
285
|
+
const row = createTooltipRow(content, document, "part");
|
|
286
|
+
row.tabIndex = 0;
|
|
287
|
+
row.dataset.hoverPartIndex = String(index);
|
|
288
|
+
row.setAttribute("role", "document");
|
|
289
|
+
row.setAttribute("aria-label", plainHoverText(markdown) || "Hover information");
|
|
290
|
+
row.append(renderTooltipMarkdown(document, markdown, content.theme, {
|
|
291
|
+
codeBackground: content.markdownCodeBackground,
|
|
292
|
+
classNamespace: content.classNamespace,
|
|
293
|
+
codeTokenizer: content.codeTokenizer
|
|
294
|
+
}));
|
|
295
|
+
const button = createCopyButton(document, plainHoverText(markdown), content.classNamespace);
|
|
296
|
+
row.append(button);
|
|
297
|
+
installCopyButtonVisibility(row, button);
|
|
298
|
+
return row;
|
|
299
|
+
}
|
|
300
|
+
function createTooltipRow(content, document, part) {
|
|
301
|
+
const row = document.createElement("div");
|
|
302
|
+
row.className = tooltipClassName(content.classNamespace, part);
|
|
303
|
+
Object.assign(row.style, {
|
|
304
|
+
position: "relative",
|
|
305
|
+
minWidth: "0",
|
|
306
|
+
padding: "6px 30px 6px 10px",
|
|
307
|
+
borderTop: `1px solid ${HOVER_COLORS.separator}`,
|
|
308
|
+
boxSizing: "border-box",
|
|
309
|
+
cursor: "text"
|
|
310
|
+
});
|
|
311
|
+
return row;
|
|
312
|
+
}
|
|
313
|
+
function noteSection(content, document, note) {
|
|
314
|
+
const section = createTooltipRow(content, document, "notes");
|
|
315
|
+
section.tabIndex = 0;
|
|
316
|
+
section.setAttribute("role", "document");
|
|
317
|
+
section.setAttribute("aria-label", noteCopyText(note));
|
|
318
|
+
section.append(noteRow(document, note));
|
|
319
|
+
for (const action of note.actions ?? []) section.append(tooltipAction(document, content, action));
|
|
320
|
+
const button = createCopyButton(document, noteCopyText(note), content.classNamespace);
|
|
321
|
+
section.append(button);
|
|
322
|
+
installCopyButtonVisibility(section, button);
|
|
323
|
+
return section;
|
|
324
|
+
}
|
|
325
|
+
function loadingSection(content, document) {
|
|
326
|
+
const row = createTooltipRow(content, document, "loading");
|
|
327
|
+
row.setAttribute("role", "status");
|
|
328
|
+
row.setAttribute("aria-live", "polite");
|
|
329
|
+
row.textContent = "Loading…";
|
|
330
|
+
row.style.color = HOVER_COLORS.secondaryForeground;
|
|
331
|
+
row.style.cursor = "progress";
|
|
332
|
+
return row;
|
|
333
|
+
}
|
|
334
|
+
function createCopyButton(document, copyText, classNamespace) {
|
|
335
|
+
const button = document.createElement("button");
|
|
336
|
+
button.type = "button";
|
|
337
|
+
button.className = tooltipClassName(classNamespace, "copy");
|
|
338
|
+
Object.assign(button.style, {
|
|
339
|
+
position: "absolute",
|
|
340
|
+
top: "4px",
|
|
341
|
+
right: "5px",
|
|
342
|
+
display: "inline-grid",
|
|
343
|
+
placeItems: "center",
|
|
344
|
+
width: "22px",
|
|
345
|
+
height: "22px",
|
|
346
|
+
border: "1px solid transparent",
|
|
347
|
+
borderRadius: "4px",
|
|
348
|
+
padding: "0",
|
|
349
|
+
background: "transparent",
|
|
350
|
+
color: HOVER_COLORS.secondaryForeground,
|
|
351
|
+
cursor: "pointer",
|
|
352
|
+
opacity: "0",
|
|
353
|
+
userSelect: "none"
|
|
354
|
+
});
|
|
355
|
+
setCopyButtonState(button, "idle");
|
|
356
|
+
button.addEventListener("mouseenter", () => styleCopyButtonHover(button, true));
|
|
357
|
+
button.addEventListener("mouseleave", () => styleCopyButtonHover(button, false));
|
|
358
|
+
button.addEventListener("click", (event) => {
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
event.stopPropagation();
|
|
361
|
+
handleCopyButtonClick(button, copyText);
|
|
362
|
+
});
|
|
363
|
+
return button;
|
|
364
|
+
}
|
|
365
|
+
function installCopyButtonVisibility(row, button) {
|
|
366
|
+
const show = () => {
|
|
367
|
+
button.style.opacity = "1";
|
|
368
|
+
};
|
|
369
|
+
const hide = (event) => {
|
|
370
|
+
if (targetInsideElement(row, event.relatedTarget)) return;
|
|
371
|
+
button.style.opacity = "0";
|
|
372
|
+
};
|
|
373
|
+
row.addEventListener("mouseenter", show);
|
|
374
|
+
row.addEventListener("mouseleave", hide);
|
|
375
|
+
row.addEventListener("focusin", show);
|
|
376
|
+
row.addEventListener("focusout", hide);
|
|
377
|
+
}
|
|
378
|
+
function setCopyButtonState(button, state) {
|
|
379
|
+
button.title = copyButtonLabel(state);
|
|
380
|
+
button.setAttribute("aria-label", copyButtonLabel(state));
|
|
381
|
+
button.style.color = copyButtonColor(state);
|
|
382
|
+
button.replaceChildren(copyButtonIcon(button.ownerDocument, state));
|
|
383
|
+
}
|
|
384
|
+
function styleCopyButtonHover(button, active) {
|
|
385
|
+
Object.assign(button.style, {
|
|
386
|
+
background: active ? "color-mix(in srgb, var(--editor-foreground, #a1a1aa) 14%, transparent)" : "transparent",
|
|
387
|
+
borderColor: active ? "color-mix(in srgb, var(--editor-foreground, #a1a1aa) 22%, transparent)" : "transparent"
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
function copyButtonLabel(state) {
|
|
391
|
+
if (state === "copied") return "Copied hover text";
|
|
392
|
+
if (state === "failed") return "Copy failed";
|
|
393
|
+
return "Copy hover text";
|
|
394
|
+
}
|
|
395
|
+
function copyButtonColor(state) {
|
|
396
|
+
if (state === "copied") return HOVER_COLORS.actionSuccess;
|
|
397
|
+
if (state === "failed") return HOVER_COLORS.actionFailure;
|
|
398
|
+
return HOVER_COLORS.secondaryForeground;
|
|
399
|
+
}
|
|
400
|
+
function copyButtonIcon(document, state) {
|
|
401
|
+
const icon = document.createElementNS(SVG_NS, "svg");
|
|
402
|
+
icon.setAttribute("viewBox", "0 0 24 24");
|
|
403
|
+
icon.setAttribute("width", "16");
|
|
404
|
+
icon.setAttribute("height", "16");
|
|
405
|
+
icon.setAttribute("aria-hidden", "true");
|
|
406
|
+
icon.setAttribute("fill", "none");
|
|
407
|
+
icon.setAttribute("stroke", "currentColor");
|
|
408
|
+
icon.setAttribute("stroke-width", "2");
|
|
409
|
+
icon.setAttribute("stroke-linecap", "round");
|
|
410
|
+
icon.setAttribute("stroke-linejoin", "round");
|
|
411
|
+
for (const pathData of copyButtonIconPaths(state)) {
|
|
412
|
+
const path = document.createElementNS(SVG_NS, "path");
|
|
413
|
+
path.setAttribute("d", pathData);
|
|
414
|
+
icon.append(path);
|
|
415
|
+
}
|
|
416
|
+
return icon;
|
|
417
|
+
}
|
|
418
|
+
function copyButtonIconPaths(state) {
|
|
419
|
+
if (state === "copied") return ["M20 6 9 17l-5-5"];
|
|
420
|
+
if (state === "failed") return [
|
|
421
|
+
"M12 8v5",
|
|
422
|
+
"M12 17h.01",
|
|
423
|
+
"M10.3 4h3.4L22 19H2L10.3 4Z"
|
|
424
|
+
];
|
|
425
|
+
return [
|
|
426
|
+
"M8 8h12v12H8Z",
|
|
427
|
+
"M4 4h12v2",
|
|
428
|
+
"M4 4v12h2"
|
|
429
|
+
];
|
|
430
|
+
}
|
|
431
|
+
async function handleCopyButtonClick(button, copyText) {
|
|
432
|
+
showCopyButtonStatus(button, await copyTextToClipboard(button.ownerDocument, copyText));
|
|
433
|
+
}
|
|
434
|
+
function showCopyButtonStatus(button, copied) {
|
|
435
|
+
setCopyButtonState(button, copied ? "copied" : "failed");
|
|
436
|
+
setTimeout(() => {
|
|
437
|
+
if (!button.isConnected) return;
|
|
438
|
+
setCopyButtonState(button, "idle");
|
|
439
|
+
}, COPY_BUTTON_RESET_DELAY_MS);
|
|
440
|
+
}
|
|
441
|
+
async function copyTextToClipboard(document, text) {
|
|
442
|
+
const clipboard = document.defaultView?.navigator.clipboard;
|
|
443
|
+
if (!clipboard) return copyTextWithTextarea(document, text);
|
|
444
|
+
try {
|
|
445
|
+
await clipboard.writeText(text);
|
|
446
|
+
return true;
|
|
447
|
+
} catch {
|
|
448
|
+
return copyTextWithTextarea(document, text);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function copyTextWithTextarea(document, text) {
|
|
452
|
+
const textarea = document.createElement("textarea");
|
|
453
|
+
textarea.value = text;
|
|
454
|
+
textarea.setAttribute("readonly", "");
|
|
455
|
+
Object.assign(textarea.style, {
|
|
456
|
+
position: "fixed",
|
|
457
|
+
top: "-9999px",
|
|
458
|
+
left: "-9999px",
|
|
459
|
+
opacity: "0"
|
|
460
|
+
});
|
|
461
|
+
document.body.append(textarea);
|
|
462
|
+
textarea.select();
|
|
463
|
+
try {
|
|
464
|
+
return document.execCommand("copy");
|
|
465
|
+
} catch {
|
|
466
|
+
return false;
|
|
467
|
+
} finally {
|
|
468
|
+
textarea.remove();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function plainHoverText(markdown) {
|
|
472
|
+
return markdown.replace(/^```[^\n]*\n/gm, "").replace(/^```\s*$/gm, "").trim();
|
|
473
|
+
}
|
|
474
|
+
function noteCopyText(note) {
|
|
475
|
+
const details = noteDetailsText(note);
|
|
476
|
+
return [`${note.text}${details ? ` ${details}` : ""}`, ...(note.related ?? []).map((link) => `${link.label}${link.text}`)].join("\n").trim();
|
|
477
|
+
}
|
|
478
|
+
function noteDetailsText(note) {
|
|
479
|
+
if (note.source && note.code) return `${note.source}(${note.code})`;
|
|
480
|
+
if (note.source) return note.source;
|
|
481
|
+
if (note.code) return `(${note.code})`;
|
|
482
|
+
return "";
|
|
483
|
+
}
|
|
484
|
+
function noteRow(document, note) {
|
|
485
|
+
const row = document.createElement("div");
|
|
486
|
+
row.style.minWidth = "0";
|
|
487
|
+
const message = document.createElement("span");
|
|
488
|
+
message.textContent = note.text;
|
|
489
|
+
message.style.whiteSpace = "pre-wrap";
|
|
490
|
+
row.append(message);
|
|
491
|
+
const details = noteDetails(document, note);
|
|
492
|
+
if (details) row.append(details);
|
|
493
|
+
for (const link of note.related ?? []) row.append(relatedRow(document, link));
|
|
494
|
+
return row;
|
|
495
|
+
}
|
|
496
|
+
function noteDetails(document, note) {
|
|
497
|
+
if (!note.source && !note.code) return null;
|
|
498
|
+
const details = document.createElement("span");
|
|
499
|
+
details.style.opacity = "0.6";
|
|
500
|
+
details.style.paddingLeft = "6px";
|
|
501
|
+
if (!note.codeHref || !note.code) {
|
|
502
|
+
details.textContent = noteDetailsText(note);
|
|
503
|
+
return details;
|
|
504
|
+
}
|
|
505
|
+
if (note.source) details.append(note.source);
|
|
506
|
+
const link = document.createElement("a");
|
|
507
|
+
link.href = note.codeHref;
|
|
508
|
+
link.target = "_blank";
|
|
509
|
+
link.rel = "noreferrer";
|
|
510
|
+
link.textContent = `(${note.code})`;
|
|
511
|
+
styleNoteLink(link);
|
|
512
|
+
details.append(link);
|
|
513
|
+
return details;
|
|
514
|
+
}
|
|
515
|
+
function relatedRow(document, link) {
|
|
516
|
+
const row = document.createElement("div");
|
|
517
|
+
row.style.marginTop = "8px";
|
|
518
|
+
const anchor = document.createElement("a");
|
|
519
|
+
anchor.textContent = link.label;
|
|
520
|
+
anchor.setAttribute("role", "button");
|
|
521
|
+
anchor.tabIndex = 0;
|
|
522
|
+
styleNoteLink(anchor);
|
|
523
|
+
const open = (event) => {
|
|
524
|
+
event.preventDefault();
|
|
525
|
+
event.stopPropagation();
|
|
526
|
+
link.open();
|
|
527
|
+
};
|
|
528
|
+
anchor.addEventListener("click", open);
|
|
529
|
+
anchor.addEventListener("keydown", (event) => {
|
|
530
|
+
if (event.key === "Enter" || event.key === " ") open(event);
|
|
531
|
+
});
|
|
532
|
+
const text = document.createElement("span");
|
|
533
|
+
text.textContent = link.text;
|
|
534
|
+
row.append(anchor, text);
|
|
535
|
+
return row;
|
|
536
|
+
}
|
|
537
|
+
function styleNoteLink(anchor) {
|
|
538
|
+
anchor.style.color = "var(--editor-caret-color, #93c5fd)";
|
|
539
|
+
anchor.style.textDecoration = "underline";
|
|
540
|
+
anchor.style.cursor = "pointer";
|
|
541
|
+
}
|
|
542
|
+
function createResizeHandle(document, classNamespace, edge) {
|
|
543
|
+
const handle = document.createElement("div");
|
|
544
|
+
handle.className = tooltipClassName(classNamespace, `resize-${edge}`);
|
|
545
|
+
handle.dataset.tooltipResizeEdge = edge;
|
|
546
|
+
handle.setAttribute("aria-hidden", "true");
|
|
547
|
+
Object.assign(handle.style, resizeHandleStyle(edge));
|
|
548
|
+
return handle;
|
|
549
|
+
}
|
|
550
|
+
function resizeHandleStyle(edge) {
|
|
551
|
+
if (edge === "right") return {
|
|
552
|
+
position: "absolute",
|
|
553
|
+
top: "0",
|
|
554
|
+
right: "-2px",
|
|
555
|
+
width: `${TOOLTIP_RESIZE_HANDLE_PX}px`,
|
|
556
|
+
height: "100%",
|
|
557
|
+
cursor: "ew-resize",
|
|
558
|
+
userSelect: "none"
|
|
559
|
+
};
|
|
560
|
+
return {
|
|
561
|
+
position: "absolute",
|
|
562
|
+
left: "0",
|
|
563
|
+
width: "100%",
|
|
564
|
+
height: `${TOOLTIP_RESIZE_HANDLE_PX}px`,
|
|
565
|
+
cursor: "ns-resize",
|
|
566
|
+
userSelect: "none"
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function updateResizeHandles(tooltip, placement, classNamespace) {
|
|
570
|
+
const top = tooltip.querySelector(`.${tooltipClassName(classNamespace, "resize-top")}`);
|
|
571
|
+
const bottom = tooltip.querySelector(`.${tooltipClassName(classNamespace, "resize-bottom")}`);
|
|
572
|
+
if (!top || !bottom) return;
|
|
573
|
+
top.hidden = placement !== "bottom";
|
|
574
|
+
top.style.top = "-2px";
|
|
575
|
+
bottom.hidden = placement !== "top";
|
|
576
|
+
bottom.style.bottom = "-2px";
|
|
577
|
+
}
|
|
578
|
+
function resizeEdgeFromTarget(target) {
|
|
579
|
+
if (!(target instanceof HTMLElement)) return null;
|
|
580
|
+
const edge = target.dataset.tooltipResizeEdge;
|
|
581
|
+
if (edge === "right" || edge === "top" || edge === "bottom") return edge;
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
function beginTooltipResize(tooltip, editor, event, edge) {
|
|
585
|
+
const rect = tooltip.getBoundingClientRect();
|
|
586
|
+
const styledMaxHeight = Number.parseFloat(tooltip.style.maxHeight);
|
|
587
|
+
return {
|
|
588
|
+
edge,
|
|
589
|
+
startX: event.clientX,
|
|
590
|
+
startY: event.clientY,
|
|
591
|
+
startWidth: rect.width,
|
|
592
|
+
startHeight: rect.height,
|
|
593
|
+
maxWidth: tooltipMaximumWidth(editor, tooltip.ownerDocument),
|
|
594
|
+
maxHeight: Number.isFinite(styledMaxHeight) ? styledMaxHeight : tooltipMaximumHeight(editor, tooltip.ownerDocument)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function applyTooltipResize(tooltip, resize, event) {
|
|
598
|
+
if (resize.edge === "right") {
|
|
599
|
+
const width = clamp(resize.startWidth + event.clientX - resize.startX, TOOLTIP_MIN_WIDTH_PX, resize.maxWidth);
|
|
600
|
+
tooltip.style.width = `${Math.round(width)}px`;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const delta = resize.edge === "top" ? resize.startY - event.clientY : event.clientY - resize.startY;
|
|
604
|
+
const height = clamp(resize.startHeight + delta, TOOLTIP_MIN_HEIGHT_PX, resize.maxHeight);
|
|
605
|
+
tooltip.style.height = `${Math.round(height)}px`;
|
|
606
|
+
}
|
|
607
|
+
function rememberTooltipDimensions(tooltip) {
|
|
608
|
+
const rect = tooltip.getBoundingClientRect();
|
|
609
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
610
|
+
lastTooltipDimensions.set(tooltip.ownerDocument, {
|
|
611
|
+
width: rect.width,
|
|
612
|
+
height: rect.height
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
function applyTooltipDimensions(tooltip, editor, resetToRememberedSize) {
|
|
616
|
+
const maximumWidth = tooltipMaximumWidth(editor, tooltip.ownerDocument);
|
|
617
|
+
const maximumHeight = tooltipMaximumHeight(editor, tooltip.ownerDocument);
|
|
618
|
+
tooltip.style.maxWidth = `${maximumWidth}px`;
|
|
619
|
+
tooltip.style.maxHeight = `${maximumHeight}px`;
|
|
620
|
+
if (!resetToRememberedSize) return;
|
|
621
|
+
const last = lastTooltipDimensions.get(tooltip.ownerDocument);
|
|
622
|
+
tooltip.style.width = last ? `${Math.min(last.width, maximumWidth)}px` : "max-content";
|
|
623
|
+
tooltip.style.height = last ? `${Math.min(last.height, maximumHeight)}px` : "auto";
|
|
624
|
+
}
|
|
625
|
+
function tooltipMaximumWidth(editor, document) {
|
|
626
|
+
const viewportWidth = document.defaultView?.innerWidth ?? 0;
|
|
627
|
+
const editorWidth = editor.getBoundingClientRect().width;
|
|
628
|
+
const desired = Math.max(editorWidth * TOOLTIP_MAX_WIDTH_FRACTION, 750);
|
|
629
|
+
const viewportMaximum = viewportWidth - 24;
|
|
630
|
+
return Math.max(TOOLTIP_MIN_WIDTH_PX, Math.min(desired, viewportMaximum));
|
|
631
|
+
}
|
|
632
|
+
function tooltipMaximumHeight(editor, document) {
|
|
633
|
+
const viewportHeight = document.defaultView?.innerHeight ?? 0;
|
|
634
|
+
const editorHeight = editor.getBoundingClientRect().height;
|
|
635
|
+
const lastHeight = lastTooltipDimensions.get(document)?.height ?? 0;
|
|
636
|
+
const desired = Math.max(editorHeight / 4, TOOLTIP_MIN_MAX_HEIGHT_PX, lastHeight);
|
|
637
|
+
const viewportMaximum = viewportHeight - 24;
|
|
638
|
+
return Math.max(TOOLTIP_MIN_HEIGHT_PX, Math.min(desired, viewportMaximum));
|
|
639
|
+
}
|
|
640
|
+
function setTooltipBodyMaxHeight(element, maxHeight) {
|
|
641
|
+
const body = tooltipBody(element);
|
|
642
|
+
if (!body) return;
|
|
643
|
+
body.style.maxHeight = `${Math.max(1, maxHeight - 2)}px`;
|
|
644
|
+
}
|
|
645
|
+
function tooltipBody(element) {
|
|
646
|
+
return element.querySelector(`.${tooltipClassNameForElement(element, "body")}`);
|
|
647
|
+
}
|
|
648
|
+
function tooltipScrollState(tooltip) {
|
|
649
|
+
const body = tooltipBody(tooltip);
|
|
650
|
+
return {
|
|
651
|
+
left: body?.scrollLeft ?? 0,
|
|
652
|
+
top: body?.scrollTop ?? 0
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function restoreTooltipScroll(tooltip, state) {
|
|
656
|
+
const body = tooltipBody(tooltip);
|
|
657
|
+
if (!body) return;
|
|
658
|
+
body.scrollLeft = state.left;
|
|
659
|
+
body.scrollTop = state.top;
|
|
660
|
+
}
|
|
661
|
+
function tooltipFocusState(tooltip, active) {
|
|
662
|
+
if (!active || !tooltip.contains(active)) return {
|
|
663
|
+
partIndex: null,
|
|
664
|
+
inside: false,
|
|
665
|
+
element: null
|
|
666
|
+
};
|
|
667
|
+
return {
|
|
668
|
+
partIndex: active.closest("[data-hover-part-index]")?.dataset.hoverPartIndex ?? null,
|
|
669
|
+
inside: true,
|
|
670
|
+
element: active instanceof HTMLElement ? active : null
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function restoreTooltipFocus(tooltip, previous, requested) {
|
|
674
|
+
if (!previous.inside && !requested) return;
|
|
675
|
+
if (previous.element && tooltip.contains(previous.element)) return previous.element.focus();
|
|
676
|
+
if (previous.partIndex !== null) {
|
|
677
|
+
const part = tooltip.querySelector(`[data-hover-part-index="${previous.partIndex}"]`);
|
|
678
|
+
if (part) return part.focus();
|
|
679
|
+
}
|
|
680
|
+
const firstPart = tooltip.querySelector("[data-hover-part-index], [tabindex=\"0\"]");
|
|
681
|
+
if (firstPart) return firstPart.focus();
|
|
682
|
+
tooltip.focus();
|
|
683
|
+
}
|
|
684
|
+
function scrollTooltipBody(body, key) {
|
|
685
|
+
if (key === "ArrowDown") body.scrollTop += TOOLTIP_SCROLL_STEP_PX;
|
|
686
|
+
else if (key === "ArrowUp") body.scrollTop -= TOOLTIP_SCROLL_STEP_PX;
|
|
687
|
+
else if (key === "PageDown") body.scrollTop += Math.max(1, body.clientHeight);
|
|
688
|
+
else if (key === "PageUp") body.scrollTop -= Math.max(1, body.clientHeight);
|
|
689
|
+
else if (key === "Home") body.scrollTop = 0;
|
|
690
|
+
else if (key === "End") body.scrollTop = body.scrollHeight;
|
|
691
|
+
else return false;
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
function isInteractiveKeyboardTarget(target) {
|
|
695
|
+
if (!(target instanceof Element)) return false;
|
|
696
|
+
return Boolean(target.closest("button, a, input, textarea, select"));
|
|
697
|
+
}
|
|
698
|
+
function selectionInsideTooltip(document, tooltip) {
|
|
699
|
+
const selection = document.getSelection();
|
|
700
|
+
if (!selection || selection.isCollapsed) return false;
|
|
701
|
+
return tooltip.contains(selection.anchorNode) || tooltip.contains(selection.focusNode);
|
|
702
|
+
}
|
|
703
|
+
function syncEditorThemeVariables(target, source) {
|
|
704
|
+
const style = source.ownerDocument.defaultView?.getComputedStyle(source);
|
|
705
|
+
if (!style) return;
|
|
706
|
+
for (const variable of TOOLTIP_THEME_VARIABLES) {
|
|
707
|
+
const value = source.style.getPropertyValue(variable).trim() || style.getPropertyValue(variable).trim();
|
|
708
|
+
if (value) {
|
|
709
|
+
target.style.setProperty(variable, value);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
target.style.removeProperty(variable);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function tooltipClassName(classNamespace, part) {
|
|
716
|
+
const base = `editor-${classNamespace}-hover`;
|
|
717
|
+
return part ? `${base}-${part}` : base;
|
|
718
|
+
}
|
|
719
|
+
function tooltipClassNameForElement(element, part) {
|
|
720
|
+
return tooltipClassName(tooltipNamespaceFromClassName(element.className), part);
|
|
721
|
+
}
|
|
722
|
+
function tooltipNamespaceFromClassName(className) {
|
|
723
|
+
return /^editor-(.+)-hover(?:\s|$)/.exec(className)?.[1] ?? "plugin";
|
|
724
|
+
}
|
|
725
|
+
function targetInsideElement(element, target) {
|
|
726
|
+
if (!(target instanceof Node)) return false;
|
|
727
|
+
return element.contains(target);
|
|
728
|
+
}
|
|
729
|
+
function pointToRectDistance(rect, clientX, clientY) {
|
|
730
|
+
const horizontal = Math.max(rect.left - clientX, 0, clientX - rect.right);
|
|
731
|
+
const vertical = Math.max(rect.top - clientY, 0, clientY - rect.bottom);
|
|
732
|
+
return Math.hypot(horizontal, vertical);
|
|
733
|
+
}
|
|
734
|
+
function rectContainsPoint(rect, clientX, clientY) {
|
|
735
|
+
if (clientX < rect.left || clientX > rect.right) return false;
|
|
736
|
+
if (clientY < rect.top) return false;
|
|
737
|
+
return clientY <= rect.bottom;
|
|
738
|
+
}
|
|
739
|
+
function sameRect(left, right) {
|
|
740
|
+
return left.left === right.left && left.top === right.top && left.width === right.width && left.height === right.height;
|
|
741
|
+
}
|
|
742
|
+
function clamp(value, minimum, maximum) {
|
|
743
|
+
return Math.max(minimum, Math.min(value, maximum));
|
|
744
|
+
}
|
|
745
|
+
async function runTooltipAction(button, failure, action) {
|
|
746
|
+
if (button.getAttribute("aria-disabled") === "true") return;
|
|
747
|
+
button.setAttribute("aria-disabled", "true");
|
|
748
|
+
button.setAttribute("aria-busy", "true");
|
|
749
|
+
failure.textContent = "";
|
|
750
|
+
try {
|
|
751
|
+
await action.run();
|
|
752
|
+
} catch (error) {
|
|
753
|
+
failure.textContent = error instanceof Error ? error.message : "Could not complete action.";
|
|
754
|
+
} finally {
|
|
755
|
+
button.removeAttribute("aria-disabled");
|
|
756
|
+
button.removeAttribute("aria-busy");
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
function tooltipAction(document, content, action) {
|
|
760
|
+
const existing = content.actionRows.get(action);
|
|
761
|
+
if (existing) return existing;
|
|
762
|
+
const row = createTooltipRow(content, document, "action");
|
|
763
|
+
content.actionRows.set(action, row);
|
|
764
|
+
const button = document.createElement("button");
|
|
765
|
+
button.type = "button";
|
|
766
|
+
button.textContent = action.label;
|
|
767
|
+
Object.assign(button.style, {
|
|
768
|
+
background: "transparent",
|
|
769
|
+
border: "0",
|
|
770
|
+
color: "inherit",
|
|
771
|
+
cursor: "pointer",
|
|
772
|
+
font: "inherit",
|
|
773
|
+
textDecoration: "underline"
|
|
774
|
+
});
|
|
775
|
+
const failure = document.createElement("span");
|
|
776
|
+
failure.setAttribute("role", "status");
|
|
777
|
+
button.addEventListener("click", () => {
|
|
778
|
+
runTooltipAction(button, failure, action);
|
|
779
|
+
});
|
|
780
|
+
row.append(button, failure);
|
|
781
|
+
return row;
|
|
782
|
+
}
|
|
783
|
+
//#endregion
|
|
784
|
+
export { COPY_BUTTON_RESET_DELAY_MS, HOVER_ASYNC_DISPATCH_DELAY_MS, HOVER_LOADING_DELAY_MS, HOVER_REQUEST_DEBOUNCE_MS, TOOLTIP_HIDE_DELAY_MS, createTooltipController };
|
|
785
|
+
|
|
786
|
+
//# sourceMappingURL=tooltip.js.map
|