@astryxdesign/core 0.7.7-canary.f9ac85f → 0.7.7
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/CHANGELOG.md +104 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +79 -9
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -1
- package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
- package/dist/BottomSheet/bottomSheetStack.js +5 -4
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +30 -2
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +7 -1
- package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +5 -2
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +13 -27
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +22 -41
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +5 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +4 -3
- package/dist/Drawer/Drawer.js +0 -1
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
- package/dist/Drawer/useDrawerDialogPresence.js +5 -1
- package/dist/Field/PanelSearchInput.d.ts +9 -1
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +3 -1
- package/dist/InfoTip/InfoTip.d.ts +2 -2
- package/dist/InfoTip/InfoTip.js +4 -4
- package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +25 -20
- package/dist/Layer/usePressPin.d.ts +3 -5
- package/dist/Layer/usePressPin.d.ts.map +1 -1
- package/dist/Layer/usePressPin.js +4 -6
- package/dist/MobileNav/MobileNav.js +0 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +2 -4
- package/dist/Outline/Outline.d.ts +2 -4
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +4 -52
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +1 -0
- package/dist/Outline/useScrollSpy.js +3 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +134 -179
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +5 -0
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +118 -260
- package/dist/Resizable/resizeGesture.d.ts +91 -0
- package/dist/Resizable/resizeGesture.d.ts.map +1 -0
- package/dist/Resizable/resizeGesture.js +251 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +3 -9
- package/dist/Token/Token.d.ts +2 -2
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +12 -4
- package/dist/Token/TokenNamingContext.d.ts +14 -0
- package/dist/Token/TokenNamingContext.d.ts.map +1 -0
- package/dist/Token/TokenNamingContext.js +23 -0
- package/dist/Tokenizer/Tokenizer.d.ts +46 -59
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +35 -58
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
- package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
- package/dist/Tooltip/Tooltip.d.ts +25 -30
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +24 -5
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +28 -29
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +20 -8
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +4 -7
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -7
- package/dist/astryx.css +5 -5
- package/dist/gestures/GestureHost.d.ts +27 -1
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +90 -8
- package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
- package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
- package/dist/hooks/useHighlightedOptionScroll.js +103 -5
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +4 -2
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +1 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
- package/dist/internal/SheetConfirmation.d.ts +11 -0
- package/dist/internal/SheetConfirmation.d.ts.map +1 -0
- package/dist/internal/SheetConfirmation.js +92 -0
- package/dist/internal/sheetDraft.d.ts +35 -0
- package/dist/internal/sheetDraft.d.ts.map +1 -0
- package/dist/internal/sheetDraft.js +65 -0
- package/dist/internal/touch-sheet.d.ts +21 -0
- package/dist/internal/touch-sheet.d.ts.map +1 -0
- package/dist/internal/touch-sheet.js +19 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
- package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
- package/dist/motion/reducedMotion.d.ts +24 -0
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +78 -1
- package/dist/utils/clickGuard.d.ts +8 -1
- package/dist/utils/clickGuard.d.ts.map +1 -1
- package/dist/utils/clickGuard.js +24 -1
- package/locales/en.json +4 -0
- package/locales/pseudo.json +3 -0
- package/package.json +9 -9
- package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
- package/src/BottomSheet/BottomSheet.tsx +95 -10
- package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
- package/src/BottomSheet/bottomSheetStack.ts +8 -4
- package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
- package/src/Button/Button.tsx +37 -1
- package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
- package/src/CheckboxList/CheckboxList.tsx +6 -0
- package/src/CheckboxList/CheckboxListContext.tsx +6 -0
- package/src/CheckboxList/CheckboxListItem.tsx +11 -2
- package/src/DateInput/DateInput.tsx +8 -2
- package/src/DateInput/MonthScroller.tsx +18 -22
- package/src/DateInput/Wheel.tsx +30 -32
- package/src/DateTimeInput/DateTimeInput.tsx +8 -2
- package/src/Dialog/Dialog.tsx +19 -4
- package/src/Drawer/Drawer.tsx +1 -1
- package/src/Drawer/useDrawerDialogPresence.ts +5 -1
- package/src/Field/PanelSearchInput.tsx +11 -0
- package/src/InfoTip/InfoTip.doc.mjs +3 -3
- package/src/InfoTip/InfoTip.tsx +5 -5
- package/src/Layer/layerAnimations.stylex.ts +24 -16
- package/src/Layer/usePressPin.ts +5 -11
- package/src/MobileNav/MobileNav.tsx +1 -1
- package/src/MultiSelector/hooks.ts +2 -4
- package/src/Outline/Outline.doc.mjs +177 -51
- package/src/Outline/Outline.tsx +5 -57
- package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
- package/src/Outline/useOutlineFromMarkdown.ts +1 -0
- package/src/Outline/useScrollSpy.ts +3 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
- package/src/PowerSearch/PowerSearch.tsx +196 -183
- package/src/RadioList/RadioList.doc.mjs +4 -2
- package/src/RadioList/RadioList.tsx +4 -0
- package/src/Resizable/ResizeHandle.tsx +133 -328
- package/src/Resizable/resizeGesture.ts +368 -0
- package/src/SideNav/SideNavItem.tsx +3 -7
- package/src/Token/Token.tsx +13 -4
- package/src/Token/TokenNamingContext.ts +28 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
- package/src/Tokenizer/Tokenizer.tsx +81 -86
- package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
- package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
- package/src/Tooltip/Tooltip.doc.mjs +123 -54
- package/src/Tooltip/Tooltip.tsx +36 -39
- package/src/Tooltip/index.ts +0 -1
- package/src/Tooltip/useTooltip.doc.mjs +147 -33
- package/src/Tooltip/useTooltip.tsx +39 -32
- package/src/TopNav/TopNavItem.tsx +6 -5
- package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
- package/src/TopNav/TopNavMenu.tsx +6 -5
- package/src/gestures/GestureHost.ts +105 -10
- package/src/hooks/useHighlightedOptionScroll.ts +113 -9
- package/src/hooks/useMenuPress.ts +8 -2
- package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
- package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
- package/src/i18n/generated-locales/en.generated.ts +1 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
- package/src/internal/SheetConfirmation.tsx +101 -0
- package/src/internal/sheetDraft.ts +80 -0
- package/src/internal/touch-sheet.ts +21 -0
- package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
- package/src/motion/reducedMotion.ts +79 -1
- package/src/utils/clickGuard.ts +37 -1
- package/dist/internal/tokenizer-presentation.d.ts +0 -21
- package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
- package/dist/internal/tokenizer-presentation.js +0 -21
- package/src/internal/tokenizer-presentation.ts +0 -25
|
@@ -22,4 +22,28 @@ export declare function reducedMotionFade(element?: Element | null, options?: {
|
|
|
22
22
|
/** The change starts from the release of a touch drag (DR2). */
|
|
23
23
|
touchRelease?: boolean;
|
|
24
24
|
}): ReducedMotionFade | null;
|
|
25
|
+
/** A running Reduced Motion dip. */
|
|
26
|
+
export interface ReducedMotionDip {
|
|
27
|
+
/** Stops the dip. A change that has not landed yet never lands. */
|
|
28
|
+
cancel(): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Plays `fade` on `element` as a dip, and runs `land` at its middle: the
|
|
32
|
+
* fade-out, the change, then the fade-in. The fade-out holds at 0 until `land`
|
|
33
|
+
* has run, and the fade-in starts only after it. So the change never shows at
|
|
34
|
+
* full opacity, however late the main thread runs. A timer at the middle can
|
|
35
|
+
* fire after the fade-in has begun.
|
|
36
|
+
*
|
|
37
|
+
* `land` must apply the change before it returns: the fade-in starts right
|
|
38
|
+
* after it. `from` is the opacity the fade-out starts at: a leaving element
|
|
39
|
+
* fades out from where it is.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```
|
|
43
|
+
* const dip = playReducedMotionDip(scroller, fade, () => scroller.scrollTo({top}));
|
|
44
|
+
* // A newer change replaces it:
|
|
45
|
+
* dip.cancel();
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function playReducedMotionDip(element: Element, fade: ReducedMotionFade, land: () => void, from?: number): ReducedMotionDip;
|
|
25
49
|
//# sourceMappingURL=reducedMotion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducedMotion.d.ts","sourceRoot":"","sources":["../../src/motion/reducedMotion.ts"],"names":[],"mappings":"AAqDA,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,2DACoB,CAAC;AAE3D,gFAAgF;AAChF,eAAO,MAAM,4BAA4B,iEACuB,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,EACxB,OAAO,GAAE;IACP,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;CACnB,GACL,iBAAiB,GAAG,IAAI,CAmB1B"}
|
|
1
|
+
{"version":3,"file":"reducedMotion.d.ts","sourceRoot":"","sources":["../../src/motion/reducedMotion.ts"],"names":[],"mappings":"AAqDA,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,2DACoB,CAAC;AAE3D,gFAAgF;AAChF,eAAO,MAAM,4BAA4B,iEACuB,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,EACxB,OAAO,GAAE;IACP,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;CACnB,GACL,iBAAiB,GAAG,IAAI,CAmB1B;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,MAAM,IAAI,EAChB,IAAI,SAAI,GACP,gBAAgB,CAyClB"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file reducedMotion.ts
|
|
5
5
|
* @input The Reduced Motion preference, the primary pointer, the motion tables
|
|
6
6
|
* @output reducedMotionFade (the fade the animator would play, for motion it
|
|
7
|
-
* does not drive) and the
|
|
7
|
+
* does not drive), playReducedMotionDip, and the queries a transition spells
|
|
8
8
|
* @position The one Reduced Motion policy (design:reduced-motion) for code
|
|
9
9
|
* beside the animator: a raw Web Animations fade, a CSS transition
|
|
10
10
|
*
|
|
@@ -86,4 +86,81 @@ export function reducedMotionFade(element, options = {}) {
|
|
|
86
86
|
duration: reducedMotionCrossFade.durationMs,
|
|
87
87
|
easing: `cubic-bezier(${curve.join(', ')})`
|
|
88
88
|
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A running Reduced Motion dip. */
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Plays `fade` on `element` as a dip, and runs `land` at its middle: the
|
|
95
|
+
* fade-out, the change, then the fade-in. The fade-out holds at 0 until `land`
|
|
96
|
+
* has run, and the fade-in starts only after it. So the change never shows at
|
|
97
|
+
* full opacity, however late the main thread runs. A timer at the middle can
|
|
98
|
+
* fire after the fade-in has begun.
|
|
99
|
+
*
|
|
100
|
+
* `land` must apply the change before it returns: the fade-in starts right
|
|
101
|
+
* after it. `from` is the opacity the fade-out starts at: a leaving element
|
|
102
|
+
* fades out from where it is.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```
|
|
106
|
+
* const dip = playReducedMotionDip(scroller, fade, () => scroller.scrollTo({top}));
|
|
107
|
+
* // A newer change replaces it:
|
|
108
|
+
* dip.cancel();
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function playReducedMotionDip(element, fade, land, from = 1) {
|
|
112
|
+
const half = fade.duration / 2;
|
|
113
|
+
let isCancelled = false;
|
|
114
|
+
let fadeIn = null;
|
|
115
|
+
const fadeOut = element.animate([{
|
|
116
|
+
opacity: from,
|
|
117
|
+
easing: fade.easing
|
|
118
|
+
}, {
|
|
119
|
+
opacity: 0
|
|
120
|
+
}], {
|
|
121
|
+
duration: half,
|
|
122
|
+
fill: 'forwards'
|
|
123
|
+
});
|
|
124
|
+
fadeOut.finished.then(() => {
|
|
125
|
+
if (isCancelled) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
land();
|
|
129
|
+
// The fade-in holds 0 until it starts: after a long stall, its start can
|
|
130
|
+
// come after the frame that runs this.
|
|
131
|
+
fadeIn = element.animate([{
|
|
132
|
+
opacity: 0,
|
|
133
|
+
easing: fade.easing
|
|
134
|
+
}, {
|
|
135
|
+
opacity: 1
|
|
136
|
+
}], {
|
|
137
|
+
duration: half,
|
|
138
|
+
fill: 'backwards'
|
|
139
|
+
});
|
|
140
|
+
// The fade-in keeps the dip's own schedule: it starts where the fade-out
|
|
141
|
+
// ended, so the dip keeps its length. The clock is read after the
|
|
142
|
+
// landing, because a frame's timestamp does not count the landing's own
|
|
143
|
+
// work. After a long stall, half of the fade-in still plays.
|
|
144
|
+
const fadeOutEnd = endTime(fadeOut, half);
|
|
145
|
+
if (fadeOutEnd != null) {
|
|
146
|
+
fadeIn.startTime = Math.max(fadeOutEnd, performance.now() - half / 2);
|
|
147
|
+
}
|
|
148
|
+
// The fade-in now holds the opacity, so the held 0 can go.
|
|
149
|
+
fadeOut.cancel();
|
|
150
|
+
},
|
|
151
|
+
// A cancelled fade-out rejects `finished`: nothing lands.
|
|
152
|
+
() => {});
|
|
153
|
+
return {
|
|
154
|
+
cancel() {
|
|
155
|
+
isCancelled = true;
|
|
156
|
+
fadeOut.cancel();
|
|
157
|
+
fadeIn?.cancel();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** When `animation` was due to end, on its timeline (ms), if it has started. */
|
|
163
|
+
function endTime(animation, duration) {
|
|
164
|
+
const start = animation.startTime;
|
|
165
|
+
return typeof start === 'number' ? start + duration : null;
|
|
89
166
|
}
|
|
@@ -18,9 +18,16 @@ export interface ClickGuardOptions {
|
|
|
18
18
|
* @default false
|
|
19
19
|
*/
|
|
20
20
|
readonly takesLateMouse?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The menu press model's arm at a finger's release: the guard also cancels
|
|
23
|
+
* that touch's `touchend`, so the browser creates no click for it. A
|
|
24
|
+
* cancelled `touchend` disarms the guard.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
readonly cancelsTouchEnd?: boolean;
|
|
21
28
|
}
|
|
22
29
|
/** Swallow the browser's next click on `doc` (see the file header). */
|
|
23
|
-
export declare function armClickGuard(doc: Document, { staysInstalled, takesLateMouse }?: ClickGuardOptions): void;
|
|
30
|
+
export declare function armClickGuard(doc: Document, { staysInstalled, takesLateMouse, cancelsTouchEnd, }?: ClickGuardOptions): void;
|
|
24
31
|
/** Stop swallowing on `doc`; idempotent. */
|
|
25
32
|
export declare function disarmClickGuard(doc: Document): void;
|
|
26
33
|
/** Whether the guard on `doc` would swallow the next click. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clickGuard.d.ts","sourceRoot":"","sources":["../../src/utils/clickGuard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clickGuard.d.ts","sourceRoot":"","sources":["../../src/utils/clickGuard.ts"],"names":[],"mappings":"AA0CA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AA8BD,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,GAAG,EAAE,QAAQ,EACb,EACE,cAAsB,EACtB,cAAsB,EACtB,eAAuB,GACxB,GAAE,iBAAsB,GACxB,IAAI,CA+CN;AAED,4CAA4C;AAC5C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAYpD;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAOzD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAQ/C"}
|
package/dist/utils/clickGuard.js
CHANGED
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
// `touchend`, aimed at whatever is under the finger by then. They go with
|
|
27
27
|
// the click, the next `pointerdown` or the window, as the guard does.
|
|
28
28
|
//
|
|
29
|
+
// The menu press model's arm at a finger's release also cancels that touch's
|
|
30
|
+
// `touchend`, so the browser creates no click and no compatibility mouse
|
|
31
|
+
// events. A busy main thread can otherwise hold that click past the window,
|
|
32
|
+
// when a drill-in has put another row under the finger.
|
|
33
|
+
//
|
|
29
34
|
// Some clicks always pass:
|
|
30
35
|
// - a click with `detail` 0: a keyboard's, a screen reader's, or one that the
|
|
31
36
|
// page dispatches;
|
|
@@ -67,7 +72,8 @@ function listenForClicks(view) {
|
|
|
67
72
|
/** Swallow the browser's next click on `doc` (see the file header). */
|
|
68
73
|
export function armClickGuard(doc, {
|
|
69
74
|
staysInstalled = false,
|
|
70
|
-
takesLateMouse = false
|
|
75
|
+
takesLateMouse = false,
|
|
76
|
+
cancelsTouchEnd = false
|
|
71
77
|
} = {}) {
|
|
72
78
|
const view = doc.defaultView;
|
|
73
79
|
if (view == null) {
|
|
@@ -96,6 +102,23 @@ export function armClickGuard(doc, {
|
|
|
96
102
|
}));
|
|
97
103
|
}
|
|
98
104
|
}
|
|
105
|
+
if (cancelsTouchEnd) {
|
|
106
|
+
// A touch sends its pointerup before its touchend, so the next touchend is
|
|
107
|
+
// that touch's own. The next pointerdown disarms the guard first.
|
|
108
|
+
const stopTouchEnd = subscribeInput(view, 'touchend', event => {
|
|
109
|
+
stopTouchEnd();
|
|
110
|
+
// A touchend that the browser took for a scroll is not cancelable.
|
|
111
|
+
// The guard stays armed for a click that may follow it.
|
|
112
|
+
if (event.cancelable) {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
// No click follows a cancelled touchend.
|
|
115
|
+
disarmClickGuard(doc);
|
|
116
|
+
}
|
|
117
|
+
}, {
|
|
118
|
+
cancels: true
|
|
119
|
+
});
|
|
120
|
+
guard.unsubscribers.push(stopTouchEnd);
|
|
121
|
+
}
|
|
99
122
|
guard.timer = setTimeout(() => disarmClickGuard(doc), CLICK_GUARD_WINDOW_MS);
|
|
100
123
|
}
|
|
101
124
|
|
package/locales/en.json
CHANGED
|
@@ -1123,6 +1123,10 @@
|
|
|
1123
1123
|
"defaultMessage": "All filters removed",
|
|
1124
1124
|
"description": "Screen-reader announcement after a PowerSearch field's Clear all removes every filter. Distinct from @astryx.tokenizer.allRemoved: PowerSearch words its items as filters."
|
|
1125
1125
|
},
|
|
1126
|
+
"@astryx.powersearch.editableFiltersRemoved": {
|
|
1127
|
+
"defaultMessage": "Editable filters removed",
|
|
1128
|
+
"description": "Screen-reader announcement after a PowerSearch field's Clear all removes the filters the user can edit and keeps its read-only filters. Used instead of @astryx.powersearch.allFiltersRemoved while a read-only filter remains."
|
|
1129
|
+
},
|
|
1126
1130
|
"@astryx.resizable.collapsed": {
|
|
1127
1131
|
"defaultMessage": "Collapsed",
|
|
1128
1132
|
"description": "aria-valuetext for the Resizable drag handle (role=separator) while its panel is collapsed to zero size. Replaces the numeric value announcement, since the numeric value is clamped to the minimum while collapsed. Adjective describing the panel state, not a verb/command."
|
package/locales/pseudo.json
CHANGED
|
@@ -842,6 +842,9 @@
|
|
|
842
842
|
"@astryx.powersearch.allFiltersRemoved": {
|
|
843
843
|
"defaultMessage": "⟦Àłł ƒíłţéřš řéɱóṽéð⟧"
|
|
844
844
|
},
|
|
845
|
+
"@astryx.powersearch.editableFiltersRemoved": {
|
|
846
|
+
"defaultMessage": "⟦Éðíţàƀłé ƒíłţéřš řéɱóṽéð⟧"
|
|
847
|
+
},
|
|
845
848
|
"@astryx.resizable.collapsed": {
|
|
846
849
|
"defaultMessage": "⟦Çółłàþšéð⟧"
|
|
847
850
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.7
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -829,10 +829,10 @@
|
|
|
829
829
|
"types": "./dist/internal/touch.d.ts",
|
|
830
830
|
"default": "./dist/internal/touch.js"
|
|
831
831
|
},
|
|
832
|
-
"./internal/
|
|
833
|
-
"source": "./src/internal/
|
|
834
|
-
"types": "./dist/internal/
|
|
835
|
-
"default": "./dist/internal/
|
|
832
|
+
"./internal/touch-sheet": {
|
|
833
|
+
"source": "./src/internal/touch-sheet.ts",
|
|
834
|
+
"types": "./dist/internal/touch-sheet.d.ts",
|
|
835
|
+
"default": "./dist/internal/touch-sheet.js"
|
|
836
836
|
}
|
|
837
837
|
},
|
|
838
838
|
"files": [
|
|
@@ -865,9 +865,6 @@
|
|
|
865
865
|
"react-dom": ">=19.0.0"
|
|
866
866
|
},
|
|
867
867
|
"devDependencies": {
|
|
868
|
-
"@astryxdesign/a11y-spec": "0.7.7-canary.f9ac85f",
|
|
869
|
-
"@astryxdesign/cli": "0.7.7-canary.f9ac85f",
|
|
870
|
-
"@astryxdesign/interaction-harness": "0.7.7-canary.f9ac85f",
|
|
871
868
|
"@babel/cli": "^7.29.7",
|
|
872
869
|
"@babel/core": "^7.29.7",
|
|
873
870
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -878,7 +875,10 @@
|
|
|
878
875
|
"@testing-library/jest-dom": "^6.6.0",
|
|
879
876
|
"@testing-library/react": "^16.3.2",
|
|
880
877
|
"@types/babel__core": "^7.20.5",
|
|
881
|
-
"tailwindcss": "^4.3.2"
|
|
878
|
+
"tailwindcss": "^4.3.2",
|
|
879
|
+
"@astryxdesign/a11y-spec": "0.0.0",
|
|
880
|
+
"@astryxdesign/interaction-harness": "0.0.0",
|
|
881
|
+
"@astryxdesign/cli": "0.7.7"
|
|
882
882
|
},
|
|
883
883
|
"dependencies": {
|
|
884
884
|
"intl-messageformat": "^11.2.9"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useCloseMobileDrawerOnActivate.ts
|
|
7
|
+
* @input AppShellMobileContext, TopNav and SideNav render modes
|
|
8
|
+
* @output useCloseMobileDrawerOnActivate, useIsInAppShellMobileDrawer
|
|
9
|
+
* @position Internal AppShell helper for rows in the mobile drawer. Not
|
|
10
|
+
* exported from the package.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {useAppShellMobile} from './AppShellMobileContext';
|
|
14
|
+
import {useSideNavRenderMode} from '../SideNav/SideNavRenderContext';
|
|
15
|
+
import {useTopNavRenderMode} from '../TopNav/TopNavRenderContext';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns a function that wraps a row's click handler. While
|
|
19
|
+
* `isInMobileDrawer` is true, the wrapped handler calls the row's handler and
|
|
20
|
+
* then closes the AppShell mobile drawer. Otherwise the row's handler comes
|
|
21
|
+
* back unchanged. Every drawer row closes the drawer through this one rule.
|
|
22
|
+
*/
|
|
23
|
+
export function useCloseMobileDrawerOnActivate(
|
|
24
|
+
isInMobileDrawer: boolean,
|
|
25
|
+
): <Args extends unknown[]>(
|
|
26
|
+
onClick: ((...args: Args) => void) | undefined,
|
|
27
|
+
) => ((...args: Args) => void) | undefined {
|
|
28
|
+
const {closeMobileNav} = useAppShellMobile();
|
|
29
|
+
return onClick => {
|
|
30
|
+
if (!isInMobileDrawer) {
|
|
31
|
+
return onClick;
|
|
32
|
+
}
|
|
33
|
+
return (...args) => {
|
|
34
|
+
onClick?.(...args);
|
|
35
|
+
closeMobileNav();
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether the caller renders inside the AppShell mobile drawer: in TopNav
|
|
42
|
+
* content or SideNav content shown there.
|
|
43
|
+
*/
|
|
44
|
+
export function useIsInAppShellMobileDrawer(): boolean {
|
|
45
|
+
const topNavRenderMode = useTopNavRenderMode();
|
|
46
|
+
const sideNavRenderMode = useSideNavRenderMode();
|
|
47
|
+
return (
|
|
48
|
+
topNavRenderMode === 'drawer' ||
|
|
49
|
+
sideNavRenderMode === 'drawer' ||
|
|
50
|
+
sideNavRenderMode === 'drawer-content'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
import {useDevWarning} from '../hooks/useDevWarning';
|
|
76
76
|
import {useScrollLock} from '../hooks/useScrollLock';
|
|
77
77
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
78
|
+
import {observeMutations} from '../utils/sharedMutationObserver';
|
|
78
79
|
import {mergeProps} from '../utils/mergeProps';
|
|
79
80
|
import {LayerDepthProvider} from '../Layer/LayerDepthContext';
|
|
80
81
|
import {ModalAnnounceProvider} from '../hooks/ModalAnnounceProvider';
|
|
@@ -147,12 +148,17 @@ const styles = stylex.create({
|
|
|
147
148
|
},
|
|
148
149
|
// An open standalone dialog positions its panel: the panel sits at its
|
|
149
150
|
// block end, centred, with no wrapper. A tap beside a narrower panel lands
|
|
150
|
-
// on the dialog, which light dismiss reads.
|
|
151
|
-
//
|
|
151
|
+
// on the dialog, which light dismiss reads. A flex row, not a grid: WebKit
|
|
152
|
+
// lays a grid host out again on each render inside the sheet, which makes
|
|
153
|
+
// each update slower. Not a column: WebKit then drops the content's
|
|
154
|
+
// block-end padding from the scroll range. In a row the panel's height is
|
|
155
|
+
// its own cross size, so a sheet taller than the viewport runs past the top
|
|
156
|
+
// edge, as in a grid. The stack's recede applies to this dialog too, so a
|
|
157
|
+
// covered sheet shrinks and lifts as one.
|
|
152
158
|
dialogPanelHost: {
|
|
153
|
-
display: '
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
display: 'flex',
|
|
160
|
+
justifyContent: 'center',
|
|
161
|
+
alignItems: 'flex-end',
|
|
156
162
|
},
|
|
157
163
|
// A non-modal sheet is in the top layer as a manual popover, so it has no
|
|
158
164
|
// page z-index (AST-027). Below the Popover API floor, `show()` leaves it
|
|
@@ -207,6 +213,10 @@ const styles = stylex.create({
|
|
|
207
213
|
transitionTimingFunction: 'linear',
|
|
208
214
|
},
|
|
209
215
|
},
|
|
216
|
+
// A closing sheet that keeps focus takes no pointer input during its exit.
|
|
217
|
+
exitHoldingFocus: {
|
|
218
|
+
pointerEvents: 'none',
|
|
219
|
+
},
|
|
210
220
|
positioner: {
|
|
211
221
|
position: 'absolute',
|
|
212
222
|
insetInline: 0,
|
|
@@ -255,6 +265,24 @@ function stackPositionerStyles(view: BottomSheetStackView) {
|
|
|
255
265
|
}
|
|
256
266
|
|
|
257
267
|
/** The `data-sheet-stack` value for a host: only set while sheets stack. */
|
|
268
|
+
/** Stops a click, from any input, before it reaches its target. */
|
|
269
|
+
function ignoreActivation(event: React.MouseEvent): void {
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
event.stopPropagation();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Stops every key except Tab before it reaches the focused control, so no
|
|
276
|
+
* key handler runs and no text changes (a search field's Enter, a typed
|
|
277
|
+
* letter, Backspace, an arrow that reopens a list). Tab still moves focus.
|
|
278
|
+
*/
|
|
279
|
+
function ignoreKeyDuringExit(event: React.KeyboardEvent): void {
|
|
280
|
+
if (event.key !== 'Tab') {
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
event.stopPropagation();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
258
286
|
function stackStateAttribute(
|
|
259
287
|
view: BottomSheetStackView,
|
|
260
288
|
isOpen: boolean,
|
|
@@ -445,10 +473,14 @@ function StandaloneBottomSheet({
|
|
|
445
473
|
// `isOpen` restarts the flag during render, and the layout effect below
|
|
446
474
|
// settles a close.
|
|
447
475
|
const [isCloseSettled, setIsCloseSettled] = useState(false);
|
|
476
|
+
// Whether focus was inside the sheet when it closed. Such a sheet stays out
|
|
477
|
+
// of `inert` and `aria-hidden` until its exit ends and focus has left.
|
|
478
|
+
const [isExitFocusHeld, setIsExitFocusHeld] = useState(false);
|
|
448
479
|
const [settledIsOpen, setSettledIsOpen] = useState(isOpen);
|
|
449
480
|
if (settledIsOpen !== isOpen) {
|
|
450
481
|
setSettledIsOpen(isOpen);
|
|
451
482
|
setIsCloseSettled(false);
|
|
483
|
+
setIsExitFocusHeld(false);
|
|
452
484
|
}
|
|
453
485
|
const panelState: BottomSheetPanelState = isOpen
|
|
454
486
|
? {kind: 'open', entering: stackView.isReplacing}
|
|
@@ -576,9 +608,8 @@ function StandaloneBottomSheet({
|
|
|
576
608
|
}, [hasScrim, shouldPresent, writeScrim]);
|
|
577
609
|
useLayoutEffect(() => {
|
|
578
610
|
if (!isOpen) {
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
// focus was when the person closed the sheet.
|
|
611
|
+
// Where focus was when the person closed the sheet. The closing sheet
|
|
612
|
+
// is not inert yet, so focus has not moved.
|
|
582
613
|
const dialog = dialogRef.current;
|
|
583
614
|
hadFocusAtCloseRef.current =
|
|
584
615
|
dialog != null && dialog.contains(dialog.ownerDocument.activeElement);
|
|
@@ -591,6 +622,8 @@ function StandaloneBottomSheet({
|
|
|
591
622
|
// that was not paired as a replace is an exit from here on.
|
|
592
623
|
// eslint-disable-next-line @eslint-react/set-state-in-effect -- settles the close once the registry has answered
|
|
593
624
|
setIsCloseSettled(true);
|
|
625
|
+
// Read with the close, before the sheet can go inert.
|
|
626
|
+
setIsExitFocusHeld(hadFocusAtCloseRef.current);
|
|
594
627
|
}
|
|
595
628
|
}, [hasScrim, isOpen, shouldPresent]);
|
|
596
629
|
|
|
@@ -694,7 +727,44 @@ function StandaloneBottomSheet({
|
|
|
694
727
|
);
|
|
695
728
|
|
|
696
729
|
const {ref: modalityRef} = useInteractionModalityTracking();
|
|
697
|
-
|
|
730
|
+
// Ends the exit hold once focus has left the sheet: moved elsewhere, or
|
|
731
|
+
// dropped to the page because the focused control was removed or disabled.
|
|
732
|
+
// Native listeners, because React drops the `focusout` a browser sends while
|
|
733
|
+
// React removes the focused element, and Firefox and WebKit send no
|
|
734
|
+
// `focusout` for a removed element at all, so a subtree change is read too.
|
|
735
|
+
// Each reads the focus after the event. They attach only while a closing
|
|
736
|
+
// sheet holds focus, so a closed or open sheet observes nothing.
|
|
737
|
+
const isWatchingExitFocus =
|
|
738
|
+
!isOpen && isPresented && !isReplaced && isExitFocusHeld;
|
|
739
|
+
const exitFocusRef = useCallback(
|
|
740
|
+
(dialog: HTMLDialogElement | null) => {
|
|
741
|
+
if (dialog == null || !isWatchingExitFocus) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const releaseIfFocusLeft = () => {
|
|
745
|
+
if (!dialog.contains(dialog.ownerDocument.activeElement)) {
|
|
746
|
+
setIsExitFocusHeld(false);
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
const onFocusOut = () => queueMicrotask(releaseIfFocusLeft);
|
|
750
|
+
dialog.addEventListener('focusout', onFocusOut);
|
|
751
|
+
const unobserve = observeMutations(
|
|
752
|
+
dialog,
|
|
753
|
+
{
|
|
754
|
+
childList: true,
|
|
755
|
+
subtree: true,
|
|
756
|
+
attributeFilter: ['disabled', 'hidden', 'inert'],
|
|
757
|
+
},
|
|
758
|
+
releaseIfFocusLeft,
|
|
759
|
+
);
|
|
760
|
+
return () => {
|
|
761
|
+
dialog.removeEventListener('focusout', onFocusOut);
|
|
762
|
+
unobserve();
|
|
763
|
+
};
|
|
764
|
+
},
|
|
765
|
+
[isWatchingExitFocus],
|
|
766
|
+
);
|
|
767
|
+
const dialogComposedRef = useMergedRefs(dialogRef, modalityRef, exitFocusRef);
|
|
698
768
|
useScrollLock(shouldPresent && hasScrim);
|
|
699
769
|
useDevWarning(
|
|
700
770
|
'BottomSheet',
|
|
@@ -764,7 +834,16 @@ function StandaloneBottomSheet({
|
|
|
764
834
|
// that contains no presented sheet is marked inert and hidden as well, so
|
|
765
835
|
// the state is explicit; one that does contain the top sheet is left alone,
|
|
766
836
|
// because inert on it would reach the top sheet too.
|
|
767
|
-
|
|
837
|
+
// A sheet that closes with focus inside it is neither inert nor hidden
|
|
838
|
+
// while that focus stays: `inert` would drop the focus to the page, and
|
|
839
|
+
// `aria-hidden` would hide the focused control. When the exit ends, the
|
|
840
|
+
// dialog closes and focus returns in the same task. When focus leaves the
|
|
841
|
+
// sheet first (into a sheet opened above it, for example), the sheet goes
|
|
842
|
+
// inert at once. Until the close settles, no closing sheet is inert, so the
|
|
843
|
+
// first commit of a close never hides focus.
|
|
844
|
+
const isExitHoldingFocus =
|
|
845
|
+
isExiting && !isReplaced && (!isCloseSettled || isExitFocusHeld);
|
|
846
|
+
const isInert = (isExiting && !isExitHoldingFocus) || stackView.isBlocked;
|
|
768
847
|
|
|
769
848
|
return (
|
|
770
849
|
<dialog
|
|
@@ -779,6 +858,7 @@ function StandaloneBottomSheet({
|
|
|
779
858
|
hasScrim && styles.scrim,
|
|
780
859
|
hasScrim && isEntrySettled && styles.scrimSettled,
|
|
781
860
|
hasScrim && isExiting && styles.scrimClosing,
|
|
861
|
+
isExitHoldingFocus && styles.exitHoldingFocus,
|
|
782
862
|
!hasScrim && styles.dialogNonModal,
|
|
783
863
|
)}
|
|
784
864
|
ref={dialogComposedRef}
|
|
@@ -800,6 +880,11 @@ function StandaloneBottomSheet({
|
|
|
800
880
|
hasScrim && stackView.depth > 0 ? stackView.depth : undefined
|
|
801
881
|
}
|
|
802
882
|
onCancel={handleCancel}
|
|
883
|
+
// A closing sheet that keeps focus takes no activation during its
|
|
884
|
+
// exit: no click and no key but Tab reaches the control that has focus.
|
|
885
|
+
onClickCapture={isExitHoldingFocus ? ignoreActivation : undefined}
|
|
886
|
+
onKeyDownCapture={isExitHoldingFocus ? ignoreKeyDuringExit : undefined}
|
|
887
|
+
onKeyUpCapture={isExitHoldingFocus ? ignoreKeyDuringExit : undefined}
|
|
803
888
|
// Presses inside the sheet do not reach handlers around where it is
|
|
804
889
|
// written. The dialog, not the panel, is the root, as in a switcher.
|
|
805
890
|
{...layerClickBoundary({onClick: handleClick})}>
|
|
@@ -296,14 +296,19 @@ const styles = stylex.create({
|
|
|
296
296
|
},
|
|
297
297
|
content: {
|
|
298
298
|
// Preserve the body's block formatting (including margin/float isolation)
|
|
299
|
-
// and its definite percentage-height basis.
|
|
299
|
+
// and its definite percentage-height basis. fit-content lets this real
|
|
300
300
|
// observed box grow with async content beyond a fixed-height viewport.
|
|
301
301
|
// This box is also the sheet's container: it carries the padding and
|
|
302
302
|
// publishes it, so a lone Section child escapes it exactly as in Dialog.
|
|
303
303
|
display: 'flow-root',
|
|
304
304
|
boxSizing: 'border-box',
|
|
305
305
|
height: '100%',
|
|
306
|
-
|
|
306
|
+
// fit-content, not min-content: they are one size in the block axis, but
|
|
307
|
+
// WebKit ignores min-content on a block's min-height. There the box keeps
|
|
308
|
+
// its body's height on a budget-height sheet. Content placed directly in
|
|
309
|
+
// it then overflows the box, and its block-end padding leaves the scroll
|
|
310
|
+
// range.
|
|
311
|
+
minHeight: 'fit-content',
|
|
307
312
|
},
|
|
308
313
|
tallKeyboardBody: {
|
|
309
314
|
scrollPaddingBlockEnd: MOBILE_KEYBOARD_BOTTOM_CLEARANCE,
|
|
@@ -866,12 +866,16 @@ function openModalsOutside(element: Element): HTMLDialogElement[] {
|
|
|
866
866
|
if (dialog === element || dialog.contains(element)) {
|
|
867
867
|
continue;
|
|
868
868
|
}
|
|
869
|
-
// A modal on its way out
|
|
870
|
-
//
|
|
871
|
-
//
|
|
869
|
+
// A modal on its way out blocks nothing any more: it is gone for this
|
|
870
|
+
// decision. A closing sheet keeps its dialog modal through the exit
|
|
871
|
+
// animation; it is inert and hidden then, unless focus was inside it at
|
|
872
|
+
// the close, so the registry's own record of the close counts too.
|
|
872
873
|
if (
|
|
873
874
|
dialog.hasAttribute('inert') ||
|
|
874
|
-
dialog.getAttribute('aria-hidden') === 'true'
|
|
875
|
+
dialog.getAttribute('aria-hidden') === 'true' ||
|
|
876
|
+
state.entries.some(
|
|
877
|
+
entry => !entry.isOpen && entry.getElement() === dialog,
|
|
878
|
+
)
|
|
875
879
|
) {
|
|
876
880
|
continue;
|
|
877
881
|
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* @input Uses React use/useRef/useEffect, stylex, theme tokens, BreadcrumbContext,
|
|
8
8
|
* rtlStyles for the contextual default slash, and (for the menu trigger)
|
|
9
9
|
* usePopover + the shared DropdownMenu item API (renderDropdownItems,
|
|
10
|
-
* DropdownMenuContext, MENU_ITEM_SELECTOR, useListFocus)
|
|
10
|
+
* DropdownMenuContext, MENU_ITEM_SELECTOR, useListFocus), and
|
|
11
|
+
* the AppShell mobile drawer helpers
|
|
11
12
|
* @output Exports BreadcrumbItem component and BreadcrumbItemProps
|
|
12
13
|
* @position Individual breadcrumb item; used inside Breadcrumbs
|
|
13
14
|
*
|
|
@@ -72,6 +73,10 @@ import {
|
|
|
72
73
|
import type {DropdownMenuOption} from '../DropdownMenu/DropdownMenu';
|
|
73
74
|
|
|
74
75
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
76
|
+
import {
|
|
77
|
+
useCloseMobileDrawerOnActivate,
|
|
78
|
+
useIsInAppShellMobileDrawer,
|
|
79
|
+
} from '../AppShell/useCloseMobileDrawerOnActivate';
|
|
75
80
|
import {useInteractionModalityTracking} from '../utils/interactionModality';
|
|
76
81
|
import {isRenderable} from '../utils/isRenderable';
|
|
77
82
|
// =============================================================================
|
|
@@ -306,6 +311,11 @@ export function BreadcrumbItem({
|
|
|
306
311
|
// aria-current on this instead of guessing the <li>'s last child.
|
|
307
312
|
const contentRef = useRef<HTMLElement>(null);
|
|
308
313
|
|
|
314
|
+
const closeDrawerAfter = useCloseMobileDrawerOnActivate(
|
|
315
|
+
useIsInAppShellMobileDrawer(),
|
|
316
|
+
);
|
|
317
|
+
const handleActivate = closeDrawerAfter(onClick);
|
|
318
|
+
|
|
309
319
|
const isCurrent = isCurrentProp === true;
|
|
310
320
|
const isAutoCandidate = isCurrentProp == null;
|
|
311
321
|
const hasMenu = menu != null;
|
|
@@ -456,7 +466,7 @@ export function BreadcrumbItem({
|
|
|
456
466
|
<LinkComponent
|
|
457
467
|
ref={contentRef}
|
|
458
468
|
href={href}
|
|
459
|
-
onClick={
|
|
469
|
+
onClick={handleActivate}
|
|
460
470
|
{...focusOutlineProps.focusVisible(
|
|
461
471
|
itemStyles.link,
|
|
462
472
|
isSupporting ? itemStyles.supportingLink : itemStyles.defaultLink,
|
|
@@ -467,7 +477,7 @@ export function BreadcrumbItem({
|
|
|
467
477
|
<button
|
|
468
478
|
ref={contentRef as React.RefObject<HTMLButtonElement | null>}
|
|
469
479
|
type="button"
|
|
470
|
-
onClick={
|
|
480
|
+
onClick={handleActivate}
|
|
471
481
|
{...focusOutlineProps.focusVisible(
|
|
472
482
|
itemStyles.link,
|
|
473
483
|
itemStyles.buttonReset,
|