@sovereignfs/ui 0.54.0 → 0.89.1
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/LICENSE +373 -661
- package/NOTICE +8 -0
- package/README.md +4 -6
- package/dist/Accordion.module.css +1 -1
- package/dist/Alert.module.css +1 -1
- package/dist/AppsLauncher.module.css +102 -0
- package/dist/Avatar.module.css +1 -1
- package/dist/Badge.module.css +30 -7
- package/dist/BalanceChip.module.css +1 -1
- package/dist/Button.module.css +23 -2
- package/dist/Card.module.css +7 -1
- package/dist/CardTile.module.css +187 -0
- package/dist/CheckableListRow.module.css +8 -0
- package/dist/Checkbox.module.css +8 -0
- package/dist/CodeTextarea.module.css +7 -1
- package/dist/Collapsible.module.css +1 -1
- package/dist/ColorPicker.module.css +138 -0
- package/dist/Combobox.module.css +8 -2
- package/dist/Command.module.css +7 -1
- package/dist/ConfirmDialog.module.css +8 -2
- package/dist/ConsentPrompt.module.css +21 -0
- package/dist/DatePicker.module.css +7 -1
- package/dist/Dialog.module.css +130 -21
- package/dist/DragHandleRow.module.css +6 -0
- package/dist/Drawer.module.css +36 -7
- package/dist/FileDropzone.module.css +25 -1
- package/dist/Header.module.css +68 -0
- package/dist/HeaderFooterLayout.module.css +21 -0
- package/dist/IconPicker.module.css +15 -5
- package/dist/Input.module.css +7 -1
- package/dist/Kbd.module.css +1 -1
- package/dist/LegalLinks.module.css +9 -2
- package/dist/Markdown.module.css +29 -0
- package/dist/Menu.module.css +1 -0
- package/dist/Message.module.css +10 -1
- package/dist/MessageScroller.module.css +12 -1
- package/dist/MobileAppsDrawer.module.css +19 -14
- package/dist/MobileFooter.module.css +7 -1
- package/dist/MobileHeader.module.css +14 -1
- package/dist/NavList.module.css +139 -0
- package/dist/NavTabs.module.css +8 -2
- package/dist/NotificationsPanel.module.css +268 -0
- package/dist/OverlayHeader.module.css +1 -1
- package/dist/PageContainer.module.css +62 -0
- package/dist/PageHeader.module.css +54 -0
- package/dist/PageLayout.module.css +51 -0
- package/dist/Popover.module.css +1 -1
- package/dist/QrCode.module.css +17 -0
- package/dist/QuantityStepper.module.css +5 -3
- package/dist/RadioGroup.module.css +6 -0
- package/dist/Resizable.module.css +3 -3
- package/dist/RootLayout.module.css +12 -0
- package/dist/SegmentedControl.module.css +7 -1
- package/dist/Select.module.css +7 -1
- package/dist/Sheet.module.css +20 -2
- package/dist/SplitPane.module.css +5 -5
- package/dist/StatusBadge.module.css +1 -1
- package/dist/StepStrip.module.css +55 -0
- package/dist/SuggestionInput.module.css +14 -3
- package/dist/SwipableMobileCarousel.module.css +12 -3
- package/dist/SwipableMobileCarouselDots.module.css +21 -0
- package/dist/SwipeStack.module.css +101 -0
- package/dist/SystemBanner.module.css +16 -0
- package/dist/Table.module.css +2 -2
- package/dist/Tabs.module.css +7 -1
- package/dist/TagInput.module.css +8 -2
- package/dist/Textarea.module.css +15 -1
- package/dist/ThreeColumnLayout.module.css +35 -0
- package/dist/Toast.module.css +67 -3
- package/dist/Toggle.module.css +7 -0
- package/dist/Tooltip.module.css +7 -1
- package/dist/UserMenu.module.css +95 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
- package/dist/index.d.ts +1156 -76
- package/dist/index.js +4223 -1513
- package/dist/tokens/fonts.css +53 -0
- package/dist/tokens/primitives.css +23 -12
- package/dist/tokens/semantic.css +32 -0
- package/dist/tokens/theme-presets.css +33 -0
- package/dist/tokens.css +2 -1
- package/package.json +46 -19
- package/src/__tests__/motion.test.tsx +0 -105
- package/src/components/Accordion/Accordion.module.css +0 -3
- package/src/components/Accordion/Accordion.stories.tsx +0 -110
- package/src/components/Accordion/Accordion.tsx +0 -53
- package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
- package/src/components/Alert/Alert.module.css +0 -62
- package/src/components/Alert/Alert.stories.tsx +0 -96
- package/src/components/Alert/Alert.tsx +0 -58
- package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
- package/src/components/AspectRatio/AspectRatio.module.css +0 -15
- package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
- package/src/components/AspectRatio/AspectRatio.tsx +0 -21
- package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
- package/src/components/Avatar/Avatar.module.css +0 -38
- package/src/components/Avatar/Avatar.tsx +0 -42
- package/src/components/Badge/Badge.module.css +0 -154
- package/src/components/Badge/Badge.stories.tsx +0 -226
- package/src/components/Badge/Badge.tsx +0 -81
- package/src/components/Badge/__tests__/Badge.test.tsx +0 -88
- package/src/components/BalanceChip/BalanceChip.module.css +0 -32
- package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
- package/src/components/BalanceChip/BalanceChip.tsx +0 -33
- package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
- package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
- package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
- package/src/components/Button/Button.module.css +0 -153
- package/src/components/Button/Button.stories.tsx +0 -83
- package/src/components/Button/Button.tsx +0 -52
- package/src/components/Button/__tests__/Button.test.tsx +0 -66
- package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
- package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
- package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
- package/src/components/Calendar/Calendar.module.css +0 -126
- package/src/components/Calendar/Calendar.tsx +0 -213
- package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
- package/src/components/Calendar/dateUtils.ts +0 -97
- package/src/components/Card/Card.module.css +0 -42
- package/src/components/Card/Card.tsx +0 -34
- package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
- package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
- package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
- package/src/components/Checkbox/Checkbox.module.css +0 -111
- package/src/components/Checkbox/Checkbox.tsx +0 -78
- package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
- package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
- package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
- package/src/components/Collapsible/Collapsible.module.css +0 -70
- package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
- package/src/components/Collapsible/Collapsible.tsx +0 -66
- package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
- package/src/components/Combobox/Combobox.module.css +0 -122
- package/src/components/Combobox/Combobox.stories.tsx +0 -79
- package/src/components/Combobox/Combobox.tsx +0 -186
- package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
- package/src/components/Command/Command.module.css +0 -80
- package/src/components/Command/Command.stories.tsx +0 -104
- package/src/components/Command/Command.tsx +0 -168
- package/src/components/Command/__tests__/Command.test.tsx +0 -162
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -124
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -146
- package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
- package/src/components/ContextMenu/ContextMenu.tsx +0 -100
- package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
- package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
- package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
- package/src/components/DataTable/DataTable.module.css +0 -28
- package/src/components/DataTable/DataTable.stories.tsx +0 -74
- package/src/components/DataTable/DataTable.tsx +0 -154
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
- package/src/components/DatePicker/DatePicker.module.css +0 -61
- package/src/components/DatePicker/DatePicker.tsx +0 -103
- package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
- package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
- package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
- package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
- package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
- package/src/components/Dialog/Dialog.module.css +0 -226
- package/src/components/Dialog/Dialog.stories.tsx +0 -147
- package/src/components/Dialog/Dialog.tsx +0 -160
- package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -137
- package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
- package/src/components/DragHandleRow/DragHandleRow.tsx +0 -61
- package/src/components/Drawer/Drawer.module.css +0 -92
- package/src/components/Drawer/Drawer.stories.tsx +0 -132
- package/src/components/Drawer/Drawer.tsx +0 -176
- package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -199
- package/src/components/EmptyState/EmptyState.module.css +0 -27
- package/src/components/EmptyState/EmptyState.tsx +0 -27
- package/src/components/FileDropzone/FileDropzone.module.css +0 -63
- package/src/components/FileDropzone/FileDropzone.tsx +0 -109
- package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
- package/src/components/FormField/FormField.module.css +0 -33
- package/src/components/FormField/FormField.tsx +0 -78
- package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
- package/src/components/HoverCard/HoverCard.module.css +0 -3
- package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
- package/src/components/HoverCard/HoverCard.tsx +0 -111
- package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
- package/src/components/Icon/Icon.module.css +0 -27
- package/src/components/Icon/Icon.stories.tsx +0 -95
- package/src/components/Icon/Icon.tsx +0 -43
- package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
- package/src/components/Icon/icons/activity.tsx +0 -20
- package/src/components/Icon/icons/alert-triangle.tsx +0 -22
- package/src/components/Icon/icons/apple.tsx +0 -21
- package/src/components/Icon/icons/banana.tsx +0 -21
- package/src/components/Icon/icons/beef.tsx +0 -22
- package/src/components/Icon/icons/beer.tsx +0 -24
- package/src/components/Icon/icons/bell.tsx +0 -21
- package/src/components/Icon/icons/calendar.tsx +0 -23
- package/src/components/Icon/icons/candy.tsx +0 -24
- package/src/components/Icon/icons/carrot.tsx +0 -22
- package/src/components/Icon/icons/check.tsx +0 -20
- package/src/components/Icon/icons/chevron-down.tsx +0 -20
- package/src/components/Icon/icons/chevron-left.tsx +0 -20
- package/src/components/Icon/icons/chevron-right.tsx +0 -20
- package/src/components/Icon/icons/chevron-up.tsx +0 -20
- package/src/components/Icon/icons/circle-check.tsx +0 -21
- package/src/components/Icon/icons/circle-x.tsx +0 -22
- package/src/components/Icon/icons/coffee.tsx +0 -23
- package/src/components/Icon/icons/cookie.tsx +0 -25
- package/src/components/Icon/icons/croissant.tsx +0 -24
- package/src/components/Icon/icons/cup-soda.tsx +0 -23
- package/src/components/Icon/icons/drumstick.tsx +0 -21
- package/src/components/Icon/icons/egg.tsx +0 -20
- package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
- package/src/components/Icon/icons/external-link.tsx +0 -22
- package/src/components/Icon/icons/eye-off.tsx +0 -23
- package/src/components/Icon/icons/eye.tsx +0 -21
- package/src/components/Icon/icons/file.tsx +0 -21
- package/src/components/Icon/icons/fish.tsx +0 -25
- package/src/components/Icon/icons/grid-2x2.tsx +0 -22
- package/src/components/Icon/icons/house.tsx +0 -21
- package/src/components/Icon/icons/index.ts +0 -234
- package/src/components/Icon/icons/info.tsx +0 -22
- package/src/components/Icon/icons/lock.tsx +0 -21
- package/src/components/Icon/icons/log-out.tsx +0 -22
- package/src/components/Icon/icons/mail.tsx +0 -21
- package/src/components/Icon/icons/menu.tsx +0 -22
- package/src/components/Icon/icons/milk.tsx +0 -22
- package/src/components/Icon/icons/package.tsx +0 -23
- package/src/components/Icon/icons/pencil.tsx +0 -21
- package/src/components/Icon/icons/pizza.tsx +0 -24
- package/src/components/Icon/icons/plus.tsx +0 -21
- package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
- package/src/components/Icon/icons/salad.tsx +0 -24
- package/src/components/Icon/icons/search.tsx +0 -21
- package/src/components/Icon/icons/settings.tsx +0 -21
- package/src/components/Icon/icons/shield.tsx +0 -20
- package/src/components/Icon/icons/shopping-basket.tsx +0 -26
- package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
- package/src/components/Icon/icons/smartphone.tsx +0 -21
- package/src/components/Icon/icons/snowflake.tsx +0 -31
- package/src/components/Icon/icons/spray-can.tsx +0 -29
- package/src/components/Icon/icons/trash-2.tsx +0 -24
- package/src/components/Icon/icons/upload.tsx +0 -22
- package/src/components/Icon/icons/user.tsx +0 -21
- package/src/components/Icon/icons/wine.tsx +0 -23
- package/src/components/Icon/icons/x.tsx +0 -21
- package/src/components/IconPicker/IconPicker.module.css +0 -63
- package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
- package/src/components/IconPicker/IconPicker.tsx +0 -90
- package/src/components/Input/Input.module.css +0 -52
- package/src/components/Input/Input.stories.tsx +0 -86
- package/src/components/Input/Input.tsx +0 -14
- package/src/components/Input/__tests__/Input.test.tsx +0 -22
- package/src/components/Item/Item.module.css +0 -70
- package/src/components/Item/Item.stories.tsx +0 -62
- package/src/components/Item/Item.tsx +0 -54
- package/src/components/Item/__tests__/Item.test.tsx +0 -39
- package/src/components/Kbd/Kbd.module.css +0 -14
- package/src/components/Kbd/Kbd.stories.tsx +0 -32
- package/src/components/Kbd/Kbd.tsx +0 -12
- package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
- package/src/components/Label/Label.module.css +0 -11
- package/src/components/Label/Label.stories.tsx +0 -40
- package/src/components/Label/Label.tsx +0 -24
- package/src/components/Label/__tests__/Label.test.tsx +0 -32
- package/src/components/LegalLinks/LegalLinks.module.css +0 -24
- package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
- package/src/components/LegalLinks/LegalLinks.tsx +0 -40
- package/src/components/Markdown/Markdown.module.css +0 -49
- package/src/components/Markdown/Markdown.stories.tsx +0 -30
- package/src/components/Markdown/Markdown.tsx +0 -168
- package/src/components/Marker/Marker.module.css +0 -33
- package/src/components/Marker/Marker.stories.tsx +0 -39
- package/src/components/Marker/Marker.tsx +0 -34
- package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
- package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
- package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
- package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
- package/src/components/Menu/Menu.module.css +0 -89
- package/src/components/Menu/Menu.tsx +0 -159
- package/src/components/Menu/__tests__/Menu.test.tsx +0 -197
- package/src/components/Menubar/Menubar.stories.tsx +0 -52
- package/src/components/Menubar/Menubar.tsx +0 -42
- package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
- package/src/components/Message/Message.module.css +0 -99
- package/src/components/Message/Message.stories.tsx +0 -79
- package/src/components/Message/Message.tsx +0 -41
- package/src/components/Message/__tests__/Message.test.tsx +0 -46
- package/src/components/MessageScroller/MessageScroller.module.css +0 -54
- package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
- package/src/components/MessageScroller/MessageScroller.tsx +0 -72
- package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -81
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
- package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
- package/src/components/MobileFooter/MobileFooter.module.css +0 -73
- package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
- package/src/components/MobileFooter/MobileFooter.tsx +0 -109
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -71
- package/src/components/MobileHeader/MobileHeader.module.css +0 -63
- package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
- package/src/components/MobileHeader/MobileHeader.tsx +0 -77
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -58
- package/src/components/NavTabs/NavTabs.module.css +0 -50
- package/src/components/NavTabs/NavTabs.tsx +0 -61
- package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
- package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
- package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
- package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
- package/src/components/OfflineGate/OfflineGate.tsx +0 -44
- package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
- package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
- package/src/components/OverlayHeader/OverlayHeader.tsx +0 -62
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -51
- package/src/components/PageContainer/PageContainer.module.css +0 -20
- package/src/components/PageContainer/PageContainer.tsx +0 -31
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -37
- package/src/components/PageHeader/PageHeader.module.css +0 -32
- package/src/components/PageHeader/PageHeader.tsx +0 -35
- package/src/components/Pagination/Pagination.module.css +0 -98
- package/src/components/Pagination/Pagination.stories.tsx +0 -49
- package/src/components/Pagination/Pagination.tsx +0 -87
- package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
- package/src/components/Popover/Popover.module.css +0 -74
- package/src/components/Popover/Popover.stories.tsx +0 -357
- package/src/components/Popover/Popover.tsx +0 -208
- package/src/components/Popover/__tests__/Popover.test.tsx +0 -98
- package/src/components/Progress/Progress.module.css +0 -20
- package/src/components/Progress/Progress.stories.tsx +0 -40
- package/src/components/Progress/Progress.tsx +0 -35
- package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
- package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
- package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
- package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
- package/src/components/RadioGroup/RadioGroup.module.css +0 -88
- package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
- package/src/components/RadioGroup/RadioGroup.tsx +0 -99
- package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
- package/src/components/Resizable/Resizable.module.css +0 -70
- package/src/components/Resizable/Resizable.stories.tsx +0 -82
- package/src/components/Resizable/Resizable.tsx +0 -313
- package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
- package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
- package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
- package/src/components/ScrollArea/ScrollArea.module.css +0 -20
- package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
- package/src/components/ScrollArea/ScrollArea.tsx +0 -27
- package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
- package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
- package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
- package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
- package/src/components/Select/Select.module.css +0 -82
- package/src/components/Select/Select.stories.tsx +0 -115
- package/src/components/Select/Select.tsx +0 -48
- package/src/components/Select/__tests__/Select.test.tsx +0 -59
- package/src/components/Sheet/Sheet.module.css +0 -55
- package/src/components/Sheet/Sheet.tsx +0 -109
- package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
- package/src/components/Slider/Slider.module.css +0 -95
- package/src/components/Slider/Slider.stories.tsx +0 -64
- package/src/components/Slider/Slider.tsx +0 -77
- package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
- package/src/components/Spinner/Spinner.module.css +0 -39
- package/src/components/Spinner/Spinner.tsx +0 -23
- package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
- package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
- package/src/components/SplitPane/SplitPane.module.css +0 -85
- package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
- package/src/components/SplitPane/SplitPane.tsx +0 -149
- package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
- package/src/components/StatusBadge/StatusBadge.module.css +0 -59
- package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
- package/src/components/StatusBadge/StatusBadge.tsx +0 -65
- package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
- package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
- package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -55
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -227
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -133
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
- package/src/components/SwipableMobileCarousel/context.ts +0 -13
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -49
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -62
- package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
- package/src/components/SystemBanner/SystemBanner.module.css +0 -69
- package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
- package/src/components/SystemBanner/SystemBanner.tsx +0 -50
- package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
- package/src/components/Table/Table.module.css +0 -45
- package/src/components/Table/Table.stories.tsx +0 -125
- package/src/components/Table/Table.tsx +0 -51
- package/src/components/Table/__tests__/Table.test.tsx +0 -50
- package/src/components/Tabs/Tabs.module.css +0 -56
- package/src/components/Tabs/Tabs.stories.tsx +0 -100
- package/src/components/Tabs/Tabs.tsx +0 -46
- package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
- package/src/components/TagInput/TagInput.module.css +0 -141
- package/src/components/TagInput/TagInput.stories.tsx +0 -109
- package/src/components/TagInput/TagInput.tsx +0 -174
- package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
- package/src/components/Textarea/Textarea.module.css +0 -39
- package/src/components/Textarea/Textarea.tsx +0 -15
- package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -29
- package/src/components/Toast/Toast.module.css +0 -132
- package/src/components/Toast/Toast.stories.tsx +0 -191
- package/src/components/Toast/Toast.tsx +0 -139
- package/src/components/Toggle/Toggle.module.css +0 -57
- package/src/components/Toggle/Toggle.stories.tsx +0 -95
- package/src/components/Toggle/Toggle.tsx +0 -39
- package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
- package/src/components/Tooltip/Tooltip.module.css +0 -56
- package/src/components/Tooltip/Tooltip.tsx +0 -30
- package/src/components/Typography/Typography.module.css +0 -47
- package/src/components/Typography/Typography.stories.tsx +0 -62
- package/src/components/Typography/Typography.tsx +0 -30
- package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
- package/src/css-modules.d.ts +0 -4
- package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
- package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
- package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
- package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
- package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
- package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
- package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -93
- package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
- package/src/hooks/index.ts +0 -24
- package/src/hooks/useCarouselRouteSync.ts +0 -92
- package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
- package/src/hooks/useDoubleTap.ts +0 -91
- package/src/hooks/useIsMobile.ts +0 -33
- package/src/hooks/useIsOffline.ts +0 -41
- package/src/hooks/useLongPress.ts +0 -185
- package/src/hooks/useOfflineTileState.ts +0 -45
- package/src/hooks/useResponsiveLayout.ts +0 -36
- package/src/hooks/useSnapCarousel.ts +0 -79
- package/src/hooks/useSwipeReveal.ts +0 -107
- package/src/index.ts +0 -251
- package/src/motion.ts +0 -78
- package/src/overlay-shell.ts +0 -94
- package/src/scroll-lock.ts +0 -22
- package/src/stories/Avatar.stories.tsx +0 -44
- package/src/stories/Card.stories.tsx +0 -35
- package/src/stories/Checkbox.stories.tsx +0 -45
- package/src/stories/DesignSystemOverview.stories.tsx +0 -3187
- package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
- package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
- package/src/stories/DragHandleRow.stories.tsx +0 -41
- package/src/stories/EmptyState.stories.tsx +0 -33
- package/src/stories/FileDropzone.stories.tsx +0 -69
- package/src/stories/FormField.stories.tsx +0 -47
- package/src/stories/InteractionHooks.stories.tsx +0 -925
- package/src/stories/MobilePatterns.stories.tsx +0 -1536
- package/src/stories/NavTabs.stories.tsx +0 -53
- package/src/stories/OfflineGate.stories.tsx +0 -42
- package/src/stories/PageContainer.stories.tsx +0 -37
- package/src/stories/PageHeader.stories.tsx +0 -42
- package/src/stories/ResponsiveSurface.stories.tsx +0 -47
- package/src/stories/Spinner.stories.tsx +0 -25
- package/src/stories/SwipableMobileCarousel.stories.tsx +0 -304
- package/src/stories/Textarea.stories.tsx +0 -51
- package/src/stories/TokenGallery.stories.tsx +0 -602
- package/src/stories/Tooltip.stories.tsx +0 -59
- package/src/tokens/primitives.css +0 -157
- package/src/tokens/semantic.css +0 -130
- package/src/tokens.css +0 -5
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
.nav {
|
|
2
|
-
min-width: 0;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.list {
|
|
6
|
-
display: flex;
|
|
7
|
-
align-items: center;
|
|
8
|
-
gap: var(--sv-space-1);
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 0;
|
|
11
|
-
list-style: none;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.link,
|
|
15
|
-
.trigger {
|
|
16
|
-
display: inline-flex;
|
|
17
|
-
align-items: center;
|
|
18
|
-
height: 36px;
|
|
19
|
-
padding: 0 var(--sv-space-3);
|
|
20
|
-
border: none;
|
|
21
|
-
border-radius: var(--sv-radius-md);
|
|
22
|
-
background: transparent;
|
|
23
|
-
color: var(--sv-color-text-primary);
|
|
24
|
-
font-family: var(--sv-font-family);
|
|
25
|
-
font-size: var(--sv-font-size-sm);
|
|
26
|
-
font-weight: var(--sv-font-weight-medium);
|
|
27
|
-
text-decoration: none;
|
|
28
|
-
cursor: pointer;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@media (hover: hover) {
|
|
32
|
-
.link:hover,
|
|
33
|
-
.trigger:hover {
|
|
34
|
-
background: var(--sv-color-surface-sunken);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.link:focus-visible,
|
|
39
|
-
.trigger:focus-visible {
|
|
40
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
41
|
-
outline-offset: 2px;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.triggerOpen {
|
|
45
|
-
background: var(--sv-color-surface-sunken);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@media (pointer: coarse) {
|
|
49
|
-
.link,
|
|
50
|
-
.trigger {
|
|
51
|
-
min-height: var(--sv-touch-target-min, 44px);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { NavigationMenu } from './NavigationMenu';
|
|
3
|
-
|
|
4
|
-
const meta = {
|
|
5
|
-
title: 'Components/NavigationMenu',
|
|
6
|
-
component: NavigationMenu,
|
|
7
|
-
parameters: {
|
|
8
|
-
layout: 'padded',
|
|
9
|
-
docs: {
|
|
10
|
-
description: {
|
|
11
|
-
component:
|
|
12
|
-
'Top-level nav bar where some items open a flyout panel. Desktop-oriented by design — a hover-triggered flyout bar has no mobile equivalent. Once one flyout is open, hovering a sibling switches directly to it; ArrowLeft/ArrowRight move focus between triggers.',
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
args: { 'aria-label': 'Main' },
|
|
17
|
-
} satisfies Meta<typeof NavigationMenu>;
|
|
18
|
-
|
|
19
|
-
export default meta;
|
|
20
|
-
type Story = StoryObj<typeof meta>;
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
const FlyoutContent = ({ links }: { links: string[] }) => (
|
|
25
|
-
<div style={{ padding: 'var(--sv-space-3)', display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
26
|
-
{links.map((link) => (
|
|
27
|
-
<a
|
|
28
|
-
key={link}
|
|
29
|
-
href={`/${link.toLowerCase().replace(/\s+/g, '-')}`}
|
|
30
|
-
style={{
|
|
31
|
-
fontSize: 14,
|
|
32
|
-
color: 'var(--sv-color-text-primary)',
|
|
33
|
-
textDecoration: 'none',
|
|
34
|
-
padding: 'var(--sv-space-1) var(--sv-space-2)',
|
|
35
|
-
}}
|
|
36
|
-
>
|
|
37
|
-
{link}
|
|
38
|
-
</a>
|
|
39
|
-
))}
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
export const Default: Story = {
|
|
44
|
-
args: {
|
|
45
|
-
items: [
|
|
46
|
-
{ label: 'Home', href: '/home' },
|
|
47
|
-
{
|
|
48
|
-
label: 'Products',
|
|
49
|
-
content: <FlyoutContent links={['Tasks', 'Ledger', 'Shopper', 'Wallet']} />,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: 'Company',
|
|
53
|
-
content: <FlyoutContent links={['About', 'Blog', 'Careers']} />,
|
|
54
|
-
},
|
|
55
|
-
{ label: 'Docs', href: '/docs' },
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
};
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useRef, useState } from 'react';
|
|
4
|
-
import type { ReactNode, KeyboardEvent as ReactKeyboardEvent } from 'react';
|
|
5
|
-
import { Popover } from '../Popover/Popover';
|
|
6
|
-
import styles from './NavigationMenu.module.css';
|
|
7
|
-
|
|
8
|
-
export interface NavigationMenuItem {
|
|
9
|
-
label: string;
|
|
10
|
-
/** A plain link with no flyout — mutually exclusive with `content`. */
|
|
11
|
-
href?: string;
|
|
12
|
-
/** Flyout panel content shown when this item is triggered. Pass a
|
|
13
|
-
* function instead of a plain node when the content needs to close its
|
|
14
|
-
* own flyout after an action (e.g. `Menubar`'s menu items) — it receives
|
|
15
|
-
* a `close` callback. */
|
|
16
|
-
content?: ReactNode | ((close: () => void) => ReactNode);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface NavigationMenuProps {
|
|
20
|
-
items: NavigationMenuItem[];
|
|
21
|
-
/** Renders a plain-link item's anchor. Defaults to `<a href>`, which
|
|
22
|
-
* causes a full page reload — pass Next's `<Link>` (or equivalent) to
|
|
23
|
-
* keep navigation client-side. */
|
|
24
|
-
renderLink?: (href: string, children: ReactNode) => ReactNode;
|
|
25
|
-
'aria-label'?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* NavigationMenu — top-level nav bar where some items open a flyout panel.
|
|
30
|
-
*
|
|
31
|
-
* Desktop-oriented by design — a hover-triggered flyout bar has no mobile
|
|
32
|
-
* equivalent (Research 0004); it renders as-is on touch rather than
|
|
33
|
-
* attempting a redesign no consumer has asked for yet.
|
|
34
|
-
*
|
|
35
|
-
* Each flyout is its own `Popover` instance rather than one shared
|
|
36
|
-
* positioned panel, reusing its collision detection per item. Once one
|
|
37
|
-
* flyout is open, moving the pointer to a sibling trigger switches directly
|
|
38
|
-
* to that one — standard menu-bar behavior — and ArrowLeft/ArrowRight move
|
|
39
|
-
* focus between top-level triggers (a roving-tabindex bar, per the
|
|
40
|
-
* WAI-ARIA menu pattern), Escape closes.
|
|
41
|
-
*/
|
|
42
|
-
export function NavigationMenu({
|
|
43
|
-
items,
|
|
44
|
-
renderLink,
|
|
45
|
-
'aria-label': ariaLabel,
|
|
46
|
-
}: NavigationMenuProps) {
|
|
47
|
-
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
|
48
|
-
const triggerRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
49
|
-
|
|
50
|
-
function handleKeyDown(e: ReactKeyboardEvent, index: number) {
|
|
51
|
-
if (e.key === 'Escape') {
|
|
52
|
-
setOpenIndex(null);
|
|
53
|
-
triggerRefs.current[index]?.focus();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
|
|
57
|
-
e.preventDefault();
|
|
58
|
-
const direction = e.key === 'ArrowRight' ? 1 : -1;
|
|
59
|
-
const nextIndex = (index + direction + items.length) % items.length;
|
|
60
|
-
triggerRefs.current[nextIndex]?.focus();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<nav aria-label={ariaLabel} className={styles.nav}>
|
|
66
|
-
<ul className={styles.list}>
|
|
67
|
-
{items.map((item, index) => {
|
|
68
|
-
if (!item.content) {
|
|
69
|
-
const linkChildren = item.label;
|
|
70
|
-
return (
|
|
71
|
-
<li key={item.label}>
|
|
72
|
-
{item.href && renderLink ? (
|
|
73
|
-
renderLink(item.href, linkChildren)
|
|
74
|
-
) : (
|
|
75
|
-
<a href={item.href} className={styles.link}>
|
|
76
|
-
{linkChildren}
|
|
77
|
-
</a>
|
|
78
|
-
)}
|
|
79
|
-
</li>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const isOpen = openIndex === index;
|
|
84
|
-
return (
|
|
85
|
-
<li key={item.label}>
|
|
86
|
-
<Popover
|
|
87
|
-
aria-label={item.label}
|
|
88
|
-
open={isOpen}
|
|
89
|
-
onClose={() => setOpenIndex((current) => (current === index ? null : current))}
|
|
90
|
-
trigger={
|
|
91
|
-
<button
|
|
92
|
-
ref={(el) => {
|
|
93
|
-
triggerRefs.current[index] = el;
|
|
94
|
-
}}
|
|
95
|
-
type="button"
|
|
96
|
-
className={[styles.trigger, isOpen ? styles.triggerOpen : '']
|
|
97
|
-
.filter(Boolean)
|
|
98
|
-
.join(' ')}
|
|
99
|
-
aria-expanded={isOpen}
|
|
100
|
-
onClick={() => setOpenIndex((current) => (current === index ? null : index))}
|
|
101
|
-
onMouseEnter={() => {
|
|
102
|
-
// Once a sibling is already open, hovering another
|
|
103
|
-
// trigger switches directly to it — standard menu-bar
|
|
104
|
-
// behavior. Otherwise hovering alone does not open
|
|
105
|
-
// one; that stays an explicit click, since a nav bar
|
|
106
|
-
// (unlike HoverCard) isn't a "preview" surface.
|
|
107
|
-
setOpenIndex((current) => (current !== null ? index : current));
|
|
108
|
-
}}
|
|
109
|
-
onKeyDown={(e) => handleKeyDown(e, index)}
|
|
110
|
-
>
|
|
111
|
-
{item.label}
|
|
112
|
-
</button>
|
|
113
|
-
}
|
|
114
|
-
>
|
|
115
|
-
{typeof item.content === 'function'
|
|
116
|
-
? item.content(() => setOpenIndex(null))
|
|
117
|
-
: item.content}
|
|
118
|
-
</Popover>
|
|
119
|
-
</li>
|
|
120
|
-
);
|
|
121
|
-
})}
|
|
122
|
-
</ul>
|
|
123
|
-
</nav>
|
|
124
|
-
);
|
|
125
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
-
import { NavigationMenu } from '../NavigationMenu';
|
|
5
|
-
|
|
6
|
-
afterEach(cleanup);
|
|
7
|
-
|
|
8
|
-
const items = [
|
|
9
|
-
{ label: 'Home', href: '/home' },
|
|
10
|
-
{ label: 'Products', content: <div>Products flyout</div> },
|
|
11
|
-
{ label: 'Company', content: <div>Company flyout</div> },
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
describe('NavigationMenu', () => {
|
|
15
|
-
it('renders a plain link for an item with no content', () => {
|
|
16
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
17
|
-
const link = screen.getByRole('link', { name: 'Home' });
|
|
18
|
-
expect(link.getAttribute('href')).toBe('/home');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('renders a toggle trigger for an item with content, collapsed by default', () => {
|
|
22
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
23
|
-
expect(screen.getByRole('button', { name: 'Products' }).getAttribute('aria-expanded')).toBe(
|
|
24
|
-
'false',
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('opens a flyout on trigger click', () => {
|
|
29
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
30
|
-
fireEvent.click(screen.getByRole('button', { name: 'Products' }));
|
|
31
|
-
expect(screen.getByText('Products flyout')).toBeDefined();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('closes on a second click of the same trigger', () => {
|
|
35
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
36
|
-
const trigger = screen.getByRole('button', { name: 'Products' });
|
|
37
|
-
fireEvent.click(trigger);
|
|
38
|
-
fireEvent.click(trigger);
|
|
39
|
-
expect(screen.queryByText('Products flyout')).toBeNull();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('switches directly to a sibling on hover once one flyout is already open', () => {
|
|
43
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
44
|
-
fireEvent.click(screen.getByRole('button', { name: 'Products' }));
|
|
45
|
-
expect(screen.getByText('Products flyout')).toBeDefined();
|
|
46
|
-
|
|
47
|
-
fireEvent.mouseEnter(screen.getByRole('button', { name: 'Company' }));
|
|
48
|
-
expect(screen.getByText('Company flyout')).toBeDefined();
|
|
49
|
-
expect(screen.queryByText('Products flyout')).toBeNull();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('does not open a flyout on hover alone when nothing else is open', () => {
|
|
53
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
54
|
-
fireEvent.mouseEnter(screen.getByRole('button', { name: 'Products' }));
|
|
55
|
-
expect(screen.queryByText('Products flyout')).toBeNull();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('ArrowRight/ArrowLeft move focus between top-level triggers', () => {
|
|
59
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
60
|
-
const products = screen.getByRole('button', { name: 'Products' });
|
|
61
|
-
const company = screen.getByRole('button', { name: 'Company' });
|
|
62
|
-
products.focus();
|
|
63
|
-
fireEvent.keyDown(products, { key: 'ArrowRight' });
|
|
64
|
-
expect(document.activeElement).toBe(company);
|
|
65
|
-
fireEvent.keyDown(company, { key: 'ArrowLeft' });
|
|
66
|
-
expect(document.activeElement).toBe(products);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('Escape closes the open flyout and returns focus to its trigger', () => {
|
|
70
|
-
render(<NavigationMenu items={items} aria-label="Main" />);
|
|
71
|
-
const trigger = screen.getByRole('button', { name: 'Products' });
|
|
72
|
-
fireEvent.click(trigger);
|
|
73
|
-
fireEvent.keyDown(trigger, { key: 'Escape' });
|
|
74
|
-
expect(screen.queryByText('Products flyout')).toBeNull();
|
|
75
|
-
expect(document.activeElement).toBe(trigger);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('uses a caller-supplied renderLink for plain links instead of a bare <a>', () => {
|
|
79
|
-
render(
|
|
80
|
-
<NavigationMenu
|
|
81
|
-
items={items}
|
|
82
|
-
aria-label="Main"
|
|
83
|
-
renderLink={(href, children) => (
|
|
84
|
-
<button type="button" data-href={href}>
|
|
85
|
-
{children}
|
|
86
|
-
</button>
|
|
87
|
-
)}
|
|
88
|
-
/>,
|
|
89
|
-
);
|
|
90
|
-
expect(screen.queryByRole('link', { name: 'Home' })).toBeNull();
|
|
91
|
-
expect(screen.getByRole('button', { name: 'Home' })).toHaveProperty('dataset.href', '/home');
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
import { useIsOffline } from '../../hooks/useIsOffline';
|
|
5
|
-
import { EmptyState } from '../EmptyState/EmptyState';
|
|
6
|
-
|
|
7
|
-
export interface OfflineGateProps {
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
/** Defaults to a generic "this section" phrasing — pass the surface's own name. */
|
|
10
|
-
surfaceName?: string;
|
|
11
|
-
className?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* OfflineGate — blocks administrative/settings surfaces from rendering
|
|
16
|
-
* (possibly stale) cached content while the device has no network, showing an
|
|
17
|
-
* explanatory empty state instead (research 0012, epic task 2.32).
|
|
18
|
-
*
|
|
19
|
-
* Distinct from `OfflineBanner`: the banner is an informational overlay that
|
|
20
|
-
* coexists with normal content everywhere in the shell. This is a hard block
|
|
21
|
-
* for surfaces where operating against stale data is actively wrong — e.g. a
|
|
22
|
-
* Console user list or an Account billing page reflects a point-in-time
|
|
23
|
-
* snapshot the moment it's server-rendered, and any cached copy replayed
|
|
24
|
-
* later has no way to signal that it may already be stale. `OfflineGate`
|
|
25
|
-
* doesn't try to tell staleness apart from freshness; it removes the
|
|
26
|
-
* question by not rendering the content at all while offline.
|
|
27
|
-
*
|
|
28
|
-
* `children` is rendered unconditionally while online, so this adds no cost
|
|
29
|
-
* to the common case — it only decides what to show, never how the wrapped
|
|
30
|
-
* content itself is fetched or cached.
|
|
31
|
-
*/
|
|
32
|
-
export function OfflineGate({ children, surfaceName, className }: OfflineGateProps) {
|
|
33
|
-
const isOffline = useIsOffline();
|
|
34
|
-
if (!isOffline) return <>{children}</>;
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<EmptyState
|
|
38
|
-
icon="alert-triangle"
|
|
39
|
-
heading="You're offline"
|
|
40
|
-
description={`${surfaceName ?? 'This section'} needs a connection — reconnect to continue.`}
|
|
41
|
-
className={className}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { act, cleanup, render, screen } from '@testing-library/react';
|
|
4
|
-
import { OfflineGate } from '../OfflineGate';
|
|
5
|
-
|
|
6
|
-
describe('OfflineGate', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: true });
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
vi.unstubAllGlobals();
|
|
12
|
-
cleanup();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('renders children unchanged while online', () => {
|
|
16
|
-
render(
|
|
17
|
-
<OfflineGate>
|
|
18
|
-
<p>Live content</p>
|
|
19
|
-
</OfflineGate>,
|
|
20
|
-
);
|
|
21
|
-
expect(screen.getByText('Live content')).toBeDefined();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('replaces children with an explanatory empty state while offline', () => {
|
|
25
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
26
|
-
render(
|
|
27
|
-
<OfflineGate>
|
|
28
|
-
<p>Live content</p>
|
|
29
|
-
</OfflineGate>,
|
|
30
|
-
);
|
|
31
|
-
expect(screen.queryByText('Live content')).toBeNull();
|
|
32
|
-
expect(screen.getByText("You're offline")).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('names the surface in the offline message when provided', () => {
|
|
36
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
37
|
-
render(
|
|
38
|
-
<OfflineGate surfaceName="Console">
|
|
39
|
-
<p>Live content</p>
|
|
40
|
-
</OfflineGate>,
|
|
41
|
-
);
|
|
42
|
-
expect(screen.getByText(/Console needs a connection/)).toBeDefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('falls back to generic phrasing with no surface name', () => {
|
|
46
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
47
|
-
render(
|
|
48
|
-
<OfflineGate>
|
|
49
|
-
<p>Live content</p>
|
|
50
|
-
</OfflineGate>,
|
|
51
|
-
);
|
|
52
|
-
expect(screen.getByText(/This section needs a connection/)).toBeDefined();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('swaps back to children on reconnect', () => {
|
|
56
|
-
vi.stubGlobal('navigator', { ...window.navigator, onLine: false });
|
|
57
|
-
render(
|
|
58
|
-
<OfflineGate>
|
|
59
|
-
<p>Live content</p>
|
|
60
|
-
</OfflineGate>,
|
|
61
|
-
);
|
|
62
|
-
expect(screen.getByText("You're offline")).toBeDefined();
|
|
63
|
-
act(() => window.dispatchEvent(new Event('online')));
|
|
64
|
-
expect(screen.getByText('Live content')).toBeDefined();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/* OverlayHeader — shared fixed secondary header for Dialog's mobile mode,
|
|
2
|
-
* Sheet, and Drawer. Not itself pinned via position — the consumer renders it
|
|
3
|
-
* as a flex-shrink: 0 sibling before its own scrolling content region (see
|
|
4
|
-
* the component's own doc comment). */
|
|
5
|
-
|
|
6
|
-
.header {
|
|
7
|
-
flex-shrink: 0;
|
|
8
|
-
background: var(--sv-color-surface);
|
|
9
|
-
border-bottom: 1px solid var(--sv-color-border);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.row {
|
|
13
|
-
display: flex;
|
|
14
|
-
align-items: center;
|
|
15
|
-
gap: var(--sv-space-2);
|
|
16
|
-
padding: var(--sv-space-3) var(--sv-space-4);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.backButton {
|
|
20
|
-
display: inline-flex;
|
|
21
|
-
align-items: center;
|
|
22
|
-
justify-content: center;
|
|
23
|
-
width: var(--sv-touch-target-min, 44px);
|
|
24
|
-
height: var(--sv-touch-target-min, 44px);
|
|
25
|
-
flex-shrink: 0;
|
|
26
|
-
/* Pull the touch-target padding back so the chevron glyph still sits close
|
|
27
|
-
to the title, matching the visual (not hit-area) spacing of .row's own
|
|
28
|
-
gap — otherwise a 44px box reads as a large empty gap before the title. */
|
|
29
|
-
margin-left: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2);
|
|
30
|
-
margin-right: calc(
|
|
31
|
-
-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2 + var(--sv-space-1)
|
|
32
|
-
);
|
|
33
|
-
border: none;
|
|
34
|
-
border-radius: var(--sv-radius-md);
|
|
35
|
-
background: transparent;
|
|
36
|
-
color: var(--sv-color-text-muted);
|
|
37
|
-
cursor: pointer;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@media (hover: hover) {
|
|
41
|
-
.backButton:hover {
|
|
42
|
-
background: var(--sv-color-surface-sunken);
|
|
43
|
-
color: var(--sv-color-text-primary);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.backButton:focus-visible {
|
|
48
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
49
|
-
outline-offset: -2px;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.title {
|
|
53
|
-
flex: 1;
|
|
54
|
-
min-width: 0;
|
|
55
|
-
overflow: hidden;
|
|
56
|
-
text-overflow: ellipsis;
|
|
57
|
-
white-space: nowrap;
|
|
58
|
-
font-size: var(--sv-font-size-xl);
|
|
59
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
60
|
-
color: var(--sv-color-text-primary);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.action {
|
|
64
|
-
flex-shrink: 0;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.closeButton {
|
|
68
|
-
display: inline-flex;
|
|
69
|
-
align-items: center;
|
|
70
|
-
justify-content: center;
|
|
71
|
-
width: var(--sv-touch-target-min, 44px);
|
|
72
|
-
height: var(--sv-touch-target-min, 44px);
|
|
73
|
-
flex-shrink: 0;
|
|
74
|
-
/* Pull the touch-target padding back so the × sits flush with the row's own
|
|
75
|
-
edge padding, same reasoning as .backButton above. */
|
|
76
|
-
margin-right: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-sm)) / 2);
|
|
77
|
-
border: none;
|
|
78
|
-
border-radius: var(--sv-radius-md);
|
|
79
|
-
background: transparent;
|
|
80
|
-
color: var(--sv-color-text-muted);
|
|
81
|
-
cursor: pointer;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@media (hover: hover) {
|
|
85
|
-
.closeButton:hover {
|
|
86
|
-
background: var(--sv-color-surface-sunken);
|
|
87
|
-
color: var(--sv-color-text-primary);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.closeButton:focus-visible {
|
|
92
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
93
|
-
outline-offset: -2px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.secondRow {
|
|
97
|
-
padding: 0 var(--sv-space-2) var(--sv-space-2);
|
|
98
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { Icon } from '../Icon/Icon';
|
|
3
|
-
import styles from './OverlayHeader.module.css';
|
|
4
|
-
|
|
5
|
-
export interface OverlayHeaderProps {
|
|
6
|
-
/** Overlay title, shown between the optional back button and the trailing
|
|
7
|
-
* slots. Optional — some consumers (e.g. `Dialog`'s mobile bar) need the
|
|
8
|
-
* row's close button present even when no title applies. */
|
|
9
|
-
title?: string;
|
|
10
|
-
/** Always renders a close button; called on click. */
|
|
11
|
-
onClose: () => void;
|
|
12
|
-
/** When provided, renders a back-chevron button before the title (for
|
|
13
|
-
* hierarchical navigation within a single overlay — going back without
|
|
14
|
-
* fully closing it). Omit for a flat overlay that only ever closes. */
|
|
15
|
-
onBack?: () => void;
|
|
16
|
-
/** Trailing slot next to the close button — e.g. a "Save" action. */
|
|
17
|
-
action?: ReactNode;
|
|
18
|
-
/** A second row below the title row — e.g. a tab strip. */
|
|
19
|
-
secondRow?: ReactNode;
|
|
20
|
-
className?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* OverlayHeader — the shared fixed secondary header for `Dialog`'s mobile
|
|
25
|
-
* mode, `Sheet`, and `Drawer`: title + close (+ optional back button /
|
|
26
|
-
* trailing action / tab-strip row). Not itself `position: sticky` or
|
|
27
|
-
* `position: fixed` — it stays visually pinned because the consuming overlay
|
|
28
|
-
* renders it as a non-scrolling flex sibling before its own scrollable
|
|
29
|
-
* content region, the same technique `Dialog`'s `.mobileBar` already used
|
|
30
|
-
* before this component existed to generalize it.
|
|
31
|
-
*
|
|
32
|
-
* Content that needs a fully custom header (e.g. an editable title, or
|
|
33
|
-
* controls that aren't a simple title/back/action shape) should not use this
|
|
34
|
-
* component — render its own header row instead; `Sheet`'s `title` prop is
|
|
35
|
-
* optional specifically to leave that escape hatch open.
|
|
36
|
-
*/
|
|
37
|
-
export function OverlayHeader({
|
|
38
|
-
title,
|
|
39
|
-
onClose,
|
|
40
|
-
onBack,
|
|
41
|
-
action,
|
|
42
|
-
secondRow,
|
|
43
|
-
className,
|
|
44
|
-
}: OverlayHeaderProps) {
|
|
45
|
-
return (
|
|
46
|
-
<div className={[styles.header, className].filter(Boolean).join(' ')}>
|
|
47
|
-
<div className={styles.row}>
|
|
48
|
-
{onBack && (
|
|
49
|
-
<button type="button" className={styles.backButton} aria-label="Back" onClick={onBack}>
|
|
50
|
-
<Icon name="chevron-left" size="md" aria-hidden />
|
|
51
|
-
</button>
|
|
52
|
-
)}
|
|
53
|
-
<span className={styles.title}>{title}</span>
|
|
54
|
-
{action && <div className={styles.action}>{action}</div>}
|
|
55
|
-
<button type="button" className={styles.closeButton} aria-label="Close" onClick={onClose}>
|
|
56
|
-
<Icon name="x" size="sm" aria-hidden />
|
|
57
|
-
</button>
|
|
58
|
-
</div>
|
|
59
|
-
{secondRow && <div className={styles.secondRow}>{secondRow}</div>}
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
-
import { OverlayHeader } from '../OverlayHeader';
|
|
5
|
-
|
|
6
|
-
describe('OverlayHeader', () => {
|
|
7
|
-
afterEach(cleanup);
|
|
8
|
-
|
|
9
|
-
it('renders the title and calls onClose on the close button', () => {
|
|
10
|
-
const onClose = vi.fn();
|
|
11
|
-
render(<OverlayHeader title="Account" onClose={onClose} />);
|
|
12
|
-
expect(screen.getByText('Account')).toBeTruthy();
|
|
13
|
-
fireEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
14
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('does not render a back button when onBack is omitted', () => {
|
|
18
|
-
render(<OverlayHeader title="Account" onClose={() => {}} />);
|
|
19
|
-
expect(screen.queryByRole('button', { name: 'Back' })).toBeNull();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('renders and wires a back button when onBack is provided', () => {
|
|
23
|
-
const onBack = vi.fn();
|
|
24
|
-
render(<OverlayHeader title="Account" onClose={() => {}} onBack={onBack} />);
|
|
25
|
-
fireEvent.click(screen.getByRole('button', { name: 'Back' }));
|
|
26
|
-
expect(onBack).toHaveBeenCalledOnce();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('renders the trailing action slot', () => {
|
|
30
|
-
render(
|
|
31
|
-
<OverlayHeader
|
|
32
|
-
title="Edit list"
|
|
33
|
-
onClose={() => {}}
|
|
34
|
-
action={<button type="button">Save</button>}
|
|
35
|
-
/>,
|
|
36
|
-
);
|
|
37
|
-
expect(screen.getByRole('button', { name: 'Save' })).toBeTruthy();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('renders just the close button when title is omitted', () => {
|
|
41
|
-
const onClose = vi.fn();
|
|
42
|
-
render(<OverlayHeader onClose={onClose} />);
|
|
43
|
-
fireEvent.click(screen.getByRole('button', { name: 'Close' }));
|
|
44
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('renders the second row', () => {
|
|
48
|
-
render(<OverlayHeader title="Account" onClose={() => {}} secondRow={<nav>Tab strip</nav>} />);
|
|
49
|
-
expect(screen.getByText('Tab strip')).toBeTruthy();
|
|
50
|
-
});
|
|
51
|
-
});
|