@astryxdesign/core 0.4.0 → 0.4.1-canary.8138bfc
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 +39 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +1 -2
- package/dist/AspectRatio/index.d.ts.map +1 -1
- package/dist/AspectRatio/index.js +1 -2
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +1 -2
- package/dist/Badge/index.d.ts.map +1 -1
- package/dist/Badge/index.js +1 -2
- package/dist/Banner/Banner.d.ts +3 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +17 -3
- package/dist/Blockquote/Blockquote.d.ts.map +1 -1
- package/dist/Blockquote/Blockquote.js +1 -2
- package/dist/Blockquote/index.d.ts.map +1 -1
- package/dist/Blockquote/index.js +1 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +1 -2
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +1 -2
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +1 -2
- package/dist/Center/index.d.ts.map +1 -1
- package/dist/Center/index.js +1 -2
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +1 -2
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -0
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +7 -8
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +23 -10
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +44 -16
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +32 -13
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +10 -3
- package/dist/Field/PanelSearchInput.d.ts +81 -0
- package/dist/Field/PanelSearchInput.d.ts.map +1 -0
- package/dist/Field/PanelSearchInput.js +151 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +21 -10
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +1 -2
- package/dist/Grid/index.d.ts.map +1 -1
- package/dist/Grid/index.js +1 -2
- package/dist/Icon/globalIconRegistry.d.ts +5 -4
- package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
- package/dist/Icon/globalIconRegistry.js +5 -4
- package/dist/Indicator/index.d.ts +1 -1
- package/dist/Indicator/index.d.ts.map +1 -1
- package/dist/Indicator/types.d.ts +10 -0
- package/dist/Indicator/types.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +24 -50
- package/dist/MultiSelector/MultiSelector.d.ts +8 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +134 -102
- package/dist/NavItem/navItemStyles.stylex.d.ts +17 -5
- package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
- package/dist/NavItem/navItemStyles.stylex.js +11 -5
- package/dist/Popover/usePopover.d.ts +15 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +14 -2
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +2 -3
- package/dist/Section/Section.d.ts.map +1 -1
- package/dist/Section/Section.js +1 -2
- package/dist/Section/index.d.ts.map +1 -1
- package/dist/Section/index.js +1 -2
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +135 -95
- package/dist/SideNav/SideNav.d.ts +7 -9
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +32 -5
- package/dist/SideNav/SideNavCollapseButton.d.ts +25 -9
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +39 -15
- package/dist/SideNav/SideNavCollapseContext.d.ts +21 -0
- package/dist/SideNav/SideNavCollapseContext.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseContext.js +16 -2
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +70 -24
- package/dist/SideNav/SideNavItem.d.ts +7 -2
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +114 -75
- package/dist/SideNav/SideNavSection.d.ts.map +1 -1
- package/dist/SideNav/SideNavSection.js +7 -16
- package/dist/SideNav/index.d.ts +1 -1
- package/dist/SideNav/index.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -2
- package/dist/Skeleton/index.d.ts.map +1 -1
- package/dist/Skeleton/index.js +1 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +2 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +3 -3
- package/dist/Table/TableRow.d.ts +1 -1
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +4 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +7 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +14 -3
- package/dist/Timestamp/formatInstant.d.ts +11 -1
- package/dist/Timestamp/formatInstant.d.ts.map +1 -1
- package/dist/Timestamp/formatInstant.js +3 -1
- package/dist/TopNav/TopNavMegaMenu.js +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +2 -2
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -1
- package/dist/VisuallyHidden/VisuallyHidden.js +1 -2
- package/dist/VisuallyHidden/index.d.ts.map +1 -1
- package/dist/VisuallyHidden/index.js +1 -2
- package/dist/astryx.css +20 -17
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +17 -4
- package/dist/theme/defineTheme.d.ts +2 -2
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +2 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +23 -0
- package/dist/utils/focusOutline.stylex.d.ts +34 -19
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +46 -33
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/interactionModality.d.ts +34 -0
- package/dist/utils/interactionModality.d.ts.map +1 -0
- package/dist/utils/interactionModality.js +70 -0
- package/locales/en.json +44 -0
- package/locales/pseudo.json +33 -0
- package/package.json +3 -3
- package/src/AppShell/AppShell.doc.mjs +3 -0
- package/src/AspectRatio/AspectRatio.tsx +0 -2
- package/src/AspectRatio/index.ts +0 -2
- package/src/Badge/Badge.tsx +0 -2
- package/src/Badge/index.ts +0 -2
- package/src/Banner/Banner.test.tsx +58 -1
- package/src/Banner/Banner.tsx +24 -5
- package/src/Blockquote/Blockquote.tsx +0 -2
- package/src/Blockquote/index.ts +0 -2
- package/src/Button/Button.doc.mjs +2 -2
- package/src/Button/Button.tsx +5 -4
- package/src/Card/Card.tsx +0 -2
- package/src/Card/index.ts +0 -2
- package/src/Center/Center.tsx +0 -2
- package/src/Center/index.ts +0 -2
- package/src/CheckboxInput/CheckboxInput.test.tsx +8 -3
- package/src/Code/Code.tsx +0 -2
- package/src/CommandPalette/CommandPalette.test.tsx +54 -0
- package/src/CommandPalette/CommandPalette.tsx +5 -0
- package/src/ComplexSelector/ComplexSelector.test.tsx +15 -6
- package/src/ComplexSelector/ComplexSelector.tsx +6 -10
- package/src/DateInput/DateInput.test.tsx +19 -0
- package/src/DateInput/DateInput.tsx +3 -6
- package/src/DateRangeInput/DateRangeInput.tsx +3 -9
- package/src/DateTimeInput/DateTimeInput.test.tsx +79 -1
- package/src/DateTimeInput/DateTimeInput.tsx +23 -9
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +2 -1
- package/src/Field/PanelSearchInput.tsx +271 -0
- package/src/FileInput/FileInput.tsx +21 -8
- package/src/Grid/Grid.tsx +0 -2
- package/src/Grid/index.ts +0 -2
- package/src/Icon/globalIconRegistry.tsx +5 -4
- package/src/Indicator/Indicator.doc.mjs +1 -1
- package/src/Indicator/index.ts +1 -0
- package/src/Indicator/types.ts +11 -0
- package/src/Lightbox/Lightbox.tsx +2 -11
- package/src/MultiSelector/MultiSelector.doc.mjs +9 -0
- package/src/MultiSelector/MultiSelector.test.tsx +135 -5
- package/src/MultiSelector/MultiSelector.tsx +170 -127
- package/src/NavItem/navItemStyles.stylex.ts +31 -5
- package/src/Popover/Popover.doc.mjs +2 -0
- package/src/Popover/usePopover.tsx +37 -4
- package/src/ProgressBar/ProgressBar.tsx +2 -8
- package/src/Section/Section.tsx +0 -2
- package/src/Section/index.ts +0 -2
- package/src/Selector/Selector.doc.mjs +10 -0
- package/src/Selector/Selector.test.tsx +386 -2
- package/src/Selector/Selector.tsx +182 -122
- package/src/SideNav/SideNav.doc.mjs +13 -4
- package/src/SideNav/SideNav.test.tsx +771 -2
- package/src/SideNav/SideNav.tsx +37 -16
- package/src/SideNav/SideNavCollapseButton.doc.mjs +28 -6
- package/src/SideNav/SideNavCollapseButton.tsx +67 -15
- package/src/SideNav/SideNavCollapseContext.ts +29 -9
- package/src/SideNav/SideNavHeading.tsx +28 -10
- package/src/SideNav/SideNavItem.doc.mjs +13 -0
- package/src/SideNav/SideNavItem.tsx +92 -84
- package/src/SideNav/SideNavSection.tsx +6 -20
- package/src/SideNav/index.ts +2 -0
- package/src/Skeleton/Skeleton.tsx +0 -2
- package/src/Skeleton/index.ts +0 -2
- package/src/Slider/Slider.tsx +2 -11
- package/src/Switch/Switch.tsx +10 -2
- package/src/Table/Table.test.tsx +31 -0
- package/src/Table/TableRow.tsx +6 -0
- package/src/TimeInput/TimeInput.test.tsx +54 -0
- package/src/TimeInput/TimeInput.tsx +17 -2
- package/src/Timestamp/Timestamp.test.tsx +91 -14
- package/src/Timestamp/Timestamp.tsx +14 -2
- package/src/Timestamp/formatInstant.ts +16 -1
- package/src/TopNav/TopNavMegaMenu.doc.mjs +5 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +43 -0
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
- package/src/TopNav/TopNavMenu.test.tsx +41 -0
- package/src/TopNav/TopNavMenu.tsx +8 -2
- package/src/TreeList/TreeList.test.tsx +3 -2
- package/src/VisuallyHidden/VisuallyHidden.tsx +0 -2
- package/src/VisuallyHidden/index.ts +0 -2
- package/src/hooks/useInputStatusIcon.tsx +8 -8
- package/src/serverSafeComponents.test.ts +665 -0
- package/src/theme/defineTheme.ts +3 -0
- package/src/theme/generateThemeRules.test.ts +17 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/tokens.stylex.ts +18 -0
- package/src/theme/tokens.test.ts +37 -0
- package/src/utils/focusOutline.stylex.ts +37 -23
- package/src/utils/index.ts +4 -0
- package/src/utils/interactionModality.ts +74 -0
package/src/SideNav/SideNav.tsx
CHANGED
|
@@ -36,6 +36,7 @@ import {mergeProps, mergeRefs} from '../utils';
|
|
|
36
36
|
import {
|
|
37
37
|
SideNavCollapseContext,
|
|
38
38
|
type SideNavCollapseState,
|
|
39
|
+
type SideNavCollapsibleConfig,
|
|
39
40
|
type SideNavImperativeCollapseHandle,
|
|
40
41
|
} from './SideNavCollapseContext';
|
|
41
42
|
import {SideNavCollapseButton} from './SideNavCollapseButton';
|
|
@@ -45,6 +46,7 @@ import {useResizable} from '../Resizable/useResizable';
|
|
|
45
46
|
import type {ResizableConfig} from '../Resizable/useResizable';
|
|
46
47
|
import {ResizeHandle} from '../Resizable/ResizeHandle';
|
|
47
48
|
import {themeProps} from '../utils/themeProps';
|
|
49
|
+
import {SizeProvider} from '../SizeContext/SizeContext';
|
|
48
50
|
import {useTranslator} from '../i18n';
|
|
49
51
|
|
|
50
52
|
// =============================================================================
|
|
@@ -185,6 +187,13 @@ const styles = stylex.create({
|
|
|
185
187
|
},
|
|
186
188
|
});
|
|
187
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Cascaded to the icon rows through `SizeContext` so the built-in collapse
|
|
192
|
+
* button and the consumer's `footerIcons` come out one height. An explicit
|
|
193
|
+
* `size` on a child still wins.
|
|
194
|
+
*/
|
|
195
|
+
const FOOTER_ICON_SIZE = 'sm';
|
|
196
|
+
|
|
188
197
|
// =============================================================================
|
|
189
198
|
// Types
|
|
190
199
|
// =============================================================================
|
|
@@ -197,6 +206,9 @@ export interface SideNavProps extends BaseProps<HTMLElement> {
|
|
|
197
206
|
* Imperative collapse handle for SideNavCollapseButton instances rendered
|
|
198
207
|
* outside this SideNav. This intentionally stays separate from `ref`, which
|
|
199
208
|
* continues to expose the root HTMLElement.
|
|
209
|
+
*
|
|
210
|
+
* @deprecated Hand the same controlled `collapsible` config to SideNav and
|
|
211
|
+
* to the outside button instead.
|
|
200
212
|
*/
|
|
201
213
|
handleRef?: React.Ref<SideNavImperativeCollapseHandle>;
|
|
202
214
|
|
|
@@ -270,21 +282,14 @@ export interface SideNavProps extends BaseProps<HTMLElement> {
|
|
|
270
282
|
* - `true` — enables collapse with default toggle button and uncontrolled state
|
|
271
283
|
* - Object — enables collapse with advanced configuration:
|
|
272
284
|
* - `defaultIsCollapsed` — start collapsed (uncontrolled)
|
|
273
|
-
* - `isCollapsed` + `onCollapsedChange` — controlled mode
|
|
285
|
+
* - `isCollapsed` + `onCollapsedChange` — controlled mode. Pass the same
|
|
286
|
+
* object to a `SideNavCollapseButton` rendered outside this SideNav
|
|
274
287
|
* - `hasButton` — render built-in collapse button (default: true)
|
|
275
288
|
* - `buttonLabel` — accessibility label for the collapse button
|
|
276
289
|
*
|
|
277
290
|
* @default false
|
|
278
291
|
*/
|
|
279
|
-
collapsible?:
|
|
280
|
-
| boolean
|
|
281
|
-
| {
|
|
282
|
-
defaultIsCollapsed?: boolean;
|
|
283
|
-
isCollapsed?: boolean;
|
|
284
|
-
onCollapsedChange?: (isCollapsed: boolean) => void;
|
|
285
|
-
hasButton?: boolean;
|
|
286
|
-
buttonLabel?: string;
|
|
287
|
-
};
|
|
292
|
+
collapsible?: boolean | SideNavCollapsibleConfig;
|
|
288
293
|
}
|
|
289
294
|
|
|
290
295
|
// =============================================================================
|
|
@@ -375,6 +380,8 @@ export function SideNav({
|
|
|
375
380
|
const toggle = useCallback(() => {
|
|
376
381
|
const next = !collapsed;
|
|
377
382
|
|
|
383
|
+
// Deprecated `handleRef` path only: an out-of-tree button reads this
|
|
384
|
+
// snapshot while rendering, which can happen before SideNav re-renders.
|
|
378
385
|
collapseStateRef.current = {
|
|
379
386
|
...collapseStateRef.current,
|
|
380
387
|
isCollapsed: next,
|
|
@@ -429,7 +436,9 @@ export function SideNav({
|
|
|
429
436
|
style,
|
|
430
437
|
)}>
|
|
431
438
|
{header}
|
|
432
|
-
<div {...stylex.props(styles.topbarIcons)}>
|
|
439
|
+
<div {...stylex.props(styles.topbarIcons)}>
|
|
440
|
+
<SizeProvider value={FOOTER_ICON_SIZE}>{footerIcons}</SizeProvider>
|
|
441
|
+
</div>
|
|
433
442
|
</div>
|
|
434
443
|
);
|
|
435
444
|
}
|
|
@@ -441,7 +450,13 @@ export function SideNav({
|
|
|
441
450
|
|
|
442
451
|
if (renderMode === 'drawer') {
|
|
443
452
|
return (
|
|
444
|
-
<MobileNav
|
|
453
|
+
<MobileNav
|
|
454
|
+
header={header}
|
|
455
|
+
data-testid={testId}
|
|
456
|
+
xstyle={xstyle}
|
|
457
|
+
className={className}
|
|
458
|
+
style={style}
|
|
459
|
+
{...props}>
|
|
445
460
|
{topContent}
|
|
446
461
|
{children}
|
|
447
462
|
{hasDrawerFooter && (
|
|
@@ -449,7 +464,9 @@ export function SideNav({
|
|
|
449
464
|
{footer}
|
|
450
465
|
{footerIcons && (
|
|
451
466
|
<div {...stylex.props(styles.drawerFooterIcons)}>
|
|
452
|
-
{
|
|
467
|
+
<SizeProvider value={FOOTER_ICON_SIZE}>
|
|
468
|
+
{footerIcons}
|
|
469
|
+
</SizeProvider>
|
|
453
470
|
</div>
|
|
454
471
|
)}
|
|
455
472
|
</div>
|
|
@@ -472,7 +489,9 @@ export function SideNav({
|
|
|
472
489
|
{footer}
|
|
473
490
|
{footerIcons && (
|
|
474
491
|
<div {...stylex.props(styles.drawerFooterIcons)}>
|
|
475
|
-
{
|
|
492
|
+
<SizeProvider value={FOOTER_ICON_SIZE}>
|
|
493
|
+
{footerIcons}
|
|
494
|
+
</SizeProvider>
|
|
476
495
|
</div>
|
|
477
496
|
)}
|
|
478
497
|
</div>
|
|
@@ -544,8 +563,10 @@ export function SideNav({
|
|
|
544
563
|
styles.footerRow,
|
|
545
564
|
collapsed && styles.footerRowCollapsed,
|
|
546
565
|
)}>
|
|
547
|
-
|
|
548
|
-
|
|
566
|
+
<SizeProvider value={FOOTER_ICON_SIZE}>
|
|
567
|
+
{showCollapseButton && <SideNavCollapseButton />}
|
|
568
|
+
{footerIcons}
|
|
569
|
+
</SizeProvider>
|
|
549
570
|
</div>
|
|
550
571
|
</div>
|
|
551
572
|
)}
|
|
@@ -7,18 +7,28 @@ export const docs = {
|
|
|
7
7
|
subComponentOf: 'SideNav',
|
|
8
8
|
displayName: 'Side Nav Collapse Button',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
|
-
description: 'Toggle button for sidenav collapse. Place inside SideNav (reads context automatically) or outside (
|
|
10
|
+
description: 'Toggle button for sidenav collapse. Place inside SideNav (reads context automatically) or outside it (hand the same controlled collapsible config to both). Renders as an icon-only ghost button by default.',
|
|
11
11
|
props: [
|
|
12
|
+
{
|
|
13
|
+
name: 'collapsible',
|
|
14
|
+
type: '{isCollapsed: boolean, onCollapsedChange: (isCollapsed: boolean) => void}',
|
|
15
|
+
description: 'The same controlled collapsible config passed to SideNav. Only needed when the button is rendered outside the sidenav, where collapse context cannot reach it.',
|
|
16
|
+
},
|
|
12
17
|
{
|
|
13
18
|
name: 'handleRef',
|
|
14
19
|
type: 'RefObject<SideNavImperativeCollapseHandle | null>',
|
|
15
|
-
description: 'Imperative collapse handle from SideNav
|
|
20
|
+
description: 'Deprecated. Imperative collapse handle from SideNav; pass collapsible instead.',
|
|
16
21
|
},
|
|
17
22
|
{
|
|
18
23
|
name: 'label',
|
|
19
24
|
type: 'string',
|
|
20
25
|
description: 'Custom button label. When provided, renders as a text button with chevron. When omitted, renders icon-only.',
|
|
21
26
|
},
|
|
27
|
+
{
|
|
28
|
+
name: 'size',
|
|
29
|
+
type: "'sm' | 'md' | 'lg'",
|
|
30
|
+
description: "Button size. Defaults to the size its container cascades — 'sm' inside a SideNav footer — and to 'md' with no container. Set it when the button sits outside a sized container and has to match its neighbours.",
|
|
31
|
+
},
|
|
22
32
|
{
|
|
23
33
|
name: 'children',
|
|
24
34
|
type: 'ReactNode',
|
|
@@ -31,18 +41,28 @@ export const docsZh = {
|
|
|
31
41
|
name: 'SideNavCollapseButton',
|
|
32
42
|
isHiddenFromOverview: true,
|
|
33
43
|
displayName: 'Side Nav Collapse Button',
|
|
34
|
-
description: '侧边栏折叠切换按钮。放置在 SideNav
|
|
44
|
+
description: '侧边栏折叠切换按钮。放置在 SideNav 内部(自动读取上下文)或外部(向两者传入同一个受控 collapsible 配置)。默认渲染为仅图标的 ghost 按钮。',
|
|
35
45
|
props: [
|
|
46
|
+
{
|
|
47
|
+
name: 'collapsible',
|
|
48
|
+
type: '{isCollapsed: boolean, onCollapsedChange: (isCollapsed: boolean) => void}',
|
|
49
|
+
description: '与 SideNav 相同的受控 collapsible 配置。仅在按钮渲染在侧边栏外部、无法读取折叠上下文时需要。',
|
|
50
|
+
},
|
|
36
51
|
{
|
|
37
52
|
name: 'handleRef',
|
|
38
53
|
type: 'RefObject<SideNavImperativeCollapseHandle | null>',
|
|
39
|
-
description: '
|
|
54
|
+
description: '已废弃。来自 SideNav 的命令式折叠句柄;请改用 collapsible。',
|
|
40
55
|
},
|
|
41
56
|
{
|
|
42
57
|
name: 'label',
|
|
43
58
|
type: 'string',
|
|
44
59
|
description: '自定义按钮标签。提供时渲染为带箭头的文本按钮。省略时渲染为仅图标按钮。',
|
|
45
60
|
},
|
|
61
|
+
{
|
|
62
|
+
name: 'size',
|
|
63
|
+
type: "'sm' | 'md' | 'lg'",
|
|
64
|
+
description: "按钮尺寸。默认使用容器级联的尺寸(SideNav 页脚内为 'sm'),无容器时为 'md'。当按钮位于已设定尺寸的容器之外且需要与相邻元素保持一致时显式设置。",
|
|
65
|
+
},
|
|
46
66
|
{
|
|
47
67
|
name: 'children',
|
|
48
68
|
type: 'ReactNode',
|
|
@@ -55,10 +75,12 @@ export const docsDense = {
|
|
|
55
75
|
name: 'SideNavCollapseButton',
|
|
56
76
|
isHiddenFromOverview: true,
|
|
57
77
|
displayName: 'Side Nav Collapse Button',
|
|
58
|
-
description: 'Toggle button for sidenav collapse. Place inside SideNav (reads context) or outside (pass
|
|
78
|
+
description: 'Toggle button for sidenav collapse. Place inside SideNav (reads context) or outside (pass the same controlled collapsible config both get). Icon-only ghost button by default.',
|
|
59
79
|
propDescriptions: {
|
|
60
|
-
|
|
80
|
+
collapsible: 'Controlled collapsible config, same object SideNav gets. Only needed when button rendered outside sidenav.',
|
|
81
|
+
handleRef: 'Deprecated — pass collapsible instead.',
|
|
61
82
|
label: 'Custom label. Text button w/ chevron when provided, icon-only when omitted.',
|
|
83
|
+
size: "Button size. Inherits from the container ('sm' in a SideNav footer), else 'md'.",
|
|
62
84
|
children: 'Custom content. Overrides default chevron icon + label.',
|
|
63
85
|
},
|
|
64
86
|
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @position Composable toggle button for sidenav collapse
|
|
10
10
|
*
|
|
11
11
|
* Place inside SideNav (reads context automatically) or outside
|
|
12
|
-
* (pass
|
|
12
|
+
* (pass the same controlled `collapsible` config both get).
|
|
13
13
|
*
|
|
14
14
|
* SYNC: When modified, update:
|
|
15
15
|
* - /packages/core/src/SideNav/SideNav.doc.mjs
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - /packages/cli/assets/templates/blocks/components/SideNav/ (showcase blocks)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import React, {type ReactNode} from 'react';
|
|
20
|
+
import React, {useCallback, type ReactNode} from 'react';
|
|
21
21
|
import * as stylex from '@stylexjs/stylex';
|
|
22
22
|
import {durationVars, easeVars} from '../theme/tokens.stylex';
|
|
23
23
|
import {Icon} from '../Icon';
|
|
@@ -27,21 +27,34 @@ import {composeEventHandlers, rtlStyles} from '../utils';
|
|
|
27
27
|
import {
|
|
28
28
|
useSideNavCollapse,
|
|
29
29
|
type SideNavCollapseState,
|
|
30
|
+
type SideNavControlledCollapsible,
|
|
30
31
|
type SideNavImperativeCollapseHandle,
|
|
31
32
|
} from './SideNavCollapseContext';
|
|
32
33
|
import {useAppShellMobile} from '../AppShell/AppShellMobileContext';
|
|
33
34
|
import {useTranslator} from '../i18n';
|
|
35
|
+
import type {ElementSize} from '../SizeContext/SizeContext';
|
|
34
36
|
|
|
35
37
|
// =============================================================================
|
|
36
38
|
// Styles
|
|
37
39
|
// =============================================================================
|
|
38
40
|
|
|
39
41
|
const styles = stylex.create({
|
|
42
|
+
// A flex container, so the glyph is a flex item. Left to blockify as a flex
|
|
43
|
+
// item of Button's icon slot, this span gets a line box and seats the
|
|
44
|
+
// chevron on its text baseline — 2.42px above the button's centre.
|
|
45
|
+
chevronMirror: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
},
|
|
40
50
|
chevron: {
|
|
41
51
|
display: 'inline-flex',
|
|
42
52
|
alignItems: 'center',
|
|
43
53
|
transitionProperty: 'transform',
|
|
44
|
-
transitionDuration:
|
|
54
|
+
transitionDuration: {
|
|
55
|
+
default: durationVars['--duration-fast'],
|
|
56
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
57
|
+
},
|
|
45
58
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
46
59
|
},
|
|
47
60
|
chevronCollapsed: {
|
|
@@ -56,8 +69,16 @@ const styles = stylex.create({
|
|
|
56
69
|
export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement> {
|
|
57
70
|
ref?: React.Ref<HTMLButtonElement>;
|
|
58
71
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
72
|
+
* The same controlled `collapsible` config given to SideNav
|
|
73
|
+
* (`{isCollapsed, onCollapsedChange}`). Needed only when the button is
|
|
74
|
+
* rendered outside the sidenav, where collapse context cannot reach it.
|
|
75
|
+
*/
|
|
76
|
+
collapsible?: SideNavControlledCollapsible;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Imperative handle from SideNav.
|
|
80
|
+
*
|
|
81
|
+
* @deprecated Pass `collapsible` instead.
|
|
61
82
|
*/
|
|
62
83
|
handleRef?: React.RefObject<SideNavImperativeCollapseHandle | null>;
|
|
63
84
|
|
|
@@ -67,6 +88,13 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
|
|
|
67
88
|
*/
|
|
68
89
|
label?: string;
|
|
69
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Button size. Defaults to the size its container cascades — `sm` in a
|
|
93
|
+
* SideNav footer — or `md` outside one. Set it for placements with no row
|
|
94
|
+
* to inherit from, e.g. a button placed in a `TopNav`.
|
|
95
|
+
*/
|
|
96
|
+
size?: ElementSize;
|
|
97
|
+
|
|
70
98
|
/**
|
|
71
99
|
* Custom button content. Overrides the default chevron icon and label.
|
|
72
100
|
*/
|
|
@@ -82,7 +110,8 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
|
|
|
82
110
|
*
|
|
83
111
|
* Place anywhere inside SideNav (header, topContent, footer, footerIcons)
|
|
84
112
|
* and it reads collapse state from context automatically. For placement
|
|
85
|
-
* outside the sidenav (e.g. in TopNav or content area),
|
|
113
|
+
* outside the sidenav (e.g. in TopNav or content area), hold the state and
|
|
114
|
+
* hand the same `collapsible` config to both.
|
|
86
115
|
*
|
|
87
116
|
* @example
|
|
88
117
|
* ```
|
|
@@ -93,22 +122,27 @@ export interface SideNavCollapseButtonProps extends BaseProps<HTMLButtonElement>
|
|
|
93
122
|
*
|
|
94
123
|
* @example
|
|
95
124
|
* ```
|
|
96
|
-
* const
|
|
97
|
-
*
|
|
98
|
-
* <
|
|
125
|
+
* const [isCollapsed, setIsCollapsed] = useState(false);
|
|
126
|
+
* const collapsible = {isCollapsed, onCollapsedChange: setIsCollapsed};
|
|
127
|
+
* <TopNav endContent={<SideNavCollapseButton collapsible={collapsible} />} />
|
|
128
|
+
* <SideNav collapsible={{...collapsible, hasButton: false}}>...</SideNav>
|
|
99
129
|
* ```
|
|
100
130
|
*/
|
|
101
131
|
export function SideNavCollapseButton({
|
|
102
132
|
ref,
|
|
133
|
+
collapsible,
|
|
103
134
|
handleRef,
|
|
104
135
|
label,
|
|
136
|
+
size,
|
|
105
137
|
children,
|
|
106
138
|
onClick: onClickProp,
|
|
107
139
|
...props
|
|
108
140
|
}: SideNavCollapseButtonProps) {
|
|
109
141
|
const t = useTranslator();
|
|
110
|
-
const {isCollapsed, toggle, isCollapsible} =
|
|
111
|
-
|
|
142
|
+
const {isCollapsed, toggle, isCollapsible} = useSideNavCollapseState(
|
|
143
|
+
collapsible,
|
|
144
|
+
handleRef,
|
|
145
|
+
);
|
|
112
146
|
const {isMobile} = useAppShellMobile();
|
|
113
147
|
|
|
114
148
|
// Hide when not collapsible, or when in mobile mode (sidenav is in
|
|
@@ -127,6 +161,7 @@ export function SideNavCollapseButton({
|
|
|
127
161
|
: t('@astryx.sideNavCollapseButton.collapseSidebar'))
|
|
128
162
|
}
|
|
129
163
|
variant="ghost"
|
|
164
|
+
size={size}
|
|
130
165
|
{...props}
|
|
131
166
|
onClick={composeEventHandlers(onClickProp, toggle)}
|
|
132
167
|
icon={
|
|
@@ -135,7 +170,7 @@ export function SideNavCollapseButton({
|
|
|
135
170
|
// into) the state rotation: both are `transform`, so on a single
|
|
136
171
|
// element one would overwrite the other and the chevron would stop
|
|
137
172
|
// mirroring under RTL. See utils/rtlStyles.ts.
|
|
138
|
-
<span {...stylex.props(rtlStyles.mirror)}>
|
|
173
|
+
<span {...stylex.props(styles.chevronMirror, rtlStyles.mirror)}>
|
|
139
174
|
{/* `sm` (1rem) matches what this glyph already renders at: Button's
|
|
140
175
|
icon slot pins its wrapper to 16px, and the registry SVG is
|
|
141
176
|
1em, so the chevron is 16px today. */}
|
|
@@ -156,11 +191,30 @@ export function SideNavCollapseButton({
|
|
|
156
191
|
SideNavCollapseButton.displayName = 'SideNavCollapseButton';
|
|
157
192
|
|
|
158
193
|
function useSideNavCollapseState(
|
|
194
|
+
collapsible: SideNavControlledCollapsible | undefined,
|
|
159
195
|
handleRef:
|
|
160
196
|
React.RefObject<SideNavImperativeCollapseHandle | null> | undefined,
|
|
161
197
|
): SideNavCollapseState {
|
|
162
198
|
const contextCollapseState = useSideNavCollapse();
|
|
163
199
|
|
|
200
|
+
const {isCollapsed, onCollapsedChange} = collapsible ?? {};
|
|
201
|
+
const toggleCollapsible = useCallback(
|
|
202
|
+
() => onCollapsedChange?.(!isCollapsed),
|
|
203
|
+
[isCollapsed, onCollapsedChange],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const toggleHandle = useCallback(() => {
|
|
207
|
+
handleRef?.current?.getCollapseState()?.toggle();
|
|
208
|
+
}, [handleRef]);
|
|
209
|
+
|
|
210
|
+
if (collapsible != null) {
|
|
211
|
+
return {
|
|
212
|
+
isCollapsed: isCollapsed ?? false,
|
|
213
|
+
toggle: toggleCollapsible,
|
|
214
|
+
isCollapsible: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
164
218
|
if (handleRef == null) {
|
|
165
219
|
return contextCollapseState;
|
|
166
220
|
}
|
|
@@ -169,9 +223,7 @@ function useSideNavCollapseState(
|
|
|
169
223
|
|
|
170
224
|
return {
|
|
171
225
|
isCollapsed: externalCollapseState?.isCollapsed ?? false,
|
|
172
|
-
toggle:
|
|
173
|
-
handleRef.current?.getCollapseState()?.toggle();
|
|
174
|
-
},
|
|
226
|
+
toggle: toggleHandle,
|
|
175
227
|
isCollapsible: externalCollapseState?.isCollapsible ?? true,
|
|
176
228
|
};
|
|
177
229
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Provides collapse state to SideNavCollapseButton and other
|
|
12
12
|
* sidenav children. Set by SideNav when isCollapsible is true.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* A button rendered outside the SideNav tree is out of context's reach and
|
|
14
|
+
* takes the controlled `collapsible` config as a prop instead.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import {createContext, use} from 'react';
|
|
@@ -25,18 +25,38 @@ export interface SideNavCollapseState {
|
|
|
25
25
|
isCollapsible: boolean;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/** Object form of SideNav's `collapsible` prop. */
|
|
29
|
+
export interface SideNavCollapsibleConfig {
|
|
30
|
+
defaultIsCollapsed?: boolean;
|
|
31
|
+
isCollapsed?: boolean;
|
|
32
|
+
onCollapsedChange?: (isCollapsed: boolean) => void;
|
|
33
|
+
hasButton?: boolean;
|
|
34
|
+
buttonLabel?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The controlled form: the consumer holds the state, so it can be handed to
|
|
39
|
+
* both SideNav and a SideNavCollapseButton rendered outside it.
|
|
40
|
+
*/
|
|
41
|
+
export interface SideNavControlledCollapsible extends SideNavCollapsibleConfig {
|
|
42
|
+
isCollapsed: boolean;
|
|
43
|
+
onCollapsedChange: (isCollapsed: boolean) => void;
|
|
44
|
+
}
|
|
28
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated Pass the same controlled `collapsible` config to SideNav and to
|
|
48
|
+
* the out-of-tree SideNavCollapseButton instead. The state then reaches the
|
|
49
|
+
* button through props rather than through a ref.
|
|
50
|
+
*/
|
|
29
51
|
export interface SideNavImperativeCollapseHandle {
|
|
30
52
|
getCollapseState: () => SideNavCollapseState | null;
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
export const SideNavCollapseContext = createContext<SideNavCollapseState>(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
);
|
|
55
|
+
export const SideNavCollapseContext = createContext<SideNavCollapseState>({
|
|
56
|
+
isCollapsed: false,
|
|
57
|
+
toggle: () => {},
|
|
58
|
+
isCollapsible: false,
|
|
59
|
+
});
|
|
40
60
|
SideNavCollapseContext.displayName = 'SideNavCollapseContext';
|
|
41
61
|
|
|
42
62
|
/**
|
|
@@ -36,6 +36,7 @@ import {Link} from '../Link';
|
|
|
36
36
|
import {Icon} from '../Icon';
|
|
37
37
|
import {Tooltip} from '../Tooltip';
|
|
38
38
|
import {navItemStyles} from '../NavItem/navItemStyles.stylex';
|
|
39
|
+
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
39
40
|
import {useSideNavCollapse} from './SideNavCollapseContext';
|
|
40
41
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
41
42
|
import type {LinkComponentType} from '../Link/types';
|
|
@@ -415,7 +416,11 @@ export function SideNavHeading({
|
|
|
415
416
|
data-testid={testId}
|
|
416
417
|
{...mergeProps(
|
|
417
418
|
themeProps('side-nav-heading'),
|
|
418
|
-
|
|
419
|
+
focusOutlineProps.focusVisible(
|
|
420
|
+
navItemStyles.item,
|
|
421
|
+
styles.rootCollapsed,
|
|
422
|
+
xstyle,
|
|
423
|
+
),
|
|
419
424
|
className,
|
|
420
425
|
style,
|
|
421
426
|
)}>
|
|
@@ -434,7 +439,7 @@ export function SideNavHeading({
|
|
|
434
439
|
{...triggerProps}
|
|
435
440
|
{...mergeProps(
|
|
436
441
|
themeProps('side-nav-heading'),
|
|
437
|
-
|
|
442
|
+
focusOutlineProps.focusVisible(
|
|
438
443
|
navItemStyles.item,
|
|
439
444
|
styles.rootCollapsed,
|
|
440
445
|
styles.menuTrigger,
|
|
@@ -452,7 +457,7 @@ export function SideNavHeading({
|
|
|
452
457
|
{...contentProps}>
|
|
453
458
|
<button
|
|
454
459
|
type="button"
|
|
455
|
-
{...
|
|
460
|
+
{...focusOutlineProps.focusVisible(styles.popoverHeading)}
|
|
456
461
|
onClick={triggerProps.onClick}>
|
|
457
462
|
{icon && <span {...stylex.props(styles.icon)}>{icon}</span>}
|
|
458
463
|
<span {...stylex.props(styles.textContainer)}>
|
|
@@ -549,7 +554,10 @@ export function SideNavHeading({
|
|
|
549
554
|
{hasAnyHref && headingHref && menu ? (
|
|
550
555
|
<LinkComponent
|
|
551
556
|
href={headingHref}
|
|
552
|
-
{...
|
|
557
|
+
{...focusOutlineProps.focusVisible(
|
|
558
|
+
styles.heading,
|
|
559
|
+
styles.headingLink,
|
|
560
|
+
)}>
|
|
553
561
|
{heading}
|
|
554
562
|
</LinkComponent>
|
|
555
563
|
) : (
|
|
@@ -586,7 +594,7 @@ export function SideNavHeading({
|
|
|
586
594
|
const popoverHeadingContent = (
|
|
587
595
|
<button
|
|
588
596
|
type="button"
|
|
589
|
-
{...
|
|
597
|
+
{...focusOutlineProps.focusVisible(styles.popoverHeading)}
|
|
590
598
|
onClick={triggerProps.onClick}>
|
|
591
599
|
{icon && <span {...stylex.props(styles.icon)}>{icon}</span>}
|
|
592
600
|
{renderTextContent(
|
|
@@ -609,7 +617,11 @@ export function SideNavHeading({
|
|
|
609
617
|
data-testid={testId}
|
|
610
618
|
{...mergeProps(
|
|
611
619
|
themeProps('side-nav-heading'),
|
|
612
|
-
|
|
620
|
+
focusOutlineProps.focusVisible(
|
|
621
|
+
styles.root,
|
|
622
|
+
styles.menuTrigger,
|
|
623
|
+
xstyle,
|
|
624
|
+
),
|
|
613
625
|
className,
|
|
614
626
|
style,
|
|
615
627
|
)}
|
|
@@ -646,7 +658,10 @@ export function SideNavHeading({
|
|
|
646
658
|
triggerProps.onClick();
|
|
647
659
|
}}
|
|
648
660
|
{...popover.triggerProps}
|
|
649
|
-
{...
|
|
661
|
+
{...focusOutlineProps.focusVisible(
|
|
662
|
+
styles.chevron,
|
|
663
|
+
styles.interactive,
|
|
664
|
+
)}>
|
|
650
665
|
<Icon
|
|
651
666
|
icon="chevronDown"
|
|
652
667
|
size="sm"
|
|
@@ -701,7 +716,7 @@ export function SideNavHeading({
|
|
|
701
716
|
<LinkComponent
|
|
702
717
|
href={headingHref}
|
|
703
718
|
aria-label={heading}
|
|
704
|
-
{...
|
|
719
|
+
{...focusOutlineProps.focusVisible(styles.icon)}>
|
|
705
720
|
{icon}
|
|
706
721
|
</LinkComponent>
|
|
707
722
|
) : (
|
|
@@ -717,7 +732,10 @@ export function SideNavHeading({
|
|
|
717
732
|
triggerProps.onClick();
|
|
718
733
|
}}
|
|
719
734
|
{...popover.triggerProps}
|
|
720
|
-
{...
|
|
735
|
+
{...focusOutlineProps.focusVisible(
|
|
736
|
+
styles.chevron,
|
|
737
|
+
styles.interactive,
|
|
738
|
+
)}>
|
|
721
739
|
<Icon
|
|
722
740
|
icon="chevronDown"
|
|
723
741
|
size="sm"
|
|
@@ -770,7 +788,7 @@ export function SideNavHeading({
|
|
|
770
788
|
<LinkComponent
|
|
771
789
|
href={headingHref}
|
|
772
790
|
aria-label={heading}
|
|
773
|
-
{...
|
|
791
|
+
{...focusOutlineProps.focusVisible(styles.icon)}>
|
|
774
792
|
{icon}
|
|
775
793
|
</LinkComponent>
|
|
776
794
|
) : (
|
|
@@ -91,6 +91,12 @@ export const docs = {
|
|
|
91
91
|
description: 'Enables collapse behavior for items with children. Pass true for uncontrolled (starts expanded), or an object for controlled mode.',
|
|
92
92
|
default: 'false',
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
name: 'size',
|
|
96
|
+
type: "'sm' | 'md' | 'lg'",
|
|
97
|
+
description: 'Size variant for the nav item row.',
|
|
98
|
+
default: "'md'",
|
|
99
|
+
},
|
|
94
100
|
],
|
|
95
101
|
};
|
|
96
102
|
|
|
@@ -159,6 +165,12 @@ export const docsZh = {
|
|
|
159
165
|
description: '启用带子项的折叠行为。传 true 为非受控模式(默认展开),或传对象用于受控模式。',
|
|
160
166
|
default: 'false',
|
|
161
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: 'size',
|
|
170
|
+
type: "'sm' | 'md' | 'lg'",
|
|
171
|
+
description: '导航项的尺寸变体。',
|
|
172
|
+
default: "'md'",
|
|
173
|
+
},
|
|
162
174
|
],
|
|
163
175
|
};
|
|
164
176
|
|
|
@@ -179,5 +191,6 @@ export const docsDense = {
|
|
|
179
191
|
endContent: 'Right-side content such as badges or counts.',
|
|
180
192
|
children: 'Sub-items for nesting.',
|
|
181
193
|
collapsible: 'Enables collapse for items w/ children. true=uncontrolled, object=controlled mode.',
|
|
194
|
+
size: "Row size variant: 'sm' | 'md' | 'lg'.",
|
|
182
195
|
},
|
|
183
196
|
};
|