@astryxdesign/core 0.5.2-canary.c9c8564 → 0.5.2-canary.e4f8e4e
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/BottomSheet/BottomSheet.d.ts +3 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -3
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/Button/Button.d.ts +2 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -4
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +34 -17
- package/dist/ContextMenu/ContextMenu.d.ts +14 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +149 -26
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +340 -26
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/menuWidth.d.ts +14 -0
- package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
- package/dist/DropdownMenu/menuWidth.js +35 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
- package/dist/DropdownMenu/useMenuOverflow.js +60 -0
- package/dist/MoreMenu/MoreMenu.d.ts +8 -2
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +2 -0
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +97 -68
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/Popover/Popover.d.ts +10 -6
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +141 -16
- package/dist/Popover/usePopover.d.ts +4 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +52 -5
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +106 -80
- package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
- package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
- package/dist/Selector/SelectorBottomSheet.js +83 -0
- package/dist/Selector/index.d.ts +1 -1
- package/dist/Selector/index.d.ts.map +1 -1
- package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
- package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
- package/dist/Selector/selectorPresentation.stylex.js +21 -0
- package/dist/Selector/useSelectorPresentation.d.ts +32 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
- package/dist/Selector/useSelectorPresentation.js +90 -0
- package/dist/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- package/dist/astryx.css +19 -1
- package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
- package/dist/hooks/useAdaptivePresentation.js +19 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
- package/dist/hooks/useFocusReturnVisibility.js +35 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +12 -0
- package/locales/en.json +20 -0
- package/locales/pseudo.json +15 -0
- package/package.json +2 -2
- package/src/BottomSheet/BottomSheet.doc.mjs +6 -0
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +7 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/Button/Button.test.tsx +10 -0
- package/src/Button/Button.tsx +13 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatToolCalls.test.tsx +44 -0
- package/src/Chat/ChatToolCalls.tsx +36 -15
- package/src/ContextMenu/ContextMenu.doc.mjs +13 -1
- package/src/ContextMenu/ContextMenu.test.tsx +131 -1
- package/src/ContextMenu/ContextMenu.tsx +199 -32
- package/src/ContextMenu/index.ts +1 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +201 -40
- package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
- package/src/DropdownMenu/DropdownMenu.tsx +459 -28
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
- package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
- package/src/DropdownMenu/index.ts +2 -0
- package/src/DropdownMenu/menuWidth.ts +51 -0
- package/src/DropdownMenu/useMenuOverflow.ts +77 -0
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +17 -0
- package/src/MoreMenu/MoreMenu.test.tsx +47 -0
- package/src/MoreMenu/MoreMenu.tsx +13 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +16 -0
- package/src/MultiSelector/MultiSelector.test.tsx +127 -0
- package/src/MultiSelector/MultiSelector.tsx +121 -73
- package/src/MultiSelector/index.ts +1 -0
- package/src/Popover/Popover.doc.mjs +11 -11
- package/src/Popover/Popover.test.tsx +397 -2
- package/src/Popover/Popover.tsx +228 -21
- package/src/Popover/usePopover.tsx +92 -15
- package/src/ProgressBar/ProgressBar.spec.md +208 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +56 -0
- package/src/Selector/Selector.doc.mjs +12 -0
- package/src/Selector/Selector.test.tsx +135 -0
- package/src/Selector/Selector.tsx +141 -82
- package/src/Selector/SelectorBottomSheet.tsx +97 -0
- package/src/Selector/index.ts +1 -0
- package/src/Selector/selectorPresentation.stylex.ts +20 -0
- package/src/Selector/useSelectorPresentation.ts +147 -0
- package/src/Toast/ToastViewport.test.tsx +118 -4
- package/src/Toast/useToastGesture.ts +61 -8
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/hooks/useAdaptivePresentation.ts +35 -0
- package/src/hooks/useFocusReturnVisibility.ts +45 -0
- package/src/hooks/useFocusTrap.test.tsx +40 -0
- package/src/hooks/useFocusTrap.ts +13 -0
- package/src/theme/themingTargets.test.ts +91 -25
|
@@ -22,6 +22,7 @@ import { LayerDepthProvider } from "../Layer/LayerDepthContext.js";
|
|
|
22
22
|
import "../theme/tokens.stylex.js";
|
|
23
23
|
import { colorVars, spacingVars, radiusVars, shadowVars } from "../theme/tokens.stylex.js";
|
|
24
24
|
import { Button } from "../Button/index.js";
|
|
25
|
+
import { FOCUSABLE_SELECTOR } from "../hooks/focusableSelector.js";
|
|
25
26
|
import { rtlStyles } from "../utils/index.js";
|
|
26
27
|
import { useTranslator } from "../i18n/index.js";
|
|
27
28
|
import { useDevWarning } from "../hooks/useDevWarning.js";
|
|
@@ -29,6 +30,24 @@ import { mergeProps } from "../utils/mergeProps.js";
|
|
|
29
30
|
import { themeProps } from "../utils/themeProps.js";
|
|
30
31
|
import { stableClassName } from "../naming.js";
|
|
31
32
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
33
|
+
const FALLBACK_CLOSE_SELECTOR = '[data-astryx-popover-fallback-close]';
|
|
34
|
+
function attemptFocus(element) {
|
|
35
|
+
try {
|
|
36
|
+
element.focus();
|
|
37
|
+
} catch {
|
|
38
|
+
// Ignore non-focusable elements; the caller will try the next target.
|
|
39
|
+
}
|
|
40
|
+
return document.activeElement === element;
|
|
41
|
+
}
|
|
42
|
+
function focusFirstContentControl(container) {
|
|
43
|
+
const focusable = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(element => element.closest(FALLBACK_CLOSE_SELECTOR) == null);
|
|
44
|
+
for (const element of focusable) {
|
|
45
|
+
if (attemptFocus(element)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
32
51
|
const styles = {
|
|
33
52
|
surface: {
|
|
34
53
|
kWkggS: "x1prclbq",
|
|
@@ -39,6 +58,7 @@ const styles = {
|
|
|
39
58
|
},
|
|
40
59
|
contentWrapper: {
|
|
41
60
|
kVAEAm: "x1n2onr6",
|
|
61
|
+
kI3sdo: "x1a2a7pz",
|
|
42
62
|
$$css: true
|
|
43
63
|
},
|
|
44
64
|
closeButtonWrapper: {
|
|
@@ -129,6 +149,7 @@ function usePopoverImplementation(options = {}) {
|
|
|
129
149
|
|
|
130
150
|
// Track whether to skip auto-focus for the current open event
|
|
131
151
|
const skipAutoFocusRef = useRef(false);
|
|
152
|
+
const focusTargetRef = useRef('first');
|
|
132
153
|
|
|
133
154
|
// Core layer for popover positioning
|
|
134
155
|
const layer = useLayerInternal({
|
|
@@ -148,20 +169,40 @@ function usePopoverImplementation(options = {}) {
|
|
|
148
169
|
isActive: layer.isOpen,
|
|
149
170
|
onEscape: hasEscapeDismiss || hasLightDismiss ? layer.hide : undefined
|
|
150
171
|
});
|
|
172
|
+
const focusInitialTarget = useCallback(() => {
|
|
173
|
+
const container = contentRef.current;
|
|
174
|
+
if (!container) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (focusFirstContentControl(container)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (role === 'dialog') {
|
|
181
|
+
attemptFocus(container);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
focusFirst();
|
|
185
|
+
}, [contentRef, focusFirst, role]);
|
|
151
186
|
|
|
152
187
|
// Auto-focus first element when popover opens (unless skipped)
|
|
153
188
|
useEffect(() => {
|
|
154
189
|
if (layer.isOpen && hasAutoFocus && !skipAutoFocusRef.current) {
|
|
155
190
|
// Use requestAnimationFrame to ensure DOM is ready
|
|
156
191
|
requestAnimationFrame(() => {
|
|
157
|
-
|
|
192
|
+
const container = contentRef.current;
|
|
193
|
+
if (focusTargetRef.current === 'container' && role === 'dialog' && container) {
|
|
194
|
+
attemptFocus(container);
|
|
195
|
+
} else {
|
|
196
|
+
focusInitialTarget();
|
|
197
|
+
}
|
|
158
198
|
});
|
|
159
199
|
}
|
|
160
|
-
// Reset
|
|
200
|
+
// Reset per-open focus preferences after the popover closes.
|
|
161
201
|
if (!layer.isOpen) {
|
|
162
202
|
skipAutoFocusRef.current = false;
|
|
203
|
+
focusTargetRef.current = 'first';
|
|
163
204
|
}
|
|
164
|
-
}, [layer.isOpen, hasAutoFocus,
|
|
205
|
+
}, [contentRef, layer.isOpen, hasAutoFocus, focusInitialTarget, role]);
|
|
165
206
|
|
|
166
207
|
// Combined ref for trigger element (layer anchor + our ref)
|
|
167
208
|
const triggerRef = useCallback(el => {
|
|
@@ -172,20 +213,22 @@ function usePopoverImplementation(options = {}) {
|
|
|
172
213
|
// Show function with optional skipAutoFocus
|
|
173
214
|
const show = useCallback(showOptions => {
|
|
174
215
|
skipAutoFocusRef.current = showOptions?.skipAutoFocus ?? false;
|
|
216
|
+
focusTargetRef.current = showOptions?.focusTarget ?? 'first';
|
|
175
217
|
layer.show();
|
|
176
218
|
}, [layer]);
|
|
177
219
|
|
|
178
220
|
// Toggle function
|
|
179
|
-
const
|
|
221
|
+
const toggleWithOptions = useCallback(showOptions => {
|
|
180
222
|
if (layer.wasJustDismissed()) {
|
|
181
223
|
return;
|
|
182
224
|
}
|
|
183
225
|
if (layer.isOpen) {
|
|
184
226
|
layer.hide();
|
|
185
227
|
} else {
|
|
186
|
-
show();
|
|
228
|
+
show(showOptions);
|
|
187
229
|
}
|
|
188
230
|
}, [layer, show]);
|
|
231
|
+
const toggle = useCallback(() => toggleWithOptions(), [toggleWithOptions]);
|
|
189
232
|
|
|
190
233
|
// ARIA attributes for the trigger
|
|
191
234
|
const triggerProps = {
|
|
@@ -210,11 +253,13 @@ function usePopoverImplementation(options = {}) {
|
|
|
210
253
|
role: role === 'dialog' ? 'dialog' : undefined,
|
|
211
254
|
"aria-modal": role === 'dialog' && isModal ? true : undefined,
|
|
212
255
|
"aria-label": role === 'dialog' ? dialogLabel : undefined,
|
|
256
|
+
tabIndex: role === 'dialog' ? -1 : undefined,
|
|
213
257
|
...mergeProps({
|
|
214
258
|
...surfaceProps,
|
|
215
259
|
className: surfaceClassName
|
|
216
260
|
}, stylex.props(styles.contentWrapper, hasSurface && styles.surface, xstyle), className, style),
|
|
217
261
|
children: [children, hasCloseButton && /*#__PURE__*/_jsx("div", {
|
|
262
|
+
"data-astryx-popover-fallback-close": "",
|
|
218
263
|
...stylex.props(styles.closeButtonWrapper, rtlStyles.centerInline('100%')),
|
|
219
264
|
children: /*#__PURE__*/_jsx(Button, {
|
|
220
265
|
variant: "secondary",
|
|
@@ -235,6 +280,7 @@ function usePopoverImplementation(options = {}) {
|
|
|
235
280
|
show,
|
|
236
281
|
hide: layer.hide,
|
|
237
282
|
toggle,
|
|
283
|
+
toggleWithOptions,
|
|
238
284
|
wasJustDismissed: layer.wasJustDismissed,
|
|
239
285
|
isOpen: layer.isOpen,
|
|
240
286
|
id: layer.id,
|
|
@@ -245,6 +291,7 @@ function usePopoverImplementation(options = {}) {
|
|
|
245
291
|
export function usePopover(options = {}) {
|
|
246
292
|
const {
|
|
247
293
|
wasJustDismissed: _,
|
|
294
|
+
toggleWithOptions: __,
|
|
248
295
|
...popover
|
|
249
296
|
} = usePopoverImplementation(options);
|
|
250
297
|
return popover;
|
|
@@ -20,8 +20,10 @@ import { type LayerPlacement } from '../Layer/useLayer';
|
|
|
20
20
|
import type { SelectorOptionType, SelectorOptionData } from './types';
|
|
21
21
|
import type { BaseProps } from '../BaseProps';
|
|
22
22
|
import type { SizeValue } from '../utils/types';
|
|
23
|
+
import type { AdaptivePresentation } from '../hooks/useAdaptivePresentation';
|
|
23
24
|
export type SelectorSize = 'sm' | 'md' | 'lg';
|
|
24
25
|
export type SelectorVariant = 'input' | 'ghost';
|
|
26
|
+
export type SelectorPresentation = AdaptivePresentation;
|
|
25
27
|
export type SelectorStatusType = 'warning' | 'error' | 'success';
|
|
26
28
|
export interface SelectorStatus {
|
|
27
29
|
/**
|
|
@@ -215,6 +217,14 @@ interface SelectorPropsBase<T extends SelectorOptionType = SelectorOptionType> e
|
|
|
215
217
|
* `placement="above"` for bottom-fixed toolbars).
|
|
216
218
|
*/
|
|
217
219
|
placement?: LayerPlacement;
|
|
220
|
+
/**
|
|
221
|
+
* How the option list is presented.
|
|
222
|
+
* - 'popover': anchored to the trigger
|
|
223
|
+
* - 'bottom-sheet': modal sheet suited to compact touch screens
|
|
224
|
+
* - 'adaptive': bottom sheet on compact coarse-pointer screens, otherwise popover
|
|
225
|
+
* @default 'popover'
|
|
226
|
+
*/
|
|
227
|
+
presentation?: SelectorPresentation;
|
|
218
228
|
/**
|
|
219
229
|
* Whether the dropdown starts open on mount.
|
|
220
230
|
* Useful for showcases and previews.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EASZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EASZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAEpD,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAiB7E,OAAO,KAAK,EAAC,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAepE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAS9C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,kCAAkC,CAAC;AA6W3E,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,CAAC;AAEhD,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,IAAI,EAAE,kBAAkB,CAAC;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,iBAAiB,CACzB,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,CACjD,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,cAAc,CAAC;IACpD;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,EAAE,CAAC,EAAE,CAAC;IAIb;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IAEpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAEjC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,SAAS,CAAC;IAEzD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,SAAS,CAAC;IAExD;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAE5B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,oBAAoB,CAAC;IAEpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,KAAK,yBAAyB,CAC5B,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,IAC/C,iBAAiB,CAAC,CAAC,CAAC,GAAG;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD,CAAC;AAEF,KAAK,sBAAsB,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,IAC3E,iBAAiB,CAAC,CAAC,CAAC,GAAG;IACrB;;;;;OAKG;IACH,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D,CAAC;AAEJ,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,IACzE,yBAAyB,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AA4C3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,kBAAkB,EACnD,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,qBA2iCxB;yBA5iCe,QAAQ"}
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import React, { useCallback, useEffect, useId, useMemo, useOptimistic, useRef, useState, useTransition } from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
|
-
import { usePopoverInternal } from "../Popover/usePopover.js";
|
|
22
21
|
import { useTooltip } from "../Tooltip/index.js";
|
|
23
22
|
import { Icon, renderIconSlot } from "../Icon/index.js";
|
|
24
23
|
import { useIndicator } from "../Indicator/index.js";
|
|
@@ -48,6 +47,9 @@ import { groupStyles } from "../InputGroup/groupStyles.js";
|
|
|
48
47
|
import { useInputGroup } from "../InputGroup/InputGroupContext.js";
|
|
49
48
|
import { VisuallyHidden } from "../VisuallyHidden/index.js";
|
|
50
49
|
import { useTranslator } from "../i18n/index.js";
|
|
50
|
+
import { SelectorBottomSheet } from "./SelectorBottomSheet.js";
|
|
51
|
+
import { useSelectorPresentation } from "./useSelectorPresentation.js";
|
|
52
|
+
import { selectorPresentationStyles } from "./selectorPresentation.stylex.js";
|
|
51
53
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
52
54
|
const styles = {
|
|
53
55
|
triggerContainer: {
|
|
@@ -316,12 +318,14 @@ export function Selector(props) {
|
|
|
316
318
|
emptyText: emptyTextFromProps,
|
|
317
319
|
emptySearchText: emptySearchTextFromProps,
|
|
318
320
|
placement,
|
|
321
|
+
presentation = 'popover',
|
|
319
322
|
isDefaultOpen = false,
|
|
320
323
|
'data-testid': testId,
|
|
321
324
|
width,
|
|
322
325
|
xstyle,
|
|
323
326
|
className,
|
|
324
327
|
style,
|
|
328
|
+
onFocus,
|
|
325
329
|
hasClear: hasClearProp,
|
|
326
330
|
id,
|
|
327
331
|
...rest
|
|
@@ -426,26 +430,32 @@ export function Selector(props) {
|
|
|
426
430
|
// Clear any lingering result count when the popover closes so stale status
|
|
427
431
|
// text does not linger in the a11y tree.
|
|
428
432
|
announce('');
|
|
429
|
-
triggerRef.current?.focus();
|
|
430
433
|
}, [announce]);
|
|
431
|
-
const
|
|
434
|
+
const surface = useSelectorPresentation({
|
|
435
|
+
presentation,
|
|
432
436
|
onHide: handleLayerHide,
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
437
|
+
triggerRef,
|
|
438
|
+
popoverOptions: {
|
|
439
|
+
hasLightDismiss: true,
|
|
440
|
+
hasCloseButton: false,
|
|
441
|
+
hasAutoFocus: false,
|
|
442
|
+
// The popup's own role="listbox" is the exposed semantics; the trigger
|
|
443
|
+
// keeps DOM focus, so wrapping it in a modal dialog would misrepresent it.
|
|
444
|
+
role: 'none',
|
|
445
|
+
// The theme target belongs on the SURFACE that paints the popup, which
|
|
446
|
+
// `usePopover` owns — not on the scrolling list inside it.
|
|
447
|
+
surfaceTarget: 'selector-popup'
|
|
448
|
+
}
|
|
442
449
|
});
|
|
450
|
+
const {
|
|
451
|
+
popover
|
|
452
|
+
} = surface;
|
|
443
453
|
const keepOpenProps = useKeepLayerOpenProps(popover.id, popover.isOpen);
|
|
444
454
|
|
|
445
455
|
// Open dropdown on mount when isDefaultOpen is true
|
|
446
456
|
useEffect(() => {
|
|
447
457
|
if (isDefaultOpen) {
|
|
448
|
-
|
|
458
|
+
surface.show();
|
|
449
459
|
}
|
|
450
460
|
// eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
|
|
451
461
|
}, []);
|
|
@@ -488,7 +498,7 @@ export function Selector(props) {
|
|
|
488
498
|
// per arrival at that state rather than on every re-render.
|
|
489
499
|
const announcedEmptyRef = useRef(null);
|
|
490
500
|
useEffect(() => {
|
|
491
|
-
const isPanelEmpty =
|
|
501
|
+
const isPanelEmpty = surface.isOpen && !isLoading && searchQuery === '' && selectableItems.length === 0;
|
|
492
502
|
if (!isPanelEmpty) {
|
|
493
503
|
announcedEmptyRef.current = null;
|
|
494
504
|
return;
|
|
@@ -498,7 +508,7 @@ export function Selector(props) {
|
|
|
498
508
|
}
|
|
499
509
|
announcedEmptyRef.current = emptyAnnouncement;
|
|
500
510
|
announce(emptyAnnouncement);
|
|
501
|
-
}, [
|
|
511
|
+
}, [surface.isOpen, isLoading, searchQuery, selectableItems.length, emptyAnnouncement, announce]);
|
|
502
512
|
|
|
503
513
|
// Calculate offset to position selected item over trigger. Explicit
|
|
504
514
|
// placement opts out of the selector-specific overlay behavior and uses the
|
|
@@ -567,17 +577,17 @@ export function Selector(props) {
|
|
|
567
577
|
onItemMouseEnter
|
|
568
578
|
} = useCombobox({
|
|
569
579
|
selectableItems: filteredItems,
|
|
570
|
-
wasJustDismissed:
|
|
580
|
+
wasJustDismissed: surface.wasJustDismissed,
|
|
571
581
|
// The optimistic value, not the raw prop: with a pending changeAction the
|
|
572
582
|
// prop still holds the old selection, so the popup would open with the
|
|
573
583
|
// highlight on it and Delete/Backspace could clear a value the action has
|
|
574
584
|
// already replaced.
|
|
575
585
|
value: optimisticValue,
|
|
576
586
|
isDisabled,
|
|
577
|
-
isOpen:
|
|
587
|
+
isOpen: surface.isOpen,
|
|
578
588
|
hasSearch,
|
|
579
589
|
onOpen: useCallback(() => {
|
|
580
|
-
|
|
590
|
+
surface.show();
|
|
581
591
|
if (hasSearch) {
|
|
582
592
|
requestAnimationFrame(() => {
|
|
583
593
|
const input = searchRef.current;
|
|
@@ -589,8 +599,8 @@ export function Selector(props) {
|
|
|
589
599
|
}
|
|
590
600
|
});
|
|
591
601
|
}
|
|
592
|
-
}, [
|
|
593
|
-
onClose:
|
|
602
|
+
}, [surface, hasSearch]),
|
|
603
|
+
onClose: surface.hide,
|
|
594
604
|
onSelect: commitValue,
|
|
595
605
|
onClear: hasClear ? clearValue : undefined,
|
|
596
606
|
onSearchSeed: appendSearchQuery,
|
|
@@ -609,10 +619,10 @@ export function Selector(props) {
|
|
|
609
619
|
// when closed — the optimistic one, so a pending changeAction cannot strand
|
|
610
620
|
// cycling on the first match. -1 means nothing is selected or highlighted,
|
|
611
621
|
// which the hook reads as "search from the top".
|
|
612
|
-
getCurrentIndex: () =>
|
|
622
|
+
getCurrentIndex: () => surface.isOpen ? highlightedIndex : selectedItemIndex,
|
|
613
623
|
onMatch: index => {
|
|
614
624
|
const item = selectableItems[index];
|
|
615
|
-
if (
|
|
625
|
+
if (surface.isOpen) {
|
|
616
626
|
setHighlightedIndex(index);
|
|
617
627
|
} else if (item.value !== optimisticValue) {
|
|
618
628
|
commitValue(item.value);
|
|
@@ -635,13 +645,13 @@ export function Selector(props) {
|
|
|
635
645
|
// cursor walks off-screen once navigation passes the visible window. Mirrors
|
|
636
646
|
// CommandPaletteItem's scrollIntoView({block: 'nearest'}) behavior.
|
|
637
647
|
useEffect(() => {
|
|
638
|
-
if (!
|
|
648
|
+
if (!surface.isOpen || highlightedIndex < 0) {
|
|
639
649
|
return;
|
|
640
650
|
}
|
|
641
651
|
document.getElementById(getItemId(highlightedIndex))?.scrollIntoView?.({
|
|
642
652
|
block: 'nearest'
|
|
643
653
|
});
|
|
644
|
-
}, [
|
|
654
|
+
}, [surface.isOpen, highlightedIndex, getItemId]);
|
|
645
655
|
|
|
646
656
|
// Handle clear button click
|
|
647
657
|
const handleClear = useCallback(e => {
|
|
@@ -671,17 +681,17 @@ export function Selector(props) {
|
|
|
671
681
|
label: t('@astryx.selector.searchOptions')
|
|
672
682
|
}),
|
|
673
683
|
...themeProps('selector-search'),
|
|
674
|
-
xstyle: variant !== 'ghost' && styles.searchRowInput
|
|
684
|
+
xstyle: surface.activePresentation === 'popover' && variant !== 'ghost' && styles.searchRowInput
|
|
675
685
|
// When hasSearch is set, focus moves into this input on open, so it —
|
|
676
686
|
// not the trigger — must be the combobox that reports the highlighted
|
|
677
687
|
// option via aria-activedescendant (comboboxes-4). A bare searchbox
|
|
678
688
|
// left the highlight silent to screen readers.
|
|
679
689
|
,
|
|
680
690
|
role: "combobox",
|
|
681
|
-
"aria-expanded":
|
|
691
|
+
"aria-expanded": surface.isOpen,
|
|
682
692
|
"aria-controls": listboxId,
|
|
683
693
|
"aria-autocomplete": "list",
|
|
684
|
-
"aria-activedescendant":
|
|
694
|
+
"aria-activedescendant": surface.isOpen && highlightedIndex >= 0 ? getItemId(highlightedIndex) : undefined,
|
|
685
695
|
value: searchQuery,
|
|
686
696
|
onValueChange: handleSearchChange,
|
|
687
697
|
onContainerKeyDown: e => {
|
|
@@ -724,7 +734,7 @@ export function Selector(props) {
|
|
|
724
734
|
},
|
|
725
735
|
placeholder: searchPlaceholder
|
|
726
736
|
});
|
|
727
|
-
}, [hasSearch, searchId, listboxId, searchQuery, hasQuery, searchPlaceholder, handleSearchChange, onKeyDown,
|
|
737
|
+
}, [hasSearch, searchId, listboxId, searchQuery, hasQuery, searchPlaceholder, handleSearchChange, onKeyDown, surface.isOpen, surface.activePresentation, highlightedIndex, getItemId, variant, t]);
|
|
728
738
|
|
|
729
739
|
// The single-selection mark, resolved from the theme once per render. A
|
|
730
740
|
// theme that maps `check` to another indicator (a radio, say) changes every
|
|
@@ -917,6 +927,63 @@ export function Selector(props) {
|
|
|
917
927
|
children: selectedItem?.label ?? placeholder
|
|
918
928
|
})]
|
|
919
929
|
});
|
|
930
|
+
const panelContent = hasSearch ? /*#__PURE__*/_jsxs("div", {
|
|
931
|
+
children: [renderSearch(), /*#__PURE__*/_jsx(Divider, {}), /*#__PURE__*/_jsx("div", {
|
|
932
|
+
ref: listboxRef,
|
|
933
|
+
id: listboxId,
|
|
934
|
+
role: "listbox",
|
|
935
|
+
"aria-labelledby": triggerId,
|
|
936
|
+
...{
|
|
937
|
+
0: {
|
|
938
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x7a5moj x1hc1fzr x1ey1afo"
|
|
939
|
+
},
|
|
940
|
+
1: {
|
|
941
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x1hc1fzr x1ey1afo x2hg6jq"
|
|
942
|
+
}
|
|
943
|
+
}[!!(surface.activePresentation === 'popover' && variant !== 'ghost') << 0],
|
|
944
|
+
children: renderOptions()
|
|
945
|
+
})]
|
|
946
|
+
}) : /*#__PURE__*/_jsx("div", {
|
|
947
|
+
ref: listboxRef,
|
|
948
|
+
id: listboxId,
|
|
949
|
+
role: "listbox",
|
|
950
|
+
"aria-labelledby": triggerId,
|
|
951
|
+
"aria-activedescendant": surface.isOpen && highlightedIndex >= 0 ? getItemId(highlightedIndex) : undefined,
|
|
952
|
+
tabIndex: surface.activePresentation === 'bottom-sheet' ? 0 : undefined,
|
|
953
|
+
onKeyDown: surface.activePresentation === 'bottom-sheet' ? handleTriggerKeyDown : undefined,
|
|
954
|
+
...{
|
|
955
|
+
0: {
|
|
956
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x7a5moj x1hc1fzr x1ey1afo"
|
|
957
|
+
},
|
|
958
|
+
2: {
|
|
959
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x1hc1fzr x1ey1afo x2hg6jq"
|
|
960
|
+
},
|
|
961
|
+
1: {
|
|
962
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x7a5moj xg01cxk xq2gx43"
|
|
963
|
+
},
|
|
964
|
+
3: {
|
|
965
|
+
className: "x9f619 xuyqlj2 x1odjw0f x1a2a7pz xu0wf1k x2hg6jq xg01cxk xq2gx43"
|
|
966
|
+
}
|
|
967
|
+
}[!!(surface.activePresentation === 'popover' && variant !== 'ghost') << 1 | !!(surface.activePresentation === 'popover' && !isPositioned) << 0],
|
|
968
|
+
children: renderOptions()
|
|
969
|
+
});
|
|
970
|
+
const selectionSurface = surface.activePresentation === 'bottom-sheet' ? /*#__PURE__*/_jsx(SelectorBottomSheet, {
|
|
971
|
+
isOpen: surface.isSheetOpen,
|
|
972
|
+
onOpenChange: surface.onSheetOpenChange,
|
|
973
|
+
finalFocusRef: triggerRef,
|
|
974
|
+
initialFocusRef: hasSearch ? searchRef : listboxRef,
|
|
975
|
+
label: label,
|
|
976
|
+
children: panelContent
|
|
977
|
+
}) : popover.render(panelContent, {
|
|
978
|
+
placement: popoverPlacement,
|
|
979
|
+
alignment: 'start',
|
|
980
|
+
// The system's standard menu clearance, except in overlay mode:
|
|
981
|
+
// there the measured negative margin owns the block geometry and
|
|
982
|
+
// the menu is meant to sit on the trigger, not clear it.
|
|
983
|
+
offset: shouldOverlaySelectedItem ? undefined : spacingVars['--spacing-1'],
|
|
984
|
+
xstyle: [styles.popover, layerAnimations[popoverPlacement]],
|
|
985
|
+
style: popoverOffsetStyle
|
|
986
|
+
});
|
|
920
987
|
const selectorContent = /*#__PURE__*/_jsxs(_Fragment, {
|
|
921
988
|
children: [/*#__PURE__*/_jsxs("div", {
|
|
922
989
|
ref: el => {
|
|
@@ -934,7 +1001,7 @@ export function Selector(props) {
|
|
|
934
1001
|
size,
|
|
935
1002
|
status: status?.type ?? null,
|
|
936
1003
|
disabled: isDisabled ? 'disabled' : null
|
|
937
|
-
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, variant === 'ghost' && interactionOverlayStyles.backgroundImage, variant === 'ghost' && focusOutlineStyles.focusWithin, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, !selectedItem && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, inputGroup && styles.triggerInGroup, xstyle), className, style),
|
|
1004
|
+
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, variant === 'ghost' && interactionOverlayStyles.backgroundImage, variant === 'ghost' && focusOutlineStyles.focusWithin, surface.isTriggerFocusRingSuppressed && selectorPresentationStyles.pointerRestoredFocus, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, !selectedItem && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, inputGroup && styles.triggerInGroup, xstyle), className, style),
|
|
938
1005
|
children: [startIcon && renderIconSlot(startIcon, {
|
|
939
1006
|
size: 'sm',
|
|
940
1007
|
color: 'secondary'
|
|
@@ -951,10 +1018,10 @@ export function Selector(props) {
|
|
|
951
1018
|
,
|
|
952
1019
|
role: hasSearch ? undefined : 'combobox',
|
|
953
1020
|
...rest,
|
|
954
|
-
"aria-haspopup":
|
|
955
|
-
"aria-expanded":
|
|
1021
|
+
"aria-haspopup": surface.activePresentation === 'bottom-sheet' ? 'dialog' : 'listbox',
|
|
1022
|
+
"aria-expanded": surface.isOpen,
|
|
956
1023
|
"aria-controls": listboxId,
|
|
957
|
-
"aria-activedescendant": !hasSearch &&
|
|
1024
|
+
"aria-activedescendant": !hasSearch && surface.isOpen && highlightedIndex >= 0 ? getItemId(highlightedIndex) : undefined,
|
|
958
1025
|
"aria-describedby": ariaDescribedBy,
|
|
959
1026
|
"aria-labelledby": ariaLabelledBy,
|
|
960
1027
|
"aria-required": isEffectivelyRequired ? 'true' : undefined,
|
|
@@ -967,6 +1034,10 @@ export function Selector(props) {
|
|
|
967
1034
|
disabled: isDisabled && !showsDisabledMessage,
|
|
968
1035
|
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
969
1036
|
onKeyDown: handleTriggerKeyDown,
|
|
1037
|
+
onFocus: event => {
|
|
1038
|
+
onFocus?.(event);
|
|
1039
|
+
surface.onTriggerFocus(event);
|
|
1040
|
+
},
|
|
970
1041
|
tabIndex: isDisabled && !showsDisabledMessage ? -1 : 0,
|
|
971
1042
|
...{
|
|
972
1043
|
0: {
|
|
@@ -1021,7 +1092,7 @@ export function Selector(props) {
|
|
|
1021
1092
|
// the wrapper used to provide, so one element carries the mark,
|
|
1022
1093
|
// its open/closed transform, and the theme target.
|
|
1023
1094
|
,
|
|
1024
|
-
xstyle: [styles.triggerIcon, styles.triggerIconRotation,
|
|
1095
|
+
xstyle: [styles.triggerIcon, styles.triggerIconRotation, surface.isOpen && styles.triggerIconOpen]
|
|
1025
1096
|
// Stable theme target on the chevron glyph itself, so a theme can
|
|
1026
1097
|
// restyle just this icon (color, size, hover) — and its
|
|
1027
1098
|
// open/closed state — via `defineTheme`. Same-element rules in
|
|
@@ -1029,55 +1100,10 @@ export function Selector(props) {
|
|
|
1029
1100
|
// which a button-level target could not reach.
|
|
1030
1101
|
,
|
|
1031
1102
|
...themeProps('selector-indicator-icon', {
|
|
1032
|
-
state:
|
|
1103
|
+
state: surface.isOpen ? 'expanded' : 'collapsed'
|
|
1033
1104
|
})
|
|
1034
1105
|
})]
|
|
1035
|
-
}),
|
|
1036
|
-
children: [renderSearch(), /*#__PURE__*/_jsx(Divider, {}), /*#__PURE__*/_jsx("div", {
|
|
1037
|
-
ref: listboxRef,
|
|
1038
|
-
id: listboxId,
|
|
1039
|
-
role: "listbox",
|
|
1040
|
-
"aria-labelledby": triggerId,
|
|
1041
|
-
...{
|
|
1042
|
-
0: {
|
|
1043
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x7a5moj x1hc1fzr x1ey1afo"
|
|
1044
|
-
},
|
|
1045
|
-
1: {
|
|
1046
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x1hc1fzr x1ey1afo x2hg6jq"
|
|
1047
|
-
}
|
|
1048
|
-
}[!!(variant !== 'ghost') << 0],
|
|
1049
|
-
children: renderOptions()
|
|
1050
|
-
})]
|
|
1051
|
-
}) : /*#__PURE__*/_jsx("div", {
|
|
1052
|
-
ref: listboxRef,
|
|
1053
|
-
id: listboxId,
|
|
1054
|
-
role: "listbox",
|
|
1055
|
-
"aria-labelledby": triggerId,
|
|
1056
|
-
...{
|
|
1057
|
-
0: {
|
|
1058
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x7a5moj x1hc1fzr x1ey1afo"
|
|
1059
|
-
},
|
|
1060
|
-
2: {
|
|
1061
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x1hc1fzr x1ey1afo x2hg6jq"
|
|
1062
|
-
},
|
|
1063
|
-
1: {
|
|
1064
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x7a5moj xg01cxk xq2gx43"
|
|
1065
|
-
},
|
|
1066
|
-
3: {
|
|
1067
|
-
className: "x9f619 xuyqlj2 x1odjw0f xu0wf1k x2hg6jq xg01cxk xq2gx43"
|
|
1068
|
-
}
|
|
1069
|
-
}[!!(variant !== 'ghost') << 1 | !!!isPositioned << 0],
|
|
1070
|
-
children: renderOptions()
|
|
1071
|
-
}), {
|
|
1072
|
-
placement: popoverPlacement,
|
|
1073
|
-
alignment: 'start',
|
|
1074
|
-
// The system's standard menu clearance, except in overlay mode:
|
|
1075
|
-
// there the measured negative margin owns the block geometry and
|
|
1076
|
-
// the menu is meant to sit on the trigger, not clear it.
|
|
1077
|
-
offset: shouldOverlaySelectedItem ? undefined : spacingVars['--spacing-1'],
|
|
1078
|
-
xstyle: [styles.popover, layerAnimations[popoverPlacement]],
|
|
1079
|
-
style: popoverOffsetStyle
|
|
1080
|
-
}), showStatusTooltip && statusTooltip.renderTooltip(status?.message ?? ''), showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)]
|
|
1106
|
+
}), selectionSurface, showStatusTooltip && statusTooltip.renderTooltip(status?.message ?? ''), showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)]
|
|
1081
1107
|
});
|
|
1082
1108
|
if (inputGroup) {
|
|
1083
1109
|
return selectorContent;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file SelectorBottomSheet.tsx
|
|
3
|
+
* @input Uses BottomSheet, Section, and Heading
|
|
4
|
+
* @output Shared mobile listbox sheet for Selector and MultiSelector
|
|
5
|
+
* @position Internal presentation primitive for selection controls
|
|
6
|
+
*/
|
|
7
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
8
|
+
interface SelectorBottomSheetProps {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
finalFocusRef: RefObject<HTMLElement | null>;
|
|
11
|
+
initialFocusRef: RefObject<HTMLElement | null>;
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
label: string;
|
|
14
|
+
onOpenChange: (isOpen: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function SelectorBottomSheet({ children, finalFocusRef, initialFocusRef, isOpen, label, onOpenChange, }: SelectorBottomSheetProps): import("react").JSX.Element;
|
|
17
|
+
export declare namespace SelectorBottomSheet {
|
|
18
|
+
var displayName: string;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=SelectorBottomSheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectorBottomSheet.d.ts","sourceRoot":"","sources":["../../src/Selector/SelectorBottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAA4B,KAAK,SAAS,EAAE,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAsBhF,UAAU,wBAAwB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,eAAe,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC/C,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAsBD,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,aAAa,EACb,eAAe,EACf,MAAM,EACN,KAAK,EACL,YAAY,GACb,EAAE,wBAAwB,+BAyB1B;yBAhCe,mBAAmB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file SelectorBottomSheet.tsx
|
|
7
|
+
* @input Uses BottomSheet, Section, and Heading
|
|
8
|
+
* @output Shared mobile listbox sheet for Selector and MultiSelector
|
|
9
|
+
* @position Internal presentation primitive for selection controls
|
|
10
|
+
*/
|
|
11
|
+
import { lazy, Suspense, useEffect } from 'react';
|
|
12
|
+
import * as stylex from '@stylexjs/stylex';
|
|
13
|
+
import { Heading } from "../Heading/index.js";
|
|
14
|
+
import { Section } from "../Section/index.js";
|
|
15
|
+
import "../theme/tokens.stylex.js";
|
|
16
|
+
import { spacingVars } from "../theme/tokens.stylex.js";
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
const LazyBottomSheet = /*#__PURE__*/lazy(async () => import("../BottomSheet/BottomSheet.js").then(module => ({
|
|
19
|
+
default: module.BottomSheet
|
|
20
|
+
})));
|
|
21
|
+
const styles = {
|
|
22
|
+
heading: {
|
|
23
|
+
k1K539: "x1p37lm5",
|
|
24
|
+
kUOVxO: "xam6yil",
|
|
25
|
+
$$css: true
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
function SelectorBottomSheetInitialFocus({
|
|
29
|
+
isOpen,
|
|
30
|
+
targetRef
|
|
31
|
+
}) {
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!isOpen) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const frame = requestAnimationFrame(() => {
|
|
37
|
+
targetRef.current?.focus({
|
|
38
|
+
preventScroll: true
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
return () => cancelAnimationFrame(frame);
|
|
42
|
+
}, [isOpen, targetRef]);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
export function SelectorBottomSheet({
|
|
46
|
+
children,
|
|
47
|
+
finalFocusRef,
|
|
48
|
+
initialFocusRef,
|
|
49
|
+
isOpen,
|
|
50
|
+
label,
|
|
51
|
+
onOpenChange
|
|
52
|
+
}) {
|
|
53
|
+
return /*#__PURE__*/_jsx(Suspense, {
|
|
54
|
+
fallback: null,
|
|
55
|
+
children: /*#__PURE__*/_jsx(LazyBottomSheet, {
|
|
56
|
+
isOpen: isOpen,
|
|
57
|
+
onOpenChange: onOpenChange,
|
|
58
|
+
finalFocusRef: finalFocusRef,
|
|
59
|
+
label: label,
|
|
60
|
+
height: "hug",
|
|
61
|
+
purpose: "info",
|
|
62
|
+
children: /*#__PURE__*/_jsx(Section, {
|
|
63
|
+
paddingBlockStart: 4,
|
|
64
|
+
paddingBlockEnd: 0,
|
|
65
|
+
paddingInline: 1,
|
|
66
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
67
|
+
...{
|
|
68
|
+
className: "xh8yej3"
|
|
69
|
+
},
|
|
70
|
+
children: [/*#__PURE__*/_jsx(Heading, {
|
|
71
|
+
level: 3,
|
|
72
|
+
xstyle: styles.heading,
|
|
73
|
+
children: label
|
|
74
|
+
}), children, /*#__PURE__*/_jsx(SelectorBottomSheetInitialFocus, {
|
|
75
|
+
isOpen: isOpen,
|
|
76
|
+
targetRef: initialFocusRef
|
|
77
|
+
})]
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
SelectorBottomSheet.displayName = 'SelectorBottomSheet';
|
package/dist/Selector/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @output Exports Selector and types
|
|
4
4
|
* @position Public API entry point
|
|
5
5
|
*/
|
|
6
|
-
export { Selector, type SelectorProps, type SelectorSize, type SelectorStatus, type SelectorStatusType, } from './Selector';
|
|
6
|
+
export { Selector, type SelectorProps, type SelectorPresentation, type SelectorSize, type SelectorStatus, type SelectorStatusType, } from './Selector';
|
|
7
7
|
export { SelectorOption } from './SelectorOption';
|
|
8
8
|
export type { SelectorOptionType, SelectorOptionData, SelectorDivider, SelectorSection, } from './types';
|
|
9
9
|
export { useCombobox, useSelectedItemOffset } from './hooks';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Selector/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EACL,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,WAAW,EAAE,qBAAqB,EAAC,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Selector/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EACL,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,WAAW,EAAE,qBAAqB,EAAC,MAAM,SAAS,CAAC"}
|