@12-apps/ui 6.44.3 → 6.45.0
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/NATIVE.md +1 -1
- package/components-catalog.md +13 -4
- package/dist/_internal/components/data-display/AsyncStateContainer/AsyncStateContainer.js +4 -4
- package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
- package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +1 -1
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +1 -1
- package/dist/_internal/components/data-display/Carousel/Carousel.js +2 -2
- package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
- package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
- package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
- package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
- package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +37 -37
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +53 -53
- package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +4 -4
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +5 -5
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +11 -11
- package/dist/_internal/components/data-display/DataViews/base-card.js +5 -5
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +1 -1
- package/dist/_internal/components/data-display/DataViews/base-list-card-gutters.js +3 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +1 -1
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +8 -8
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +1 -1
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +15 -15
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +33 -33
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +14 -14
- package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +12 -12
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +13 -13
- package/dist/_internal/components/data-display/DataViews/data-views-pagination.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +16 -16
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +5 -5
- package/dist/_internal/components/data-display/DataViews/list-card-rails.js +1 -1
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +3 -3
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +10 -10
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +6 -6
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +11 -11
- package/dist/_internal/components/data-display/FleetMap/FleetRoster.js +3 -3
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +3 -3
- package/dist/_internal/components/data-display/LazyImage/LazyImage.js +3 -3
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +3 -3
- package/dist/_internal/components/data-display/LoadingState/LoadingState.js +3 -3
- package/dist/_internal/components/data-display/MapPreview/MapLayers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +5 -5
- package/dist/_internal/components/data-display/StatCard/StatCard.js +3 -3
- package/dist/_internal/components/data-display/index.js +35 -35
- package/dist/_internal/components/feedback/Dialog/Dialog.js +3 -3
- package/dist/_internal/components/feedback/StackedModal/StackedModal.js +6 -6
- package/dist/_internal/components/feedback/TutorialOverlay/TutorialOverlay.js +2 -2
- package/dist/_internal/components/feedback/index.js +14 -14
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.hooks.js +3 -3
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.js +5 -5
- package/dist/_internal/components/form/AddressAutocomplete/AddressAutocompleteField.js +2 -2
- package/dist/_internal/components/form/Calendar/Calendar.js +4 -4
- package/dist/_internal/components/form/Calendar/CalendarDayCell.js +2 -2
- package/dist/_internal/components/form/Calendar/CalendarMonth.js +3 -3
- package/dist/_internal/components/form/CategorySelect/CategoryPanel.js +2 -2
- package/dist/_internal/components/form/CategorySelect/CategorySelect.js +5 -5
- package/dist/_internal/components/form/CepField/CepField.js +3 -3
- package/dist/_internal/components/form/Checkbox/Checkbox.js +2 -2
- package/dist/_internal/components/form/CodeEditor/CodeEditor.js +2 -2
- package/dist/_internal/components/form/Command/Command.js +2 -2
- package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +7 -7
- package/dist/_internal/components/form/Input/Input.js +2 -2
- package/dist/_internal/components/form/Menubar/Menubar.js +2 -2
- package/dist/_internal/components/form/NumberField/NumberField.js +3 -3
- package/dist/_internal/components/form/RichTextEditor/RichTextEditor.js +2 -2
- package/dist/_internal/components/form/Switch/Switch.js +5 -5
- package/dist/_internal/components/form/Switch/Switch.parts.js +3 -3
- package/dist/_internal/components/form/Switch/Switch.styles.js +2 -2
- package/dist/_internal/components/form/UploadButton/UploadButton.js +2 -2
- package/dist/_internal/components/form/index.js +35 -35
- package/dist/_internal/components/form/total-form/Fields/CategoryField.js +6 -6
- package/dist/_internal/components/form/total-form/Fields/CepField.js +4 -4
- package/dist/_internal/components/form/total-form/Fields/TextField.js +3 -3
- package/dist/_internal/components/form/total-form/Fields/index.js +12 -12
- package/dist/_internal/components/index.js +130 -112
- package/dist/_internal/components/layout/Card/Card.js +2 -2
- package/dist/_internal/components/layout/SettingCard/SettingGroup.js +7 -7
- package/dist/_internal/components/layout/SettingCard/SettingSwitch.js +6 -6
- package/dist/_internal/components/layout/SettingCard/SettingToggle.js +7 -7
- package/dist/_internal/components/layout/SettingsLayout/SettingsLayout.js +4 -4
- package/dist/_internal/components/layout/SettingsLayout/SettingsPanel.js +3 -3
- package/dist/_internal/components/layout/SettingsLayout/SettingsSectionChips.js +2 -2
- package/dist/_internal/components/layout/Skeleton/Skeleton.js +2 -2
- package/dist/_internal/components/layout/index.js +19 -19
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +4 -4
- package/dist/_internal/components/navigation/CommandPalette/CommandPalette.js +2 -2
- package/dist/_internal/components/navigation/Pagination/Pagination.js +2 -2
- package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +45 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.helpers.js +14 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.helpers.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.js +45 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +46 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +39 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +39 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +41 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.types.js +1 -0
- package/dist/_internal/components/navigation/SectionNav/SectionNav.types.js.map +1 -0
- package/dist/_internal/components/navigation/SectionNav/__stories__/fixtures.js +127 -0
- package/dist/_internal/components/navigation/SectionNav/__stories__/fixtures.js.map +1 -0
- package/dist/_internal/components/navigation/index.js +39 -12
- package/dist/_internal/components/utility/InfiniteScroll/InfiniteScroll.js +2 -2
- package/dist/_internal/components/utility/InstallPrompt/InstallPrompt.js +2 -2
- package/dist/_internal/components/utility/index.js +8 -8
- package/dist/_internal/en-US.navigation.js +5 -3
- package/dist/_internal/pt-BR.navigation.js +5 -3
- package/dist/_internal/social-login-button/social-login-button.js +3 -3
- package/dist/_internal/user-avatar/user-avatar.js +5 -5
- package/dist/charts.js +6 -6
- package/dist/{chunk-J6TIK47Z.js → chunk-2HPNN5XA.js} +2 -2
- package/dist/{chunk-BEGTLELZ.js → chunk-2K2B5QM2.js} +5 -5
- package/dist/chunk-2OP2FYB3.js +174 -0
- package/dist/chunk-2OP2FYB3.js.map +1 -0
- package/dist/chunk-3WCUGTRH.js +240 -0
- package/dist/chunk-3WCUGTRH.js.map +1 -0
- package/dist/{chunk-3I77A65F.js → chunk-442FSG7G.js} +4 -4
- package/dist/{chunk-ZOUWHGV3.js → chunk-4MGFUPQT.js} +4 -4
- package/dist/{chunk-26QYVCDK.js → chunk-525AOF6I.js} +2 -2
- package/dist/chunk-5AK2EZG5.js +1 -0
- package/dist/chunk-5AK2EZG5.js.map +1 -0
- package/dist/{chunk-5752357K.js → chunk-5KGABAQB.js} +4 -4
- package/dist/{chunk-SDKOOVEX.js → chunk-6GAZAJXB.js} +4 -4
- package/dist/{chunk-EO6LCGGQ.js → chunk-6J32H2RN.js} +2 -2
- package/dist/chunk-6OLJ2RAT.js +62 -0
- package/dist/chunk-6OLJ2RAT.js.map +1 -0
- package/dist/{chunk-7XDET7PL.js → chunk-6OQHZVK6.js} +4 -4
- package/dist/{chunk-F3AYISXT.js → chunk-6PU3JYVY.js} +4 -4
- package/dist/{chunk-RRAWJQIO.js → chunk-A64MQ7GW.js} +2 -2
- package/dist/{chunk-ADSA76T7.js → chunk-AG3DY5TH.js} +2 -2
- package/dist/{chunk-CIBTYBBN.js → chunk-AJ4MRDKM.js} +3 -3
- package/dist/{chunk-RJ35IXFD.js → chunk-B7HYM3QB.js} +2 -2
- package/dist/{chunk-X3C3VTYS.js → chunk-BEX3FLHB.js} +2 -2
- package/dist/{chunk-C5RTSOXD.js → chunk-BUAFRLSB.js} +2 -2
- package/dist/{chunk-DH6SL3SY.js → chunk-DSDNGTH6.js} +6 -6
- package/dist/{chunk-AUQRGZE7.js → chunk-EDZYPHZT.js} +7 -2
- package/dist/chunk-EDZYPHZT.js.map +1 -0
- package/dist/{chunk-JZ7ZRZTF.js → chunk-EJMSSDLM.js} +2 -2
- package/dist/{chunk-62OGPVCL.js → chunk-EPZ7B35I.js} +7 -2
- package/dist/chunk-EPZ7B35I.js.map +1 -0
- package/dist/{chunk-GP4BJI3F.js → chunk-ETCS6OBJ.js} +4 -4
- package/dist/{chunk-MVR4GURP.js → chunk-EU76OQGR.js} +2 -2
- package/dist/{chunk-DHW5UMUG.js → chunk-FNLD2KCQ.js} +4 -4
- package/dist/{chunk-XRUW7DMH.js → chunk-FPKVHR5L.js} +2 -2
- package/dist/{chunk-HKBJZX3C.js → chunk-FQ637IDM.js} +7 -7
- package/dist/{chunk-DDL6W7JS.js → chunk-FRD7U6QO.js} +5 -5
- package/dist/{chunk-YCHOWWRE.js → chunk-G6T7KRQ3.js} +2 -2
- package/dist/{chunk-EKBUM7HK.js → chunk-GYZUJ7HH.js} +4 -4
- package/dist/{chunk-ZQ7IESR7.js → chunk-H7HEB4UC.js} +5 -5
- package/dist/{chunk-JYLBVHG2.js → chunk-HAPELHNW.js} +7 -7
- package/dist/{chunk-HNFZLXZI.js → chunk-HBP2OU4Q.js} +5 -5
- package/dist/{chunk-K7FSTB2K.js → chunk-HSNOWYEE.js} +2 -2
- package/dist/chunk-HYEOKBFV.js +73 -0
- package/dist/chunk-HYEOKBFV.js.map +1 -0
- package/dist/{chunk-GI63FQEJ.js → chunk-HYNDC3FP.js} +4 -4
- package/dist/{chunk-ZBVU2YWV.js → chunk-IITZA6U2.js} +9 -9
- package/dist/{chunk-BZFRBG2H.js → chunk-IIXMTC65.js} +4 -4
- package/dist/{chunk-JFLFZLOA.js → chunk-IJAUHR3M.js} +4 -4
- package/dist/{chunk-K5CCQBNN.js → chunk-ILKNYENU.js} +4 -4
- package/dist/{chunk-DJIFXJ4C.js → chunk-IPI3OU2P.js} +4 -4
- package/dist/{chunk-6JGATEOF.js → chunk-IWYTOCCV.js} +3 -3
- package/dist/{chunk-5ZOJLYD2.js → chunk-JE6RZ5L6.js} +4 -4
- package/dist/{chunk-DE6A46AE.js → chunk-JFL5WT3S.js} +5 -5
- package/dist/{chunk-CYPBF3BI.js → chunk-JO4UMHWR.js} +3 -3
- package/dist/{chunk-LRFXCZB4.js → chunk-JWNOM5QT.js} +4 -4
- package/dist/{chunk-NU55QUMO.js → chunk-K45ML7M7.js} +2 -2
- package/dist/{chunk-OAVRXSAS.js → chunk-K7PWC44N.js} +2 -2
- package/dist/{chunk-ZGCPDA3F.js → chunk-KQS4ZJGS.js} +5 -5
- package/dist/{chunk-PAICZOL5.js → chunk-KTX7XME6.js} +4 -4
- package/dist/{chunk-KPZ53JY6.js → chunk-L3APNYI5.js} +3 -3
- package/dist/{chunk-GKNMKPR6.js → chunk-L6Q2A32O.js} +5 -5
- package/dist/{chunk-IG26N6J2.js → chunk-MD3ND6DT.js} +3 -3
- package/dist/{chunk-DTLKF4AX.js → chunk-MQ277OLB.js} +9 -9
- package/dist/{chunk-BA6Z52QN.js → chunk-NF4GHTZ4.js} +2 -2
- package/dist/{chunk-MSAAOQAD.js → chunk-NFA7STJA.js} +4 -4
- package/dist/{chunk-7HZTYK7U.js → chunk-NLYNRE7L.js} +4 -4
- package/dist/{chunk-HOIHO2YQ.js → chunk-NRI2TZDS.js} +2 -2
- package/dist/{chunk-VQUPB7F7.js → chunk-O25RFGPX.js} +4 -4
- package/dist/{chunk-Z3HSRNJT.js → chunk-OARM5RX4.js} +5 -5
- package/dist/{chunk-JDEKR77O.js → chunk-OERKNBKK.js} +2 -2
- package/dist/{chunk-WN5PNP6F.js → chunk-OP5JR5Q5.js} +4 -4
- package/dist/{chunk-XP522FFM.js → chunk-PARTZ7XI.js} +2 -2
- package/dist/{chunk-BHE44AQE.js → chunk-PCHUIDMT.js} +2 -2
- package/dist/{chunk-NYFGFELX.js → chunk-PGPPN63I.js} +6 -6
- package/dist/{chunk-Y2CMWGIU.js → chunk-PKXCQKPB.js} +2 -2
- package/dist/{chunk-6B3QXFF5.js → chunk-PNWGQAXQ.js} +2 -2
- package/dist/{chunk-IO2X6DVH.js → chunk-PSX7DAMV.js} +4 -4
- package/dist/{chunk-OL2BEJZR.js → chunk-Q2LQVYXY.js} +2 -2
- package/dist/{chunk-CMV6BRQ3.js → chunk-Q534UJ34.js} +4 -4
- package/dist/chunk-R26KYJHK.js +1 -0
- package/dist/chunk-R26KYJHK.js.map +1 -0
- package/dist/chunk-R73CRAAI.js +1 -0
- package/dist/chunk-R73CRAAI.js.map +1 -0
- package/dist/{chunk-VKFZQSQC.js → chunk-RPFQQP5S.js} +6 -6
- package/dist/chunk-SENCNTMV.js +55 -0
- package/dist/chunk-SENCNTMV.js.map +1 -0
- package/dist/{chunk-X4VWYWFK.js → chunk-SJY4DDG7.js} +4 -4
- package/dist/{chunk-DYCTIOER.js → chunk-SRZY6QG3.js} +5 -5
- package/dist/chunk-TCFBCTZD.js +168 -0
- package/dist/chunk-TCFBCTZD.js.map +1 -0
- package/dist/{chunk-44USLRMT.js → chunk-TPPFWETI.js} +6 -6
- package/dist/{chunk-XXL4PM4M.js → chunk-TWVRPT7J.js} +2 -2
- package/dist/{chunk-SM4MT4YC.js → chunk-U2UT2NDH.js} +3 -3
- package/dist/{chunk-5MW7OYBM.js → chunk-UJOFRJLX.js} +2 -2
- package/dist/{chunk-O6IF4BZT.js → chunk-UUJ7J7NE.js} +3 -3
- package/dist/{chunk-5JCGBCFJ.js → chunk-V2GHNHP6.js} +2 -2
- package/dist/{chunk-WQ3KTELX.js → chunk-V4R6Q5ED.js} +8 -8
- package/dist/{chunk-B3QWQG62.js → chunk-V54LG3QG.js} +2 -2
- package/dist/{chunk-6B4UTB4O.js → chunk-VDSQ5H4Y.js} +3 -3
- package/dist/chunk-VHVI3D4R.js +26 -0
- package/dist/chunk-VHVI3D4R.js.map +1 -0
- package/dist/{chunk-AEKHSORY.js → chunk-VOBVYVPN.js} +5 -5
- package/dist/{chunk-B2WBDMHE.js → chunk-VPH47OIV.js} +4 -4
- package/dist/{chunk-JSOJKHEI.js → chunk-WHYVVKRS.js} +2 -2
- package/dist/{chunk-KUKY5GEX.js → chunk-WU2KXA6I.js} +2 -2
- package/dist/{chunk-VNOXUWK7.js → chunk-X5F4FRH7.js} +4 -4
- package/dist/{chunk-EOEYOPOS.js → chunk-YKALXHCS.js} +11 -11
- package/dist/{chunk-BKBELIUR.js → chunk-YWQ7QCWR.js} +6 -6
- package/dist/data-display/AsyncStateContainer.js +4 -4
- package/dist/data-display/Avatar.js +2 -2
- package/dist/data-display/Badge.js +3 -2
- package/dist/data-display/CardKit.js +11 -11
- package/dist/data-display/Carousel.js +2 -2
- package/dist/data-display/Chart.js +6 -6
- package/dist/data-display/DataGrid.js +2 -2
- package/dist/data-display/DataViews.js +65 -65
- package/dist/data-display/FleetMap.js +11 -11
- package/dist/data-display/HoverCard.js +3 -3
- package/dist/data-display/LazyImage.js +3 -3
- package/dist/data-display/Lightbox.js +3 -3
- package/dist/data-display/LoadingState.js +3 -3
- package/dist/data-display/MapPreview.js +5 -5
- package/dist/data-display/StatCard.js +3 -3
- package/dist/en-US.js +8 -6
- package/dist/feedback/ConfirmAction.js +3 -3
- package/dist/feedback/Dialog.js +3 -3
- package/dist/feedback/StackedModal.js +6 -6
- package/dist/feedback/TutorialOverlay.js +2 -2
- package/dist/form/AddressAutocomplete.js +5 -5
- package/dist/form/Calendar.js +4 -4
- package/dist/form/CategorySelect.js +5 -5
- package/dist/form/CepField.js +3 -3
- package/dist/form/Checkbox.js +2 -2
- package/dist/form/CodeEditor.js +2 -2
- package/dist/form/Command.js +2 -2
- package/dist/form/DateRangePicker.js +9 -9
- package/dist/form/Input.js +2 -2
- package/dist/form/Menubar.js +2 -2
- package/dist/form/NumberField.js +3 -3
- package/dist/form/RichTextEditor.js +2 -2
- package/dist/form/Switch.js +5 -5
- package/dist/form/UploadButton.js +2 -2
- package/dist/form/total-form.js +18 -18
- package/dist/layout/Card.js +2 -2
- package/dist/layout/ContentToolbar.js +4 -4
- package/dist/layout/SettingCard.js +11 -11
- package/dist/layout/SettingsLayout.js +4 -4
- package/dist/layout/Skeleton.js +2 -2
- package/dist/locales.js +15 -8
- package/dist/locales.js.map +1 -1
- package/dist/navigation/AppHeader.js +6 -6
- package/dist/navigation/CommandPalette.js +2 -2
- package/dist/navigation/Pagination.js +2 -2
- package/dist/navigation/SectionNav.js +52 -0
- package/dist/navigation/SectionNav.js.map +1 -0
- package/dist/pt-BR.js +4 -2
- package/dist/social-login-button.js +3 -3
- package/dist/types/components/navigation/SectionNav/SectionNav.bar.d.ts +14 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.bar.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.d.ts +16 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.helpers.d.ts +32 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.helpers.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.parts.d.ts +42 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.parts.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.primary.d.ts +29 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.primary.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.rail.d.ts +13 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.rail.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.sheet.d.ts +20 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.sheet.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.types.d.ts +102 -0
- package/dist/types/components/navigation/SectionNav/SectionNav.types.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/__stories__/fixtures.d.ts +20 -0
- package/dist/types/components/navigation/SectionNav/__stories__/fixtures.d.ts.map +1 -0
- package/dist/types/components/navigation/SectionNav/index.d.ts +7 -0
- package/dist/types/components/navigation/SectionNav/index.d.ts.map +1 -0
- package/dist/types/components/navigation/index.d.ts +1 -0
- package/dist/types/components/navigation/index.d.ts.map +1 -1
- package/dist/types/copy/navigation.d.ts +10 -0
- package/dist/types/copy/navigation.d.ts.map +1 -1
- package/dist/types/en-US.navigation.d.ts +2 -1
- package/dist/types/en-US.navigation.d.ts.map +1 -1
- package/dist/types/locales.d.ts +2 -1
- package/dist/types/locales.d.ts.map +1 -1
- package/dist/types/pt-BR.navigation.d.ts +2 -1
- package/dist/types/pt-BR.navigation.d.ts.map +1 -1
- package/dist/user-avatar.js +5 -5
- package/dist/utility/InfiniteScroll.js +2 -2
- package/dist/utility/InstallPrompt.js +2 -2
- package/package.json +5 -1
- package/src/components/navigation/SectionNav/SectionNav.bar.tsx +258 -0
- package/src/components/navigation/SectionNav/SectionNav.helpers.ts +51 -0
- package/src/components/navigation/SectionNav/SectionNav.md +91 -0
- package/src/components/navigation/SectionNav/SectionNav.parts.tsx +88 -0
- package/src/components/navigation/SectionNav/SectionNav.primary.tsx +81 -0
- package/src/components/navigation/SectionNav/SectionNav.rail.tsx +208 -0
- package/src/components/navigation/SectionNav/SectionNav.sheet.tsx +206 -0
- package/src/components/navigation/SectionNav/SectionNav.tsx +56 -0
- package/src/components/navigation/SectionNav/SectionNav.types.ts +108 -0
- package/src/components/navigation/SectionNav/__stories__/fixtures.tsx +111 -0
- package/src/components/navigation/SectionNav/index.ts +13 -0
- package/src/components/navigation/index.ts +1 -0
- package/src/copy/navigation.ts +11 -0
- package/src/en-US.navigation.ts +6 -0
- package/src/locales.ts +5 -0
- package/src/pt-BR.navigation.ts +6 -0
- package/dist/chunk-62OGPVCL.js.map +0 -1
- package/dist/chunk-AUQRGZE7.js.map +0 -1
- package/dist/chunk-OJSVHBQO.js +0 -1
- /package/dist/{chunk-OJSVHBQO.js.map → _internal/components/navigation/SectionNav/SectionNav.bar.js.map} +0 -0
- /package/dist/{chunk-J6TIK47Z.js.map → chunk-2HPNN5XA.js.map} +0 -0
- /package/dist/{chunk-BEGTLELZ.js.map → chunk-2K2B5QM2.js.map} +0 -0
- /package/dist/{chunk-3I77A65F.js.map → chunk-442FSG7G.js.map} +0 -0
- /package/dist/{chunk-ZOUWHGV3.js.map → chunk-4MGFUPQT.js.map} +0 -0
- /package/dist/{chunk-26QYVCDK.js.map → chunk-525AOF6I.js.map} +0 -0
- /package/dist/{chunk-5752357K.js.map → chunk-5KGABAQB.js.map} +0 -0
- /package/dist/{chunk-SDKOOVEX.js.map → chunk-6GAZAJXB.js.map} +0 -0
- /package/dist/{chunk-EO6LCGGQ.js.map → chunk-6J32H2RN.js.map} +0 -0
- /package/dist/{chunk-7XDET7PL.js.map → chunk-6OQHZVK6.js.map} +0 -0
- /package/dist/{chunk-F3AYISXT.js.map → chunk-6PU3JYVY.js.map} +0 -0
- /package/dist/{chunk-RRAWJQIO.js.map → chunk-A64MQ7GW.js.map} +0 -0
- /package/dist/{chunk-ADSA76T7.js.map → chunk-AG3DY5TH.js.map} +0 -0
- /package/dist/{chunk-CIBTYBBN.js.map → chunk-AJ4MRDKM.js.map} +0 -0
- /package/dist/{chunk-RJ35IXFD.js.map → chunk-B7HYM3QB.js.map} +0 -0
- /package/dist/{chunk-X3C3VTYS.js.map → chunk-BEX3FLHB.js.map} +0 -0
- /package/dist/{chunk-C5RTSOXD.js.map → chunk-BUAFRLSB.js.map} +0 -0
- /package/dist/{chunk-DH6SL3SY.js.map → chunk-DSDNGTH6.js.map} +0 -0
- /package/dist/{chunk-JZ7ZRZTF.js.map → chunk-EJMSSDLM.js.map} +0 -0
- /package/dist/{chunk-GP4BJI3F.js.map → chunk-ETCS6OBJ.js.map} +0 -0
- /package/dist/{chunk-MVR4GURP.js.map → chunk-EU76OQGR.js.map} +0 -0
- /package/dist/{chunk-DHW5UMUG.js.map → chunk-FNLD2KCQ.js.map} +0 -0
- /package/dist/{chunk-XRUW7DMH.js.map → chunk-FPKVHR5L.js.map} +0 -0
- /package/dist/{chunk-HKBJZX3C.js.map → chunk-FQ637IDM.js.map} +0 -0
- /package/dist/{chunk-DDL6W7JS.js.map → chunk-FRD7U6QO.js.map} +0 -0
- /package/dist/{chunk-YCHOWWRE.js.map → chunk-G6T7KRQ3.js.map} +0 -0
- /package/dist/{chunk-EKBUM7HK.js.map → chunk-GYZUJ7HH.js.map} +0 -0
- /package/dist/{chunk-ZQ7IESR7.js.map → chunk-H7HEB4UC.js.map} +0 -0
- /package/dist/{chunk-JYLBVHG2.js.map → chunk-HAPELHNW.js.map} +0 -0
- /package/dist/{chunk-HNFZLXZI.js.map → chunk-HBP2OU4Q.js.map} +0 -0
- /package/dist/{chunk-K7FSTB2K.js.map → chunk-HSNOWYEE.js.map} +0 -0
- /package/dist/{chunk-GI63FQEJ.js.map → chunk-HYNDC3FP.js.map} +0 -0
- /package/dist/{chunk-ZBVU2YWV.js.map → chunk-IITZA6U2.js.map} +0 -0
- /package/dist/{chunk-BZFRBG2H.js.map → chunk-IIXMTC65.js.map} +0 -0
- /package/dist/{chunk-JFLFZLOA.js.map → chunk-IJAUHR3M.js.map} +0 -0
- /package/dist/{chunk-K5CCQBNN.js.map → chunk-ILKNYENU.js.map} +0 -0
- /package/dist/{chunk-DJIFXJ4C.js.map → chunk-IPI3OU2P.js.map} +0 -0
- /package/dist/{chunk-6JGATEOF.js.map → chunk-IWYTOCCV.js.map} +0 -0
- /package/dist/{chunk-5ZOJLYD2.js.map → chunk-JE6RZ5L6.js.map} +0 -0
- /package/dist/{chunk-DE6A46AE.js.map → chunk-JFL5WT3S.js.map} +0 -0
- /package/dist/{chunk-CYPBF3BI.js.map → chunk-JO4UMHWR.js.map} +0 -0
- /package/dist/{chunk-LRFXCZB4.js.map → chunk-JWNOM5QT.js.map} +0 -0
- /package/dist/{chunk-NU55QUMO.js.map → chunk-K45ML7M7.js.map} +0 -0
- /package/dist/{chunk-OAVRXSAS.js.map → chunk-K7PWC44N.js.map} +0 -0
- /package/dist/{chunk-ZGCPDA3F.js.map → chunk-KQS4ZJGS.js.map} +0 -0
- /package/dist/{chunk-PAICZOL5.js.map → chunk-KTX7XME6.js.map} +0 -0
- /package/dist/{chunk-KPZ53JY6.js.map → chunk-L3APNYI5.js.map} +0 -0
- /package/dist/{chunk-GKNMKPR6.js.map → chunk-L6Q2A32O.js.map} +0 -0
- /package/dist/{chunk-IG26N6J2.js.map → chunk-MD3ND6DT.js.map} +0 -0
- /package/dist/{chunk-DTLKF4AX.js.map → chunk-MQ277OLB.js.map} +0 -0
- /package/dist/{chunk-BA6Z52QN.js.map → chunk-NF4GHTZ4.js.map} +0 -0
- /package/dist/{chunk-MSAAOQAD.js.map → chunk-NFA7STJA.js.map} +0 -0
- /package/dist/{chunk-7HZTYK7U.js.map → chunk-NLYNRE7L.js.map} +0 -0
- /package/dist/{chunk-HOIHO2YQ.js.map → chunk-NRI2TZDS.js.map} +0 -0
- /package/dist/{chunk-VQUPB7F7.js.map → chunk-O25RFGPX.js.map} +0 -0
- /package/dist/{chunk-Z3HSRNJT.js.map → chunk-OARM5RX4.js.map} +0 -0
- /package/dist/{chunk-JDEKR77O.js.map → chunk-OERKNBKK.js.map} +0 -0
- /package/dist/{chunk-WN5PNP6F.js.map → chunk-OP5JR5Q5.js.map} +0 -0
- /package/dist/{chunk-XP522FFM.js.map → chunk-PARTZ7XI.js.map} +0 -0
- /package/dist/{chunk-BHE44AQE.js.map → chunk-PCHUIDMT.js.map} +0 -0
- /package/dist/{chunk-NYFGFELX.js.map → chunk-PGPPN63I.js.map} +0 -0
- /package/dist/{chunk-Y2CMWGIU.js.map → chunk-PKXCQKPB.js.map} +0 -0
- /package/dist/{chunk-6B3QXFF5.js.map → chunk-PNWGQAXQ.js.map} +0 -0
- /package/dist/{chunk-IO2X6DVH.js.map → chunk-PSX7DAMV.js.map} +0 -0
- /package/dist/{chunk-OL2BEJZR.js.map → chunk-Q2LQVYXY.js.map} +0 -0
- /package/dist/{chunk-CMV6BRQ3.js.map → chunk-Q534UJ34.js.map} +0 -0
- /package/dist/{chunk-VKFZQSQC.js.map → chunk-RPFQQP5S.js.map} +0 -0
- /package/dist/{chunk-X4VWYWFK.js.map → chunk-SJY4DDG7.js.map} +0 -0
- /package/dist/{chunk-DYCTIOER.js.map → chunk-SRZY6QG3.js.map} +0 -0
- /package/dist/{chunk-44USLRMT.js.map → chunk-TPPFWETI.js.map} +0 -0
- /package/dist/{chunk-XXL4PM4M.js.map → chunk-TWVRPT7J.js.map} +0 -0
- /package/dist/{chunk-SM4MT4YC.js.map → chunk-U2UT2NDH.js.map} +0 -0
- /package/dist/{chunk-5MW7OYBM.js.map → chunk-UJOFRJLX.js.map} +0 -0
- /package/dist/{chunk-O6IF4BZT.js.map → chunk-UUJ7J7NE.js.map} +0 -0
- /package/dist/{chunk-5JCGBCFJ.js.map → chunk-V2GHNHP6.js.map} +0 -0
- /package/dist/{chunk-WQ3KTELX.js.map → chunk-V4R6Q5ED.js.map} +0 -0
- /package/dist/{chunk-B3QWQG62.js.map → chunk-V54LG3QG.js.map} +0 -0
- /package/dist/{chunk-6B4UTB4O.js.map → chunk-VDSQ5H4Y.js.map} +0 -0
- /package/dist/{chunk-AEKHSORY.js.map → chunk-VOBVYVPN.js.map} +0 -0
- /package/dist/{chunk-B2WBDMHE.js.map → chunk-VPH47OIV.js.map} +0 -0
- /package/dist/{chunk-JSOJKHEI.js.map → chunk-WHYVVKRS.js.map} +0 -0
- /package/dist/{chunk-KUKY5GEX.js.map → chunk-WU2KXA6I.js.map} +0 -0
- /package/dist/{chunk-VNOXUWK7.js.map → chunk-X5F4FRH7.js.map} +0 -0
- /package/dist/{chunk-EOEYOPOS.js.map → chunk-YKALXHCS.js.map} +0 -0
- /package/dist/{chunk-BKBELIUR.js.map → chunk-YWQ7QCWR.js.map} +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import Typography from '@mui/material/Typography/index.js';
|
|
2
|
+
import { useTheme } from '@mui/material/styles/index.js';
|
|
3
|
+
import { useState, type ElementType, type ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
import type { SectionNavCopy } from '../../../copy';
|
|
6
|
+
import { rem } from '../../../tokens/scales';
|
|
7
|
+
|
|
8
|
+
import { barSlots, menuActive, menuCount, type BarSlot } from './SectionNav.helpers';
|
|
9
|
+
import { Box, CONTROL_RESET, CountedIcon, controlProps, focusRing } from './SectionNav.parts';
|
|
10
|
+
import { RaisedActionButton } from './SectionNav.primary';
|
|
11
|
+
import { SectionNavSheet } from './SectionNav.sheet';
|
|
12
|
+
import type { SectionNavMenu, SectionNavProps } from './SectionNav.types';
|
|
13
|
+
|
|
14
|
+
/** Which of the bar's two menus is open, if any. */
|
|
15
|
+
type OpenMenu = 'primary' | 'more' | null;
|
|
16
|
+
|
|
17
|
+
/** The bar's height above the safe area — tall enough for an icon over a label. */
|
|
18
|
+
const barHeight = (theme: Parameters<typeof rem>[0]): string => rem(theme, 60);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The bar's full height on screen, the safe area included — where its sheets
|
|
22
|
+
* stop, and what a host reserves at the foot of its content so nothing it
|
|
23
|
+
* floats (a toast, a receipt) lands under the bar.
|
|
24
|
+
*/
|
|
25
|
+
export function sectionNavBarInset(theme: Parameters<typeof rem>[0]): string {
|
|
26
|
+
return `calc(${barHeight(theme)} + env(safe-area-inset-bottom))`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* One slot: icon over label, a count on the icon.
|
|
31
|
+
*
|
|
32
|
+
* `current` is the page the viewer is on and is what `aria-current` reports;
|
|
33
|
+
* `lit` is only how the slot looks, and goes out while a sheet is open so the
|
|
34
|
+
* bar shows where the thumb is. Opening a sheet is `aria-expanded`'s to say.
|
|
35
|
+
*
|
|
36
|
+
* A destination is a link; the "more" slot is a button that opens its sheet.
|
|
37
|
+
* Either way the whole slot is the target, so a thumb landing on the label
|
|
38
|
+
* lands on the control.
|
|
39
|
+
*/
|
|
40
|
+
function Slot({
|
|
41
|
+
label,
|
|
42
|
+
icon,
|
|
43
|
+
count,
|
|
44
|
+
current,
|
|
45
|
+
lit,
|
|
46
|
+
copy,
|
|
47
|
+
testId,
|
|
48
|
+
control,
|
|
49
|
+
expanded,
|
|
50
|
+
}: {
|
|
51
|
+
label: string;
|
|
52
|
+
icon: ReactNode;
|
|
53
|
+
count: number | undefined;
|
|
54
|
+
current: boolean;
|
|
55
|
+
lit: boolean;
|
|
56
|
+
copy: SectionNavCopy;
|
|
57
|
+
testId: string;
|
|
58
|
+
control: Record<string, unknown>;
|
|
59
|
+
expanded?: boolean;
|
|
60
|
+
}): React.JSX.Element {
|
|
61
|
+
const theme = useTheme();
|
|
62
|
+
return (
|
|
63
|
+
<Box
|
|
64
|
+
{...control}
|
|
65
|
+
aria-current={current ? 'page' : undefined}
|
|
66
|
+
aria-expanded={expanded}
|
|
67
|
+
data-lit={lit ? 'true' : undefined}
|
|
68
|
+
data-testid={testId}
|
|
69
|
+
sx={{
|
|
70
|
+
...CONTROL_RESET,
|
|
71
|
+
...focusRing(theme),
|
|
72
|
+
flex: 1,
|
|
73
|
+
minWidth: 0,
|
|
74
|
+
display: 'flex',
|
|
75
|
+
flexDirection: 'column',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
justifyContent: 'center',
|
|
78
|
+
gap: 0.25,
|
|
79
|
+
position: 'relative',
|
|
80
|
+
color: lit ? 'primary.main' : 'text.secondary',
|
|
81
|
+
// The current slot is marked by more than its colour: a short rule on
|
|
82
|
+
// its top edge, so the state survives a colour-blind reading.
|
|
83
|
+
'&[data-lit="true"]::before': {
|
|
84
|
+
content: '""',
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
top: 0,
|
|
87
|
+
insetInline: '22%',
|
|
88
|
+
height: rem(theme, 3),
|
|
89
|
+
borderBottomLeftRadius: rem(theme, 3),
|
|
90
|
+
borderBottomRightRadius: rem(theme, 3),
|
|
91
|
+
bgcolor: 'primary.main',
|
|
92
|
+
},
|
|
93
|
+
'& svg': { fontSize: rem(theme, 22) },
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<CountedIcon icon={icon} count={count} label={copy.badge} testId={`${testId}-badge`} />
|
|
97
|
+
<Typography
|
|
98
|
+
component="span"
|
|
99
|
+
variant="caption"
|
|
100
|
+
sx={{
|
|
101
|
+
maxWidth: '100%',
|
|
102
|
+
overflow: 'hidden',
|
|
103
|
+
textOverflow: 'ellipsis',
|
|
104
|
+
whiteSpace: 'nowrap',
|
|
105
|
+
lineHeight: 1.3,
|
|
106
|
+
fontWeight: lit ? 700 : 400,
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{label}
|
|
110
|
+
</Typography>
|
|
111
|
+
</Box>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** One slot of the bar, whichever kind it is. */
|
|
116
|
+
function BarSlotView({
|
|
117
|
+
slot,
|
|
118
|
+
primary,
|
|
119
|
+
more,
|
|
120
|
+
open,
|
|
121
|
+
toggle,
|
|
122
|
+
close,
|
|
123
|
+
linkComponent,
|
|
124
|
+
copy,
|
|
125
|
+
dataTestId,
|
|
126
|
+
}: {
|
|
127
|
+
slot: BarSlot;
|
|
128
|
+
primary: SectionNavMenu | undefined;
|
|
129
|
+
more: SectionNavMenu | undefined;
|
|
130
|
+
open: OpenMenu;
|
|
131
|
+
toggle: (menu: Exclude<OpenMenu, null>) => void;
|
|
132
|
+
close: () => void;
|
|
133
|
+
linkComponent: ElementType | undefined;
|
|
134
|
+
copy: SectionNavCopy;
|
|
135
|
+
dataTestId: string;
|
|
136
|
+
}): React.JSX.Element | null {
|
|
137
|
+
if (slot.kind === 'primary' && primary) {
|
|
138
|
+
return (
|
|
139
|
+
<RaisedActionButton
|
|
140
|
+
label={primary.label}
|
|
141
|
+
icon={primary.icon}
|
|
142
|
+
open={open === 'primary'}
|
|
143
|
+
closeLabel={copy.close}
|
|
144
|
+
dataTestId={`${dataTestId}-primary`}
|
|
145
|
+
onClick={() => toggle('primary')}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (slot.kind === 'more' && more) {
|
|
150
|
+
return (
|
|
151
|
+
<Slot
|
|
152
|
+
label={more.label}
|
|
153
|
+
icon={more.icon}
|
|
154
|
+
count={menuCount(more)}
|
|
155
|
+
current={false}
|
|
156
|
+
lit={open === 'more' || (open === null && menuActive(more))}
|
|
157
|
+
expanded={open === 'more'}
|
|
158
|
+
copy={copy}
|
|
159
|
+
testId={`${dataTestId}-more`}
|
|
160
|
+
control={controlProps({ href: undefined, linkComponent, onClick: () => toggle('more') })}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (slot.kind !== 'destination') return null;
|
|
165
|
+
const { destination } = slot;
|
|
166
|
+
return (
|
|
167
|
+
<Slot
|
|
168
|
+
label={destination.label}
|
|
169
|
+
icon={destination.icon}
|
|
170
|
+
count={destination.badge}
|
|
171
|
+
current={destination.active === true}
|
|
172
|
+
lit={open === null && destination.active === true}
|
|
173
|
+
copy={copy}
|
|
174
|
+
testId={`${dataTestId}-dest-${destination.id}`}
|
|
175
|
+
control={controlProps({ href: destination.href, linkComponent, onClick: close })}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The phone layout: a bottom bar, with its menus as bottom sheets. */
|
|
181
|
+
export function SectionNavBar({
|
|
182
|
+
label,
|
|
183
|
+
destinations,
|
|
184
|
+
primary,
|
|
185
|
+
more,
|
|
186
|
+
linkComponent,
|
|
187
|
+
copy,
|
|
188
|
+
dataTestId,
|
|
189
|
+
}: Required<Pick<SectionNavProps, 'label' | 'destinations' | 'copy' | 'dataTestId'>> &
|
|
190
|
+
Pick<SectionNavProps, 'primary' | 'more'> & { linkComponent: ElementType | undefined }): React.JSX.Element {
|
|
191
|
+
const theme = useTheme();
|
|
192
|
+
const [open, setOpen] = useState<OpenMenu>(null);
|
|
193
|
+
const toggle = (menu: Exclude<OpenMenu, null>): void => setOpen((current) => (current === menu ? null : menu));
|
|
194
|
+
const close = (): void => setOpen(null);
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<>
|
|
198
|
+
<Box
|
|
199
|
+
component="nav"
|
|
200
|
+
aria-label={label}
|
|
201
|
+
data-testid={dataTestId}
|
|
202
|
+
sx={{
|
|
203
|
+
flex: 'none',
|
|
204
|
+
display: 'flex',
|
|
205
|
+
alignItems: 'stretch',
|
|
206
|
+
boxSizing: 'border-box',
|
|
207
|
+
height: sectionNavBarInset(theme),
|
|
208
|
+
paddingBottom: 'env(safe-area-inset-bottom)',
|
|
209
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
210
|
+
bgcolor: 'background.paper',
|
|
211
|
+
// Above the backdrop ONLY while one of its own sheets is open, so the
|
|
212
|
+
// primary button can close what it opened. Raised all the time it
|
|
213
|
+
// would sit over every dialog the page opens, and on a small phone a
|
|
214
|
+
// dialog's own footer is exactly where the bar is.
|
|
215
|
+
position: 'relative',
|
|
216
|
+
zIndex: open === null ? 'auto' : theme.zIndex.modal + 1,
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
{barSlots(destinations, more !== undefined, primary !== undefined).map((slot) => (
|
|
220
|
+
<BarSlotView
|
|
221
|
+
key={slot.kind === 'destination' ? `dest-${slot.destination.id}` : `menu-${slot.kind}`}
|
|
222
|
+
slot={slot}
|
|
223
|
+
primary={primary}
|
|
224
|
+
more={more}
|
|
225
|
+
open={open}
|
|
226
|
+
toggle={toggle}
|
|
227
|
+
close={close}
|
|
228
|
+
linkComponent={linkComponent}
|
|
229
|
+
copy={copy}
|
|
230
|
+
dataTestId={dataTestId}
|
|
231
|
+
/>
|
|
232
|
+
))}
|
|
233
|
+
</Box>
|
|
234
|
+
{primary ? (
|
|
235
|
+
<SectionNavSheet
|
|
236
|
+
menu={primary}
|
|
237
|
+
open={open === 'primary'}
|
|
238
|
+
onClose={close}
|
|
239
|
+
linkComponent={linkComponent}
|
|
240
|
+
copy={copy}
|
|
241
|
+
testId={`${dataTestId}-primary-sheet`}
|
|
242
|
+
bottomOffset={sectionNavBarInset(theme)}
|
|
243
|
+
/>
|
|
244
|
+
) : null}
|
|
245
|
+
{more ? (
|
|
246
|
+
<SectionNavSheet
|
|
247
|
+
menu={more}
|
|
248
|
+
open={open === 'more'}
|
|
249
|
+
onClose={close}
|
|
250
|
+
linkComponent={linkComponent}
|
|
251
|
+
copy={copy}
|
|
252
|
+
testId={`${dataTestId}-more-sheet`}
|
|
253
|
+
bottomOffset={sectionNavBarInset(theme)}
|
|
254
|
+
/>
|
|
255
|
+
) : null}
|
|
256
|
+
</>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SectionNavDestination, SectionNavMenu } from './SectionNav.types';
|
|
2
|
+
|
|
3
|
+
/** A count worth drawing: positive and finite. Zero and "unknown" draw nothing. */
|
|
4
|
+
export function shownCount(count: number | undefined): number | undefined {
|
|
5
|
+
return count !== undefined && Number.isFinite(count) && count > 0 ? count : undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The "more" slot's badge: its entries' counts, rolled up.
|
|
10
|
+
*
|
|
11
|
+
* The same reason a folded sidebar parent shows its children's counts — the
|
|
12
|
+
* sheet starts closed, so a count only visible once it is opened is one nobody
|
|
13
|
+
* sees.
|
|
14
|
+
*/
|
|
15
|
+
export function menuCount(menu: SectionNavMenu | undefined): number | undefined {
|
|
16
|
+
if (!menu) return undefined;
|
|
17
|
+
const total = menu.groups
|
|
18
|
+
.flatMap((group) => group.entries)
|
|
19
|
+
.reduce((sum, entry) => sum + (shownCount(entry.badge) ?? 0), 0);
|
|
20
|
+
return shownCount(total);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Is the viewer on one of this menu's entries? Then its slot reads as current. */
|
|
24
|
+
export function menuActive(menu: SectionNavMenu | undefined): boolean {
|
|
25
|
+
return menu?.groups.some((group) => group.entries.some((entry) => entry.active === true)) ?? false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** What one bar slot holds, in left-to-right order. */
|
|
29
|
+
export type BarSlot =
|
|
30
|
+
| { kind: 'destination'; destination: SectionNavDestination }
|
|
31
|
+
| { kind: 'more' }
|
|
32
|
+
| { kind: 'primary' };
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The bar's slots: the destinations, then `more`, with `primary` in the MIDDLE.
|
|
36
|
+
*
|
|
37
|
+
* The middle is where a thumb rests and where a raised button reads as the
|
|
38
|
+
* one thing this bar is for. With four slots it lands between the second and
|
|
39
|
+
* the third; with an odd count it takes the lower middle, so the heavier side
|
|
40
|
+
* is the right one, under the thumb of a right-handed phone.
|
|
41
|
+
*/
|
|
42
|
+
export function barSlots(
|
|
43
|
+
destinations: readonly SectionNavDestination[],
|
|
44
|
+
hasMore: boolean,
|
|
45
|
+
hasPrimary: boolean,
|
|
46
|
+
): BarSlot[] {
|
|
47
|
+
const slots: BarSlot[] = destinations.map((destination) => ({ kind: 'destination', destination }));
|
|
48
|
+
if (hasMore) slots.push({ kind: 'more' });
|
|
49
|
+
if (hasPrimary) slots.splice(Math.floor(slots.length / 2), 0, { kind: 'primary' });
|
|
50
|
+
return slots;
|
|
51
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# SectionNav
|
|
2
|
+
|
|
3
|
+
A section's own navigation: the handful of screens one kind of work moves
|
|
4
|
+
between, drawn as a **bottom bar** on a phone or a **rail** on a wide screen.
|
|
5
|
+
|
|
6
|
+
It knows no product. The host decides every destination, label, icon, count,
|
|
7
|
+
which destination is current, and how links navigate. The component decides
|
|
8
|
+
only how they are drawn: the order of the bar's slots, the raised primary
|
|
9
|
+
action, the sheets, and the rail's sections.
|
|
10
|
+
|
|
11
|
+
## When to use it
|
|
12
|
+
|
|
13
|
+
- A work area a person moves around many times an hour, where a menu behind a
|
|
14
|
+
☰ costs a tap and a scroll every time.
|
|
15
|
+
- Use `Tabs` instead when the screens are views of ONE thing on one page, and
|
|
16
|
+
`NavigationMenu` for a whole app's menu.
|
|
17
|
+
|
|
18
|
+
## Layouts
|
|
19
|
+
|
|
20
|
+
| `layout` | draws | menus |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `bar` | a bottom row: the destinations, `more` last, `primary` raised in the middle | open as bottom sheets over the page, stopping above the bar |
|
|
23
|
+
| `rail` | a vertical list: `back`, `heading`, the destinations, then each menu | listed under their `title`: nothing is folded on a wide screen |
|
|
24
|
+
|
|
25
|
+
Which layout to use is the HOST's call. A bar beside a sidebar is two
|
|
26
|
+
navigations competing for one glance, and only the host knows what else is on
|
|
27
|
+
screen.
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
- **layout**: `'bar' | 'rail'`.
|
|
32
|
+
- **label**: the navigation landmark's accessible name.
|
|
33
|
+
- **destinations**: `SectionNavDestination[]`, each with `id`, `label`, `icon`,
|
|
34
|
+
`href`, `badge?` and `active?`.
|
|
35
|
+
- **primary**: a `SectionNavMenu` (`label`, `icon`, `title`, `groups`). In the
|
|
36
|
+
bar it is the raised button, whose name becomes `copy.close` while its sheet
|
|
37
|
+
is open.
|
|
38
|
+
- **more**: a `SectionNavMenu`. In the bar it is the last slot, and its badge
|
|
39
|
+
is the sum of its entries' badges.
|
|
40
|
+
- **back**, **heading**: the rail only.
|
|
41
|
+
- **linkComponent**: the element every `href` renders through, for example a
|
|
42
|
+
router adapter taking `href`. It defaults to a plain `<a>`. The adapter must
|
|
43
|
+
pass on `className` (the styling), `onClick` (a sheet entry closes its sheet
|
|
44
|
+
through it), `aria-current` and `data-testid`; one that drops `onClick`
|
|
45
|
+
leaves the sheet open after the navigation.
|
|
46
|
+
- **copy**: `SectionNavCopy` (`close`, `badge(count)`), REQUIRED. The component
|
|
47
|
+
carries no words. `PT_BR_SECTION_NAV_COPY` and `EN_US_SECTION_NAV_COPY` are
|
|
48
|
+
named packs; `SECTION_NAV_COPY` is the locale pack.
|
|
49
|
+
- **dataTestId**: the prefix for every test id (`section-nav`).
|
|
50
|
+
|
|
51
|
+
A menu's **groups** carry `entries`. Each entry has an `href` (a link) or an
|
|
52
|
+
`onSelect` (an action). `layout: 'grid'` draws three tiles across in the sheet;
|
|
53
|
+
`list` (the default) draws one row per entry with its `description`. Choosing
|
|
54
|
+
an entry closes the sheet.
|
|
55
|
+
|
|
56
|
+
## Counts
|
|
57
|
+
|
|
58
|
+
`badge` is how many things behind a destination wait on the viewer. `0` and
|
|
59
|
+
`undefined` draw nothing, because "nothing waiting" and "not known yet" are not
|
|
60
|
+
numbers. The count's accessible name is `copy.badge(n)`, and it is quiet
|
|
61
|
+
(`aria-live="off"`) because it moves on a poll.
|
|
62
|
+
|
|
63
|
+
## Docking the bar
|
|
64
|
+
|
|
65
|
+
Dock the bar at the foot of a flex column. `sectionNavBarInset(theme)` is its
|
|
66
|
+
full height, the phone's safe area included. Reserve it at the foot of the
|
|
67
|
+
content, so nothing the page floats (a toast, a receipt) lands under the bar.
|
|
68
|
+
|
|
69
|
+
The bar rises above a modal backdrop only while one of its OWN sheets is open,
|
|
70
|
+
so a pointer can close the sheet from the button that opened it. The rest of
|
|
71
|
+
the time a dialog the page opens sits above it. The sheet is a modal dialog
|
|
72
|
+
named by the menu's `title`: while it is open, focus stays inside it, and
|
|
73
|
+
Escape or the sheet's own close button is the way out for a keyboard or a
|
|
74
|
+
screen reader.
|
|
75
|
+
|
|
76
|
+
`aria-current="page"` stays on the destination the viewer is on, sheet open or
|
|
77
|
+
not. While a sheet is open only the LOOK moves to its slot (`data-lit`), and
|
|
78
|
+
the slot reports the sheet through `aria-expanded`.
|
|
79
|
+
|
|
80
|
+
## RaisedActionButton
|
|
81
|
+
|
|
82
|
+
The raised round button on its own, for a host that already draws its own bar:
|
|
83
|
+
drop it between two tabs in a flex row. Without `open` it is a plain action.
|
|
84
|
+
With `open` it reports `aria-expanded`, takes `closeLabel` as its name, and its
|
|
85
|
+
icon turns 45° while open.
|
|
86
|
+
|
|
87
|
+
## Test ids
|
|
88
|
+
|
|
89
|
+
`<prefix>-dest-<id>`, `<prefix>-primary`, `<prefix>-more`,
|
|
90
|
+
`<prefix>-{primary,more}-sheet`, `<prefix>-{primary,more}-sheet-entry-<id>` (bar),
|
|
91
|
+
`<prefix>-{primary,more}-entry-<id>` (rail), and `<prefix>-back`.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Box from '@mui/material/Box/index.js';
|
|
2
|
+
import type { SxProps, Theme } from '@mui/material/styles/index.js';
|
|
3
|
+
import type { ElementType, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
import { rem } from '../../../tokens/scales';
|
|
6
|
+
import { Badge } from '../../data-display/Badge';
|
|
7
|
+
|
|
8
|
+
import { shownCount } from './SectionNav.helpers';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An icon with its count on the shoulder.
|
|
12
|
+
*
|
|
13
|
+
* Quiet to assistive tech on change: the count moves on a poll, and a control
|
|
14
|
+
* that announces itself every minute is noise. Its NAME carries the number,
|
|
15
|
+
* so it is read when the control is.
|
|
16
|
+
*/
|
|
17
|
+
export function CountedIcon({
|
|
18
|
+
icon,
|
|
19
|
+
count,
|
|
20
|
+
label,
|
|
21
|
+
testId,
|
|
22
|
+
}: {
|
|
23
|
+
icon: ReactNode;
|
|
24
|
+
count: number | undefined;
|
|
25
|
+
label: (count: number) => string;
|
|
26
|
+
testId: string;
|
|
27
|
+
}): React.JSX.Element {
|
|
28
|
+
const shown = shownCount(count);
|
|
29
|
+
if (shown === undefined) return <>{icon}</>;
|
|
30
|
+
return (
|
|
31
|
+
// The library's corner count, in the warning role the sidebar's pill is
|
|
32
|
+
// drawn from.
|
|
33
|
+
<Badge
|
|
34
|
+
content={shown}
|
|
35
|
+
variant="count"
|
|
36
|
+
color="warning"
|
|
37
|
+
max={99}
|
|
38
|
+
aria-label={label(shown)}
|
|
39
|
+
aria-live="off"
|
|
40
|
+
data-testid={testId}
|
|
41
|
+
>
|
|
42
|
+
{icon}
|
|
43
|
+
</Badge>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The keyboard ring every control in the nav draws, in the theme's own colour. */
|
|
48
|
+
export function focusRing(theme: Theme): SxProps<Theme> {
|
|
49
|
+
return {
|
|
50
|
+
'&:focus-visible': {
|
|
51
|
+
outline: `${rem(theme, 2)} solid ${theme.palette.primary.main}`,
|
|
52
|
+
outlineOffset: rem(theme, -2),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The element an entry or destination renders as: the host's link for an
|
|
59
|
+
* `href`, a button for an `onSelect`.
|
|
60
|
+
*
|
|
61
|
+
* Returned as props for a `Box`, so the control stays ONE interactive
|
|
62
|
+
* element — never a button wrapped around a link.
|
|
63
|
+
*/
|
|
64
|
+
export function controlProps({
|
|
65
|
+
href,
|
|
66
|
+
onClick,
|
|
67
|
+
linkComponent,
|
|
68
|
+
}: {
|
|
69
|
+
href: string | undefined;
|
|
70
|
+
onClick?: () => void;
|
|
71
|
+
linkComponent: ElementType | undefined;
|
|
72
|
+
}): Record<string, unknown> {
|
|
73
|
+
if (href !== undefined) return { component: linkComponent ?? 'a', href, onClick };
|
|
74
|
+
return { component: 'button', type: 'button', onClick };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The base reset every clickable in the nav shares: no underline, no button chrome. */
|
|
78
|
+
export const CONTROL_RESET = {
|
|
79
|
+
border: 0,
|
|
80
|
+
background: 'none',
|
|
81
|
+
font: 'inherit',
|
|
82
|
+
color: 'inherit',
|
|
83
|
+
textDecoration: 'none',
|
|
84
|
+
cursor: 'pointer',
|
|
85
|
+
margin: 0,
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
export { Box };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useTheme } from '@mui/material/styles/index.js';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { rem } from '../../../tokens/scales';
|
|
5
|
+
|
|
6
|
+
import { Box, CONTROL_RESET, focusRing } from './SectionNav.parts';
|
|
7
|
+
|
|
8
|
+
export interface RaisedActionButtonProps {
|
|
9
|
+
/** The button's accessible name — it has no visible label. */
|
|
10
|
+
label: string;
|
|
11
|
+
icon: ReactNode;
|
|
12
|
+
onClick: () => void;
|
|
13
|
+
/**
|
|
14
|
+
* Whether what the button opens is open. Omit for a button that opens
|
|
15
|
+
* nothing (a plain action): it then carries no `aria-expanded` and its icon
|
|
16
|
+
* never turns.
|
|
17
|
+
*/
|
|
18
|
+
open?: boolean;
|
|
19
|
+
/** The name it takes while `open` — the same control closes what it opened. REQUIRED with `open`. */
|
|
20
|
+
closeLabel?: string;
|
|
21
|
+
dataTestId?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The raised round button in the middle of a bottom bar — the one control in
|
|
26
|
+
* the row that is an ACTION rather than a place.
|
|
27
|
+
*
|
|
28
|
+
* Lifted half out of the bar so it reads as different in kind from the tabs
|
|
29
|
+
* beside it, and unlabelled so it costs the row less width than a tab: that
|
|
30
|
+
* width is what lets a bar keep four labelled tabs AND this button at 320px.
|
|
31
|
+
*
|
|
32
|
+
* `SectionNav` draws it for its `primary` menu. It is exported on its own for
|
|
33
|
+
* a bar the host already draws — drop it between two tabs, in a flex row.
|
|
34
|
+
*/
|
|
35
|
+
export function RaisedActionButton({
|
|
36
|
+
label,
|
|
37
|
+
icon,
|
|
38
|
+
onClick,
|
|
39
|
+
open,
|
|
40
|
+
closeLabel,
|
|
41
|
+
dataTestId = 'raised-action',
|
|
42
|
+
}: RaisedActionButtonProps): React.JSX.Element {
|
|
43
|
+
const theme = useTheme();
|
|
44
|
+
const size = rem(theme, 52);
|
|
45
|
+
const expanded = open === true;
|
|
46
|
+
return (
|
|
47
|
+
<Box sx={{ flex: 'none', display: 'flex', alignItems: 'flex-start', justifyContent: 'center', px: 0.5 }}>
|
|
48
|
+
<Box
|
|
49
|
+
component="button"
|
|
50
|
+
type="button"
|
|
51
|
+
onClick={onClick}
|
|
52
|
+
aria-label={expanded && closeLabel ? closeLabel : label}
|
|
53
|
+
aria-expanded={open}
|
|
54
|
+
data-testid={dataTestId}
|
|
55
|
+
sx={{
|
|
56
|
+
...CONTROL_RESET,
|
|
57
|
+
...focusRing(theme),
|
|
58
|
+
width: size,
|
|
59
|
+
height: size,
|
|
60
|
+
// Lifted half out of the bar: the one control that is not a place.
|
|
61
|
+
marginTop: rem(theme, -18),
|
|
62
|
+
borderRadius: '50%',
|
|
63
|
+
display: 'flex',
|
|
64
|
+
alignItems: 'center',
|
|
65
|
+
justifyContent: 'center',
|
|
66
|
+
bgcolor: 'primary.main',
|
|
67
|
+
color: 'primary.contrastText',
|
|
68
|
+
boxShadow: theme.shadows[6],
|
|
69
|
+
'& svg': {
|
|
70
|
+
fontSize: rem(theme, 26),
|
|
71
|
+
transition: theme.transitions.create('transform', { duration: theme.transitions.duration.shorter }),
|
|
72
|
+
transform: expanded ? 'rotate(45deg)' : 'none',
|
|
73
|
+
},
|
|
74
|
+
'@media (prefers-reduced-motion: reduce)': { '& svg': { transition: 'none' } },
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{icon}
|
|
78
|
+
</Box>
|
|
79
|
+
</Box>
|
|
80
|
+
);
|
|
81
|
+
}
|