@bendyline/docblocks-react 2.2.1 → 2.3.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 +7 -2
- package/THIRD_PARTY_NOTICES.txt +11972 -0
- package/dist/{ExportToolbarControls-2VB43ITO.js → ExportToolbarControls-6MO4NX6H.js} +142 -73
- package/dist/{GitToolbarControl-FW3BZFXV.js → GitToolbarControl-VJVK2B4X.js} +4 -3
- package/dist/{GitUI-GMH3PAYL.js → GitUI-CAKFVKWY.js} +4 -3
- package/dist/chunk-APC4KI4B.js +0 -0
- package/dist/{chunk-BPGEBGAN.js → chunk-JDJVRDOP.js} +23 -0
- package/dist/{chunk-TFMO7EVO.js → chunk-MRUK56JS.js} +23 -4
- package/dist/chunk-VRRL6VTD.js +606 -0
- package/dist/editor.d.ts +12 -1
- package/dist/editor.js +9 -3
- package/dist/index.d.ts +72 -8
- package/dist/index.js +2251 -806
- package/dist/settings/index.d.ts +34 -2
- package/dist/settings/index.js +9 -3
- package/package.json +19 -9
- package/src/styles/docblocks.css +540 -5
- package/dist/chunk-3QSZY74C.js +0 -255
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMenuKeyboard
|
|
3
|
+
} from "./chunk-MRUK56JS.js";
|
|
1
4
|
import {
|
|
2
5
|
loadTransformStyleSummaries
|
|
3
6
|
} from "./chunk-YBEYTVU2.js";
|
|
@@ -36,6 +39,8 @@ function loadRunExportModule() {
|
|
|
36
39
|
runExportModulePromise ?? (runExportModulePromise = import("./run-export-Z5BVFLIR.js"));
|
|
37
40
|
return runExportModulePromise;
|
|
38
41
|
}
|
|
42
|
+
var ExportCancelledError = class extends Error {
|
|
43
|
+
};
|
|
39
44
|
var SHARED_USE_MODES = Object.freeze({
|
|
40
45
|
slideshow: "slideshow",
|
|
41
46
|
video: "video",
|
|
@@ -69,10 +74,10 @@ function loadVideoExportModules() {
|
|
|
69
74
|
return videoExportModulesPromise;
|
|
70
75
|
}
|
|
71
76
|
function exportErrorMessage(caught) {
|
|
72
|
-
const detail = caught instanceof Error ? caught.message.trim() : "";
|
|
77
|
+
const detail = caught instanceof Error ? caught.message.replace(/^Error invoking remote method '[^']+': Error:\s*/, "").trim() : "";
|
|
73
78
|
return detail ? `Export failed: ${detail}` : "Export failed. The document could not be exported.";
|
|
74
79
|
}
|
|
75
|
-
function quickLabel(opts, transformSummaries) {
|
|
80
|
+
function quickLabel(opts, transformSummaries, destinationPath) {
|
|
76
81
|
const baseExt = FORMAT_EXTENSIONS[opts.format].toUpperCase().replace(".", "");
|
|
77
82
|
const isRecursiveHtml = opts.format === "html" && opts.includeLinkedDocs;
|
|
78
83
|
const ext = opts.format === "html" && (opts.htmlBundle === "zip" || isRecursiveHtml) ? "ZIP" : baseExt;
|
|
@@ -92,13 +97,16 @@ function quickLabel(opts, transformSummaries) {
|
|
|
92
97
|
if (transform) parts.push(transform.name);
|
|
93
98
|
}
|
|
94
99
|
if (parts.length > 0) {
|
|
95
|
-
|
|
100
|
+
const label2 = `Export ${ext} with ${parts.join(" + ")}`;
|
|
101
|
+
return destinationPath ? `${label2} to ${destinationPath}` : label2;
|
|
96
102
|
}
|
|
97
|
-
|
|
103
|
+
const label = `Export ${ext}`;
|
|
104
|
+
return destinationPath ? `${label} to ${destinationPath}` : label;
|
|
98
105
|
}
|
|
99
106
|
function ExportToolbarControls({
|
|
100
107
|
selectedFile,
|
|
101
108
|
mediaContainer,
|
|
109
|
+
mediaProvider,
|
|
102
110
|
saveBlob,
|
|
103
111
|
destinationAdapter,
|
|
104
112
|
trigger = "menu",
|
|
@@ -123,9 +131,14 @@ function ExportToolbarControls({
|
|
|
123
131
|
const [exporting, setExporting] = useState(false);
|
|
124
132
|
const [exportError, setExportError] = useState(null);
|
|
125
133
|
const [destinationTarget, setDestinationTarget] = useState(null);
|
|
134
|
+
const [quickDestination, setQuickDestination] = useState(null);
|
|
135
|
+
const [quickDestinationPending, setQuickDestinationPending] = useState(false);
|
|
126
136
|
const destinationRequestRef = useRef(0);
|
|
127
|
-
const
|
|
137
|
+
const menuContainerRef = useRef(null);
|
|
138
|
+
const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } = useMenuKeyboard(menuOpen, setMenuOpen);
|
|
128
139
|
const lastOptions = loadLastExportOptions();
|
|
140
|
+
const quickDestinationKey = lastOptions ? JSON.stringify([selectedFile, lastOptions]) : null;
|
|
141
|
+
const quickDestinationTarget = quickDestination?.key === quickDestinationKey ? quickDestination.target : null;
|
|
129
142
|
const docThemeId = useMemo(() => {
|
|
130
143
|
const fm = markdownDoc?.frontmatter;
|
|
131
144
|
if (!fm) return null;
|
|
@@ -142,12 +155,17 @@ function ExportToolbarControls({
|
|
|
142
155
|
useEffect(() => {
|
|
143
156
|
if (!menuOpen) return;
|
|
144
157
|
const onPointerDown = (e) => {
|
|
145
|
-
if (
|
|
146
|
-
|
|
158
|
+
if (menuContainerRef.current && !menuContainerRef.current.contains(e.target)) {
|
|
159
|
+
closeMenu(false);
|
|
147
160
|
}
|
|
148
161
|
};
|
|
149
162
|
document.addEventListener("pointerdown", onPointerDown);
|
|
150
163
|
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
164
|
+
}, [closeMenu, menuOpen]);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (menuOpen) return;
|
|
167
|
+
setQuickDestination(null);
|
|
168
|
+
setQuickDestinationPending(false);
|
|
151
169
|
}, [menuOpen]);
|
|
152
170
|
useEffect(() => {
|
|
153
171
|
if (!menuOpen || lastOptions?.format !== "pptx" || !lastOptions.transformStyle || transformSummaries.length > 0) {
|
|
@@ -163,9 +181,33 @@ function ExportToolbarControls({
|
|
|
163
181
|
cancelled = true;
|
|
164
182
|
};
|
|
165
183
|
}, [lastOptions?.format, lastOptions?.transformStyle, menuOpen, transformSummaries.length]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
if (!menuOpen || !destinationAdapter || !quickDestinationKey) return;
|
|
186
|
+
const quickOptions = loadLastExportOptions();
|
|
187
|
+
if (!quickOptions) return;
|
|
188
|
+
let cancelled = false;
|
|
189
|
+
setQuickDestination(null);
|
|
190
|
+
setQuickDestinationPending(true);
|
|
191
|
+
void loadRunExportModule().then(
|
|
192
|
+
({ buildExportFilename }) => destinationAdapter.resolveTarget(buildExportFilename(selectedFile, quickOptions))
|
|
193
|
+
).then((target) => {
|
|
194
|
+
if (!cancelled) setQuickDestination({ key: quickDestinationKey, target });
|
|
195
|
+
}).catch(() => {
|
|
196
|
+
if (!cancelled) setQuickDestination(null);
|
|
197
|
+
}).finally(() => {
|
|
198
|
+
if (!cancelled) setQuickDestinationPending(false);
|
|
199
|
+
});
|
|
200
|
+
return () => {
|
|
201
|
+
cancelled = true;
|
|
202
|
+
};
|
|
203
|
+
}, [destinationAdapter, menuOpen, quickDestinationKey, selectedFile]);
|
|
166
204
|
const handleToggleMenu = useCallback(() => {
|
|
205
|
+
if (!menuOpen && destinationAdapter) {
|
|
206
|
+
setQuickDestination(null);
|
|
207
|
+
setQuickDestinationPending(true);
|
|
208
|
+
}
|
|
167
209
|
setMenuOpen((prev) => !prev);
|
|
168
|
-
}, []);
|
|
210
|
+
}, [destinationAdapter, menuOpen]);
|
|
169
211
|
const refreshDestination = useCallback(
|
|
170
212
|
async (options) => {
|
|
171
213
|
if (!destinationAdapter) return;
|
|
@@ -245,6 +287,7 @@ function ExportToolbarControls({
|
|
|
245
287
|
const pickedTarget = await destinationAdapter.pickTarget(filename, destinationTarget);
|
|
246
288
|
if (pickedTarget === null) return;
|
|
247
289
|
setDestinationTarget(pickedTarget);
|
|
290
|
+
setQuickDestination(null);
|
|
248
291
|
} catch {
|
|
249
292
|
}
|
|
250
293
|
},
|
|
@@ -254,7 +297,8 @@ function ExportToolbarControls({
|
|
|
254
297
|
async (blob, filename, target) => {
|
|
255
298
|
if (!destinationAdapter) return;
|
|
256
299
|
const savedTarget = await destinationAdapter.saveBlob(blob, filename, target);
|
|
257
|
-
if (savedTarget)
|
|
300
|
+
if (!savedTarget) throw new ExportCancelledError();
|
|
301
|
+
setDestinationTarget(savedTarget);
|
|
258
302
|
},
|
|
259
303
|
[destinationAdapter]
|
|
260
304
|
);
|
|
@@ -279,7 +323,7 @@ function ExportToolbarControls({
|
|
|
279
323
|
);
|
|
280
324
|
setDialogOpen(false);
|
|
281
325
|
} catch (caught) {
|
|
282
|
-
setExportError(exportErrorMessage(caught));
|
|
326
|
+
if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
|
|
283
327
|
} finally {
|
|
284
328
|
setExporting(false);
|
|
285
329
|
}
|
|
@@ -303,10 +347,8 @@ function ExportToolbarControls({
|
|
|
303
347
|
const { runExport } = await loadRunExportModule();
|
|
304
348
|
let quickTarget = null;
|
|
305
349
|
if (destinationAdapter) {
|
|
306
|
-
|
|
307
|
-
quickTarget =
|
|
308
|
-
buildExportFilename(selectedFile, lastOptions)
|
|
309
|
-
);
|
|
350
|
+
if (!quickDestinationTarget) return;
|
|
351
|
+
quickTarget = quickDestinationTarget;
|
|
310
352
|
}
|
|
311
353
|
await runExport(
|
|
312
354
|
markdownSource,
|
|
@@ -316,7 +358,7 @@ function ExportToolbarControls({
|
|
|
316
358
|
destinationAdapter ? async (blob, filename) => saveToDestination(blob, filename, quickTarget) : saveBlob
|
|
317
359
|
);
|
|
318
360
|
} catch (caught) {
|
|
319
|
-
setExportError(exportErrorMessage(caught));
|
|
361
|
+
if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
|
|
320
362
|
} finally {
|
|
321
363
|
setExporting(false);
|
|
322
364
|
}
|
|
@@ -327,7 +369,8 @@ function ExportToolbarControls({
|
|
|
327
369
|
mediaContainer,
|
|
328
370
|
saveBlob,
|
|
329
371
|
saveToDestination,
|
|
330
|
-
selectedFile
|
|
372
|
+
selectedFile,
|
|
373
|
+
quickDestinationTarget
|
|
331
374
|
]);
|
|
332
375
|
const handleDismissExportError = useCallback(() => {
|
|
333
376
|
setExportError(null);
|
|
@@ -347,13 +390,15 @@ function ExportToolbarControls({
|
|
|
347
390
|
"data-tooltip": exporting ? "Exporting..." : "Export document",
|
|
348
391
|
children: /* @__PURE__ */ jsx(ExportGlyph, {})
|
|
349
392
|
}
|
|
350
|
-
) : /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu", ref:
|
|
393
|
+
) : /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu", ref: menuContainerRef, children: [
|
|
351
394
|
/* @__PURE__ */ jsxs(
|
|
352
395
|
"button",
|
|
353
396
|
{
|
|
397
|
+
ref: triggerRef,
|
|
354
398
|
type: "button",
|
|
355
399
|
className: "db-toolbar-menu-trigger",
|
|
356
400
|
onClick: handleToggleMenu,
|
|
401
|
+
onKeyDown: handleTriggerKeyDown,
|
|
357
402
|
"aria-label": "Export and share",
|
|
358
403
|
"aria-haspopup": "menu",
|
|
359
404
|
"aria-expanded": menuOpen,
|
|
@@ -364,62 +409,85 @@ function ExportToolbarControls({
|
|
|
364
409
|
]
|
|
365
410
|
}
|
|
366
411
|
),
|
|
367
|
-
menuOpen && /* @__PURE__ */ jsxs(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
412
|
+
menuOpen && /* @__PURE__ */ jsxs(
|
|
413
|
+
"div",
|
|
414
|
+
{
|
|
415
|
+
ref: menuRef,
|
|
416
|
+
className: "db-toolbar-menu-dropdown db-export-toolbar-menu-dropdown",
|
|
417
|
+
role: "menu",
|
|
418
|
+
onKeyDown: handleMenuKeyDown,
|
|
419
|
+
children: [
|
|
420
|
+
lastOptions && /* @__PURE__ */ jsx(
|
|
421
|
+
"button",
|
|
422
|
+
{
|
|
423
|
+
type: "button",
|
|
424
|
+
role: "menuitem",
|
|
425
|
+
tabIndex: -1,
|
|
426
|
+
className: "db-toolbar-menu-item",
|
|
427
|
+
onClick: handleQuickExport,
|
|
428
|
+
disabled: exporting || Boolean(destinationAdapter) && !quickDestinationTarget,
|
|
429
|
+
title: quickDestinationTarget ? quickLabel(
|
|
430
|
+
lastOptions,
|
|
431
|
+
transformSummaries,
|
|
432
|
+
quickDestinationTarget.displayPath
|
|
433
|
+
) : void 0,
|
|
434
|
+
children: quickDestinationTarget ? quickLabel(
|
|
435
|
+
lastOptions,
|
|
436
|
+
transformSummaries,
|
|
437
|
+
quickDestinationTarget.displayPath
|
|
438
|
+
) : quickLabel(lastOptions, transformSummaries) + (destinationAdapter ? quickDestinationPending ? " (finding destination...)" : " (destination unavailable)" : "")
|
|
439
|
+
}
|
|
440
|
+
),
|
|
441
|
+
/* @__PURE__ */ jsx(
|
|
442
|
+
"button",
|
|
443
|
+
{
|
|
444
|
+
type: "button",
|
|
445
|
+
role: "menuitem",
|
|
446
|
+
tabIndex: -1,
|
|
447
|
+
className: "db-toolbar-menu-item",
|
|
448
|
+
onClick: handleOpenDialog,
|
|
449
|
+
children: "Export..."
|
|
450
|
+
}
|
|
451
|
+
),
|
|
452
|
+
/* @__PURE__ */ jsx(
|
|
453
|
+
"button",
|
|
454
|
+
{
|
|
455
|
+
type: "button",
|
|
456
|
+
role: "menuitem",
|
|
457
|
+
tabIndex: -1,
|
|
458
|
+
className: "db-toolbar-menu-item",
|
|
459
|
+
onClick: handleOpenShareDialog,
|
|
460
|
+
children: "Share link with content embedded..."
|
|
461
|
+
}
|
|
462
|
+
),
|
|
463
|
+
showVideoExport && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
464
|
+
/* @__PURE__ */ jsx("div", { className: "db-toolbar-menu-divider", role: "separator" }),
|
|
465
|
+
/* @__PURE__ */ jsx(
|
|
466
|
+
"button",
|
|
467
|
+
{
|
|
468
|
+
type: "button",
|
|
469
|
+
role: "menuitem",
|
|
470
|
+
tabIndex: -1,
|
|
471
|
+
className: "db-toolbar-menu-item",
|
|
472
|
+
onClick: () => void handleOpenVideoModal("mp4"),
|
|
473
|
+
children: "Export video..."
|
|
474
|
+
}
|
|
475
|
+
),
|
|
476
|
+
showAnimatedGifExport && /* @__PURE__ */ jsx(
|
|
477
|
+
"button",
|
|
478
|
+
{
|
|
479
|
+
type: "button",
|
|
480
|
+
role: "menuitem",
|
|
481
|
+
tabIndex: -1,
|
|
482
|
+
className: "db-toolbar-menu-item",
|
|
483
|
+
onClick: () => void handleOpenVideoModal("gif"),
|
|
484
|
+
children: "Export animated gif..."
|
|
485
|
+
}
|
|
486
|
+
)
|
|
487
|
+
] })
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
)
|
|
423
491
|
] }),
|
|
424
492
|
dialogOpen && /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
425
493
|
ExportDialog,
|
|
@@ -476,6 +544,7 @@ function ExportToolbarControls({
|
|
|
476
544
|
{
|
|
477
545
|
doc: videoDoc,
|
|
478
546
|
playerScript: videoModules.playerScript,
|
|
547
|
+
...mediaProvider ? { mediaProvider } : {},
|
|
479
548
|
colorScheme,
|
|
480
549
|
uiPalette: videoExportPalette,
|
|
481
550
|
defaultConfig: {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
useMenuKeyboard
|
|
3
|
-
} from "./chunk-TFMO7EVO.js";
|
|
1
|
+
import "./chunk-APC4KI4B.js";
|
|
4
2
|
import {
|
|
5
3
|
isFileDirty,
|
|
6
4
|
useGitContext
|
|
7
5
|
} from "./chunk-HFYPTTCH.js";
|
|
6
|
+
import {
|
|
7
|
+
useMenuKeyboard
|
|
8
|
+
} from "./chunk-MRUK56JS.js";
|
|
8
9
|
|
|
9
10
|
// src/Git/GitToolbarControl.tsx
|
|
10
11
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
useMenuKeyboard
|
|
3
|
-
} from "./chunk-TFMO7EVO.js";
|
|
1
|
+
import "./chunk-APC4KI4B.js";
|
|
4
2
|
import {
|
|
5
3
|
BADGE_GLYPHS,
|
|
6
4
|
BADGE_LABELS,
|
|
@@ -9,6 +7,9 @@ import {
|
|
|
9
7
|
summarizeDirty,
|
|
10
8
|
useGitContext
|
|
11
9
|
} from "./chunk-HFYPTTCH.js";
|
|
10
|
+
import {
|
|
11
|
+
useMenuKeyboard
|
|
12
|
+
} from "./chunk-MRUK56JS.js";
|
|
12
13
|
import {
|
|
13
14
|
Dialog
|
|
14
15
|
} from "./chunk-LG6HAWCK.js";
|
|
File without changes
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
// src/editor.ts
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
var PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
|
|
4
|
+
function previewViewportPresetForOrientation(isPortrait) {
|
|
5
|
+
return isPortrait ? "portrait" : "landscape";
|
|
6
|
+
}
|
|
7
|
+
function useResponsivePreviewViewportPreset() {
|
|
8
|
+
const [isPortrait, setIsPortrait] = useState(() => {
|
|
9
|
+
if (typeof globalThis.matchMedia !== "function") return false;
|
|
10
|
+
return globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY).matches;
|
|
11
|
+
});
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (typeof globalThis.matchMedia !== "function") return;
|
|
14
|
+
const query = globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY);
|
|
15
|
+
const update = () => setIsPortrait(query.matches);
|
|
16
|
+
update();
|
|
17
|
+
query.addEventListener("change", update);
|
|
18
|
+
return () => query.removeEventListener("change", update);
|
|
19
|
+
}, []);
|
|
20
|
+
return previewViewportPresetForOrientation(isPortrait);
|
|
21
|
+
}
|
|
2
22
|
var EMPTY_DOCUMENT_PROMPTS = [
|
|
3
23
|
"Start typing your content, or drop images on top of me...",
|
|
4
24
|
"Write anything -- paste markdown, drag in images, or just start typing...",
|
|
@@ -64,6 +84,9 @@ function pickEmptyDocumentPrompt(random = Math.random) {
|
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
export {
|
|
87
|
+
PORTRAIT_FORM_FACTOR_QUERY,
|
|
88
|
+
previewViewportPresetForOrientation,
|
|
89
|
+
useResponsivePreviewViewportPreset,
|
|
67
90
|
EMPTY_DOCUMENT_PROMPTS,
|
|
68
91
|
pickEmptyDocumentPrompt
|
|
69
92
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
1
|
+
// src/components/useMenuKeyboard.ts
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef
|
|
6
|
+
} from "react";
|
|
3
7
|
function useMenuKeyboard(open, setOpen) {
|
|
4
8
|
const menuRef = useRef(null);
|
|
5
9
|
const triggerRef = useRef(null);
|
|
@@ -22,7 +26,7 @@ function useMenuKeyboard(open, setOpen) {
|
|
|
22
26
|
(returnFocus) => {
|
|
23
27
|
pendingFocusRef.current = "none";
|
|
24
28
|
setOpen(false);
|
|
25
|
-
if (returnFocus) triggerRef.current?.focus();
|
|
29
|
+
if (returnFocus) triggerRef.current?.focus({ preventScroll: true });
|
|
26
30
|
},
|
|
27
31
|
[setOpen]
|
|
28
32
|
);
|
|
@@ -32,6 +36,17 @@ function useMenuKeyboard(open, setOpen) {
|
|
|
32
36
|
pendingFocusRef.current = "none";
|
|
33
37
|
focusItem(where === "last" ? items().length - 1 : 0);
|
|
34
38
|
}, [open, focusItem, items]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!open) return;
|
|
41
|
+
const handleDocumentKeyDown = (event) => {
|
|
42
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
event.stopPropagation();
|
|
45
|
+
closeMenu(true);
|
|
46
|
+
};
|
|
47
|
+
document.addEventListener("keydown", handleDocumentKeyDown);
|
|
48
|
+
return () => document.removeEventListener("keydown", handleDocumentKeyDown);
|
|
49
|
+
}, [closeMenu, open]);
|
|
35
50
|
const handleTriggerKeyDown = useCallback(
|
|
36
51
|
(event) => {
|
|
37
52
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
@@ -48,12 +63,16 @@ function useMenuKeyboard(open, setOpen) {
|
|
|
48
63
|
);
|
|
49
64
|
const handleMenuKeyDown = useCallback(
|
|
50
65
|
(event) => {
|
|
51
|
-
if (event.key === "Escape"
|
|
66
|
+
if (event.key === "Escape") {
|
|
52
67
|
event.preventDefault();
|
|
53
68
|
event.stopPropagation();
|
|
54
69
|
closeMenu(true);
|
|
55
70
|
return;
|
|
56
71
|
}
|
|
72
|
+
if (event.key === "Tab") {
|
|
73
|
+
closeMenu(false);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
57
76
|
const all = items();
|
|
58
77
|
if (all.length === 0) return;
|
|
59
78
|
const current = all.indexOf(document.activeElement);
|