@12-apps/ui 6.39.16 → 6.39.18
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/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
- package/dist/_internal/components/data-display/Carousel/Carousel.js +3 -3
- package/dist/_internal/components/data-display/Chart/SpecChart.js +3 -3
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +14 -14
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +26 -25
- package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +5 -4
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +5 -4
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +5 -4
- package/dist/_internal/components/data-display/DataViews/base-card.js +2 -2
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +5 -5
- package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +4 -4
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-display-tabs.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +13 -13
- package/dist/_internal/components/data-display/DataViews/data-views-layout-context.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +7 -6
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +5 -5
- package/dist/_internal/components/data-display/DataViews/list-card-rails.js +3 -3
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +1 -1
- package/dist/_internal/components/data-display/DataViews/use-saved-views-controller.js +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +11 -11
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +8 -8
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +12 -12
- package/dist/_internal/components/data-display/FleetMap/FleetRoster.js +2 -2
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +1 -1
- package/dist/_internal/components/data-display/MapPreview/MapDecorations.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.hooks.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +7 -7
- package/dist/_internal/components/data-display/MapPreview/MapTiles.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/StaticMapView.js +4 -4
- package/dist/_internal/components/data-display/MapPreview/useMapPreview.js +3 -3
- package/dist/_internal/components/data-display/Progress/Progress.js +2 -2
- package/dist/_internal/components/data-display/Sheet/Sheet.js +3 -3
- package/dist/_internal/components/data-display/Table/Table.js +2 -2
- package/dist/_internal/components/data-display/index.js +36 -36
- package/dist/_internal/components/feedback/Dialog/Dialog.focus.js +10 -0
- package/dist/_internal/components/feedback/Dialog/Dialog.js +4 -3
- package/dist/_internal/components/feedback/Dialog/Dialog.slots.js +5 -3
- package/dist/_internal/components/feedback/Dialog/Dialog.styles.js +1 -1
- package/dist/_internal/components/feedback/index.js +4 -3
- package/dist/_internal/components/form/Autocomplete/Autocomplete.js +2 -2
- package/dist/_internal/components/form/Autocomplete/useAutocomplete.js +1 -1
- package/dist/_internal/components/form/CodeEditor/CodeEditor.hooks.js +2 -2
- package/dist/_internal/components/form/CodeEditor/CodeEditor.js +3 -3
- package/dist/_internal/components/form/CodeEditor/CodeEditor.monaco.js +3 -1
- package/dist/_internal/components/form/index.js +9 -6
- package/dist/_internal/components/index.js +50 -46
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +8 -7
- package/dist/_internal/components/navigation/index.js +8 -7
- package/dist/_internal/user-avatar/user-avatar.js +3 -3
- package/dist/charts.js +11 -11
- package/dist/{chunk-V2UZQS2K.js → chunk-2622YFM6.js} +4 -4
- package/dist/{chunk-KTCOJFZB.js → chunk-2OWKL4CC.js} +5 -5
- package/dist/{chunk-QSRZOXQI.js → chunk-2WVHZIWP.js} +2 -2
- package/dist/{chunk-P22W4N5K.js → chunk-37USSARD.js} +2 -2
- package/dist/{chunk-RSDYS5B3.js → chunk-4RPFI474.js} +6 -6
- package/dist/{chunk-HD6ZNARS.js → chunk-5F5Q6VJ2.js} +7 -1
- package/dist/chunk-5F5Q6VJ2.js.map +1 -0
- package/dist/{chunk-SAOCDKNK.js → chunk-5JCGBCFJ.js} +4 -4
- package/dist/{chunk-3BNQDOHC.js → chunk-5OBGW3FG.js} +5 -5
- package/dist/{chunk-EMEXXAQI.js → chunk-66O55OZI.js} +6 -6
- package/dist/{chunk-SA65SDOY.js → chunk-A2TANYNR.js} +2 -2
- package/dist/{chunk-I5T5PQQW.js → chunk-BRM3T4LQ.js} +4 -4
- package/dist/{chunk-PG5C2AA4.js → chunk-DF4QODJ6.js} +2 -2
- package/dist/{chunk-ESCF5VMQ.js → chunk-DRSY37PB.js} +2 -2
- package/dist/{chunk-DYNJQ3HJ.js → chunk-FLDLUAHW.js} +2 -2
- package/dist/{chunk-OGB25P3N.js → chunk-HQZRI7UX.js} +2 -2
- package/dist/{chunk-HUPR5JXY.js → chunk-HWSS6C7C.js} +2 -2
- package/dist/{chunk-T3DNYPEA.js → chunk-I4MRBEWU.js} +12 -12
- package/dist/{chunk-HR7YTBR7.js → chunk-IASDP44E.js} +4 -4
- package/dist/chunk-IDMG332I.js +1 -0
- package/dist/chunk-IDMG332I.js.map +1 -0
- package/dist/{chunk-5WT4YWXC.js → chunk-IRD45DRW.js} +2 -2
- package/dist/{chunk-ENRBXB6L.js → chunk-JK5UJM4Y.js} +3 -3
- package/dist/{chunk-5NT2M2JI.js → chunk-JKIKHWD2.js} +4 -4
- package/dist/{chunk-WVJGNIJZ.js → chunk-JYR6VV4Y.js} +3 -3
- package/dist/{chunk-5ETBB2DG.js → chunk-K5V6TCYB.js} +4 -4
- package/dist/{chunk-3IF3I3FI.js → chunk-KHPL2J3B.js} +2 -2
- package/dist/{chunk-AXKLCWFR.js → chunk-KN4ZE2XZ.js} +14 -14
- package/dist/{chunk-QG73YT6D.js → chunk-L36S7YJB.js} +2 -2
- package/dist/{chunk-M3MHYE2M.js → chunk-L566FMDD.js} +4 -4
- package/dist/chunk-LV7LSPBF.js +35 -0
- package/dist/chunk-LV7LSPBF.js.map +1 -0
- package/dist/{chunk-HBRKA67N.js → chunk-M4P7NS2L.js} +20 -12
- package/dist/chunk-M4P7NS2L.js.map +1 -0
- package/dist/{chunk-HQWJUHR4.js → chunk-M4QXG5JP.js} +4 -4
- package/dist/{chunk-IFS3BRRL.js → chunk-MCJRHF7Z.js} +3 -3
- package/dist/{chunk-JGRBHNK6.js → chunk-ME3WYPYK.js} +2 -2
- package/dist/{chunk-GAMLBJE6.js → chunk-MQ277OLB.js} +14 -8
- package/dist/chunk-MQ277OLB.js.map +1 -0
- package/dist/{chunk-QXSV7Z57.js → chunk-MXCIUKTQ.js} +5 -5
- package/dist/{chunk-UBEAJFMW.js → chunk-O25SH2MA.js} +5 -5
- package/dist/{chunk-ARDLCGBK.js → chunk-O2DLEAUP.js} +3 -3
- package/dist/{chunk-WCRGSMLV.js → chunk-SCZABICW.js} +3 -3
- package/dist/{chunk-2WU52FLP.js → chunk-ST7B54JN.js} +9 -4
- package/dist/chunk-ST7B54JN.js.map +1 -0
- package/dist/{chunk-LH46NYGR.js → chunk-TEEJ4GNY.js} +2 -2
- package/dist/{chunk-O4AHF45C.js → chunk-VY6L74JQ.js} +2 -2
- package/dist/{chunk-DQZCQV2C.js → chunk-Y6G4I42P.js} +2 -2
- package/dist/{chunk-XZJM6EPC.js → chunk-YKEO6UHN.js} +4 -4
- package/dist/{chunk-4Y6W7BT4.js → chunk-YQCRQHVU.js} +2 -2
- package/dist/{chunk-WWGD5YVW.js → chunk-YTY7TD7S.js} +2 -2
- package/dist/{chunk-5HW37NSW.js → chunk-Z2JH5KW5.js} +16 -2
- package/dist/{chunk-5HW37NSW.js.map → chunk-Z2JH5KW5.js.map} +1 -1
- package/dist/{chunk-HMAPQV4C.js → chunk-ZWCYSAI5.js} +2 -2
- package/dist/data-display/Avatar.js +2 -2
- package/dist/data-display/Carousel.js +3 -3
- package/dist/data-display/Chart.js +11 -11
- package/dist/data-display/DataViews.js +34 -33
- package/dist/data-display/FleetMap.js +12 -12
- package/dist/data-display/Lightbox.js +1 -1
- package/dist/data-display/MapPreview.js +7 -7
- package/dist/data-display/Progress.js +2 -2
- package/dist/data-display/Sheet.js +3 -3
- package/dist/data-display/Table.js +2 -2
- package/dist/feedback/Dialog.js +4 -3
- package/dist/form/Autocomplete.js +2 -2
- package/dist/form/CodeEditor.js +8 -5
- package/dist/navigation/AppHeader.js +8 -7
- package/dist/types/components/feedback/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/components/feedback/Dialog/Dialog.focus.d.ts +51 -0
- package/dist/types/components/feedback/Dialog/Dialog.focus.d.ts.map +1 -0
- package/dist/types/components/feedback/Dialog/Dialog.slots.d.ts +14 -0
- package/dist/types/components/feedback/Dialog/Dialog.slots.d.ts.map +1 -1
- package/dist/types/components/feedback/Dialog/Dialog.styles.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/useAutocomplete.d.ts.map +1 -1
- package/dist/types/components/form/CodeEditor/CodeEditor.monaco.d.ts +32 -2
- package/dist/types/components/form/CodeEditor/CodeEditor.monaco.d.ts.map +1 -1
- package/dist/types/components/form/CodeEditor/index.d.ts +2 -0
- package/dist/types/components/form/CodeEditor/index.d.ts.map +1 -1
- package/dist/user-avatar.js +3 -3
- package/package.json +1 -1
- package/src/components/feedback/Dialog/Dialog.focus.ts +90 -0
- package/src/components/feedback/Dialog/Dialog.md +23 -3
- package/src/components/feedback/Dialog/Dialog.slots.ts +32 -0
- package/src/components/feedback/Dialog/Dialog.styles.ts +15 -0
- package/src/components/feedback/Dialog/Dialog.tsx +11 -3
- package/src/components/form/Autocomplete/useAutocomplete.ts +35 -10
- package/src/components/form/CodeEditor/CodeEditor.md +70 -0
- package/src/components/form/CodeEditor/CodeEditor.monaco.ts +37 -2
- package/src/components/form/CodeEditor/index.ts +2 -0
- package/dist/chunk-2WU52FLP.js.map +0 -1
- package/dist/chunk-GAMLBJE6.js.map +0 -1
- package/dist/chunk-HBRKA67N.js.map +0 -1
- package/dist/chunk-HD6ZNARS.js.map +0 -1
- package/dist/chunk-JEBPCAWD.js +0 -1
- /package/dist/{chunk-JEBPCAWD.js.map → _internal/components/feedback/Dialog/Dialog.focus.js.map} +0 -0
- /package/dist/{chunk-V2UZQS2K.js.map → chunk-2622YFM6.js.map} +0 -0
- /package/dist/{chunk-KTCOJFZB.js.map → chunk-2OWKL4CC.js.map} +0 -0
- /package/dist/{chunk-QSRZOXQI.js.map → chunk-2WVHZIWP.js.map} +0 -0
- /package/dist/{chunk-P22W4N5K.js.map → chunk-37USSARD.js.map} +0 -0
- /package/dist/{chunk-RSDYS5B3.js.map → chunk-4RPFI474.js.map} +0 -0
- /package/dist/{chunk-SAOCDKNK.js.map → chunk-5JCGBCFJ.js.map} +0 -0
- /package/dist/{chunk-3BNQDOHC.js.map → chunk-5OBGW3FG.js.map} +0 -0
- /package/dist/{chunk-EMEXXAQI.js.map → chunk-66O55OZI.js.map} +0 -0
- /package/dist/{chunk-SA65SDOY.js.map → chunk-A2TANYNR.js.map} +0 -0
- /package/dist/{chunk-I5T5PQQW.js.map → chunk-BRM3T4LQ.js.map} +0 -0
- /package/dist/{chunk-PG5C2AA4.js.map → chunk-DF4QODJ6.js.map} +0 -0
- /package/dist/{chunk-ESCF5VMQ.js.map → chunk-DRSY37PB.js.map} +0 -0
- /package/dist/{chunk-DYNJQ3HJ.js.map → chunk-FLDLUAHW.js.map} +0 -0
- /package/dist/{chunk-OGB25P3N.js.map → chunk-HQZRI7UX.js.map} +0 -0
- /package/dist/{chunk-HUPR5JXY.js.map → chunk-HWSS6C7C.js.map} +0 -0
- /package/dist/{chunk-T3DNYPEA.js.map → chunk-I4MRBEWU.js.map} +0 -0
- /package/dist/{chunk-HR7YTBR7.js.map → chunk-IASDP44E.js.map} +0 -0
- /package/dist/{chunk-5WT4YWXC.js.map → chunk-IRD45DRW.js.map} +0 -0
- /package/dist/{chunk-ENRBXB6L.js.map → chunk-JK5UJM4Y.js.map} +0 -0
- /package/dist/{chunk-5NT2M2JI.js.map → chunk-JKIKHWD2.js.map} +0 -0
- /package/dist/{chunk-WVJGNIJZ.js.map → chunk-JYR6VV4Y.js.map} +0 -0
- /package/dist/{chunk-5ETBB2DG.js.map → chunk-K5V6TCYB.js.map} +0 -0
- /package/dist/{chunk-3IF3I3FI.js.map → chunk-KHPL2J3B.js.map} +0 -0
- /package/dist/{chunk-AXKLCWFR.js.map → chunk-KN4ZE2XZ.js.map} +0 -0
- /package/dist/{chunk-QG73YT6D.js.map → chunk-L36S7YJB.js.map} +0 -0
- /package/dist/{chunk-M3MHYE2M.js.map → chunk-L566FMDD.js.map} +0 -0
- /package/dist/{chunk-HQWJUHR4.js.map → chunk-M4QXG5JP.js.map} +0 -0
- /package/dist/{chunk-IFS3BRRL.js.map → chunk-MCJRHF7Z.js.map} +0 -0
- /package/dist/{chunk-JGRBHNK6.js.map → chunk-ME3WYPYK.js.map} +0 -0
- /package/dist/{chunk-QXSV7Z57.js.map → chunk-MXCIUKTQ.js.map} +0 -0
- /package/dist/{chunk-UBEAJFMW.js.map → chunk-O25SH2MA.js.map} +0 -0
- /package/dist/{chunk-ARDLCGBK.js.map → chunk-O2DLEAUP.js.map} +0 -0
- /package/dist/{chunk-WCRGSMLV.js.map → chunk-SCZABICW.js.map} +0 -0
- /package/dist/{chunk-LH46NYGR.js.map → chunk-TEEJ4GNY.js.map} +0 -0
- /package/dist/{chunk-O4AHF45C.js.map → chunk-VY6L74JQ.js.map} +0 -0
- /package/dist/{chunk-DQZCQV2C.js.map → chunk-Y6G4I42P.js.map} +0 -0
- /package/dist/{chunk-XZJM6EPC.js.map → chunk-YKEO6UHN.js.map} +0 -0
- /package/dist/{chunk-4Y6W7BT4.js.map → chunk-YQCRQHVU.js.map} +0 -0
- /package/dist/{chunk-WWGD5YVW.js.map → chunk-YTY7TD7S.js.map} +0 -0
- /package/dist/{chunk-HMAPQV4C.js.map → chunk-ZWCYSAI5.js.map} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHERE INITIAL FOCUS LANDS WHEN A DIALOG OPENS (FUT-2696).
|
|
3
|
+
*
|
|
4
|
+
* MUI's `FocusTrap` focuses ITS OWN root when nothing inside already has
|
|
5
|
+
* focus (`FocusTrap.js`: `if (!rootRef.current.contains(doc.activeElement))
|
|
6
|
+
* … rootRef.current.focus()`). For `MuiDialog` that root is
|
|
7
|
+
* `.MuiDialog-container` — the `role="presentation"` wrapper the Transition
|
|
8
|
+
* clones its ref onto — which sits OUTSIDE the `role="dialog"` paper. A
|
|
9
|
+
* screen reader user landing there hears the page behind the dialog, not the
|
|
10
|
+
* dialog, and the first Tab does not start from the dialog's own controls.
|
|
11
|
+
*
|
|
12
|
+
* The WAI-ARIA APG dialog pattern wants focus INSIDE the dialog on open: the
|
|
13
|
+
* first tabbable descendant of the element carrying `role="dialog"`, or that
|
|
14
|
+
* element itself (given `tabIndex={-1}`, since it is not otherwise reachable
|
|
15
|
+
* by Tab) when it holds nothing focusable.
|
|
16
|
+
*
|
|
17
|
+
* `focusDialogOnEntered` is wired to `TransitionProps.onEntered` /
|
|
18
|
+
* `SlideProps.onEntered` on both renderers rather than to a mount effect,
|
|
19
|
+
* because it needs to run AFTER `FocusTrap`'s own mount effect has already
|
|
20
|
+
* had its turn — `onEntered` fires once the enter transition has finished,
|
|
21
|
+
* well after every mount effect has settled, so there is nothing left to
|
|
22
|
+
* race. A caller's own `autoFocus` on a field inside the dialog already wins
|
|
23
|
+
* against `FocusTrap` for the same reason `FocusTrap` documents: React's
|
|
24
|
+
* `autoFocus` runs synchronously during commit, before `FocusTrap`'s effect
|
|
25
|
+
* even checks whether focus is already inside its root — so by the time this
|
|
26
|
+
* runs, that field is already `contains()`-ed by the dialog and this backs
|
|
27
|
+
* off rather than moving focus away from it.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The candidate list `FocusTrap`'s own `defaultGetTabbable` uses
|
|
32
|
+
* (`@mui/material/Unstable_TrapFocus/FocusTrap.js`), so "first tabbable
|
|
33
|
+
* descendant" means the same thing here as it does to the trap that then
|
|
34
|
+
* keeps Tab cycling inside it. Like the trap, a match whose resolved
|
|
35
|
+
* `tabIndex` is -1 is skipped: `<button tabIndex={-1}>` is focusable but out
|
|
36
|
+
* of the Tab order, so it is not the first Tab stop.
|
|
37
|
+
*/
|
|
38
|
+
const FOCUSABLE_SELECTOR = [
|
|
39
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
40
|
+
'select:not([disabled])',
|
|
41
|
+
'textarea:not([disabled])',
|
|
42
|
+
'a[href]',
|
|
43
|
+
'button:not([disabled])',
|
|
44
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
45
|
+
'audio[controls]',
|
|
46
|
+
'video[controls]',
|
|
47
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
48
|
+
].join(',');
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Move focus into `target`: its first tabbable descendant, or `target`
|
|
52
|
+
* itself — given `tabIndex={-1}` if it does not already carry one, exactly as
|
|
53
|
+
* `FocusTrap` gives its own root — when nothing inside is focusable.
|
|
54
|
+
*
|
|
55
|
+
* Backs off when focus already sits on a DESCENDANT of `target` other than
|
|
56
|
+
* `target` itself: that is a caller's `autoFocus` (or an explicit `.focus()`
|
|
57
|
+
* of their own) that arrived before this ran, and it keeps winning. Focus
|
|
58
|
+
* already sitting ON `target` (as `FocusTrap` leaves it on the drawer's
|
|
59
|
+
* paper, which is its own trap root) is not a caller's choice, so it does
|
|
60
|
+
* not block a first-tabbable descendant from taking over.
|
|
61
|
+
*/
|
|
62
|
+
export function focusFirstTabbable(target: HTMLElement): void {
|
|
63
|
+
const active = target.ownerDocument.activeElement;
|
|
64
|
+
if (active !== target && target.contains(active)) return;
|
|
65
|
+
|
|
66
|
+
const tabbable = Array.from(target.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).find(
|
|
67
|
+
(element) => element.tabIndex >= 0,
|
|
68
|
+
);
|
|
69
|
+
if (tabbable) {
|
|
70
|
+
tabbable.focus();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!target.hasAttribute('tabindex')) target.setAttribute('tabindex', '-1');
|
|
74
|
+
target.focus();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `node` is what the transition hands back: `.MuiDialog-container` for
|
|
79
|
+
* `ModalDialog` (the paper is a descendant, carrying `role="dialog"`), or the
|
|
80
|
+
* drawer's own paper directly for `DrawerDialog` (`Slide` wraps the paper with
|
|
81
|
+
* no intermediate container, and the paper carries no `role` at all — see
|
|
82
|
+
* `Dialog.md`). Either way, focus the `[role="dialog"]` descendant if there is
|
|
83
|
+
* one, else `node` itself.
|
|
84
|
+
*/
|
|
85
|
+
export function focusDialogOnEntered(node: HTMLElement): void {
|
|
86
|
+
const dialog = node.getAttribute('role') === 'dialog'
|
|
87
|
+
? node
|
|
88
|
+
: (node.querySelector<HTMLElement>('[role="dialog"]') ?? node);
|
|
89
|
+
focusFirstTabbable(dialog);
|
|
90
|
+
}
|
|
@@ -317,12 +317,32 @@ Slides in from the right side of the screen, similar to a side panel or drawer.
|
|
|
317
317
|
|
|
318
318
|
The Dialog component implements comprehensive accessibility features:
|
|
319
319
|
|
|
320
|
-
- **ARIA Roles**: Uses `role="dialog"` with proper ARIA attributes
|
|
320
|
+
- **ARIA Roles**: Uses `role="dialog"` with proper ARIA attributes (the `default`,
|
|
321
|
+
`glass` and `fullscreen` variants; the `drawer` variant does not — see below)
|
|
321
322
|
- **ARIA Labels**: Proper labeling for title (`aria-labelledby`) and description (`aria-describedby`)
|
|
322
|
-
- **Focus Management
|
|
323
|
+
- **Focus Management** (FUT-2696):
|
|
323
324
|
- Focus is trapped within dialog when open
|
|
324
325
|
- Focus returns to trigger element when closed
|
|
325
|
-
-
|
|
326
|
+
- On open, focus moves to the first tabbable descendant of the
|
|
327
|
+
`role="dialog"` paper — or, when the paper holds nothing focusable, to the
|
|
328
|
+
paper itself (`tabIndex={-1}`). This is NOT where MUI's own `FocusTrap`
|
|
329
|
+
would otherwise park it: left alone, `FocusTrap` focuses its root, which
|
|
330
|
+
for MUI's `Dialog` is `.MuiDialog-container` — the wrapper AROUND the
|
|
331
|
+
paper, and so outside the element a screen reader announces as the
|
|
332
|
+
dialog. `Dialog.tsx` corrects this itself, off `TransitionProps.onEntered`
|
|
333
|
+
(see `Dialog.focus.ts`), rather than relying on MUI's default.
|
|
334
|
+
- A caller's own `autoFocus` on a field inside the dialog keeps winning:
|
|
335
|
+
React applies it synchronously on mount, before `FocusTrap` or this fix
|
|
336
|
+
ever runs, so by the time either would move focus, it is already inside
|
|
337
|
+
the dialog and both back off.
|
|
338
|
+
- The `drawer` variant gets the same first-tabbable behaviour, but its
|
|
339
|
+
paper carries no `role="dialog"` — MUI's `Drawer` sets no `role` on it at
|
|
340
|
+
all, unlike `Dialog`'s paper — and there is no `.MuiDrawer-container`
|
|
341
|
+
wrapper either: `Slide` wraps the drawer's paper directly, so it IS
|
|
342
|
+
`FocusTrap`'s own root and was never reachable only from outside it the
|
|
343
|
+
way `.MuiDialog-container` was. The drawer therefore never had this bug;
|
|
344
|
+
the same fix on it only saves a caller an extra Tab press to reach the
|
|
345
|
+
panel's first control.
|
|
326
346
|
- **Keyboard Navigation**:
|
|
327
347
|
- `Escape` - Closes dialog (unless persistent mode is enabled)
|
|
328
348
|
- `Tab` - Navigates between focusable elements
|
|
@@ -98,3 +98,35 @@ export function mergedBackdropSlot<O>(
|
|
|
98
98
|
): Slot<SlotValue, O> {
|
|
99
99
|
return mergedSlot(look, legacy, slot);
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
/** What the transition slot's `onEntered` looks like on either renderer. */
|
|
103
|
+
interface TransitionSlotValue {
|
|
104
|
+
onEntered?: (node: HTMLElement, isAppearing: boolean) => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The transition slot: `onFocusEntered` (initial-focus, FUT-2696) runs
|
|
109
|
+
* first, then the deprecated `TransitionProps`/`SlideProps`, then
|
|
110
|
+
* `slotProps.transition` — so a caller's own `onEntered` still runs, and
|
|
111
|
+
* still runs last, meaning it can move focus again if it wants to.
|
|
112
|
+
*
|
|
113
|
+
* No `sx`/`look` here: unlike the paper and the backdrop, the transition slot
|
|
114
|
+
* carries no visual look of the Dialog's own to merge under a caller's.
|
|
115
|
+
*/
|
|
116
|
+
export function mergedTransitionSlot<O>(
|
|
117
|
+
onFocusEntered: (node: HTMLElement, isAppearing: boolean) => void,
|
|
118
|
+
legacy: TransitionSlotValue | undefined,
|
|
119
|
+
slot: Slot<TransitionSlotValue, O> | undefined,
|
|
120
|
+
): Slot<TransitionSlotValue, O> {
|
|
121
|
+
const compose = (merged: TransitionSlotValue): TransitionSlotValue => ({
|
|
122
|
+
...merged,
|
|
123
|
+
onEntered: (node: HTMLElement, isAppearing: boolean) => {
|
|
124
|
+
onFocusEntered(node, isAppearing);
|
|
125
|
+
merged.onEntered?.(node, isAppearing);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
if (typeof slot === 'function') {
|
|
129
|
+
return (ownerState: O) => compose({ ...legacy, ...slot(ownerState) });
|
|
130
|
+
}
|
|
131
|
+
return compose({ ...legacy, ...slot });
|
|
132
|
+
}
|
|
@@ -125,6 +125,17 @@ function defaultVariantStyles(theme: Theme, opts: VariantStyleOptions) {
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* The paper never grows an outline of its own — same as `.MuiDialog-container`
|
|
130
|
+
* (`outline: 0`, unconditionally, in MUI's own `Dialog.js`). It matters once
|
|
131
|
+
* `focusDialogOnEntered` (FUT-2696) falls back to focusing the paper itself: a
|
|
132
|
+
* browser's default `:focus`/`:focus-visible` ring is drawn OUTSIDE the box
|
|
133
|
+
* model — it never resizes a box or shifts a sibling — but this keeps the
|
|
134
|
+
* paper visually identical whether or not that fallback ever fires, rather
|
|
135
|
+
* than relying on that guarantee.
|
|
136
|
+
*/
|
|
137
|
+
const NO_FOCUS_OUTLINE = { outline: 0 } as const;
|
|
138
|
+
|
|
128
139
|
export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProps<Theme> {
|
|
129
140
|
const decorations = {
|
|
130
141
|
...glowStylesOf(theme, opts.glow),
|
|
@@ -135,6 +146,7 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
135
146
|
return {
|
|
136
147
|
...baseStylesOf(theme, opts),
|
|
137
148
|
...decorations,
|
|
149
|
+
...NO_FOCUS_OUTLINE,
|
|
138
150
|
backgroundColor: alpha(theme.palette.background.paper, DIALOG_GLASS.backgroundAlpha),
|
|
139
151
|
backdropFilter: `blur(${rem(theme, DIALOG_GLASS.blurPx)})`,
|
|
140
152
|
border: `${DIALOG_BORDER_WIDTH}px solid ${alpha(theme.palette.primary.main, DIALOG_GLASS.borderAlpha)}`,
|
|
@@ -150,6 +162,7 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
150
162
|
maxWidth: 'none',
|
|
151
163
|
maxHeight: 'none',
|
|
152
164
|
...decorations,
|
|
165
|
+
...NO_FOCUS_OUTLINE,
|
|
153
166
|
};
|
|
154
167
|
case 'drawer': {
|
|
155
168
|
const radius = borderRadiusOf(theme, opts.borderRadius);
|
|
@@ -166,6 +179,7 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
166
179
|
...dynamicViewportHeight('height'),
|
|
167
180
|
maxHeight: 'none',
|
|
168
181
|
...decorations,
|
|
182
|
+
...NO_FOCUS_OUTLINE,
|
|
169
183
|
};
|
|
170
184
|
}
|
|
171
185
|
default:
|
|
@@ -173,6 +187,7 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
173
187
|
...baseStylesOf(theme, opts),
|
|
174
188
|
...decorations,
|
|
175
189
|
...defaultVariantStyles(theme, opts),
|
|
190
|
+
...NO_FOCUS_OUTLINE,
|
|
176
191
|
};
|
|
177
192
|
}
|
|
178
193
|
}
|
|
@@ -11,6 +11,7 @@ import Typography from '@mui/material/Typography/index.js';
|
|
|
11
11
|
import { useTheme, type SxProps, type Theme } from '@mui/material/styles/index.js';
|
|
12
12
|
import React from 'react';
|
|
13
13
|
|
|
14
|
+
import { focusDialogOnEntered } from './Dialog.focus';
|
|
14
15
|
import { hasSpacingSlot, withDialogDefaults } from './Dialog.helpers';
|
|
15
16
|
import {
|
|
16
17
|
DIALOG_ACTIONS,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
DIALOG_TITLE,
|
|
21
22
|
DIALOG_TITLED_BODY_PADDING_TOP_UNITS,
|
|
22
23
|
} from './Dialog.metrics';
|
|
23
|
-
import { mergedBackdropSlot, mergedPaperSlot } from './Dialog.slots';
|
|
24
|
+
import { mergedBackdropSlot, mergedPaperSlot, mergedTransitionSlot } from './Dialog.slots';
|
|
24
25
|
import { backdropSxOf, variantStylesOf } from './Dialog.styles';
|
|
25
26
|
import { childTestId, resolveTestId, slotTestId, withoutTestIdProps } from '../../../platform/test-id';
|
|
26
27
|
import type {
|
|
@@ -106,6 +107,8 @@ type RendererProps = DialogLooks & { children: React.ReactNode };
|
|
|
106
107
|
* `slotProps.backdrop`, not the deprecated `BackdropProps`, and a caller's
|
|
107
108
|
* backdrop props from either spelling merge over it rather than replacing it
|
|
108
109
|
* (FUT-2672) — as their paper props merge over the variant's look (FUT-2613).
|
|
110
|
+
* `slotProps.transition` gives initial focus to the paper (FUT-2696, see
|
|
111
|
+
* `Dialog.focus.ts` and `Dialog.md`) ahead of a caller's own `onEntered`.
|
|
109
112
|
*/
|
|
110
113
|
function ModalDialog({
|
|
111
114
|
paperSx,
|
|
@@ -114,7 +117,7 @@ function ModalDialog({
|
|
|
114
117
|
dialogProps,
|
|
115
118
|
children,
|
|
116
119
|
}: RendererProps & { dialogProps: MuiDialogProps }) {
|
|
117
|
-
const { PaperProps: callerPaper, BackdropProps: callerBackdrop, slotProps, ...rest } = dialogProps;
|
|
120
|
+
const { PaperProps: callerPaper, BackdropProps: callerBackdrop, TransitionProps: callerTransition, slotProps, ...rest } = dialogProps;
|
|
118
121
|
return (
|
|
119
122
|
<MuiDialog
|
|
120
123
|
{...rest}
|
|
@@ -122,6 +125,7 @@ function ModalDialog({
|
|
|
122
125
|
...slotProps,
|
|
123
126
|
paper: mergedPaperSlot(paperSx, testId, callerPaper, slotProps?.paper),
|
|
124
127
|
backdrop: mergedBackdropSlot(backdropSx, callerBackdrop, slotProps?.backdrop),
|
|
128
|
+
transition: mergedTransitionSlot(focusDialogOnEntered, callerTransition, slotProps?.transition),
|
|
125
129
|
}}
|
|
126
130
|
>
|
|
127
131
|
{children}
|
|
@@ -139,6 +143,9 @@ function ModalDialog({
|
|
|
139
143
|
* `glass` blurs it here too; the paper does not frost (FUT-2673). MUI's
|
|
140
144
|
* `Drawer` does not merge `BackdropProps` into `slotProps.backdrop` — it takes
|
|
141
145
|
* one or the other — so both are folded into the one slot here.
|
|
146
|
+
* `slotProps.transition` gives initial focus the same way `ModalDialog` does
|
|
147
|
+
* (FUT-2696, see `Dialog.focus.ts` and `Dialog.md` for why the drawer's paper
|
|
148
|
+
* differs from the modal's paper here and needed the fix less).
|
|
142
149
|
*/
|
|
143
150
|
function DrawerDialog({
|
|
144
151
|
paperSx,
|
|
@@ -147,7 +154,7 @@ function DrawerDialog({
|
|
|
147
154
|
drawerProps,
|
|
148
155
|
children,
|
|
149
156
|
}: RendererProps & { drawerProps: DrawerProps }) {
|
|
150
|
-
const { PaperProps: callerPaper, BackdropProps: callerBackdrop, slotProps, ...rest } = drawerProps;
|
|
157
|
+
const { PaperProps: callerPaper, BackdropProps: callerBackdrop, SlideProps: callerTransition, slotProps, ...rest } = drawerProps;
|
|
151
158
|
return (
|
|
152
159
|
<Drawer
|
|
153
160
|
anchor="right"
|
|
@@ -157,6 +164,7 @@ function DrawerDialog({
|
|
|
157
164
|
...slotProps,
|
|
158
165
|
paper: mergedPaperSlot(paperSx, undefined, callerPaper, slotProps?.paper),
|
|
159
166
|
backdrop: mergedBackdropSlot(backdropSx, callerBackdrop, slotProps?.backdrop),
|
|
167
|
+
transition: mergedTransitionSlot(focusDialogOnEntered, callerTransition, slotProps?.transition),
|
|
160
168
|
}}
|
|
161
169
|
>
|
|
162
170
|
{children}
|
|
@@ -101,16 +101,17 @@ function useLoadingOpen(
|
|
|
101
101
|
isLoading: boolean,
|
|
102
102
|
inputValue: string,
|
|
103
103
|
disabled: boolean,
|
|
104
|
+
userClosedDropdown: boolean,
|
|
104
105
|
setOpen: Setter<boolean>,
|
|
105
106
|
setActiveIndex: Setter<number>,
|
|
106
107
|
): void {
|
|
107
108
|
useEffect(() => {
|
|
108
|
-
if (isLoading && inputValue.trim().length > 0 && !disabled) setOpen(true);
|
|
109
|
+
if (isLoading && inputValue.trim().length > 0 && !disabled && !userClosedDropdown) setOpen(true);
|
|
109
110
|
if (disabled) {
|
|
110
111
|
setOpen(false);
|
|
111
112
|
setActiveIndex(-1);
|
|
112
113
|
}
|
|
113
|
-
}, [isLoading, inputValue, disabled, setOpen, setActiveIndex]);
|
|
114
|
+
}, [isLoading, inputValue, disabled, userClosedDropdown, setOpen, setActiveIndex]);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/** Inputs to the auto-open check, all primitive/stable so effect deps stay complete. */
|
|
@@ -197,20 +198,37 @@ function commitSingleSelect<T>(item: T, p: AutocompleteProps<T>, s: Autocomplete
|
|
|
197
198
|
onChange(label);
|
|
198
199
|
}
|
|
199
200
|
|
|
201
|
+
/** Apply a pick: open a link suggestion, or commit a search one to the input. */
|
|
202
|
+
function commitPick<T>(
|
|
203
|
+
item: T,
|
|
204
|
+
linkUrl: string | null,
|
|
205
|
+
p: AutocompleteProps<T>,
|
|
206
|
+
s: AutocompleteState<T>,
|
|
207
|
+
onChange: (v: string) => void,
|
|
208
|
+
): void {
|
|
209
|
+
if (linkUrl !== null) (p.openLink ?? defaultOpenLink)(linkUrl, item);
|
|
210
|
+
else if (p.multiple) commitMultiSelect(item, p, s, onChange);
|
|
211
|
+
else commitSingleSelect(item, p, s, onChange);
|
|
212
|
+
}
|
|
213
|
+
|
|
200
214
|
function runSelectItem<T>(item: T, p: AutocompleteProps<T>, s: AutocompleteState<T>, onChange: (v: string) => void): void {
|
|
201
215
|
const url = (p.getUrl ?? defaultGetUrl)(item);
|
|
202
216
|
const isLink = (p.getSuggestionType ?? defaultGetSuggestionType)(item) === 'link' && Boolean(url);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
217
|
+
// A single-select pick (search or link) ends the current search: the list
|
|
218
|
+
// stays closed until the user types again. `multiple` mode is unchanged —
|
|
219
|
+
// commitMultiSelect already clears inputValue and resets the flag itself,
|
|
220
|
+
// so the empty-input branch of the filter effect keeps the list closed.
|
|
221
|
+
const isSingleSelectPick = isLink || !p.multiple;
|
|
222
|
+
commitPick(item, isLink ? (url as string) : null, p, s, onChange);
|
|
210
223
|
s.setOpen(false);
|
|
211
224
|
s.setActiveIndex(-1);
|
|
212
225
|
p.onSelect?.(item);
|
|
213
226
|
s.inputRef.current?.focus();
|
|
227
|
+
// Set LAST so a re-fired `onFocus` cannot clear the flag. The close itself
|
|
228
|
+
// relies on the input never blurring during a pick (the option's
|
|
229
|
+
// `onMouseDown` preventDefault, and Enter keeps focus): `onFocus` reopens a
|
|
230
|
+
// non-empty input directly, without reading this flag.
|
|
231
|
+
if (isSingleSelectPick) s.setUserClosedDropdown(true);
|
|
214
232
|
}
|
|
215
233
|
|
|
216
234
|
function buildKeyContext<T>(
|
|
@@ -265,7 +283,14 @@ export function useAutocomplete<T>(p: AutocompleteProps<T>): AutocompleteView<T>
|
|
|
265
283
|
useDebounceCleanup(s.debounceRef);
|
|
266
284
|
useScrollActiveIntoView(s.activeIndex, optionIdPrefix, s.listRef);
|
|
267
285
|
useGhostFlagReset(s.justCompletedGhost, s.setJustCompletedGhost);
|
|
268
|
-
useLoadingOpen(
|
|
286
|
+
useLoadingOpen(
|
|
287
|
+
Boolean(p.isLoading),
|
|
288
|
+
s.inputValue,
|
|
289
|
+
Boolean(p.disabled),
|
|
290
|
+
s.userClosedDropdown,
|
|
291
|
+
s.setOpen,
|
|
292
|
+
s.setActiveIndex,
|
|
293
|
+
);
|
|
269
294
|
useLocalFilter(p, s);
|
|
270
295
|
useGhostText(p, s);
|
|
271
296
|
|
|
@@ -348,3 +348,73 @@ test('CodeEditor has no accessibility violations', async () => {
|
|
|
348
348
|
- `@monaco-editor/react`: Monaco Editor React wrapper
|
|
349
349
|
- `@mui/material`: Material-UI components and theming
|
|
350
350
|
- `@mui/icons-material`: Material-UI icons
|
|
351
|
+
|
|
352
|
+
## Monaco setup (required in the host)
|
|
353
|
+
|
|
354
|
+
`@monaco-editor/react`'s default loader fetches Monaco from
|
|
355
|
+
`cdn.jsdelivr.net` at runtime — a network dependency the CDN version does not
|
|
356
|
+
even match (`monaco-editor` here is a `dependencies` entry, so the runtime
|
|
357
|
+
version is whatever the host resolves). **A host must configure it before the
|
|
358
|
+
first `CodeEditor` mounts**, or the editor silently depends on that CDN.
|
|
359
|
+
|
|
360
|
+
This package cannot do that wiring itself: Monaco's web workers need a
|
|
361
|
+
bundler-specific import (Vite's `?worker` suffix, or another bundler's
|
|
362
|
+
equivalent), and the package is built by tsup/esbuild, which does not
|
|
363
|
+
understand that suffix. Instead, `configureCodeEditor` (exported from
|
|
364
|
+
`@12-apps/ui/form/CodeEditor`) takes the host's own resolved `monaco` module
|
|
365
|
+
and a `getWorker` factory, and does the rest:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
// A Vite entry point — main.tsx, or (as here) .storybook/preview.tsx.
|
|
369
|
+
import * as monaco from 'monaco-editor';
|
|
370
|
+
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
|
371
|
+
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
|
372
|
+
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
|
|
373
|
+
import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
|
|
374
|
+
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
|
375
|
+
import { configureCodeEditor } from '@12-apps/ui/form/CodeEditor';
|
|
376
|
+
|
|
377
|
+
configureCodeEditor({
|
|
378
|
+
monaco,
|
|
379
|
+
getWorker: (_workerId, label) => {
|
|
380
|
+
switch (label) {
|
|
381
|
+
case 'json':
|
|
382
|
+
return new JsonWorker();
|
|
383
|
+
case 'css':
|
|
384
|
+
case 'scss':
|
|
385
|
+
case 'less':
|
|
386
|
+
return new CssWorker();
|
|
387
|
+
case 'html':
|
|
388
|
+
case 'handlebars':
|
|
389
|
+
case 'razor':
|
|
390
|
+
return new HtmlWorker();
|
|
391
|
+
case 'typescript':
|
|
392
|
+
case 'javascript':
|
|
393
|
+
return new TsWorker();
|
|
394
|
+
default:
|
|
395
|
+
return new EditorWorker();
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Call it once, at module scope, before any `CodeEditor` renders. This
|
|
402
|
+
package's own Storybook (`.storybook/preview.tsx`) calls it exactly this way,
|
|
403
|
+
so its stories load Monaco from `node_modules` rather than the CDN — see
|
|
404
|
+
`CodeEditor.monaco.ts` for `configureCodeEditor`'s implementation.
|
|
405
|
+
|
|
406
|
+
**One Monaco version.** The `monaco` module a host passes in is resolved from
|
|
407
|
+
its own `node_modules`, at whatever version its lockfile picked for
|
|
408
|
+
`monaco-editor` — keep that version aligned with this package's own
|
|
409
|
+
`monaco-editor` dependency (currently `^0.53.0`) so the types this package
|
|
410
|
+
ships describe the Monaco that actually runs.
|
|
411
|
+
|
|
412
|
+
**The CDN is still available, as an explicit opt-in** — never a silent
|
|
413
|
+
default. A host that would rather not bundle Monaco can call the loader
|
|
414
|
+
itself instead of `configureCodeEditor`:
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
import { loader } from '@monaco-editor/react';
|
|
418
|
+
|
|
419
|
+
loader.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.53.0/min/vs' } });
|
|
420
|
+
```
|
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
import type { Theme } from '@mui/material/styles/index.js';
|
|
2
|
-
import type
|
|
3
|
-
import type { editor } from 'monaco-editor';
|
|
2
|
+
import { loader, type Monaco } from '@monaco-editor/react';
|
|
3
|
+
import type { editor, Environment } from 'monaco-editor';
|
|
4
4
|
|
|
5
5
|
import { uiInk, type UiInk } from '../../../tokens/ink';
|
|
6
6
|
|
|
7
7
|
type EditorChrome = UiInk['codeEditor']['light'];
|
|
8
8
|
|
|
9
|
+
export interface ConfigureCodeEditorOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The `monaco-editor` module the host's own bundler resolved, e.g.
|
|
12
|
+
* `import * as monaco from 'monaco-editor'`. Handing it to the loader is
|
|
13
|
+
* what stops `@monaco-editor/react` fetching its own copy from
|
|
14
|
+
* `cdn.jsdelivr.net` at runtime — see `loader.config` below.
|
|
15
|
+
*/
|
|
16
|
+
monaco: Monaco;
|
|
17
|
+
/**
|
|
18
|
+
* Monaco's web-worker factory. The package cannot supply this itself: wiring
|
|
19
|
+
* a worker needs a bundler-specific import (Vite's `?worker` suffix, or the
|
|
20
|
+
* equivalent in another bundler), and this package is built by tsup/esbuild,
|
|
21
|
+
* which does not understand that suffix. The host's own Vite (or other)
|
|
22
|
+
* entry point builds this from its own worker imports and passes it here.
|
|
23
|
+
*/
|
|
24
|
+
getWorker: NonNullable<Environment['getWorker']>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Points `@monaco-editor/react` at the host's own installed Monaco instead of
|
|
29
|
+
* its default CDN loader, and wires up Monaco's web workers.
|
|
30
|
+
*
|
|
31
|
+
* Bundler-agnostic on purpose: it takes the resolved `monaco` module and a
|
|
32
|
+
* `getWorker` factory rather than importing either itself, so it can be
|
|
33
|
+
* called from any Vite (or other bundler) entry point — see `CodeEditor.md`
|
|
34
|
+
* for the Storybook preview and host snippets that call it.
|
|
35
|
+
*
|
|
36
|
+
* Call this once, before the first `CodeEditor` mounts (a Vite entry point's
|
|
37
|
+
* module scope runs once, which is early enough).
|
|
38
|
+
*/
|
|
39
|
+
export const configureCodeEditor = ({ monaco, getWorker }: ConfigureCodeEditorOptions): void => {
|
|
40
|
+
loader.config({ monaco });
|
|
41
|
+
(self as unknown as { MonacoEnvironment: Environment }).MonacoEnvironment = { getWorker };
|
|
42
|
+
};
|
|
43
|
+
|
|
9
44
|
/** Monaco's colour keys, filled from one mode's chrome. */
|
|
10
45
|
const chromeColors = (chrome: EditorChrome) => ({
|
|
11
46
|
'editor.background': chrome.background,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/feedback/Dialog/Dialog.styles.ts"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material/styles/index.js';\nimport { alpha, keyframes } from '@mui/material/styles/index.js';\n\nimport type { DialogBorderRadius, DialogSize, DialogVariant } from './Dialog.base';\nimport {\n DIALOG_BACKDROP,\n DIALOG_BORDER_WIDTH,\n DIALOG_GLASS,\n DIALOG_GLOW,\n DIALOG_GRADIENT,\n DIALOG_MARGIN_UNITS,\n DIALOG_MAX_WIDTH,\n DIALOG_PULSE,\n DIALOG_RADIUS_UNITS,\n DIALOG_WIDTH_PERCENT,\n} from './Dialog.metrics';\nimport { scrim, shadowInk } from '../../../tokens/ink';\nimport { rem, rems } from '../../../tokens/relative';\nimport { dynamicViewportHeight } from '../../../utils/viewport';\n\n/**\n * Paper/backdrop styling for the Dialog component (FUT-181 gate cleanup):\n * pure helpers so `Dialog.tsx` stays within the size/complexity budget.\n *\n * Every number comes from `Dialog.metrics.ts`, which the native renderer reads\n * too — so a paper cannot be 800px wide on one side and 780 on the other.\n */\n\n// Define pulse animation — a function of the theme so the ring's spread follows the type scale.\nconst pulseAnimation = (theme: Theme) => keyframes`\n 0% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 1;\n }\n 70% {\n box-shadow: 0 0 0 ${rem(theme, DIALOG_PULSE.spread)} currentColor;\n opacity: 0;\n }\n 100% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0;\n }\n`;\n\n/** Style toggles that shape the dialog paper (extracted so each fn stays small). */\ninterface VariantStyleOptions {\n variant: DialogVariant;\n size: DialogSize;\n borderRadius: DialogBorderRadius;\n glass: boolean;\n gradient: boolean;\n glow: boolean;\n pulse: boolean;\n}\n\nfunction borderRadiusOf(theme: Theme, borderRadius: VariantStyleOptions['borderRadius']) {\n const units = DIALOG_RADIUS_UNITS[borderRadius] ?? DIALOG_RADIUS_UNITS.lg;\n // `none` is a bare 0 rather than `spacing(0)`, as this has always written it.\n return units === 0 ? '0' : theme.spacing(units);\n}\n\nfunction maxWidthOf(theme: Theme, size: VariantStyleOptions['size']): string {\n return rem(theme, DIALOG_MAX_WIDTH[size] ?? DIALOG_MAX_WIDTH.md);\n}\n\nfunction baseStylesOf(theme: Theme, opts: VariantStyleOptions) {\n return {\n borderRadius: borderRadiusOf(theme, opts.borderRadius),\n maxWidth: maxWidthOf(theme, opts.size),\n width: `${DIALOG_WIDTH_PERCENT}vw`,\n margin: theme.spacing(DIALOG_MARGIN_UNITS),\n transition: theme.transitions.create(\n ['box-shadow', 'background-color', 'backdrop-filter'],\n { duration: theme.transitions.duration.standard },\n ),\n };\n}\n\nfunction glowStylesOf(theme: Theme, glow: boolean) {\n return glow\n ? {\n boxShadow: `${rems(theme, 0, 0, DIALOG_GLOW.blurRadius)} ${alpha(theme.palette.primary.main, DIALOG_GLOW.alpha)}`,\n }\n : {};\n}\n\nfunction pulseStylesOf(theme: Theme, pulse: boolean) {\n if (!pulse) return {};\n return {\n position: 'relative' as const,\n '&::after': {\n content: '\"\"',\n position: 'absolute' as const,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n borderRadius: 'inherit',\n backgroundColor: theme.palette.primary.main,\n opacity: DIALOG_PULSE.alpha,\n animation: `${pulseAnimation(theme)} ${DIALOG_PULSE.durationMs / 1000}s infinite`,\n pointerEvents: 'none' as const,\n zIndex: DIALOG_PULSE.zIndex,\n },\n };\n}\n\n/** The default variant's paper: optional gradient/glass over the base styles. */\nfunction defaultVariantStyles(theme: Theme, opts: VariantStyleOptions) {\n const gradientStyles = opts.gradient\n ? {\n background: `linear-gradient(${DIALOG_GRADIENT.angleDeg}deg, ${alpha(theme.palette.primary.main, DIALOG_GRADIENT.stopAlpha)}, ${alpha(theme.palette.secondary.main, DIALOG_GRADIENT.stopAlpha)})`,\n backdropFilter: `blur(${rem(theme, DIALOG_GRADIENT.blurPx)})`,\n }\n : {};\n return {\n ...gradientStyles,\n backgroundColor: opts.glass\n ? alpha(theme.palette.background.paper, DIALOG_GLASS.backgroundAlpha)\n : theme.palette.background.paper,\n backdropFilter: opts.glass ? `blur(${rem(theme, DIALOG_GLASS.blurPx)})` : 'none',\n border: opts.glass\n ? `${DIALOG_BORDER_WIDTH}px solid ${alpha(theme.palette.primary.main, DIALOG_GLASS.borderAlpha)}`\n : 'none',\n };\n}\n\nexport function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProps<Theme> {\n const decorations = {\n ...glowStylesOf(theme, opts.glow),\n ...pulseStylesOf(theme, opts.pulse),\n };\n switch (opts.variant) {\n case 'glass':\n return {\n ...baseStylesOf(theme, opts),\n ...decorations,\n backgroundColor: alpha(theme.palette.background.paper, DIALOG_GLASS.backgroundAlpha),\n backdropFilter: `blur(${rem(theme, DIALOG_GLASS.blurPx)})`,\n border: `${DIALOG_BORDER_WIDTH}px solid ${alpha(theme.palette.primary.main, DIALOG_GLASS.borderAlpha)}`,\n boxShadow: `${rems(theme, 0, DIALOG_GLASS.shadow.offsetY, DIALOG_GLASS.shadow.blurRadius)} ${shadowInk(theme, DIALOG_GLASS.shadow.alpha)}`,\n };\n case 'fullscreen':\n return {\n borderRadius: 0,\n margin: 0,\n width: '100vw',\n // `100dvh`, not `100vh` — see `dynamicViewportHeight`.\n ...dynamicViewportHeight('height'),\n maxWidth: 'none',\n maxHeight: 'none',\n ...decorations,\n };\n case 'drawer': {\n const radius = borderRadiusOf(theme, opts.borderRadius);\n return {\n // `radius` is already a length (`theme.spacing(n)` → '16px', or 0): gluing\n // another `px` on wrote '16pxpx', which the browser dropped, leaving the\n // panel's leading corners square.\n borderRadius: `${radius} 0 0 ${radius}`,\n margin: 0,\n width: maxWidthOf(theme, opts.size),\n // Never wider than the screen: a 600px panel on a 375px phone put its\n // title and the start of every line off the left edge.\n maxWidth: '100%',\n ...dynamicViewportHeight('height'),\n maxHeight: 'none',\n ...decorations,\n };\n }\n default:\n return {\n ...baseStylesOf(theme, opts),\n ...decorations,\n ...defaultVariantStyles(theme, opts),\n };\n }\n}\n\nexport function backdropSxOf(theme: Theme, glass: boolean) {\n return {\n backgroundColor: scrim(theme, glass ? DIALOG_BACKDROP.alpha.glass : DIALOG_BACKDROP.alpha.plain),\n backdropFilter: glass ? `blur(${rem(theme, DIALOG_BACKDROP.blurPx)})` : 'none',\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/feedback/Dialog/Dialog.tsx"],"sourcesContent":["import CloseIcon from '@mui/icons-material/Close';\nimport Backdrop from '@mui/material/Backdrop/index.js';\nimport Box from '@mui/material/Box/index.js';\nimport MuiDialog, { type DialogProps as MuiDialogProps } from '@mui/material/Dialog/index.js';\nimport MuiDialogActions from '@mui/material/DialogActions/index.js';\nimport MuiDialogContent from '@mui/material/DialogContent/index.js';\nimport MuiDialogTitle from '@mui/material/DialogTitle/index.js';\nimport Drawer, { type DrawerProps } from '@mui/material/Drawer/index.js';\nimport IconButton from '@mui/material/IconButton/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { useTheme, type SxProps, type Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { hasSpacingSlot, withDialogDefaults } from './Dialog.helpers';\nimport {\n DIALOG_ACTIONS,\n DIALOG_BODY_PADDING_UNITS,\n DIALOG_CONTENT_PADDING_UNITS,\n DIALOG_HEADER_CHILDREN_PADDING_UNITS,\n DIALOG_TITLE,\n DIALOG_TITLED_BODY_PADDING_TOP_UNITS,\n} from './Dialog.metrics';\nimport { mergedBackdropSlot, mergedPaperSlot } from './Dialog.slots';\nimport { backdropSxOf, variantStylesOf } from './Dialog.styles';\nimport { childTestId, resolveTestId, slotTestId, withoutTestIdProps } from '../../../platform/test-id';\nimport type {\n DialogActionsProps,\n DialogContentProps,\n DialogHeaderProps,\n DialogProps,\n} from './Dialog.types';\n\n/**\n * The padding a dialog gives children that are not managing their own — and the\n * rule that takes it back when it turns out they are.\n *\n * `:has()` is doing what {@link hasSpacingSlot} cannot. A slot passed through a\n * COMPONENT (`<Body />` rendering a `DialogContent`) is invisible to any check\n * on element type: React does not know what a component renders until it has\n * rendered it, and by then the wrapper is already in the tree. The selector\n * asks the question one step later, of the DOM, where the answer exists.\n *\n * `display: contents` rather than `padding: 0`, because the padding is only\n * half of what a wrapper does. The other half is standing between the paper and\n * its children: MUI's `scroll=\"paper\"` makes the paper a flex column with\n * `overflow-y: auto`, so a `DialogContent` that is no longer its direct child\n * cannot be the thing that scrolls — the whole dialog scrolls instead and\n * carries the `DialogActions` off the bottom with it. An element with\n * `display: contents` generates no box at all: its children become the paper's\n * flex items, and its padding is never applied. Both halves, one declaration.\n *\n * A browser without `:has()` falls back to the padded box — the layout this\n * had before, not a broken one.\n */\nconst BODY_SX = {\n px: DIALOG_BODY_PADDING_UNITS.horizontal,\n pb: DIALOG_BODY_PADDING_UNITS.bottom,\n '&:has(> .MuiDialogContent-root), &:has(> .MuiDialogActions-root)': {\n display: 'contents',\n },\n} as const;\n\n/**\n * The dialog body. Consumers that pass raw children (no <DialogContent>) used\n * to render them flush against the paper edges — give them comfortable default\n * padding; <DialogContent>/<DialogActions> users keep managing their own\n * spacing.\n */\nfunction bodyOf(children: React.ReactNode, hasTitle: boolean): React.ReactNode {\n // Read lazily: both slots are declared below this function.\n if (hasSpacingSlot(children, [DialogContent, DialogActions])) return children;\n const pt = hasTitle\n ? DIALOG_BODY_PADDING_UNITS.topUnderTitle\n : DIALOG_BODY_PADDING_UNITS.top;\n return <Box sx={{ ...BODY_SX, pt }}>{children}</Box>;\n}\n\n/**\n * MUI's `onClose` for either renderer: a `persistent` dialog swallows the\n * backdrop press and Escape, and the caller's `onClose` is called with no\n * arguments, as `Dialog.base.ts` promises. The drawer used to get the caller's\n * raw `onClose` — so it closed when `persistent` said it must not, and was\n * handed MUI's `(event, reason)` (FUT-2673).\n */\nfunction closeHandlerOf(persistent: boolean, onClose: (() => void) | undefined) {\n return (_event: object, reason: 'backdropClick' | 'escapeKeyDown') => {\n if (persistent && (reason === 'backdropClick' || reason === 'escapeKeyDown')) {\n return;\n }\n onClose?.();\n };\n}\n\n/** The looks the Dialog lays under a caller's paper and backdrop props. */\ninterface DialogLooks {\n paperSx: SxProps<Theme>;\n backdropSx: SxProps<Theme>;\n testId: string | undefined;\n}\n\n/** What either renderer below takes: the looks, and the dialog's own children. */\ntype RendererProps = DialogLooks & { children: React.ReactNode };\n\n/**\n * Every variant but the drawer: MUI's `Dialog`. The scrim reaches MUI through\n * `slotProps.backdrop`, not the deprecated `BackdropProps`, and a caller's\n * backdrop props from either spelling merge over it rather than replacing it\n * (FUT-2672) — as their paper props merge over the variant's look (FUT-2613).\n */\nfunction ModalDialog({\n paperSx,\n backdropSx,\n testId,\n dialogProps,\n children,\n}: RendererProps & { dialogProps: MuiDialogProps }) {\n const { PaperProps: callerPaper, BackdropProps: callerBackdrop, slotProps, ...rest } = dialogProps;\n return (\n <MuiDialog\n {...rest}\n slotProps={{\n ...slotProps,\n paper: mergedPaperSlot(paperSx, testId, callerPaper, slotProps?.paper),\n backdrop: mergedBackdropSlot(backdropSx, callerBackdrop, slotProps?.backdrop),\n }}\n >\n {children}\n </MuiDialog>\n );\n}\n\n/**\n * The drawer variant: MUI's `Drawer`, with the variant's look on the Drawer's\n * OWN paper — on an inner Box it left the paper 0px wide and clipped the panel —\n * and a caller's paper props merged over it rather than replacing it. The\n * test id stays on the Drawer root, so the paper carries none of its own.\n *\n * The scrim rides the drawer's backdrop exactly as it rides the dialog's, so\n * `glass` blurs it here too; the paper does not frost (FUT-2673). MUI's\n * `Drawer` does not merge `BackdropProps` into `slotProps.backdrop` — it takes\n * one or the other — so both are folded into the one slot here.\n */\nfunction DrawerDialog({\n paperSx,\n backdropSx,\n testId,\n drawerProps,\n children,\n}: RendererProps & { drawerProps: DrawerProps }) {\n const { PaperProps: callerPaper, BackdropProps: callerBackdrop, slotProps, ...rest } = drawerProps;\n return (\n <Drawer\n anchor=\"right\"\n data-testid={testId}\n {...rest}\n slotProps={{\n ...slotProps,\n paper: mergedPaperSlot(paperSx, undefined, callerPaper, slotProps?.paper),\n backdrop: mergedBackdropSlot(backdropSx, callerBackdrop, slotProps?.backdrop),\n }}\n >\n {children}\n </Drawer>\n );\n}\n\nexport const Dialog: React.FC<DialogProps> = (rawProps) => {\n const {\n children,\n variant,\n size,\n title,\n description,\n showCloseButton,\n backdrop,\n persistent,\n glass,\n gradient,\n glow,\n pulse,\n borderRadius,\n onClose,\n open,\n ...others\n } = withDialogDefaults(rawProps);\n const theme = useTheme();\n // Every spelling of the test id the shared contract allows, mapped to the one\n // the DOM reads, and stripped from what is spread — `testID` is React\n // Native's name for it and is not a DOM attribute. The native `Dialog` does\n // the same in reverse.\n const testId = resolveTestId(others, 'dialog');\n const props = withoutTestIdProps(others);\n const looks: DialogLooks = {\n paperSx: variantStylesOf(theme, { variant, size, borderRadius, glass, gradient, glow, pulse }),\n backdropSx: backdropSxOf(theme, glass),\n testId,\n };\n const handleClose = closeHandlerOf(persistent, onClose);\n\n const header = title ? (\n <DialogHeader\n title={title}\n subtitle={description}\n showCloseButton={showCloseButton}\n onClose={onClose}\n dataTestId={testId}\n />\n ) : null;\n const body = bodyOf(children, Boolean(title));\n\n if (variant === 'drawer') {\n return (\n <DrawerDialog {...looks} drawerProps={{ ...props, open, onClose: handleClose }}>\n {header}\n {body}\n </DrawerDialog>\n );\n }\n\n return (\n <ModalDialog\n {...looks}\n dialogProps={{\n open,\n onClose: handleClose,\n fullScreen: variant === 'fullscreen',\n BackdropComponent: backdrop ? Backdrop : undefined,\n ...props,\n }}\n >\n {header}\n {body}\n </ModalDialog>\n );\n};\n\nexport const DialogHeader: React.FC<DialogHeaderProps> = ({\n children,\n title,\n subtitle,\n showCloseButton = true,\n onClose,\n ...others\n}) => {\n const props = withoutTestIdProps(others);\n\n if (children) {\n return (\n <Box\n data-testid={slotTestId(others, 'header', 'dialog')}\n sx={{\n p: DIALOG_HEADER_CHILDREN_PADDING_UNITS,\n borderBottom: '1px solid',\n borderColor: 'divider',\n }}\n >\n {children}\n </Box>\n );\n }\n\n return (\n <MuiDialogTitle\n data-testid={slotTestId(others, 'title', 'dialog')}\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n pb: subtitle\n ? DIALOG_TITLE.paddingBottomUnits.withSubtitle\n : DIALOG_TITLE.paddingBottomUnits.plain,\n }}\n {...props}\n >\n <Box>\n <Typography variant=\"h6\" component=\"div\">\n {title}\n </Typography>\n {subtitle && (\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ mt: DIALOG_TITLE.subtitleGapUnits }}\n >\n {subtitle}\n </Typography>\n )}\n </Box>\n {showCloseButton && onClose && (\n <IconButton\n aria-label=\"close\"\n onClick={onClose}\n data-testid={childTestId(others, 'close', 'dialog')}\n sx={{\n color: 'text.secondary',\n '&:hover': {\n backgroundColor: 'action.hover',\n },\n }}\n >\n <CloseIcon />\n </IconButton>\n )}\n </MuiDialogTitle>\n );\n};\n\n/**\n * Top padding for a body that sits directly under the title.\n *\n * MUI zeroes it there (`.MuiDialogTitle-root + &` on a non-`dividers` content),\n * assuming the title's own bottom padding is the whole gap. That assumption\n * breaks for a body opening with an outlined field: a filled field floats its\n * label ABOVE the input's border box, and `DialogContent` is `overflow-y: auto`\n * — a clipping box — so the label gets sliced in half (FUT-544: \"Tipo\" in the\n * \"Novo insumo\" dialog).\n *\n * Reserve the overhang rather than restore the full 24px: the title already\n * contributes its bottom padding, so a shrunk label's `translate(…, -9px)` is\n * all that is missing. 12px clears it and leaves the gap visually tight.\n */\nconst TITLED_BODY_PADDING_TOP = DIALOG_TITLED_BODY_PADDING_TOP_UNITS;\n\n/**\n * `.MuiDialogTitle-root + &` is `(0,2,0)`-specific and a plain `sx` entry is\n * `(0,1,0)`, so the padding above only survives the cascade by naming the class\n * again — `(0,3,0)`. Scoped to the title-following case so an untitled dialog\n * keeps MUI's roomier default.\n */\nconst TITLED_BODY_SELECTOR = '.MuiDialogTitle-root + &.MuiDialogContent-root';\n\nexport const DialogContent: React.FC<DialogContentProps> = ({\n children,\n dividers = false,\n dense = false,\n ...others\n}) => (\n <MuiDialogContent\n data-testid={slotTestId(others, 'content', 'dialog')}\n dividers={dividers}\n sx={{\n padding: dense ? DIALOG_CONTENT_PADDING_UNITS.dense : DIALOG_CONTENT_PADDING_UNITS.normal,\n [TITLED_BODY_SELECTOR]: { paddingTop: TITLED_BODY_PADDING_TOP },\n '&.MuiDialogContent-dividers': {\n borderTop: dividers ? '1px solid' : 'none',\n borderBottom: dividers ? '1px solid' : 'none',\n borderColor: 'divider',\n },\n }}\n {...withoutTestIdProps(others)}\n >\n {children}\n </MuiDialogContent>\n );\n\nexport const DialogActions: React.FC<DialogActionsProps> = ({\n children,\n alignment = 'right',\n spacing = DIALOG_ACTIONS.defaultSpacingUnits,\n ...others\n}) => {\n const getJustifyContent = () => {\n switch (alignment) {\n case 'left': return 'flex-start';\n case 'center': return 'center';\n case 'right': return 'flex-end';\n case 'space-between': return 'space-between';\n default: return 'flex-end';\n }\n };\n\n return (\n <MuiDialogActions\n data-testid={slotTestId(others, 'actions', 'dialog')}\n sx={{\n justifyContent: getJustifyContent(),\n gap: spacing,\n p: DIALOG_ACTIONS.paddingUnits,\n }}\n {...withoutTestIdProps(others)}\n >\n {children}\n </MuiDialogActions>\n );\n};"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\n\nimport {\n defaultGetKey,\n defaultGetLabel,\n defaultGetSuggestionType,\n defaultGetUrl,\n defaultOpenLink,\n filterSuggestions,\n firstGhostSuggestion,\n type MatchMode,\n} from './Autocomplete.helpers';\nimport { handleAutocompleteKey, type KeyContext } from './Autocomplete.keys';\nimport type { AutocompleteProps } from './Autocomplete.types';\n\ntype Setter<V> = React.Dispatch<React.SetStateAction<V>>;\n\n/** All mutable state + refs for one Autocomplete instance. */\ninterface AutocompleteState<T> {\n open: boolean;\n setOpen: Setter<boolean>;\n activeIndex: number;\n setActiveIndex: Setter<number>;\n ghost: string;\n setGhost: Setter<string>;\n inputValue: string;\n setInputValue: Setter<string>;\n composition: boolean;\n setComposition: Setter<boolean>;\n filteredSuggestions: T[];\n setFilteredSuggestions: Setter<T[]>;\n isInputFocused: boolean;\n setIsInputFocused: Setter<boolean>;\n justCompletedGhost: boolean;\n setJustCompletedGhost: Setter<boolean>;\n userClosedDropdown: boolean;\n setUserClosedDropdown: Setter<boolean>;\n inputRef: React.RefObject<HTMLInputElement | null>;\n listRef: React.RefObject<HTMLUListElement | null>;\n debounceRef: React.RefObject<number | undefined>;\n}\n\nfunction useAutocompleteState<T>(value: string, suggestions: T[]): AutocompleteState<T> {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const [ghost, setGhost] = useState('');\n const [inputValue, setInputValue] = useState(value);\n const [composition, setComposition] = useState(false);\n const [filteredSuggestions, setFilteredSuggestions] = useState<T[]>(suggestions);\n const [isInputFocused, setIsInputFocused] = useState(false);\n const [justCompletedGhost, setJustCompletedGhost] = useState(false);\n const [userClosedDropdown, setUserClosedDropdown] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLUListElement | null>(null);\n const debounceRef = useRef<number | undefined>(undefined);\n return {\n open, setOpen, activeIndex, setActiveIndex, ghost, setGhost, inputValue, setInputValue,\n composition, setComposition, filteredSuggestions, setFilteredSuggestions, isInputFocused,\n setIsInputFocused, justCompletedGhost, setJustCompletedGhost, userClosedDropdown,\n setUserClosedDropdown, inputRef, listRef, debounceRef,\n };\n}\n\n// ---- effects (one tiny hook each; explicit params keep deps complete) --------\n\nfunction useValueSync(value: string, setInputValue: Setter<string>): void {\n useEffect(() => setInputValue(value), [value, setInputValue]);\n}\n\nfunction useDebounceCleanup(debounceRef: React.RefObject<number | undefined>): void {\n useEffect(\n () => () => {\n if (debounceRef.current) window.clearTimeout(debounceRef.current);\n },\n [debounceRef],\n );\n}\n\nfunction useScrollActiveIntoView(\n activeIndex: number,\n optionIdPrefix: string,\n listRef: React.RefObject<HTMLUListElement | null>,\n): void {\n useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n document\n .getElementById(`${optionIdPrefix}-${activeIndex}`)\n ?.scrollIntoView({ block: 'nearest' });\n }, [activeIndex, optionIdPrefix, listRef]);\n}\n\nfunction useGhostFlagReset(justCompletedGhost: boolean, setJustCompletedGhost: Setter<boolean>): void {\n useEffect(() => {\n if (!justCompletedGhost) return;\n const timer = setTimeout(() => setJustCompletedGhost(false), 50);\n return () => clearTimeout(timer);\n }, [justCompletedGhost, setJustCompletedGhost]);\n}\n\nfunction useLoadingOpen(\n isLoading: boolean,\n inputValue: string,\n disabled: boolean,\n setOpen: Setter<boolean>,\n setActiveIndex: Setter<number>,\n): void {\n useEffect(() => {\n if (isLoading && inputValue.trim().length > 0 && !disabled) setOpen(true);\n if (disabled) {\n setOpen(false);\n setActiveIndex(-1);\n }\n }, [isLoading, inputValue, disabled, setOpen, setActiveIndex]);\n}\n\n/** Inputs to the auto-open check, all primitive/stable so effect deps stay complete. */\ninterface AutoOpenArgs {\n inputValue: string;\n isInputFocused: boolean;\n composition: boolean;\n justCompletedGhost: boolean;\n userClosedDropdown: boolean;\n activeIndex: number;\n setOpen: Setter<boolean>;\n setActiveIndex: Setter<number>;\n}\n\n/** Auto-open the dropdown once local filtering has a fresh result set. */\nfunction maybeAutoOpen(a: AutoOpenArgs, hasResults: boolean): void {\n const canAutoOpen =\n a.inputValue.trim() &&\n a.isInputFocused &&\n !a.composition &&\n !a.justCompletedGhost &&\n !a.userClosedDropdown;\n if (!canAutoOpen) return;\n a.setOpen(true);\n if (hasResults && a.activeIndex === -1) a.setActiveIndex(0);\n}\n\nfunction useLocalFilter<T>(p: AutocompleteProps<T>, s: AutocompleteState<T>): void {\n const propSuggestions = p.suggestions;\n const getLabel = p.getLabel ?? defaultGetLabel;\n const matchMode: MatchMode = p.matchMode ?? 'contains';\n const async = Boolean(p.async);\n const {\n inputValue, isInputFocused, composition, justCompletedGhost, userClosedDropdown, activeIndex,\n setFilteredSuggestions, setOpen, setActiveIndex,\n } = s;\n useEffect(() => {\n const suggestions = propSuggestions ?? [];\n if (async || !inputValue) {\n setFilteredSuggestions(suggestions);\n return;\n }\n const filtered = filterSuggestions(suggestions, inputValue, matchMode, getLabel);\n setFilteredSuggestions(filtered);\n maybeAutoOpen(\n { inputValue, isInputFocused, composition, justCompletedGhost, userClosedDropdown, activeIndex, setOpen, setActiveIndex },\n filtered.length > 0,\n );\n }, [\n propSuggestions, inputValue, async, getLabel, matchMode, isInputFocused, composition,\n justCompletedGhost, userClosedDropdown, activeIndex, setFilteredSuggestions, setOpen, setActiveIndex,\n ]);\n}\n\nfunction useGhostText<T>(p: AutocompleteProps<T>, s: AutocompleteState<T>): void {\n const getLabel = p.getLabel ?? defaultGetLabel;\n const getType = p.getSuggestionType ?? defaultGetSuggestionType;\n const showGhost = p.showGhostText ?? true;\n const { inputValue, filteredSuggestions, isInputFocused, composition, setGhost } = s;\n useEffect(() => {\n if (!inputValue || !isInputFocused || composition || !showGhost) {\n setGhost('');\n return;\n }\n const first = firstGhostSuggestion(filteredSuggestions, inputValue, getLabel, getType);\n setGhost(first ? getLabel(first).slice(inputValue.length) : '');\n }, [inputValue, filteredSuggestions, isInputFocused, composition, showGhost, getLabel, getType, setGhost]);\n}\n\n// ---- selection (split by mode to keep each function under the complexity bar) -\n\nfunction commitMultiSelect<T>(item: T, p: AutocompleteProps<T>, s: AutocompleteState<T>, onChange: (v: string) => void): void {\n const getKey = p.getKey ?? defaultGetKey;\n const current = p.selectedItems ?? [];\n if (!current.some((sel) => getKey(sel) === getKey(item))) p.onSelectedItemsChange?.([...current, item]);\n s.setInputValue('');\n onChange('');\n s.setUserClosedDropdown(false);\n}\n\nfunction commitSingleSelect<T>(item: T, p: AutocompleteProps<T>, s: AutocompleteState<T>, onChange: (v: string) => void): void {\n const label = (p.getLabel ?? defaultGetLabel)(item);\n s.setInputValue(label);\n onChange(label);\n}\n\nfunction runSelectItem<T>(item: T, p: AutocompleteProps<T>, s: AutocompleteState<T>, onChange: (v: string) => void): void {\n const url = (p.getUrl ?? defaultGetUrl)(item);\n const isLink = (p.getSuggestionType ?? defaultGetSuggestionType)(item) === 'link' && Boolean(url);\n if (isLink) {\n (p.openLink ?? defaultOpenLink)(url as string, item);\n } else if (p.multiple) {\n commitMultiSelect(item, p, s, onChange);\n } else {\n commitSingleSelect(item, p, s, onChange);\n }\n s.setOpen(false);\n s.setActiveIndex(-1);\n p.onSelect?.(item);\n s.inputRef.current?.focus();\n}\n\nfunction buildKeyContext<T>(\n p: AutocompleteProps<T>,\n s: AutocompleteState<T>,\n selectItem: (item: T) => void,\n onChange: (v: string) => void,\n): KeyContext<T> {\n return {\n open: s.open, activeIndex: s.activeIndex, filteredSuggestions: s.filteredSuggestions,\n maxVisibleItems: p.maxVisibleItems ?? 10, allowFreeText: p.allowFreeText ?? true,\n inputValue: s.inputValue, ghost: s.ghost, isInputFocused: s.isInputFocused,\n multiple: Boolean(p.multiple), selectedItems: p.selectedItems ?? [],\n getLabel: p.getLabel ?? defaultGetLabel,\n getSuggestionType: p.getSuggestionType ?? defaultGetSuggestionType,\n setOpen: s.setOpen, setActiveIndex: s.setActiveIndex, setUserClosedDropdown: s.setUserClosedDropdown,\n setInputValue: s.setInputValue, setGhost: s.setGhost, setJustCompletedGhost: s.setJustCompletedGhost,\n debouncedOnChange: onChange, selectItem, onSelectedItemsChange: p.onSelectedItemsChange,\n };\n}\n\n/** The full view-model the presentational component renders. */\nexport interface AutocompleteView<T> {\n id: string;\n listId: string;\n optionIdPrefix: string;\n state: AutocompleteState<T>;\n visibleSuggestions: T[];\n activeDescendant: string | undefined;\n selectItem: (item: T) => void;\n removeSelectedItem: (item: T) => void;\n onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n onInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n onFocus: () => void;\n onBlur: () => void;\n onClickAway: () => void;\n}\n\n/** Wire state, effects, and (plain, per-render) handlers for the Autocomplete component. */\nexport function useAutocomplete<T>(p: AutocompleteProps<T>): AutocompleteView<T> {\n const componentId = useId();\n const id = p.id || componentId;\n const optionIdPrefix = `${id}-option`;\n const s = useAutocompleteState<T>(p.value, p.suggestions ?? []);\n\n const debouncedOnChange = (val: string): void => {\n if (s.debounceRef.current) window.clearTimeout(s.debounceRef.current);\n s.debounceRef.current = window.setTimeout(() => p.onChange(val), p.debounceMs ?? 150);\n };\n\n useValueSync(p.value, s.setInputValue);\n useDebounceCleanup(s.debounceRef);\n useScrollActiveIntoView(s.activeIndex, optionIdPrefix, s.listRef);\n useGhostFlagReset(s.justCompletedGhost, s.setJustCompletedGhost);\n useLoadingOpen(Boolean(p.isLoading), s.inputValue, Boolean(p.disabled), s.setOpen, s.setActiveIndex);\n useLocalFilter(p, s);\n useGhostText(p, s);\n\n const selectItem = (item: T): void => runSelectItem(item, p, s, debouncedOnChange);\n\n return {\n id,\n listId: `${id}-listbox`,\n optionIdPrefix,\n state: s,\n visibleSuggestions: s.filteredSuggestions.slice(0, p.maxVisibleItems ?? 10),\n activeDescendant: s.activeIndex >= 0 ? `${optionIdPrefix}-${s.activeIndex}` : undefined,\n selectItem,\n removeSelectedItem: (item: T): void => {\n const getKey = p.getKey ?? defaultGetKey;\n p.onSelectedItemsChange?.((p.selectedItems ?? []).filter((sel) => getKey(sel) !== getKey(item)));\n s.inputRef.current?.focus();\n },\n onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>): void =>\n handleAutocompleteKey(e, s.composition, buildKeyContext(p, s, selectItem, debouncedOnChange)),\n onInputChange: (e: React.ChangeEvent<HTMLInputElement>): void => {\n s.setInputValue(e.target.value);\n debouncedOnChange(e.target.value);\n if (!s.open || s.filteredSuggestions.length === 0) s.setActiveIndex(-1);\n s.setUserClosedDropdown(false);\n },\n onFocus: (): void => {\n s.setIsInputFocused(true);\n s.setUserClosedDropdown(false);\n if (!s.inputValue.trim()) return;\n s.setOpen(true);\n if (s.filteredSuggestions.length > 0 && s.activeIndex === -1) s.setActiveIndex(0);\n },\n onBlur: (): void => {\n window.setTimeout(() => s.setIsInputFocused(false), 150);\n },\n onClickAway: (): void => {\n s.setOpen(false);\n s.setActiveIndex(-1);\n s.setUserClosedDropdown(true);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,WAAW,OAAO,QAAQ,gBAAgB;AA0CnD,SAAS,qBAAwB,OAAe,aAAwC;AACtF,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,EAAE;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAAc,WAAW;AAC/E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,KAAK;AAC1D,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAClE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAClE,QAAM,WAAW,OAAyB,IAAI;AAC9C,QAAM,UAAU,OAAgC,IAAI;AACpD,QAAM,cAAc,OAA2B,MAAS;AACxD,SAAO;AAAA,IACL;AAAA,IAAM;AAAA,IAAS;AAAA,IAAa;AAAA,IAAgB;AAAA,IAAO;AAAA,IAAU;AAAA,IAAY;AAAA,IACzE;AAAA,IAAa;AAAA,IAAgB;AAAA,IAAqB;AAAA,IAAwB;AAAA,IAC1E;AAAA,IAAmB;AAAA,IAAoB;AAAA,IAAuB;AAAA,IAC9D;AAAA,IAAuB;AAAA,IAAU;AAAA,IAAS;AAAA,EAC5C;AACF;AAIA,SAAS,aAAa,OAAe,eAAqC;AACxE,YAAU,MAAM,cAAc,KAAK,GAAG,CAAC,OAAO,aAAa,CAAC;AAC9D;AAEA,SAAS,mBAAmB,aAAwD;AAClF;AAAA,IACE,MAAM,MAAM;AACV,UAAI,YAAY,QAAS,QAAO,aAAa,YAAY,OAAO;AAAA,IAClE;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AACF;AAEA,SAAS,wBACP,aACA,gBACA,SACM;AACN,YAAU,MAAM;AACd,QAAI,cAAc,KAAK,CAAC,QAAQ,QAAS;AACzC,aACG,eAAe,GAAG,cAAc,IAAI,WAAW,EAAE,GAChD,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,EACzC,GAAG,CAAC,aAAa,gBAAgB,OAAO,CAAC;AAC3C;AAEA,SAAS,kBAAkB,oBAA6B,uBAA8C;AACpG,YAAU,MAAM;AACd,QAAI,CAAC,mBAAoB;AACzB,UAAM,QAAQ,WAAW,MAAM,sBAAsB,KAAK,GAAG,EAAE;AAC/D,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,oBAAoB,qBAAqB,CAAC;AAChD;AAEA,SAAS,eACP,WACA,YACA,UACA,SACA,gBACM;AACN,YAAU,MAAM;AACd,QAAI,aAAa,WAAW,KAAK,EAAE,SAAS,KAAK,CAAC,SAAU,SAAQ,IAAI;AACxE,QAAI,UAAU;AACZ,cAAQ,KAAK;AACb,qBAAe,EAAE;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,WAAW,YAAY,UAAU,SAAS,cAAc,CAAC;AAC/D;AAeA,SAAS,cAAc,GAAiB,YAA2B;AACjE,QAAM,cACJ,EAAE,WAAW,KAAK,KAClB,EAAE,kBACF,CAAC,EAAE,eACH,CAAC,EAAE,sBACH,CAAC,EAAE;AACL,MAAI,CAAC,YAAa;AAClB,IAAE,QAAQ,IAAI;AACd,MAAI,cAAc,EAAE,gBAAgB,GAAI,GAAE,eAAe,CAAC;AAC5D;AAEA,SAAS,eAAkB,GAAyB,GAA+B;AACjF,QAAM,kBAAkB,EAAE;AAC1B,QAAM,WAAW,EAAE,YAAY;AAC/B,QAAM,YAAuB,EAAE,aAAa;AAC5C,QAAM,QAAQ,QAAQ,EAAE,KAAK;AAC7B,QAAM;AAAA,IACJ;AAAA,IAAY;AAAA,IAAgB;AAAA,IAAa;AAAA,IAAoB;AAAA,IAAoB;AAAA,IACjF;AAAA,IAAwB;AAAA,IAAS;AAAA,EACnC,IAAI;AACJ,YAAU,MAAM;AACd,UAAM,cAAc,mBAAmB,CAAC;AACxC,QAAI,SAAS,CAAC,YAAY;AACxB,6BAAuB,WAAW;AAClC;AAAA,IACF;AACA,UAAM,WAAW,kBAAkB,aAAa,YAAY,WAAW,QAAQ;AAC/E,2BAAuB,QAAQ;AAC/B;AAAA,MACE,EAAE,YAAY,gBAAgB,aAAa,oBAAoB,oBAAoB,aAAa,SAAS,eAAe;AAAA,MACxH,SAAS,SAAS;AAAA,IACpB;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IAAiB;AAAA,IAAY;AAAA,IAAO;AAAA,IAAU;AAAA,IAAW;AAAA,IAAgB;AAAA,IACzE;AAAA,IAAoB;AAAA,IAAoB;AAAA,IAAa;AAAA,IAAwB;AAAA,IAAS;AAAA,EACxF,CAAC;AACH;AAEA,SAAS,aAAgB,GAAyB,GAA+B;AAC/E,QAAM,WAAW,EAAE,YAAY;AAC/B,QAAM,UAAU,EAAE,qBAAqB;AACvC,QAAM,YAAY,EAAE,iBAAiB;AACrC,QAAM,EAAE,YAAY,qBAAqB,gBAAgB,aAAa,SAAS,IAAI;AACnF,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,CAAC,kBAAkB,eAAe,CAAC,WAAW;AAC/D,eAAS,EAAE;AACX;AAAA,IACF;AACA,UAAM,QAAQ,qBAAqB,qBAAqB,YAAY,UAAU,OAAO;AACrF,aAAS,QAAQ,SAAS,KAAK,EAAE,MAAM,WAAW,MAAM,IAAI,EAAE;AAAA,EAChE,GAAG,CAAC,YAAY,qBAAqB,gBAAgB,aAAa,WAAW,UAAU,SAAS,QAAQ,CAAC;AAC3G;AAIA,SAAS,kBAAqB,MAAS,GAAyB,GAAyB,UAAqC;AAC5H,QAAM,SAAS,EAAE,UAAU;AAC3B,QAAM,UAAU,EAAE,iBAAiB,CAAC;AACpC,MAAI,CAAC,QAAQ,KAAK,CAAC,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,EAAG,GAAE,wBAAwB,CAAC,GAAG,SAAS,IAAI,CAAC;AACtG,IAAE,cAAc,EAAE;AAClB,WAAS,EAAE;AACX,IAAE,sBAAsB,KAAK;AAC/B;AAEA,SAAS,mBAAsB,MAAS,GAAyB,GAAyB,UAAqC;AAC7H,QAAM,SAAS,EAAE,YAAY,iBAAiB,IAAI;AAClD,IAAE,cAAc,KAAK;AACrB,WAAS,KAAK;AAChB;AAEA,SAAS,cAAiB,MAAS,GAAyB,GAAyB,UAAqC;AACxH,QAAM,OAAO,EAAE,UAAU,eAAe,IAAI;AAC5C,QAAM,UAAU,EAAE,qBAAqB,0BAA0B,IAAI,MAAM,UAAU,QAAQ,GAAG;AAChG,MAAI,QAAQ;AACV,KAAC,EAAE,YAAY,iBAAiB,KAAe,IAAI;AAAA,EACrD,WAAW,EAAE,UAAU;AACrB,sBAAkB,MAAM,GAAG,GAAG,QAAQ;AAAA,EACxC,OAAO;AACL,uBAAmB,MAAM,GAAG,GAAG,QAAQ;AAAA,EACzC;AACA,IAAE,QAAQ,KAAK;AACf,IAAE,eAAe,EAAE;AACnB,IAAE,WAAW,IAAI;AACjB,IAAE,SAAS,SAAS,MAAM;AAC5B;AAEA,SAAS,gBACP,GACA,GACA,YACA,UACe;AACf,SAAO;AAAA,IACL,MAAM,EAAE;AAAA,IAAM,aAAa,EAAE;AAAA,IAAa,qBAAqB,EAAE;AAAA,IACjE,iBAAiB,EAAE,mBAAmB;AAAA,IAAI,eAAe,EAAE,iBAAiB;AAAA,IAC5E,YAAY,EAAE;AAAA,IAAY,OAAO,EAAE;AAAA,IAAO,gBAAgB,EAAE;AAAA,IAC5D,UAAU,QAAQ,EAAE,QAAQ;AAAA,IAAG,eAAe,EAAE,iBAAiB,CAAC;AAAA,IAClE,UAAU,EAAE,YAAY;AAAA,IACxB,mBAAmB,EAAE,qBAAqB;AAAA,IAC1C,SAAS,EAAE;AAAA,IAAS,gBAAgB,EAAE;AAAA,IAAgB,uBAAuB,EAAE;AAAA,IAC/E,eAAe,EAAE;AAAA,IAAe,UAAU,EAAE;AAAA,IAAU,uBAAuB,EAAE;AAAA,IAC/E,mBAAmB;AAAA,IAAU;AAAA,IAAY,uBAAuB,EAAE;AAAA,EACpE;AACF;AAoBO,SAAS,gBAAmB,GAA8C;AAC/E,QAAM,cAAc,MAAM;AAC1B,QAAM,KAAK,EAAE,MAAM;AACnB,QAAM,iBAAiB,GAAG,EAAE;AAC5B,QAAM,IAAI,qBAAwB,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;AAE9D,QAAM,oBAAoB,CAAC,QAAsB;AAC/C,QAAI,EAAE,YAAY,QAAS,QAAO,aAAa,EAAE,YAAY,OAAO;AACpE,MAAE,YAAY,UAAU,OAAO,WAAW,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,cAAc,GAAG;AAAA,EACtF;AAEA,eAAa,EAAE,OAAO,EAAE,aAAa;AACrC,qBAAmB,EAAE,WAAW;AAChC,0BAAwB,EAAE,aAAa,gBAAgB,EAAE,OAAO;AAChE,oBAAkB,EAAE,oBAAoB,EAAE,qBAAqB;AAC/D,iBAAe,QAAQ,EAAE,SAAS,GAAG,EAAE,YAAY,QAAQ,EAAE,QAAQ,GAAG,EAAE,SAAS,EAAE,cAAc;AACnG,iBAAe,GAAG,CAAC;AACnB,eAAa,GAAG,CAAC;AAEjB,QAAM,aAAa,CAAC,SAAkB,cAAc,MAAM,GAAG,GAAG,iBAAiB;AAEjF,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,GAAG,EAAE;AAAA,IACb;AAAA,IACA,OAAO;AAAA,IACP,oBAAoB,EAAE,oBAAoB,MAAM,GAAG,EAAE,mBAAmB,EAAE;AAAA,IAC1E,kBAAkB,EAAE,eAAe,IAAI,GAAG,cAAc,IAAI,EAAE,WAAW,KAAK;AAAA,IAC9E;AAAA,IACA,oBAAoB,CAAC,SAAkB;AACrC,YAAM,SAAS,EAAE,UAAU;AAC3B,QAAE,yBAAyB,EAAE,iBAAiB,CAAC,GAAG,OAAO,CAAC,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,CAAC;AAC/F,QAAE,SAAS,SAAS,MAAM;AAAA,IAC5B;AAAA,IACA,WAAW,CAAC,MACV,sBAAsB,GAAG,EAAE,aAAa,gBAAgB,GAAG,GAAG,YAAY,iBAAiB,CAAC;AAAA,IAC9F,eAAe,CAAC,MAAiD;AAC/D,QAAE,cAAc,EAAE,OAAO,KAAK;AAC9B,wBAAkB,EAAE,OAAO,KAAK;AAChC,UAAI,CAAC,EAAE,QAAQ,EAAE,oBAAoB,WAAW,EAAG,GAAE,eAAe,EAAE;AACtE,QAAE,sBAAsB,KAAK;AAAA,IAC/B;AAAA,IACA,SAAS,MAAY;AACnB,QAAE,kBAAkB,IAAI;AACxB,QAAE,sBAAsB,KAAK;AAC7B,UAAI,CAAC,EAAE,WAAW,KAAK,EAAG;AAC1B,QAAE,QAAQ,IAAI;AACd,UAAI,EAAE,oBAAoB,SAAS,KAAK,EAAE,gBAAgB,GAAI,GAAE,eAAe,CAAC;AAAA,IAClF;AAAA,IACA,QAAQ,MAAY;AAClB,aAAO,WAAW,MAAM,EAAE,kBAAkB,KAAK,GAAG,GAAG;AAAA,IACzD;AAAA,IACA,aAAa,MAAY;AACvB,QAAE,QAAQ,KAAK;AACf,QAAE,eAAe,EAAE;AACnB,QAAE,sBAAsB,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CodeEditor/CodeEditor.monaco.ts"],"sourcesContent":["import type { Theme } from '@mui/material/styles/index.js';\nimport type { Monaco } from '@monaco-editor/react';\nimport type { editor } from 'monaco-editor';\n\nimport { uiInk, type UiInk } from '../../../tokens/ink';\n\ntype EditorChrome = UiInk['codeEditor']['light'];\n\n/** Monaco's colour keys, filled from one mode's chrome. */\nconst chromeColors = (chrome: EditorChrome) => ({\n 'editor.background': chrome.background,\n 'editor.foreground': chrome.foreground,\n 'editor.lineHighlightBackground': chrome.lineHighlight,\n 'editorLineNumber.foreground': chrome.lineNumber,\n 'editorIndentGuide.background': chrome.gutterBorder,\n 'editor.selectionBackground': chrome.selection,\n});\n\nconst customLightTheme = (chrome: EditorChrome) => ({\n base: 'vs' as const,\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6A737D', fontStyle: 'italic' },\n { token: 'keyword', foreground: 'D73A49' },\n { token: 'string', foreground: '032F62' },\n { token: 'number', foreground: '005CC5' },\n ],\n colors: chromeColors(chrome),\n});\n\nconst customDarkTheme = (chrome: EditorChrome) => ({\n base: 'vs-dark' as const,\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6A737D', fontStyle: 'italic' },\n { token: 'keyword', foreground: 'F97583' },\n { token: 'string', foreground: '9ECBFF' },\n { token: 'number', foreground: '79B8FF' },\n ],\n colors: chromeColors(chrome),\n});\n\n// Main component\nconst AUTO_FORMAT_DELAY_MS = 100;\n\nexport const registerEditorThemes = (monaco: Monaco, theme: Theme) => {\n const chrome = uiInk(theme).codeEditor;\n monaco.editor.defineTheme('custom-light', customLightTheme(chrome.light));\n monaco.editor.defineTheme('custom-dark', customDarkTheme(chrome.dark));\n};\n\n// Best-effort: a Monaco build without the TypeScript worker (as in jsdom) throws\n// here, and the editor is still usable without it.\nexport const configureTypeScriptDefaults = (monaco: Monaco) => {\n try {\n const tsLanguage = monaco.languages\n .getLanguages()\n .find((language) => language.id === 'typescript');\n\n if (!tsLanguage) return;\n\n monaco.languages.typescript.typescriptDefaults.setCompilerOptions({\n target: monaco.languages.typescript.ScriptTarget.ES2020,\n allowNonTsExtensions: true,\n moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,\n module: monaco.languages.typescript.ModuleKind.CommonJS,\n noEmit: true,\n esModuleInterop: true,\n jsx: monaco.languages.typescript.JsxEmit.React,\n reactNamespace: 'React',\n allowJs: true,\n typeRoots: ['node_modules/@types'],\n });\n\n monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({\n noSemanticValidation: false,\n noSyntaxValidation: false,\n noSuggestionDiagnostics: true,\n });\n } catch {\n // Silently handle any TypeScript configuration errors in test environments\n }\n};\n\nexport const scheduleAutoFormat = (mountedEditor: editor.IStandaloneCodeEditor, enabled: boolean) => {\n if (!enabled) return;\n\n window.setTimeout(() => {\n mountedEditor.getAction('editor.action.formatDocument')?.run();\n }, AUTO_FORMAT_DELAY_MS);\n};\n\nexport const registerSaveShortcut = (\n mountedEditor: editor.IStandaloneCodeEditor,\n monaco: Monaco,\n onSave?: (value: string) => void,\n) => {\n mountedEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {\n onSave?.(mountedEditor.getValue());\n });\n};\n\n// 'auto' follows the MUI palette; otherwise the caller's choice wins.\nexport const resolveEditorTheme = (themeProp: string, paletteMode: string) => {\n const wantsDark = themeProp === 'auto' ? paletteMode === 'dark' : themeProp === 'dark';\n\n return wantsDark ? 'custom-dark' : 'custom-light';\n};\n\n// Monaco's options object. Only the first five entries vary with our props; the\n// rest are fixed editor preferences.\n"],"mappings":";;;;;AASA,IAAM,eAAe,CAAC,YAA0B;AAAA,EAC9C,qBAAqB,OAAO;AAAA,EAC5B,qBAAqB,OAAO;AAAA,EAC5B,kCAAkC,OAAO;AAAA,EACzC,+BAA+B,OAAO;AAAA,EACtC,gCAAgC,OAAO;AAAA,EACvC,8BAA8B,OAAO;AACvC;AAEA,IAAM,mBAAmB,CAAC,YAA0B;AAAA,EAClD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,IACL,EAAE,OAAO,WAAW,YAAY,UAAU,WAAW,SAAS;AAAA,IAC9D,EAAE,OAAO,WAAW,YAAY,SAAS;AAAA,IACzC,EAAE,OAAO,UAAU,YAAY,SAAS;AAAA,IACxC,EAAE,OAAO,UAAU,YAAY,SAAS;AAAA,EAC1C;AAAA,EACA,QAAQ,aAAa,MAAM;AAC7B;AAEA,IAAM,kBAAkB,CAAC,YAA0B;AAAA,EACjD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,IACL,EAAE,OAAO,WAAW,YAAY,UAAU,WAAW,SAAS;AAAA,IAC9D,EAAE,OAAO,WAAW,YAAY,SAAS;AAAA,IACzC,EAAE,OAAO,UAAU,YAAY,SAAS;AAAA,IACxC,EAAE,OAAO,UAAU,YAAY,SAAS;AAAA,EAC1C;AAAA,EACA,QAAQ,aAAa,MAAM;AAC7B;AAGA,IAAM,uBAAuB;AAEtB,IAAM,uBAAuB,CAAC,QAAgB,UAAiB;AACpE,QAAM,SAAS,MAAM,KAAK,EAAE;AAC5B,SAAO,OAAO,YAAY,gBAAgB,iBAAiB,OAAO,KAAK,CAAC;AACxE,SAAO,OAAO,YAAY,eAAe,gBAAgB,OAAO,IAAI,CAAC;AACvE;AAIO,IAAM,8BAA8B,CAAC,WAAmB;AAC7D,MAAI;AACF,UAAM,aAAa,OAAO,UACvB,aAAa,EACb,KAAK,CAAC,aAAa,SAAS,OAAO,YAAY;AAElD,QAAI,CAAC,WAAY;AAEjB,WAAO,UAAU,WAAW,mBAAmB,mBAAmB;AAAA,MAChE,QAAQ,OAAO,UAAU,WAAW,aAAa;AAAA,MACjD,sBAAsB;AAAA,MACtB,kBAAkB,OAAO,UAAU,WAAW,qBAAqB;AAAA,MACnE,QAAQ,OAAO,UAAU,WAAW,WAAW;AAAA,MAC/C,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,KAAK,OAAO,UAAU,WAAW,QAAQ;AAAA,MACzC,gBAAgB;AAAA,MAChB,SAAS;AAAA,MACT,WAAW,CAAC,qBAAqB;AAAA,IACnC,CAAC;AAED,WAAO,UAAU,WAAW,mBAAmB,sBAAsB;AAAA,MACnE,sBAAsB;AAAA,MACtB,oBAAoB;AAAA,MACpB,yBAAyB;AAAA,IAC3B,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAEO,IAAM,qBAAqB,CAAC,eAA6C,YAAqB;AACnG,MAAI,CAAC,QAAS;AAEd,SAAO,WAAW,MAAM;AACtB,kBAAc,UAAU,8BAA8B,GAAG,IAAI;AAAA,EAC/D,GAAG,oBAAoB;AACzB;AAEO,IAAM,uBAAuB,CAClC,eACA,QACA,WACG;AACH,gBAAc,WAAW,OAAO,OAAO,UAAU,OAAO,QAAQ,MAAM,MAAM;AAC1E,aAAS,cAAc,SAAS,CAAC;AAAA,EACnC,CAAC;AACH;AAGO,IAAM,qBAAqB,CAAC,WAAmB,gBAAwB;AAC5E,QAAM,YAAY,cAAc,SAAS,gBAAgB,SAAS,cAAc;AAEhF,SAAO,YAAY,gBAAgB;AACrC;","names":[]}
|
package/dist/chunk-JEBPCAWD.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-JEBPCAWD.js.map
|
/package/dist/{chunk-JEBPCAWD.js.map → _internal/components/feedback/Dialog/Dialog.focus.js.map}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|