@cdx-ui/components 0.2.2 → 0.2.4
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/AGENTS.md +24 -0
- package/README.md +6 -2
- package/coverage.svg +1 -1
- package/docs/components/accordion/content.md +538 -0
- package/docs/components/alert/content.md +203 -0
- package/docs/components/alert-dialog/content.md +319 -0
- package/docs/components/avatar/content.md +368 -0
- package/docs/components/avatar/design.md +202 -0
- package/docs/components/badge/content.md +235 -0
- package/docs/components/bottom-sheet/content.md +351 -0
- package/docs/components/bottom-sheet/design.md +249 -0
- package/docs/components/breadcrumbs/content.md +169 -0
- package/docs/components/button/content.md +398 -0
- package/docs/components/button/design.md +258 -0
- package/docs/components/calendar/content.md +137 -0
- package/docs/components/card/content.md +283 -0
- package/docs/components/carousel/content.md +416 -0
- package/docs/components/checkbox/content.md +236 -0
- package/docs/components/checkbox/design.md +214 -0
- package/docs/components/chip/content.md +439 -0
- package/docs/components/chip/design.md +418 -0
- package/docs/components/color-area/content.md +86 -0
- package/docs/components/color-field/content.md +134 -0
- package/docs/components/color-picker/content.md +208 -0
- package/docs/components/color-slider/content.md +120 -0
- package/docs/components/color-swatch/content.md +64 -0
- package/docs/components/color-swatch-picker/content.md +126 -0
- package/docs/components/content.md +97 -0
- package/docs/components/currency-input/content.md +205 -0
- package/docs/components/data-grid/content.md +522 -0
- package/docs/components/date-field/content.md +147 -0
- package/docs/components/date-picker/content.md +360 -0
- package/docs/components/date-range-picker/content.md +301 -0
- package/docs/components/date-time-field/content.md +157 -0
- package/docs/components/date-time-list/content.md +105 -0
- package/docs/components/date-time-picker/content.md +272 -0
- package/docs/components/dialog/content.md +436 -0
- package/docs/components/dialog/design.md +192 -0
- package/docs/components/divider/content.md +112 -0
- package/docs/components/drawer/content.md +436 -0
- package/docs/components/field/content.md +277 -0
- package/docs/components/form/content.md +62 -0
- package/docs/components/full-screen-dialog/content.md +881 -0
- package/docs/components/heading/content.md +55 -0
- package/docs/components/icon/content.md +190 -0
- package/docs/components/icon-button/content.md +200 -0
- package/docs/components/icon-button/design.md +165 -0
- package/docs/components/image/content.md +104 -0
- package/docs/components/input/content.md +281 -0
- package/docs/components/input/design.md +389 -0
- package/docs/components/insurance-disclosure/content.md +103 -0
- package/docs/components/link/content.md +180 -0
- package/docs/components/link/design.md +160 -0
- package/docs/components/list-item/content.md +695 -0
- package/docs/components/menu/content.md +1247 -0
- package/docs/components/month-calendar/content.md +84 -0
- package/docs/components/month-picker/content.md +273 -0
- package/docs/components/otp-input/content.md +327 -0
- package/docs/components/progress-bar/content.md +87 -0
- package/docs/components/progress-circle/content.md +176 -0
- package/docs/components/progress-segmented/content.md +119 -0
- package/docs/components/quick-action-button/content.md +154 -0
- package/docs/components/quick-action-button/design.md +170 -0
- package/docs/components/radio/content.md +224 -0
- package/docs/components/range-calendar/content.md +123 -0
- package/docs/components/reorderable-list/content.md +309 -0
- package/docs/components/scoped-theme/content.md +47 -0
- package/docs/components/scroll-shadow/content.md +686 -0
- package/docs/components/select/content.md +400 -0
- package/docs/components/select/design.md +301 -0
- package/docs/components/skeleton/content.md +103 -0
- package/docs/components/slider/content.md +423 -0
- package/docs/components/stack/content.md +161 -0
- package/docs/components/switch/content.md +150 -0
- package/docs/components/switch/design.md +208 -0
- package/docs/components/table/content.md +548 -0
- package/docs/components/table-pagination/content.md +177 -0
- package/docs/components/tabs/content.md +292 -0
- package/docs/components/text/content.md +142 -0
- package/docs/components/textarea/content.md +222 -0
- package/docs/components/tile/content.md +703 -0
- package/docs/components/toast/content.md +456 -0
- package/docs/components/toggle-button/content.md +505 -0
- package/docs/components/tooltip/content.md +350 -0
- package/docs/components/virtualized-list/content.md +250 -0
- package/docs/foundations/content.md +9 -0
- package/docs/foundations/expressions/icons/content.md +19 -0
- package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
- package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
- package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
- package/docs/getting-started/content.md +4 -0
- package/docs/getting-started/designers/content.md +13 -0
- package/docs/getting-started/designers/design.md +13 -0
- package/docs/getting-started/developers/accessibility/content.md +31 -0
- package/docs/getting-started/developers/content.md +612 -0
- package/docs/getting-started/developers/styling/content.md +193 -0
- package/docs/llms.txt +193 -0
- package/docs/patterns/account-management/content.md +75 -0
- package/docs/patterns/authentication/content.md +52 -0
- package/docs/patterns/content.md +10 -0
- package/docs/patterns/dashboard/content.md +39 -0
- package/docs/patterns/empty-states/content.md +31 -0
- package/docs/patterns/forms/content.md +109 -0
- package/docs/patterns/onboarding/content.md +69 -0
- package/docs/patterns/payments/content.md +35 -0
- package/docs/patterns/settings/content.md +72 -0
- package/docs/resources/content.md +9 -0
- package/docs/resources/figma-libraries/content.md +42 -0
- package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
- package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
- package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
- package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
- package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
- package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
- package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
- package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
- package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
- package/docs/resources/release-notes/0.1.1/content.md +23 -0
- package/docs/resources/release-notes/0.1.2/content.md +7 -0
- package/docs/resources/release-notes/0.1.4/content.md +19 -0
- package/docs/resources/release-notes/0.1.5/content.md +7 -0
- package/docs/resources/release-notes/0.1.6/content.md +7 -0
- package/docs/resources/release-notes/0.2.0/content.md +31 -0
- package/docs/resources/release-notes/0.2.1/content.md +17 -0
- package/docs/resources/release-notes/0.2.2/content.md +11 -0
- package/docs/resources/release-notes/0.2.3/content.md +15 -0
- package/docs/resources/release-notes/content.md +64 -0
- package/lib/commonjs/components/Checkbox/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/index.js +11 -26
- package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/styles.js +2 -12
- package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
- package/lib/commonjs/components/Heading/index.js.map +1 -1
- package/lib/commonjs/components/Icon/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/index.js +170 -9
- package/lib/commonjs/components/ListItem/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/styles.js +38 -2
- package/lib/commonjs/components/ListItem/styles.js.map +1 -1
- package/lib/commonjs/components/Menu/index.js +11 -5
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/RangeCalendar/index.js.map +1 -1
- package/lib/commonjs/components/ReorderableList/ReorderableListShared.js +91 -0
- package/lib/commonjs/components/ReorderableList/ReorderableListShared.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/index.js +135 -0
- package/lib/commonjs/components/ReorderableList/index.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/index.web.js +233 -0
- package/lib/commonjs/components/ReorderableList/index.web.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/labels.js +47 -0
- package/lib/commonjs/components/ReorderableList/labels.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/reorderItems.js +16 -0
- package/lib/commonjs/components/ReorderableList/reorderItems.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/rowContext.js +42 -0
- package/lib/commonjs/components/ReorderableList/rowContext.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/types.js +6 -0
- package/lib/commonjs/components/ReorderableList/types.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/useReorder.js +131 -0
- package/lib/commonjs/components/ReorderableList/useReorder.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/webDnd.js +64 -0
- package/lib/commonjs/components/ReorderableList/webDnd.js.map +1 -0
- package/lib/commonjs/components/ScopedTheme/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
- package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/index.js +171 -44
- package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
- package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Skeleton/index.js.map +1 -1
- package/lib/commonjs/components/TablePagination/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/index.js +16 -23
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -10
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/components/Text/index.js.map +1 -1
- package/lib/commonjs/components/Tile/styles.js +1 -1
- package/lib/commonjs/components/Tile/styles.js.map +1 -1
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/commonjs/reorderable-list.js +17 -0
- package/lib/commonjs/reorderable-list.js.map +1 -0
- package/lib/module/components/Checkbox/index.js.map +1 -1
- package/lib/module/components/DateTimeList/index.js +12 -27
- package/lib/module/components/DateTimeList/index.js.map +1 -1
- package/lib/module/components/DateTimeList/styles.js +1 -11
- package/lib/module/components/DateTimeList/styles.js.map +1 -1
- package/lib/module/components/Heading/index.js.map +1 -1
- package/lib/module/components/Icon/index.js.map +1 -1
- package/lib/module/components/ListItem/index.js +163 -14
- package/lib/module/components/ListItem/index.js.map +1 -1
- package/lib/module/components/ListItem/styles.js +37 -1
- package/lib/module/components/ListItem/styles.js.map +1 -1
- package/lib/module/components/Menu/index.js +11 -5
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/RangeCalendar/index.js.map +1 -1
- package/lib/module/components/ReorderableList/ReorderableListShared.js +86 -0
- package/lib/module/components/ReorderableList/ReorderableListShared.js.map +1 -0
- package/lib/module/components/ReorderableList/index.js +130 -0
- package/lib/module/components/ReorderableList/index.js.map +1 -0
- package/lib/module/components/ReorderableList/index.web.js +229 -0
- package/lib/module/components/ReorderableList/index.web.js.map +1 -0
- package/lib/module/components/ReorderableList/labels.js +41 -0
- package/lib/module/components/ReorderableList/labels.js.map +1 -0
- package/lib/module/components/ReorderableList/reorderItems.js +12 -0
- package/lib/module/components/ReorderableList/reorderItems.js.map +1 -0
- package/lib/module/components/ReorderableList/rowContext.js +38 -0
- package/lib/module/components/ReorderableList/rowContext.js.map +1 -0
- package/lib/module/components/ReorderableList/types.js +4 -0
- package/lib/module/components/ReorderableList/types.js.map +1 -0
- package/lib/module/components/ReorderableList/useReorder.js +123 -0
- package/lib/module/components/ReorderableList/useReorder.js.map +1 -0
- package/lib/module/components/ReorderableList/webDnd.js +59 -0
- package/lib/module/components/ReorderableList/webDnd.js.map +1 -0
- package/lib/module/components/ScopedTheme/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/gradient.js +24 -0
- package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/module/components/ScrollShadow/index.js +170 -48
- package/lib/module/components/ScrollShadow/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/styles.js +57 -13
- package/lib/module/components/ScrollShadow/styles.js.map +1 -1
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Skeleton/index.js.map +1 -1
- package/lib/module/components/TablePagination/index.js.map +1 -1
- package/lib/module/components/Tabs/index.js +17 -24
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +0 -9
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/components/Text/index.js.map +1 -1
- package/lib/module/components/Tile/styles.js +1 -1
- package/lib/module/components/Tile/styles.js.map +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/module/reorderable-list.js +4 -0
- package/lib/module/reorderable-list.js.map +1 -0
- package/lib/typescript/components/Checkbox/index.d.ts +22 -0
- package/lib/typescript/components/Checkbox/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
- package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
- package/lib/typescript/components/Heading/index.d.ts +20 -2
- package/lib/typescript/components/Heading/index.d.ts.map +1 -1
- package/lib/typescript/components/Icon/index.d.ts +2 -0
- package/lib/typescript/components/Icon/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/index.d.ts +28 -5
- package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts +6 -1
- package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts +116 -4
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/RangeCalendar/index.d.ts +2 -0
- package/lib/typescript/components/RangeCalendar/index.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableList/ReorderableListShared.d.ts +31 -0
- package/lib/typescript/components/ReorderableList/ReorderableListShared.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/index.d.ts +8 -0
- package/lib/typescript/components/ReorderableList/index.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/index.web.d.ts +8 -0
- package/lib/typescript/components/ReorderableList/index.web.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/labels.d.ts +16 -0
- package/lib/typescript/components/ReorderableList/labels.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/reorderItems.d.ts +3 -0
- package/lib/typescript/components/ReorderableList/reorderItems.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/rowContext.d.ts +57 -0
- package/lib/typescript/components/ReorderableList/rowContext.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/types.d.ts +75 -0
- package/lib/typescript/components/ReorderableList/types.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/useReorder.d.ts +31 -0
- package/lib/typescript/components/ReorderableList/useReorder.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/webDnd.d.ts +27 -0
- package/lib/typescript/components/ReorderableList/webDnd.d.ts.map +1 -0
- package/lib/typescript/components/ScopedTheme/index.d.ts +6 -1
- package/lib/typescript/components/ScopedTheme/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
- package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
- package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
- package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Skeleton/index.d.ts +3 -0
- package/lib/typescript/components/Skeleton/index.d.ts.map +1 -1
- package/lib/typescript/components/TablePagination/index.d.ts +54 -0
- package/lib/typescript/components/TablePagination/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/styles.d.ts +0 -7
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
- package/lib/typescript/components/Text/index.d.ts +12 -2
- package/lib/typescript/components/Text/index.d.ts.map +1 -1
- package/lib/typescript/components/Tile/styles.d.ts.map +1 -1
- package/lib/typescript/components/ToggleButton/styles.d.ts +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
- package/lib/typescript/reorderable-list.d.ts +2 -0
- package/lib/typescript/reorderable-list.d.ts.map +1 -0
- package/package.json +37 -8
- package/src/components/Checkbox/index.tsx +22 -0
- package/src/components/DateTimeList/index.tsx +13 -26
- package/src/components/DateTimeList/styles.ts +1 -10
- package/src/components/Heading/index.tsx +10 -1
- package/src/components/Icon/index.tsx +2 -0
- package/src/components/ListItem/index.tsx +232 -15
- package/src/components/ListItem/styles.ts +41 -0
- package/src/components/Menu/index.tsx +128 -12
- package/src/components/RangeCalendar/index.tsx +2 -0
- package/src/components/ReorderableList/ReorderableListShared.tsx +113 -0
- package/src/components/ReorderableList/index.tsx +162 -0
- package/src/components/ReorderableList/index.web.tsx +254 -0
- package/src/components/ReorderableList/labels.ts +52 -0
- package/src/components/ReorderableList/reorderItems.ts +16 -0
- package/src/components/ReorderableList/rowContext.tsx +72 -0
- package/src/components/ReorderableList/types.ts +80 -0
- package/src/components/ReorderableList/useReorder.ts +159 -0
- package/src/components/ReorderableList/webDnd.ts +60 -0
- package/src/components/ScopedTheme/index.tsx +6 -1
- package/src/components/ScrollShadow/gradient.ts +48 -0
- package/src/components/ScrollShadow/index.tsx +300 -69
- package/src/components/ScrollShadow/styles.ts +45 -28
- package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
- package/src/components/Skeleton/index.tsx +3 -0
- package/src/components/TablePagination/index.tsx +54 -0
- package/src/components/Tabs/index.tsx +25 -35
- package/src/components/Tabs/styles.ts +0 -9
- package/src/components/Text/index.tsx +6 -1
- package/src/components/Tile/styles.ts +4 -1
- package/src/components/index.ts +5 -0
- package/src/reorderable-list.ts +1 -0
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
# Menu (https://design.candescent.com/docs/components/menu)
|
|
2
|
+
|
|
3
|
+
Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=7606-560), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/overlays-menu--default)
|
|
4
|
+
|
|
5
|
+
`Menu` combines an anchored overlay with a reusable `Menu.Content` list surface. The same items can be commands, checkbox or radio options, or custom layouts, either on the page or anchored to a trigger.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Menu } from '@cdx-ui/components';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Basic
|
|
12
|
+
|
|
13
|
+
Use `Menu.Item` for one-off commands that perform an action immediately and do not retain a checked state, such as opening a transfer flow or downloading a statement.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useState } from 'react';
|
|
17
|
+
import { Box, Button, Menu, Text } from '@cdx-ui/components';
|
|
18
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
19
|
+
|
|
20
|
+
export default function MenuBasicDemo() {
|
|
21
|
+
const [result, setResult] = useState('Choose an account action');
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Box className="items-start gap-2">
|
|
25
|
+
<Menu>
|
|
26
|
+
<Menu.Trigger asChild>
|
|
27
|
+
<Button color="action" variant="outline">
|
|
28
|
+
<Button.Label>Everyday Checking</Button.Label>
|
|
29
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
30
|
+
</Button>
|
|
31
|
+
</Menu.Trigger>
|
|
32
|
+
<Menu.Popup>
|
|
33
|
+
<Menu.Content accessibilityLabel="Everyday Checking actions">
|
|
34
|
+
<Menu.Item onPress={() => setResult('Opening account details')}>
|
|
35
|
+
<Menu.ItemLabel>View account details</Menu.ItemLabel>
|
|
36
|
+
</Menu.Item>
|
|
37
|
+
<Menu.Item onPress={() => setResult('Starting a transfer')}>
|
|
38
|
+
<Menu.ItemLabel>Transfer money</Menu.ItemLabel>
|
|
39
|
+
</Menu.Item>
|
|
40
|
+
<Menu.Item onPress={() => setResult('Preparing your latest statement')}>
|
|
41
|
+
<Menu.ItemLabel>Download statement</Menu.ItemLabel>
|
|
42
|
+
</Menu.Item>
|
|
43
|
+
<Menu.Separator />
|
|
44
|
+
<Menu.Item onPress={() => setResult('Opening alert preferences')}>
|
|
45
|
+
<Menu.ItemLabel>Manage alerts</Menu.ItemLabel>
|
|
46
|
+
</Menu.Item>
|
|
47
|
+
</Menu.Content>
|
|
48
|
+
</Menu.Popup>
|
|
49
|
+
</Menu>
|
|
50
|
+
<Text size="sm" color="secondary">
|
|
51
|
+
{result}
|
|
52
|
+
</Text>
|
|
53
|
+
</Box>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Checkbox items
|
|
59
|
+
|
|
60
|
+
Use `Menu.CheckboxItem` when each option can be enabled independently, and compose `Menu.ItemIndicator` for a trailing check. The menu stays open after each toggle.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { useState } from 'react';
|
|
64
|
+
import { Button, Menu } from '@cdx-ui/components';
|
|
65
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
66
|
+
|
|
67
|
+
export default function MenuCheckboxItemsDemo() {
|
|
68
|
+
const [showBalances, setShowBalances] = useState(true);
|
|
69
|
+
const [includePending, setIncludePending] = useState(false);
|
|
70
|
+
const [maskAccountNumbers, setMaskAccountNumbers] = useState(true);
|
|
71
|
+
const enabledCount = [showBalances, includePending, maskAccountNumbers].filter(Boolean).length;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Menu>
|
|
75
|
+
<Menu.Trigger asChild>
|
|
76
|
+
<Button color="action" variant="outline">
|
|
77
|
+
<Button.Label>Account view · {enabledCount} enabled</Button.Label>
|
|
78
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
79
|
+
</Button>
|
|
80
|
+
</Menu.Trigger>
|
|
81
|
+
<Menu.Popup>
|
|
82
|
+
<Menu.Content accessibilityLabel="Account display preferences">
|
|
83
|
+
<Menu.CheckboxItem isChecked={showBalances} onChange={setShowBalances}>
|
|
84
|
+
<Menu.ItemLabel>Show available balances</Menu.ItemLabel>
|
|
85
|
+
<Menu.ItemIndicator />
|
|
86
|
+
</Menu.CheckboxItem>
|
|
87
|
+
<Menu.CheckboxItem isChecked={includePending} onChange={setIncludePending}>
|
|
88
|
+
<Menu.ItemLabel>Include pending transactions</Menu.ItemLabel>
|
|
89
|
+
<Menu.ItemIndicator />
|
|
90
|
+
</Menu.CheckboxItem>
|
|
91
|
+
<Menu.CheckboxItem isChecked={maskAccountNumbers} onChange={setMaskAccountNumbers}>
|
|
92
|
+
<Menu.ItemLabel>Mask account numbers</Menu.ItemLabel>
|
|
93
|
+
<Menu.ItemIndicator />
|
|
94
|
+
</Menu.CheckboxItem>
|
|
95
|
+
</Menu.Content>
|
|
96
|
+
</Menu.Popup>
|
|
97
|
+
</Menu>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Radio items
|
|
103
|
+
|
|
104
|
+
Use `Menu.RadioGroup` and `Menu.RadioItem` when exactly one option may be active, and compose `Menu.ItemIndicator` to mark the current choice. The trigger reflects that choice after the menu closes.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { useState } from 'react';
|
|
108
|
+
import { Button, Menu } from '@cdx-ui/components';
|
|
109
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
110
|
+
|
|
111
|
+
const SORT_OPTIONS = [
|
|
112
|
+
{ value: 'newest', label: 'Newest transactions first' },
|
|
113
|
+
{ value: 'oldest', label: 'Oldest transactions first' },
|
|
114
|
+
{ value: 'highest', label: 'Highest amount first' },
|
|
115
|
+
] as const;
|
|
116
|
+
|
|
117
|
+
export default function MenuRadioItemsDemo() {
|
|
118
|
+
const [sortBy, setSortBy] = useState('newest');
|
|
119
|
+
const selectedLabel =
|
|
120
|
+
SORT_OPTIONS.find(({ value }) => value === sortBy)?.label ?? SORT_OPTIONS[0].label;
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Menu>
|
|
124
|
+
<Menu.Trigger asChild>
|
|
125
|
+
<Button color="action" variant="outline">
|
|
126
|
+
<Button.Label>{selectedLabel}</Button.Label>
|
|
127
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
128
|
+
</Button>
|
|
129
|
+
</Menu.Trigger>
|
|
130
|
+
<Menu.Popup>
|
|
131
|
+
<Menu.Content accessibilityLabel="Transaction sort order">
|
|
132
|
+
<Menu.RadioGroup value={sortBy} onChange={setSortBy}>
|
|
133
|
+
{SORT_OPTIONS.map(({ label, value }) => (
|
|
134
|
+
<Menu.RadioItem key={value} value={value}>
|
|
135
|
+
<Menu.ItemLabel>{label}</Menu.ItemLabel>
|
|
136
|
+
<Menu.ItemIndicator />
|
|
137
|
+
</Menu.RadioItem>
|
|
138
|
+
))}
|
|
139
|
+
</Menu.RadioGroup>
|
|
140
|
+
</Menu.Content>
|
|
141
|
+
</Menu.Popup>
|
|
142
|
+
</Menu>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## With icons
|
|
148
|
+
|
|
149
|
+
Render `Menu.Content` on its own when the list is already on the page. The same items work inside `Menu.Popup` when they should anchor to a trigger.
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
import { Menu } from '@cdx-ui/components';
|
|
153
|
+
import { ContentCopy, Edit, Person } from '@cdx-ui/icons';
|
|
154
|
+
|
|
155
|
+
export default function MenuWithIconsDemo() {
|
|
156
|
+
return (
|
|
157
|
+
<Menu>
|
|
158
|
+
<Menu.Content accessibilityLabel="Account menu">
|
|
159
|
+
<Menu.Item onPress={() => undefined}>
|
|
160
|
+
<Menu.ItemIcon as={Person} />
|
|
161
|
+
<Menu.ItemLabel>View profile</Menu.ItemLabel>
|
|
162
|
+
</Menu.Item>
|
|
163
|
+
<Menu.Item onPress={() => undefined}>
|
|
164
|
+
<Menu.ItemIcon as={Edit} />
|
|
165
|
+
<Menu.ItemLabel>Edit profile</Menu.ItemLabel>
|
|
166
|
+
</Menu.Item>
|
|
167
|
+
<Menu.Item onPress={() => undefined}>
|
|
168
|
+
<Menu.ItemIcon as={ContentCopy} />
|
|
169
|
+
<Menu.ItemLabel>Copy link</Menu.ItemLabel>
|
|
170
|
+
</Menu.Item>
|
|
171
|
+
</Menu.Content>
|
|
172
|
+
</Menu>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## With shortcuts
|
|
178
|
+
|
|
179
|
+
`Menu.ItemShortcut` is a visual hint for a keystroke. It does not register or handle that shortcut.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { Menu } from '@cdx-ui/components';
|
|
183
|
+
|
|
184
|
+
export default function MenuWithShortcutsDemo() {
|
|
185
|
+
return (
|
|
186
|
+
<Menu.Content accessibilityLabel="Edit menu" className="w-56">
|
|
187
|
+
<Menu.Item onPress={() => undefined}>
|
|
188
|
+
<Menu.ItemLabel>Undo</Menu.ItemLabel>
|
|
189
|
+
<Menu.ItemShortcut>⌘Z</Menu.ItemShortcut>
|
|
190
|
+
</Menu.Item>
|
|
191
|
+
<Menu.Item onPress={() => undefined}>
|
|
192
|
+
<Menu.ItemLabel>Redo</Menu.ItemLabel>
|
|
193
|
+
<Menu.ItemShortcut>⇧⌘Z</Menu.ItemShortcut>
|
|
194
|
+
</Menu.Item>
|
|
195
|
+
<Menu.Item onPress={() => undefined}>
|
|
196
|
+
<Menu.ItemLabel>Copy</Menu.ItemLabel>
|
|
197
|
+
<Menu.ItemShortcut>⌘C</Menu.ItemShortcut>
|
|
198
|
+
</Menu.Item>
|
|
199
|
+
</Menu.Content>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Destructive
|
|
205
|
+
|
|
206
|
+
Set `color="danger"` on the action that removes or irreversibly changes something.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { Menu } from '@cdx-ui/components';
|
|
210
|
+
import { Delete, Edit } from '@cdx-ui/icons';
|
|
211
|
+
|
|
212
|
+
export default function MenuDestructiveDemo() {
|
|
213
|
+
return (
|
|
214
|
+
<Menu.Content accessibilityLabel="Account actions" className="w-56">
|
|
215
|
+
<Menu.Item onPress={() => undefined}>
|
|
216
|
+
<Menu.ItemIcon as={Edit} />
|
|
217
|
+
<Menu.ItemLabel>Edit nickname</Menu.ItemLabel>
|
|
218
|
+
</Menu.Item>
|
|
219
|
+
<Menu.Separator />
|
|
220
|
+
<Menu.Item color="danger" onPress={() => undefined}>
|
|
221
|
+
<Menu.ItemIcon as={Delete} />
|
|
222
|
+
<Menu.ItemLabel>Delete account</Menu.ItemLabel>
|
|
223
|
+
</Menu.Item>
|
|
224
|
+
</Menu.Content>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Sections
|
|
230
|
+
|
|
231
|
+
Use `Menu.SectionLabel` to name a group and `Menu.Separator` to divide groups. Neither is a menu item, so arrow keys skip them.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { Menu } from '@cdx-ui/components';
|
|
235
|
+
|
|
236
|
+
export default function MenuSectionsDemo() {
|
|
237
|
+
return (
|
|
238
|
+
<Menu.Content accessibilityLabel="Account filters" className="w-56">
|
|
239
|
+
<Menu.SectionLabel>Status</Menu.SectionLabel>
|
|
240
|
+
<Menu.Item onPress={() => undefined}>
|
|
241
|
+
<Menu.ItemLabel>Active</Menu.ItemLabel>
|
|
242
|
+
</Menu.Item>
|
|
243
|
+
<Menu.Item onPress={() => undefined}>
|
|
244
|
+
<Menu.ItemLabel>Pending</Menu.ItemLabel>
|
|
245
|
+
</Menu.Item>
|
|
246
|
+
<Menu.Separator />
|
|
247
|
+
<Menu.SectionLabel>Type</Menu.SectionLabel>
|
|
248
|
+
<Menu.Item onPress={() => undefined}>
|
|
249
|
+
<Menu.ItemLabel>Checking</Menu.ItemLabel>
|
|
250
|
+
</Menu.Item>
|
|
251
|
+
<Menu.Item onPress={() => undefined}>
|
|
252
|
+
<Menu.ItemLabel>Savings</Menu.ItemLabel>
|
|
253
|
+
</Menu.Item>
|
|
254
|
+
</Menu.Content>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Inset
|
|
260
|
+
|
|
261
|
+
Set `inset` on items without an icon so their labels line up with icon items in the same menu.
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
import { Menu } from '@cdx-ui/components';
|
|
265
|
+
import { Edit, Person } from '@cdx-ui/icons';
|
|
266
|
+
|
|
267
|
+
export default function MenuInsetDemo() {
|
|
268
|
+
return (
|
|
269
|
+
<Menu.Content accessibilityLabel="Profile actions" className="w-56">
|
|
270
|
+
<Menu.Item onPress={() => undefined}>
|
|
271
|
+
<Menu.ItemIcon as={Person} />
|
|
272
|
+
<Menu.ItemLabel>View profile</Menu.ItemLabel>
|
|
273
|
+
</Menu.Item>
|
|
274
|
+
<Menu.Item inset onPress={() => undefined}>
|
|
275
|
+
<Menu.ItemLabel>Account settings</Menu.ItemLabel>
|
|
276
|
+
</Menu.Item>
|
|
277
|
+
<Menu.Item onPress={() => undefined}>
|
|
278
|
+
<Menu.ItemIcon as={Edit} />
|
|
279
|
+
<Menu.ItemLabel>Edit profile</Menu.ItemLabel>
|
|
280
|
+
</Menu.Item>
|
|
281
|
+
</Menu.Content>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Group header
|
|
287
|
+
|
|
288
|
+
`Menu.GroupHeader` is a non-command row. Pair it with `Menu.ItemAction` when that row needs a button, such as clearing a selection. Arrow keys skip the header, and the button is its own tab stop.
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
import { useState } from 'react';
|
|
292
|
+
import { Button, Menu } from '@cdx-ui/components';
|
|
293
|
+
|
|
294
|
+
export default function MenuGroupHeaderDemo() {
|
|
295
|
+
const [showPending, setShowPending] = useState(true);
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<Menu.Content accessibilityLabel="Transaction filters" className="w-72">
|
|
299
|
+
<Menu.GroupHeader>
|
|
300
|
+
<Menu.ItemLabel>Filter by</Menu.ItemLabel>
|
|
301
|
+
<Menu.ItemAction>
|
|
302
|
+
<Button variant="ghost" color="action" size="small" onPress={() => setShowPending(false)}>
|
|
303
|
+
<Button.Label>Clear</Button.Label>
|
|
304
|
+
</Button>
|
|
305
|
+
</Menu.ItemAction>
|
|
306
|
+
</Menu.GroupHeader>
|
|
307
|
+
<Menu.Separator />
|
|
308
|
+
<Menu.CheckboxItem isChecked={showPending} onChange={setShowPending}>
|
|
309
|
+
<Menu.ItemControl />
|
|
310
|
+
<Menu.ItemLabel>Pending transactions</Menu.ItemLabel>
|
|
311
|
+
</Menu.CheckboxItem>
|
|
312
|
+
</Menu.Content>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Item control
|
|
318
|
+
|
|
319
|
+
`Menu.ItemControl` draws presentational checkbox or radio chrome from the row's role. The row keeps the checked semantics, and the control is hidden from the accessibility tree. These rows stay open while the selection changes.
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
import { useState } from 'react';
|
|
323
|
+
import { Menu } from '@cdx-ui/components';
|
|
324
|
+
|
|
325
|
+
export default function MenuItemControlDemo() {
|
|
326
|
+
const [pending, setPending] = useState(true);
|
|
327
|
+
const [posted, setPosted] = useState(false);
|
|
328
|
+
const [status, setStatus] = useState('open');
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<Menu.Content accessibilityLabel="Transaction filters" className="w-64">
|
|
332
|
+
<Menu.SectionLabel>Activity</Menu.SectionLabel>
|
|
333
|
+
<Menu.CheckboxItem isChecked={pending} onChange={setPending}>
|
|
334
|
+
<Menu.ItemControl />
|
|
335
|
+
<Menu.ItemLabel>Pending</Menu.ItemLabel>
|
|
336
|
+
</Menu.CheckboxItem>
|
|
337
|
+
<Menu.CheckboxItem isChecked={posted} onChange={setPosted}>
|
|
338
|
+
<Menu.ItemControl />
|
|
339
|
+
<Menu.ItemLabel>Posted</Menu.ItemLabel>
|
|
340
|
+
</Menu.CheckboxItem>
|
|
341
|
+
<Menu.Separator />
|
|
342
|
+
<Menu.SectionLabel>Status</Menu.SectionLabel>
|
|
343
|
+
<Menu.RadioGroup value={status} onChange={setStatus}>
|
|
344
|
+
<Menu.RadioItem value="open">
|
|
345
|
+
<Menu.ItemControl />
|
|
346
|
+
<Menu.ItemLabel>Open</Menu.ItemLabel>
|
|
347
|
+
</Menu.RadioItem>
|
|
348
|
+
<Menu.RadioItem value="closed">
|
|
349
|
+
<Menu.ItemControl />
|
|
350
|
+
<Menu.ItemLabel>Closed</Menu.ItemLabel>
|
|
351
|
+
</Menu.RadioItem>
|
|
352
|
+
</Menu.RadioGroup>
|
|
353
|
+
</Menu.Content>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Item count
|
|
359
|
+
|
|
360
|
+
Nest `Menu.ItemCount` inside `Menu.ItemLabel` on a row that includes `Menu.ItemControl` so the count uses secondary text and wraps with the label.
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
import { useState } from 'react';
|
|
364
|
+
import { Menu } from '@cdx-ui/components';
|
|
365
|
+
|
|
366
|
+
const CATEGORIES = [
|
|
367
|
+
{ id: 'admin', label: 'Administrative services', count: 21 },
|
|
368
|
+
{ id: 'banking', label: 'Business banking & money movement', count: 10 },
|
|
369
|
+
] as const;
|
|
370
|
+
|
|
371
|
+
export default function MenuItemCountDemo() {
|
|
372
|
+
const [categories, setCategories] = useState<Record<string, boolean>>({ admin: true });
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<Menu.Content accessibilityLabel="Categories" className="w-64">
|
|
376
|
+
{CATEGORIES.map((category) => (
|
|
377
|
+
<Menu.CheckboxItem
|
|
378
|
+
key={category.id}
|
|
379
|
+
isChecked={categories[category.id] ?? false}
|
|
380
|
+
onChange={(isChecked) =>
|
|
381
|
+
setCategories((current) => ({ ...current, [category.id]: isChecked }))
|
|
382
|
+
}
|
|
383
|
+
>
|
|
384
|
+
<Menu.ItemControl />
|
|
385
|
+
<Menu.ItemLabel>
|
|
386
|
+
{category.label} <Menu.ItemCount>{category.count}</Menu.ItemCount>
|
|
387
|
+
</Menu.ItemLabel>
|
|
388
|
+
</Menu.CheckboxItem>
|
|
389
|
+
))}
|
|
390
|
+
</Menu.Content>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Item action
|
|
396
|
+
|
|
397
|
+
Put a `Button` in `Menu.ItemAction` when a command row needs a trailing action. Pressing the button does not activate the row or dismiss the menu.
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
import { useState } from 'react';
|
|
401
|
+
import { Box, Button, Menu, Text } from '@cdx-ui/components';
|
|
402
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
403
|
+
|
|
404
|
+
export default function MenuItemActionDemo() {
|
|
405
|
+
const [result, setResult] = useState('Choose an account action');
|
|
406
|
+
|
|
407
|
+
return (
|
|
408
|
+
<Box className="items-start gap-2">
|
|
409
|
+
<Menu>
|
|
410
|
+
<Menu.Trigger asChild>
|
|
411
|
+
<Button color="action" variant="outline">
|
|
412
|
+
<Button.Label>Everyday Checking</Button.Label>
|
|
413
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
414
|
+
</Button>
|
|
415
|
+
</Menu.Trigger>
|
|
416
|
+
<Menu.Popup className="w-72">
|
|
417
|
+
<Menu.Content accessibilityLabel="Everyday Checking actions">
|
|
418
|
+
<Menu.Item onPress={() => setResult('Opening account details')}>
|
|
419
|
+
<Menu.ItemLabel>View details</Menu.ItemLabel>
|
|
420
|
+
<Menu.ItemAction>
|
|
421
|
+
<Button
|
|
422
|
+
variant="ghost"
|
|
423
|
+
color="action"
|
|
424
|
+
size="small"
|
|
425
|
+
onPress={() => setResult('Copied account number')}
|
|
426
|
+
>
|
|
427
|
+
<Button.Label>Copy</Button.Label>
|
|
428
|
+
</Button>
|
|
429
|
+
</Menu.ItemAction>
|
|
430
|
+
</Menu.Item>
|
|
431
|
+
<Menu.Item onPress={() => setResult('Starting a transfer')}>
|
|
432
|
+
<Menu.ItemLabel>Transfer money</Menu.ItemLabel>
|
|
433
|
+
</Menu.Item>
|
|
434
|
+
</Menu.Content>
|
|
435
|
+
</Menu.Popup>
|
|
436
|
+
</Menu>
|
|
437
|
+
<Text size="sm" color="secondary">
|
|
438
|
+
{result}
|
|
439
|
+
</Text>
|
|
440
|
+
</Box>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## With filters
|
|
446
|
+
|
|
447
|
+
Compose a group header, section labels, checkbox rows, and radio groups when customers change several filters before dismissing the menu.
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
import { useState } from 'react';
|
|
451
|
+
import { Button, Menu } from '@cdx-ui/components';
|
|
452
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
453
|
+
|
|
454
|
+
const CATEGORIES = [
|
|
455
|
+
{ id: 'admin', label: 'Administrative services', count: 21 },
|
|
456
|
+
{ id: 'banking', label: 'Business banking & money movement', count: 10 },
|
|
457
|
+
{ id: 'growth', label: 'Engagement & growth', count: 5 },
|
|
458
|
+
{ id: 'wellbeing', label: 'Financial wellbeing', count: 5 },
|
|
459
|
+
{ id: 'security', label: 'Identity & business security', count: 5 },
|
|
460
|
+
{ id: 'intelligence', label: 'Intelligence & connectivity', count: 1 },
|
|
461
|
+
{ id: 'lending', label: 'Lending & credit', count: 3 },
|
|
462
|
+
] as const;
|
|
463
|
+
|
|
464
|
+
const STATUSES = [
|
|
465
|
+
{ id: 'all', label: 'All statuses', count: 51 },
|
|
466
|
+
{ id: 'available', label: 'Available', count: 45 },
|
|
467
|
+
{ id: 'pilot', label: 'Ready for pilot', count: 2 },
|
|
468
|
+
{ id: 'soon', label: 'Coming soon', count: 3 },
|
|
469
|
+
] as const;
|
|
470
|
+
|
|
471
|
+
const PROVIDERS = [
|
|
472
|
+
{ id: 'all', label: 'All providers', count: 51 },
|
|
473
|
+
{ id: 'partners', label: 'Partners', count: 36 },
|
|
474
|
+
{ id: 'candescent', label: 'Candescent', count: 15 },
|
|
475
|
+
] as const;
|
|
476
|
+
|
|
477
|
+
export default function MenuWithFiltersDemo() {
|
|
478
|
+
const [categories, setCategories] = useState<Record<string, boolean>>({
|
|
479
|
+
admin: true,
|
|
480
|
+
banking: false,
|
|
481
|
+
});
|
|
482
|
+
const [status, setStatus] = useState('available');
|
|
483
|
+
const [provider, setProvider] = useState('all');
|
|
484
|
+
|
|
485
|
+
const clearFilters = () => {
|
|
486
|
+
setCategories({});
|
|
487
|
+
setStatus('all');
|
|
488
|
+
setProvider('all');
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<Menu>
|
|
493
|
+
<Menu.Trigger asChild>
|
|
494
|
+
<Button color="action" variant="outline">
|
|
495
|
+
<Button.Label>Catalog filters</Button.Label>
|
|
496
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
497
|
+
</Button>
|
|
498
|
+
</Menu.Trigger>
|
|
499
|
+
<Menu.Popup className="w-72">
|
|
500
|
+
<Menu.Content accessibilityLabel="Catalog filters">
|
|
501
|
+
<Menu.GroupHeader>
|
|
502
|
+
<Menu.ItemLabel>Filter by</Menu.ItemLabel>
|
|
503
|
+
<Menu.ItemAction>
|
|
504
|
+
<Button variant="ghost" color="action" size="small" onPress={clearFilters}>
|
|
505
|
+
<Button.Label>Clear filters</Button.Label>
|
|
506
|
+
</Button>
|
|
507
|
+
</Menu.ItemAction>
|
|
508
|
+
</Menu.GroupHeader>
|
|
509
|
+
<Menu.Separator />
|
|
510
|
+
<Menu.SectionLabel>Categories</Menu.SectionLabel>
|
|
511
|
+
{CATEGORIES.map((category) => (
|
|
512
|
+
<Menu.CheckboxItem
|
|
513
|
+
key={category.id}
|
|
514
|
+
isChecked={categories[category.id] ?? false}
|
|
515
|
+
onChange={(isChecked) =>
|
|
516
|
+
setCategories((current) => ({ ...current, [category.id]: isChecked }))
|
|
517
|
+
}
|
|
518
|
+
>
|
|
519
|
+
<Menu.ItemControl />
|
|
520
|
+
<Menu.ItemLabel>
|
|
521
|
+
{category.label} <Menu.ItemCount>{category.count}</Menu.ItemCount>
|
|
522
|
+
</Menu.ItemLabel>
|
|
523
|
+
</Menu.CheckboxItem>
|
|
524
|
+
))}
|
|
525
|
+
<Menu.Separator />
|
|
526
|
+
<Menu.SectionLabel>Status</Menu.SectionLabel>
|
|
527
|
+
<Menu.RadioGroup value={status} onChange={setStatus}>
|
|
528
|
+
{STATUSES.map((item) => (
|
|
529
|
+
<Menu.RadioItem key={item.id} value={item.id}>
|
|
530
|
+
<Menu.ItemControl />
|
|
531
|
+
<Menu.ItemLabel>
|
|
532
|
+
{item.label} <Menu.ItemCount>{item.count}</Menu.ItemCount>
|
|
533
|
+
</Menu.ItemLabel>
|
|
534
|
+
</Menu.RadioItem>
|
|
535
|
+
))}
|
|
536
|
+
</Menu.RadioGroup>
|
|
537
|
+
<Menu.Separator />
|
|
538
|
+
<Menu.SectionLabel>Providers</Menu.SectionLabel>
|
|
539
|
+
<Menu.RadioGroup value={provider} onChange={setProvider}>
|
|
540
|
+
{PROVIDERS.map((item) => (
|
|
541
|
+
<Menu.RadioItem key={item.id} value={item.id}>
|
|
542
|
+
<Menu.ItemControl />
|
|
543
|
+
<Menu.ItemLabel>
|
|
544
|
+
{item.label} <Menu.ItemCount>{item.count}</Menu.ItemCount>
|
|
545
|
+
</Menu.ItemLabel>
|
|
546
|
+
</Menu.RadioItem>
|
|
547
|
+
))}
|
|
548
|
+
</Menu.RadioGroup>
|
|
549
|
+
</Menu.Content>
|
|
550
|
+
</Menu.Popup>
|
|
551
|
+
</Menu>
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## Scrollable
|
|
557
|
+
|
|
558
|
+
Use a scrollable radio menu when customers must choose one option from a longer set, such as a monthly statement period. The selected period persists, appears in the trigger, and remains checked when the menu reopens.
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
import { useState } from 'react';
|
|
562
|
+
import { Button, Menu } from '@cdx-ui/components';
|
|
563
|
+
import { CalendarMonth, KeyboardArrowDown } from '@cdx-ui/icons';
|
|
564
|
+
|
|
565
|
+
const STATEMENT_PERIODS = [
|
|
566
|
+
'August 2026',
|
|
567
|
+
'July 2026',
|
|
568
|
+
'June 2026',
|
|
569
|
+
'May 2026',
|
|
570
|
+
'April 2026',
|
|
571
|
+
'March 2026',
|
|
572
|
+
'February 2026',
|
|
573
|
+
'January 2026',
|
|
574
|
+
'December 2025',
|
|
575
|
+
'November 2025',
|
|
576
|
+
'October 2025',
|
|
577
|
+
'September 2025',
|
|
578
|
+
] as const;
|
|
579
|
+
|
|
580
|
+
export default function MenuScrollableDemo() {
|
|
581
|
+
const [selectedPeriod, setSelectedPeriod] = useState<string>(STATEMENT_PERIODS[0]);
|
|
582
|
+
|
|
583
|
+
return (
|
|
584
|
+
<Menu>
|
|
585
|
+
<Menu.Trigger asChild>
|
|
586
|
+
<Button color="action" variant="outline">
|
|
587
|
+
<Button.Label>Statement · {selectedPeriod}</Button.Label>
|
|
588
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
589
|
+
</Button>
|
|
590
|
+
</Menu.Trigger>
|
|
591
|
+
<Menu.Popup>
|
|
592
|
+
<Menu.Content scrollable accessibilityLabel="Statement period">
|
|
593
|
+
<Menu.RadioGroup value={selectedPeriod} onChange={setSelectedPeriod}>
|
|
594
|
+
{STATEMENT_PERIODS.map((period) => (
|
|
595
|
+
<Menu.RadioItem key={period} value={period}>
|
|
596
|
+
<Menu.ItemIcon as={CalendarMonth} />
|
|
597
|
+
<Menu.ItemLabel>{period}</Menu.ItemLabel>
|
|
598
|
+
<Menu.ItemIndicator />
|
|
599
|
+
</Menu.RadioItem>
|
|
600
|
+
))}
|
|
601
|
+
</Menu.RadioGroup>
|
|
602
|
+
</Menu.Content>
|
|
603
|
+
</Menu.Popup>
|
|
604
|
+
</Menu>
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Custom triggers
|
|
610
|
+
|
|
611
|
+
Use `asChild` to open a menu from any pressable component, such as a button, chip, or icon button.
|
|
612
|
+
|
|
613
|
+
```tsx
|
|
614
|
+
import { Box, Button, Card, Chip, IconButton, Menu, Text } from '@cdx-ui/components';
|
|
615
|
+
import { AccountBalance, KeyboardArrowDown, MoreHoriz } from '@cdx-ui/icons';
|
|
616
|
+
|
|
617
|
+
export default function MenuCustomTriggersDemo() {
|
|
618
|
+
return (
|
|
619
|
+
<Box className="w-full max-w-sm gap-6">
|
|
620
|
+
<Box className="items-start gap-2">
|
|
621
|
+
<Text color="secondary" size="sm">
|
|
622
|
+
Money movement
|
|
623
|
+
</Text>
|
|
624
|
+
<Menu>
|
|
625
|
+
<Menu.Trigger asChild>
|
|
626
|
+
<Button color="action">
|
|
627
|
+
<Button.Label>Move money</Button.Label>
|
|
628
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
629
|
+
</Button>
|
|
630
|
+
</Menu.Trigger>
|
|
631
|
+
<Menu.Popup>
|
|
632
|
+
<Menu.Content accessibilityLabel="Move money">
|
|
633
|
+
<Menu.Item onPress={() => undefined}>
|
|
634
|
+
<Menu.ItemLabel>Transfer between accounts</Menu.ItemLabel>
|
|
635
|
+
</Menu.Item>
|
|
636
|
+
<Menu.Item onPress={() => undefined}>
|
|
637
|
+
<Menu.ItemLabel>Pay a bill</Menu.ItemLabel>
|
|
638
|
+
</Menu.Item>
|
|
639
|
+
<Menu.Item onPress={() => undefined}>
|
|
640
|
+
<Menu.ItemLabel>Send money</Menu.ItemLabel>
|
|
641
|
+
</Menu.Item>
|
|
642
|
+
</Menu.Content>
|
|
643
|
+
</Menu.Popup>
|
|
644
|
+
</Menu>
|
|
645
|
+
</Box>
|
|
646
|
+
|
|
647
|
+
<Box className="items-start gap-2">
|
|
648
|
+
<Text color="secondary" size="sm">
|
|
649
|
+
Account
|
|
650
|
+
</Text>
|
|
651
|
+
<Menu>
|
|
652
|
+
<Menu.Trigger asChild>
|
|
653
|
+
<Chip color="action" variant="outline">
|
|
654
|
+
<Chip.Icon as={AccountBalance} position="leading" />
|
|
655
|
+
<Chip.Label>Checking ••4821</Chip.Label>
|
|
656
|
+
<Chip.Icon as={KeyboardArrowDown} position="trailing" />
|
|
657
|
+
</Chip>
|
|
658
|
+
</Menu.Trigger>
|
|
659
|
+
<Menu.Popup>
|
|
660
|
+
<Menu.Content accessibilityLabel="Checking account actions">
|
|
661
|
+
<Menu.Item onPress={() => undefined}>
|
|
662
|
+
<Menu.ItemLabel>View account activity</Menu.ItemLabel>
|
|
663
|
+
</Menu.Item>
|
|
664
|
+
<Menu.Item onPress={() => undefined}>
|
|
665
|
+
<Menu.ItemLabel>Download statements</Menu.ItemLabel>
|
|
666
|
+
</Menu.Item>
|
|
667
|
+
<Menu.Item onPress={() => undefined}>
|
|
668
|
+
<Menu.ItemLabel>Manage alerts</Menu.ItemLabel>
|
|
669
|
+
</Menu.Item>
|
|
670
|
+
</Menu.Content>
|
|
671
|
+
</Menu.Popup>
|
|
672
|
+
</Menu>
|
|
673
|
+
</Box>
|
|
674
|
+
|
|
675
|
+
<Box className="gap-2">
|
|
676
|
+
<Text color="secondary" size="sm">
|
|
677
|
+
Card management
|
|
678
|
+
</Text>
|
|
679
|
+
<Card className="flex-row items-center justify-between px-2 py-1">
|
|
680
|
+
<Text>Cash Rewards ••1842</Text>
|
|
681
|
+
<Menu placement="bottom-end">
|
|
682
|
+
<Menu.Trigger asChild>
|
|
683
|
+
<IconButton
|
|
684
|
+
as={MoreHoriz}
|
|
685
|
+
variant="ghost"
|
|
686
|
+
size="small"
|
|
687
|
+
accessibilityLabel="Cash Rewards card actions"
|
|
688
|
+
/>
|
|
689
|
+
</Menu.Trigger>
|
|
690
|
+
<Menu.Popup>
|
|
691
|
+
<Menu.Content accessibilityLabel="Cash Rewards card actions">
|
|
692
|
+
<Menu.Item onPress={() => undefined}>
|
|
693
|
+
<Menu.ItemLabel>Lock card</Menu.ItemLabel>
|
|
694
|
+
</Menu.Item>
|
|
695
|
+
<Menu.Item onPress={() => undefined}>
|
|
696
|
+
<Menu.ItemLabel>Replace card</Menu.ItemLabel>
|
|
697
|
+
</Menu.Item>
|
|
698
|
+
<Menu.Item color="danger" onPress={() => undefined}>
|
|
699
|
+
<Menu.ItemLabel>Report lost or stolen</Menu.ItemLabel>
|
|
700
|
+
</Menu.Item>
|
|
701
|
+
</Menu.Content>
|
|
702
|
+
</Menu.Popup>
|
|
703
|
+
</Menu>
|
|
704
|
+
</Card>
|
|
705
|
+
</Box>
|
|
706
|
+
</Box>
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
## Side placements
|
|
712
|
+
|
|
713
|
+
The default placement is `bottom-start`. Choose a side alone to center the popup, or append `-start` or `-end` to align it with the corresponding edge of the trigger.
|
|
714
|
+
|
|
715
|
+
```tsx
|
|
716
|
+
import { Box, Button, Menu, Text, type MenuProps } from '@cdx-ui/components';
|
|
717
|
+
|
|
718
|
+
interface PlacementMenuProps {
|
|
719
|
+
placement: NonNullable<MenuProps['placement']>;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const PLACEMENT_GROUPS = [
|
|
723
|
+
{
|
|
724
|
+
label: 'Top',
|
|
725
|
+
placements: ['top-start', 'top', 'top-end'],
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
label: 'Right',
|
|
729
|
+
placements: ['right-start', 'right', 'right-end'],
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
label: 'Bottom',
|
|
733
|
+
placements: ['bottom-start', 'bottom', 'bottom-end'],
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
label: 'Left',
|
|
737
|
+
placements: ['left-start', 'left', 'left-end'],
|
|
738
|
+
},
|
|
739
|
+
] as const satisfies readonly {
|
|
740
|
+
label: string;
|
|
741
|
+
placements: readonly PlacementMenuProps['placement'][];
|
|
742
|
+
}[];
|
|
743
|
+
|
|
744
|
+
function PlacementMenu({ placement }: PlacementMenuProps) {
|
|
745
|
+
return (
|
|
746
|
+
<Menu placement={placement}>
|
|
747
|
+
<Menu.Trigger asChild>
|
|
748
|
+
<Button variant="outline" color="action" size="small">
|
|
749
|
+
<Button.Label>{placement}</Button.Label>
|
|
750
|
+
</Button>
|
|
751
|
+
</Menu.Trigger>
|
|
752
|
+
<Menu.Popup>
|
|
753
|
+
<Menu.Content accessibilityLabel={`${placement} menu`}>
|
|
754
|
+
<Menu.Item onPress={() => undefined}>
|
|
755
|
+
<Menu.ItemLabel>View details</Menu.ItemLabel>
|
|
756
|
+
</Menu.Item>
|
|
757
|
+
<Menu.Item onPress={() => undefined}>
|
|
758
|
+
<Menu.ItemLabel>Copy link</Menu.ItemLabel>
|
|
759
|
+
</Menu.Item>
|
|
760
|
+
</Menu.Content>
|
|
761
|
+
</Menu.Popup>
|
|
762
|
+
</Menu>
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export default function MenuPlacementDemo() {
|
|
767
|
+
return (
|
|
768
|
+
<Box className="w-140 gap-6">
|
|
769
|
+
{PLACEMENT_GROUPS.map(({ label, placements }) => (
|
|
770
|
+
<Box key={label} className="items-center gap-2">
|
|
771
|
+
<Text size="sm" color="secondary">
|
|
772
|
+
{label}
|
|
773
|
+
</Text>
|
|
774
|
+
<Box className="flex-row flex-wrap justify-center gap-2">
|
|
775
|
+
{placements.map((placement) => (
|
|
776
|
+
<PlacementMenu key={placement} placement={placement} />
|
|
777
|
+
))}
|
|
778
|
+
</Box>
|
|
779
|
+
</Box>
|
|
780
|
+
))}
|
|
781
|
+
</Box>
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
The requested side is preferred rather than guaranteed. At viewport boundaries the popup flips to the opposite side when that side has more room, then shifts along the cross axis to remain within the default 8 px collision boundary.
|
|
787
|
+
|
|
788
|
+
## Automatic placements
|
|
789
|
+
|
|
790
|
+
Use `auto` to select the fitting side with the most available space. `auto-start` and `auto-end` use the same side selection while preserving the requested alignment.
|
|
791
|
+
|
|
792
|
+
```tsx
|
|
793
|
+
import { Box, Button, Menu, type MenuProps } from '@cdx-ui/components';
|
|
794
|
+
|
|
795
|
+
type AutomaticPlacement = Extract<
|
|
796
|
+
NonNullable<MenuProps['placement']>,
|
|
797
|
+
'auto' | 'auto-start' | 'auto-end'
|
|
798
|
+
>;
|
|
799
|
+
|
|
800
|
+
const AUTOMATIC_PLACEMENTS = [
|
|
801
|
+
'auto-start',
|
|
802
|
+
'auto',
|
|
803
|
+
'auto-end',
|
|
804
|
+
] as const satisfies readonly AutomaticPlacement[];
|
|
805
|
+
|
|
806
|
+
function AutomaticPlacementMenu({ placement }: Readonly<{ placement: AutomaticPlacement }>) {
|
|
807
|
+
return (
|
|
808
|
+
<Menu placement={placement}>
|
|
809
|
+
<Menu.Trigger asChild>
|
|
810
|
+
<Button variant="outline" color="action" size="small">
|
|
811
|
+
<Button.Label>{placement}</Button.Label>
|
|
812
|
+
</Button>
|
|
813
|
+
</Menu.Trigger>
|
|
814
|
+
<Menu.Popup>
|
|
815
|
+
<Menu.Content accessibilityLabel={`${placement} menu`}>
|
|
816
|
+
<Menu.Item onPress={() => undefined}>
|
|
817
|
+
<Menu.ItemLabel>View details</Menu.ItemLabel>
|
|
818
|
+
</Menu.Item>
|
|
819
|
+
<Menu.Item onPress={() => undefined}>
|
|
820
|
+
<Menu.ItemLabel>Copy link</Menu.ItemLabel>
|
|
821
|
+
</Menu.Item>
|
|
822
|
+
</Menu.Content>
|
|
823
|
+
</Menu.Popup>
|
|
824
|
+
</Menu>
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
export default function MenuAutomaticPlacementDemo() {
|
|
829
|
+
return (
|
|
830
|
+
<Box className="w-140 flex-row flex-wrap justify-center gap-3 py-20">
|
|
831
|
+
{AUTOMATIC_PLACEMENTS.map((placement) => (
|
|
832
|
+
<AutomaticPlacementMenu key={placement} placement={placement} />
|
|
833
|
+
))}
|
|
834
|
+
</Box>
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
All placements use a 4 px `offset` between the trigger and popup and a `crossOffset` of 0 by default. `offset` changes the main-axis gap, while `crossOffset` moves the popup along the perpendicular axis.
|
|
840
|
+
|
|
841
|
+
## Disabled
|
|
842
|
+
|
|
843
|
+
Set `disabled` to block activation and dim the item. Arrow keys skip disabled items.
|
|
844
|
+
|
|
845
|
+
```tsx
|
|
846
|
+
import { Menu } from '@cdx-ui/components';
|
|
847
|
+
|
|
848
|
+
export default function MenuDisabledDemo() {
|
|
849
|
+
return (
|
|
850
|
+
<Menu.Content accessibilityLabel="Account actions" className="w-64">
|
|
851
|
+
<Menu.Item onPress={() => undefined}>
|
|
852
|
+
<Menu.ItemLabel>Edit</Menu.ItemLabel>
|
|
853
|
+
</Menu.Item>
|
|
854
|
+
<Menu.Item disabled onPress={() => undefined}>
|
|
855
|
+
<Menu.ItemLabel>Archive (unavailable)</Menu.ItemLabel>
|
|
856
|
+
</Menu.Item>
|
|
857
|
+
<Menu.Item color="danger" disabled onPress={() => undefined}>
|
|
858
|
+
<Menu.ItemLabel>Delete</Menu.ItemLabel>
|
|
859
|
+
</Menu.Item>
|
|
860
|
+
</Menu.Content>
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
## Custom items
|
|
866
|
+
|
|
867
|
+
Menu item variants accept arbitrary children and style overrides, so an action or selectable item can use a richer layout while retaining its press, keyboard, and accessibility behavior.
|
|
868
|
+
|
|
869
|
+
```tsx
|
|
870
|
+
import { useState } from 'react';
|
|
871
|
+
import { Box, Button, Card, Chip, HStack, Menu, Text } from '@cdx-ui/components';
|
|
872
|
+
import { KeyboardArrowDown } from '@cdx-ui/icons';
|
|
873
|
+
|
|
874
|
+
const ACCOUNTS = [
|
|
875
|
+
{
|
|
876
|
+
id: 'checking',
|
|
877
|
+
name: 'Everyday Checking',
|
|
878
|
+
number: '••4821',
|
|
879
|
+
balance: '$5,248.10',
|
|
880
|
+
status: 'Primary',
|
|
881
|
+
statusColor: 'action',
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
id: 'savings',
|
|
885
|
+
name: 'High-Yield Savings',
|
|
886
|
+
number: '••1098',
|
|
887
|
+
balance: '$24,680.32',
|
|
888
|
+
status: '2.25% APY',
|
|
889
|
+
statusColor: 'success',
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
id: 'business',
|
|
893
|
+
name: 'Business Checking',
|
|
894
|
+
number: '••7315',
|
|
895
|
+
balance: '$12,904.76',
|
|
896
|
+
status: 'Business',
|
|
897
|
+
statusColor: 'info',
|
|
898
|
+
},
|
|
899
|
+
] as const;
|
|
900
|
+
|
|
901
|
+
type AccountId = (typeof ACCOUNTS)[number]['id'];
|
|
902
|
+
type Account = (typeof ACCOUNTS)[number];
|
|
903
|
+
|
|
904
|
+
function AccountOption({ account }: Readonly<{ account: Account }>) {
|
|
905
|
+
return (
|
|
906
|
+
<Card className="flex-1">
|
|
907
|
+
<Card.Header>
|
|
908
|
+
<Box className="flex-1 gap-0.5">
|
|
909
|
+
<Text weight="semibold">{account.name}</Text>
|
|
910
|
+
<Text size="sm" color="secondary">
|
|
911
|
+
{account.number}
|
|
912
|
+
</Text>
|
|
913
|
+
</Box>
|
|
914
|
+
<Chip color={account.statusColor} size="small">
|
|
915
|
+
<Chip.Label>{account.status}</Chip.Label>
|
|
916
|
+
</Chip>
|
|
917
|
+
</Card.Header>
|
|
918
|
+
<Card.Content>
|
|
919
|
+
<HStack space="md" className="items-end justify-between">
|
|
920
|
+
<Text size="sm" color="secondary">
|
|
921
|
+
Available balance
|
|
922
|
+
</Text>
|
|
923
|
+
<Text weight="semibold">{account.balance}</Text>
|
|
924
|
+
</HStack>
|
|
925
|
+
</Card.Content>
|
|
926
|
+
</Card>
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
export default function MenuCustomItemsDemo() {
|
|
931
|
+
const [selectedAccountId, setSelectedAccountId] = useState<AccountId>('checking');
|
|
932
|
+
const selectedAccount = ACCOUNTS.find(({ id }) => id === selectedAccountId) ?? ACCOUNTS[0];
|
|
933
|
+
|
|
934
|
+
return (
|
|
935
|
+
<Menu>
|
|
936
|
+
<Menu.Trigger asChild>
|
|
937
|
+
<Button color="action" variant="outline">
|
|
938
|
+
<Button.Label>From {selectedAccount.name}</Button.Label>
|
|
939
|
+
<Button.Icon as={KeyboardArrowDown} position="trailing" />
|
|
940
|
+
</Button>
|
|
941
|
+
</Menu.Trigger>
|
|
942
|
+
<Menu.Popup className="w-80">
|
|
943
|
+
<Menu.Content accessibilityLabel="Funding account">
|
|
944
|
+
<Menu.RadioGroup
|
|
945
|
+
value={selectedAccountId}
|
|
946
|
+
onChange={(value) => setSelectedAccountId(value as AccountId)}
|
|
947
|
+
>
|
|
948
|
+
{ACCOUNTS.map((account) => (
|
|
949
|
+
<Menu.RadioItem
|
|
950
|
+
key={account.id}
|
|
951
|
+
value={account.id}
|
|
952
|
+
accessibilityLabel={`${account.name} ${account.number}, available balance ${account.balance}`}
|
|
953
|
+
>
|
|
954
|
+
<AccountOption account={account} />
|
|
955
|
+
</Menu.RadioItem>
|
|
956
|
+
))}
|
|
957
|
+
</Menu.RadioGroup>
|
|
958
|
+
</Menu.Content>
|
|
959
|
+
</Menu.Popup>
|
|
960
|
+
</Menu>
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
## API
|
|
966
|
+
|
|
967
|
+
### Menu
|
|
968
|
+
|
|
969
|
+
Root for an overlay menu. Controls open state, preferred placement, and offsets for `Menu.Trigger` and `Menu.Popup`; omit it when rendering `Menu.Content` as a standalone list surface.
|
|
970
|
+
|
|
971
|
+
| Prop | Type | Default | Description |
|
|
972
|
+
| --- | --- | --- | --- |
|
|
973
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the root's own styles. |
|
|
974
|
+
| `children?` | `ReactNode` | — | The `Menu.Trigger` and `Menu.Popup`. |
|
|
975
|
+
| `isOpen?` | `boolean` | — | Controlled open state. When set, `defaultOpen` has no effect. |
|
|
976
|
+
| `defaultOpen?` | `boolean` | `false` | Initial open state when uncontrolled. |
|
|
977
|
+
| `onOpenChange?` | `((open: boolean) => void)` | — | Called with the next open state when the menu opens or closes. |
|
|
978
|
+
| `disabled?` | `boolean` | `false` | Prevents the trigger from opening the menu. |
|
|
979
|
+
| `placement?` | `'auto' \| 'auto-start' \| 'auto-end' \| 'top' \| 'top-start' \| 'top-end' \| 'right' \| 'right-start' \| 'right-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end'` | — | Preferred popup placement relative to the trigger. |
|
|
980
|
+
| `offset?` | `number` | — | Distance between the trigger and popup in pixels. |
|
|
981
|
+
| `crossOffset?` | `number` | — | Cross-axis adjustment from the aligned popup position in pixels. |
|
|
982
|
+
|
|
983
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
984
|
+
|
|
985
|
+
## Sub-components
|
|
986
|
+
|
|
987
|
+
### Menu.Trigger
|
|
988
|
+
|
|
989
|
+
Pressable that opens and closes an overlay menu. Use `asChild` to merge trigger behavior onto an `IconButton`, `Button`, or another pressable element.
|
|
990
|
+
|
|
991
|
+
| Prop | Type | Default | Description |
|
|
992
|
+
| --- | --- | --- | --- |
|
|
993
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the trigger's own styles. |
|
|
994
|
+
| `children?` | `ReactNode` | — | Trigger content (or the element to render when `asChild` is set). |
|
|
995
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element (for example a `Button`) as the trigger instead of the default `Pressable`. |
|
|
996
|
+
|
|
997
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
998
|
+
|
|
999
|
+
### Menu.Popup
|
|
1000
|
+
|
|
1001
|
+
Cross-platform overlay container that escapes normal layout through a document-body portal on web and a transparent modal on native. It positions the menu around its trigger and handles outside-press dismissal.
|
|
1002
|
+
|
|
1003
|
+
| Prop | Type | Default | Description |
|
|
1004
|
+
| --- | --- | --- | --- |
|
|
1005
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the popup's own styles. |
|
|
1006
|
+
| `children?` | `ReactNode` | — | Popup content, typically a `Menu.Content`. |
|
|
1007
|
+
|
|
1008
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1009
|
+
|
|
1010
|
+
### Menu.Content
|
|
1011
|
+
|
|
1012
|
+
Menu list surface. It can be rendered inside `Menu.Popup` or independently; overlay content scrolls automatically when constrained, while `scrollable` caps standalone surfaces. Use `asSurface` when another component owns the collection semantics.
|
|
1013
|
+
|
|
1014
|
+
| Prop | Type | Default | Description |
|
|
1015
|
+
| --- | --- | --- | --- |
|
|
1016
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the menu surface's own styles. |
|
|
1017
|
+
| `children?` | `ReactNode` | — | The menu's items, groups and separators. |
|
|
1018
|
+
| `scrollable?` | `boolean \| null` | `false` | Caps the surface height and scrolls its items. Content inside a `Menu.Popup` always scrolls. |
|
|
1019
|
+
| `onEscape?` | `(() => void)` | — | Called when Escape is pressed on web keyboard navigation. |
|
|
1020
|
+
| `asSurface?` | `boolean` | — | Visual-only surface without menu semantics (e.g. inside Select listbox). |
|
|
1021
|
+
|
|
1022
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1023
|
+
|
|
1024
|
+
### Menu.Item
|
|
1025
|
+
|
|
1026
|
+
An individual pressable action. The `color` prop defaults to `neutral`; use `color="danger"` for destructive actions.
|
|
1027
|
+
|
|
1028
|
+
| Prop | Type | Default | Description |
|
|
1029
|
+
| --- | --- | --- | --- |
|
|
1030
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the item's own styles. |
|
|
1031
|
+
| `children?` | `ReactNode` | — | Item content, typically a `Menu.ItemLabel` with optional icon, shortcut and indicator slots. |
|
|
1032
|
+
| `color?` | `'neutral' \| 'danger'` | `'neutral'` | Color theme applied to the item content and interaction states. |
|
|
1033
|
+
| `inset?` | `boolean \| null` | `false` | Indents the item content so its label lines up with items that have a leading icon. |
|
|
1034
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Pressable` host. |
|
|
1035
|
+
| `closeOnSelect?` | `boolean` | — | Whether activation closes an overlay Menu. Defaults to true for overlay items. |
|
|
1036
|
+
|
|
1037
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
1038
|
+
|
|
1039
|
+
### Menu.CheckboxItem
|
|
1040
|
+
|
|
1041
|
+
A controllable or uncontrolled menu option that toggles independently. Compose `Menu.ItemIndicator` to display its checked state.
|
|
1042
|
+
|
|
1043
|
+
| Prop | Type | Default | Description |
|
|
1044
|
+
| --- | --- | --- | --- |
|
|
1045
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the item's own styles. |
|
|
1046
|
+
| `children?` | `ReactNode` | — | Item content, typically a `Menu.ItemLabel` with optional icon, shortcut and indicator slots. |
|
|
1047
|
+
| `color?` | `'neutral' \| 'danger'` | `'neutral'` | Color theme applied to the item content and interaction states. |
|
|
1048
|
+
| `inset?` | `boolean \| null` | `false` | Indents the item content so its label lines up with items that have a leading icon. |
|
|
1049
|
+
| `isChecked?` | `boolean` | — | Controlled checked state. When set, `defaultIsChecked` has no effect. |
|
|
1050
|
+
| `closeOnSelect?` | `boolean` | — | Whether activation closes an overlay Menu. Defaults to true for overlay items. |
|
|
1051
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Pressable` host. |
|
|
1052
|
+
| `defaultIsChecked?` | `boolean` | — | Initial checked state when uncontrolled. |
|
|
1053
|
+
| `onChange?` | `((isChecked: boolean) => void)` | — | Called with the new checked state when the user toggles the item. |
|
|
1054
|
+
|
|
1055
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
1056
|
+
|
|
1057
|
+
### Menu.RadioGroup
|
|
1058
|
+
|
|
1059
|
+
Coordinates exclusive selection across `Menu.RadioItem` children.
|
|
1060
|
+
|
|
1061
|
+
| Prop | Type | Default | Description |
|
|
1062
|
+
| --- | --- | --- | --- |
|
|
1063
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the group's own styles. |
|
|
1064
|
+
| `children?` | `ReactNode` | — | The `Menu.RadioItem` options in the group. |
|
|
1065
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `View` host. |
|
|
1066
|
+
| `value?` | `string` | — | Controlled `value` of the selected `Menu.RadioItem`. When set, `defaultValue` has no effect. |
|
|
1067
|
+
| `defaultValue?` | `string` | — | Initially selected `value` when uncontrolled. |
|
|
1068
|
+
| `onChange?` | `((value: string) => void)` | — | Called with the `value` of the item the user selects. |
|
|
1069
|
+
|
|
1070
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1071
|
+
|
|
1072
|
+
### Menu.RadioItem
|
|
1073
|
+
|
|
1074
|
+
An exclusively selectable option within `Menu.RadioGroup`. Compose `Menu.ItemIndicator` to display its checked state.
|
|
1075
|
+
|
|
1076
|
+
| Prop | Type | Default | Description |
|
|
1077
|
+
| --- | --- | --- | --- |
|
|
1078
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the item's own styles. |
|
|
1079
|
+
| `children?` | `ReactNode` | — | Item content, typically a `Menu.ItemLabel` with optional icon, shortcut and indicator slots. |
|
|
1080
|
+
| `color?` | `'neutral' \| 'danger'` | `'neutral'` | Color theme applied to the item content and interaction states. |
|
|
1081
|
+
| `inset?` | `boolean \| null` | `false` | Indents the item content so its label lines up with items that have a leading icon. |
|
|
1082
|
+
| `closeOnSelect?` | `boolean` | — | Whether activation closes an overlay Menu. Defaults to true for overlay items. |
|
|
1083
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Pressable` host. |
|
|
1084
|
+
| `value` | `string` | — | Identifies this item within its `Menu.RadioGroup`. |
|
|
1085
|
+
|
|
1086
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
1087
|
+
|
|
1088
|
+
### Menu.ItemLabel
|
|
1089
|
+
|
|
1090
|
+
Text label rendered inside a `Menu.Item`.
|
|
1091
|
+
|
|
1092
|
+
| Prop | Type | Default | Description |
|
|
1093
|
+
| --- | --- | --- | --- |
|
|
1094
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the label's own styles. |
|
|
1095
|
+
| `children?` | `ReactNode` | — | Label text. |
|
|
1096
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Text` host. |
|
|
1097
|
+
| `isChecked?` | `boolean` | — | Checked state when used outside a selectable Menu item (e.g. `Select.ItemLabel`). |
|
|
1098
|
+
|
|
1099
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
1100
|
+
|
|
1101
|
+
### Menu.ItemIcon
|
|
1102
|
+
|
|
1103
|
+
Decorative Forge icon rendered inside a `Menu.Item`.
|
|
1104
|
+
|
|
1105
|
+
| Prop | Type | Default | Description |
|
|
1106
|
+
| --- | --- | --- | --- |
|
|
1107
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the icon's own styles. |
|
|
1108
|
+
| `children?` | `ReactNode` | — | The element to render when `asChild` is set. |
|
|
1109
|
+
| `filled?` | `boolean` | — | When `true`, renders the filled variant of the icon (if available). Defaults to `false` (outlined). |
|
|
1110
|
+
| `size?` | `string \| number` | — | Width and height of the icon, in pixels or any SVG length. The bare icon renders at 24 when omitted. |
|
|
1111
|
+
| `data-testid?` | `string` | — | Test identifier forwarded to the rendered SVG. |
|
|
1112
|
+
| `as?` | `ForgeIcon` | — | The `@cdx-ui/icons` component to render. |
|
|
1113
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the icon. |
|
|
1114
|
+
| `position?` | `'leading' \| 'trailing'` | `'leading'` | Side of the label the icon sits on; sets the spacing between them. |
|
|
1115
|
+
|
|
1116
|
+
Inherits all `ForgeIconProps` (react-native-svg `SvgProps` plus `filled` and `size`).
|
|
1117
|
+
|
|
1118
|
+
### Menu.ItemIndicator
|
|
1119
|
+
|
|
1120
|
+
Decorative checked-state icon for `Menu.CheckboxItem` and `Menu.RadioItem`.
|
|
1121
|
+
|
|
1122
|
+
| Prop | Type | Default | Description |
|
|
1123
|
+
| --- | --- | --- | --- |
|
|
1124
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the indicator's own styles. |
|
|
1125
|
+
| `children?` | `ReactNode` | — | The element to render when `asChild` is set. |
|
|
1126
|
+
| `filled?` | `boolean` | — | When `true`, renders the filled variant of the icon (if available). Defaults to `false` (outlined). |
|
|
1127
|
+
| `size?` | `string \| number` | — | Width and height of the icon, in pixels or any SVG length. The bare icon renders at 24 when omitted. |
|
|
1128
|
+
| `data-testid?` | `string` | — | Test identifier forwarded to the rendered SVG. |
|
|
1129
|
+
| `as?` | `ForgeIcon` | `Check` | The `@cdx-ui/icons` component to render. |
|
|
1130
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the icon. |
|
|
1131
|
+
| `isChecked?` | `boolean` | — | Shows the indicator. Defaults to the checked state of the enclosing item. |
|
|
1132
|
+
| `position?` | `'leading' \| 'trailing'` | `'trailing'` | Side of the label the indicator sits on; sets the spacing between them. |
|
|
1133
|
+
|
|
1134
|
+
Inherits all `ForgeIconProps` (react-native-svg `SvgProps` plus `filled` and `size`).
|
|
1135
|
+
|
|
1136
|
+
### Menu.ItemShortcut
|
|
1137
|
+
|
|
1138
|
+
Decorative text hint for a keyboard shortcut. This slot does not register or handle the shortcut.
|
|
1139
|
+
|
|
1140
|
+
| Prop | Type | Default | Description |
|
|
1141
|
+
| --- | --- | --- | --- |
|
|
1142
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the shortcut's own styles. |
|
|
1143
|
+
| `children?` | `ReactNode` | — | Shortcut hint text, such as `⌘C`. |
|
|
1144
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Text` host. |
|
|
1145
|
+
|
|
1146
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
1147
|
+
|
|
1148
|
+
### Menu.GroupHeader
|
|
1149
|
+
|
|
1150
|
+
Non-interactive header row for filter panels. Host a trailing `Menu.ItemAction` button; this slot is not a command `menuitem` and is skipped by arrow-key roving.
|
|
1151
|
+
|
|
1152
|
+
| Prop | Type | Default | Description |
|
|
1153
|
+
| --- | --- | --- | --- |
|
|
1154
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the group header's own styles. |
|
|
1155
|
+
| `children?` | `ReactNode` | — | Header content, typically a `Menu.ItemLabel` and an optional `Menu.ItemAction`. |
|
|
1156
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `View` host. |
|
|
1157
|
+
|
|
1158
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1159
|
+
|
|
1160
|
+
### Menu.ItemAction
|
|
1161
|
+
|
|
1162
|
+
Trailing action slot that isolates press from the parent row so a nested `Button` does not activate or dismiss the menu.
|
|
1163
|
+
|
|
1164
|
+
| Prop | Type | Default | Description |
|
|
1165
|
+
| --- | --- | --- | --- |
|
|
1166
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the action's own styles. |
|
|
1167
|
+
| `children?` | `ReactNode` | — | The trailing interactive control, such as a `Button`. |
|
|
1168
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `View` host. |
|
|
1169
|
+
|
|
1170
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1171
|
+
|
|
1172
|
+
### Menu.ItemControl
|
|
1173
|
+
|
|
1174
|
+
Presentational Checkbox or Radio chrome for filter rows. Hidden from the accessibility tree; the parent checkbox or radio item owns checked semantics.
|
|
1175
|
+
|
|
1176
|
+
| Prop | Type | Default | Description |
|
|
1177
|
+
| --- | --- | --- | --- |
|
|
1178
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the control's own styles. |
|
|
1179
|
+
| `children?` | `ReactNode` | — | Not rendered; the control draws a checkbox or radio that mirrors the enclosing item. |
|
|
1180
|
+
|
|
1181
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1182
|
+
|
|
1183
|
+
### Menu.ItemCount
|
|
1184
|
+
|
|
1185
|
+
Tertiary count rendered after a filter-row label. Nest it inside `Menu.ItemLabel` so it wraps with the text.
|
|
1186
|
+
|
|
1187
|
+
| Prop | Type | Default | Description |
|
|
1188
|
+
| --- | --- | --- | --- |
|
|
1189
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the count's own styles. |
|
|
1190
|
+
| `children?` | `ReactNode` | — | The count to display. |
|
|
1191
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Text` host. |
|
|
1192
|
+
|
|
1193
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
1194
|
+
|
|
1195
|
+
### Menu.SectionLabel
|
|
1196
|
+
|
|
1197
|
+
Non-interactive heading used to label a group of related menu items.
|
|
1198
|
+
|
|
1199
|
+
| Prop | Type | Default | Description |
|
|
1200
|
+
| --- | --- | --- | --- |
|
|
1201
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the section label's own styles. |
|
|
1202
|
+
| `children?` | `ReactNode` | — | Section heading text. |
|
|
1203
|
+
| `asChild?` | `boolean` | — | Render the consumer's child element in place of the default `Text` host. |
|
|
1204
|
+
|
|
1205
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
1206
|
+
|
|
1207
|
+
### Menu.Separator
|
|
1208
|
+
|
|
1209
|
+
Non-interactive divider between groups of menu items.
|
|
1210
|
+
|
|
1211
|
+
| Prop | Type | Default | Description |
|
|
1212
|
+
| --- | --- | --- | --- |
|
|
1213
|
+
| `className?` | `string` | — | Additional Uniwind/Tailwind classes, merged after the separator's own styles. |
|
|
1214
|
+
| `children?` | `ReactNode` | — | Not rendered; the separator draws its own line. |
|
|
1215
|
+
|
|
1216
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
1217
|
+
|
|
1218
|
+
## Anatomy
|
|
1219
|
+
|
|
1220
|
+
| `data-slot` | Element |
|
|
1221
|
+
| --------------------- | ----------------------------- |
|
|
1222
|
+
| `menu` | Overlay root |
|
|
1223
|
+
| `menu-trigger` | Overlay trigger |
|
|
1224
|
+
| `menu-popup` | Positioned overlay container |
|
|
1225
|
+
| `menu-content` | Menu list surface |
|
|
1226
|
+
| `menu-item` | Pressable action |
|
|
1227
|
+
| `menu-checkbox-item` | Independently checked option |
|
|
1228
|
+
| `menu-radio-group` | Exclusive option group |
|
|
1229
|
+
| `menu-radio-item` | Exclusively checked option |
|
|
1230
|
+
| `menu-item-label` | Item label |
|
|
1231
|
+
| `menu-item-icon` | Item icon |
|
|
1232
|
+
| `menu-item-shortcut` | Keyboard shortcut hint |
|
|
1233
|
+
| `menu-item-indicator` | Checked-state indicator |
|
|
1234
|
+
| `menu-item-action` | Trailing row action |
|
|
1235
|
+
| `menu-item-control` | Presentational filter control |
|
|
1236
|
+
| `menu-item-count` | Filter-row count |
|
|
1237
|
+
| `menu-group-header` | Non-command header row |
|
|
1238
|
+
| `menu-section-label` | Section heading |
|
|
1239
|
+
| `menu-separator` | Group divider |
|
|
1240
|
+
|
|
1241
|
+
## Accessibility
|
|
1242
|
+
|
|
1243
|
+
`Menu.Content` uses `role="menu"`, actions use `role="menuitem"`, checkbox items use `role="menuitemcheckbox"`, and radio items use `role="menuitemradio"`. Selectable items expose `aria-checked`; disabled items expose disabled state. In an overlay, the trigger exposes the open state and labels the menu content.
|
|
1244
|
+
|
|
1245
|
+
`Menu.GroupHeader`, `Menu.SectionLabel`, and `Menu.Separator` are not menu items and are skipped by arrow keys. A button in `Menu.ItemAction` on the header is a separate tab stop. `Menu.ItemControl` and `Menu.ItemIndicator` are presentational and hidden from the accessibility tree; the row owns the checked state. `Menu.ItemShortcut` is hidden as well.
|
|
1246
|
+
|
|
1247
|
+
On web, Arrow Up and Arrow Down move through enabled items, Home and End move to the first and last items, Enter or Space activates the focused item, Escape closes and restores trigger focus, and Tab closes before continuing from the trigger’s document position.
|