@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,73 +0,0 @@
|
|
|
1
|
-
.footer {
|
|
2
|
-
display: flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
width: 100%;
|
|
5
|
-
/* Mobile-only component — never render wider than the platform's own
|
|
6
|
-
mobile breakpoint, even if a consumer accidentally mounts it in a wider
|
|
7
|
-
container (e.g. an un-gated preview). At real mobile widths this is a
|
|
8
|
-
no-op since the viewport itself is already narrower. */
|
|
9
|
-
max-width: 768px;
|
|
10
|
-
margin-inline: auto;
|
|
11
|
-
min-height: var(--sv-shell-footer-height, 60px);
|
|
12
|
-
padding: 0 var(--sv-space-4);
|
|
13
|
-
padding-bottom: max(var(--sv-space-2), env(safe-area-inset-bottom));
|
|
14
|
-
background-color: var(--sv-color-surface);
|
|
15
|
-
border-top: 1px solid var(--sv-color-border);
|
|
16
|
-
position: relative;
|
|
17
|
-
z-index: 101;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.navItem {
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
flex: 1;
|
|
25
|
-
min-height: 52px;
|
|
26
|
-
border: none;
|
|
27
|
-
background: transparent;
|
|
28
|
-
color: var(--sv-color-text-muted);
|
|
29
|
-
font: inherit;
|
|
30
|
-
cursor: pointer;
|
|
31
|
-
text-decoration: none;
|
|
32
|
-
border-radius: var(--sv-radius-md);
|
|
33
|
-
transition: color 0.1s;
|
|
34
|
-
outline: none;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@media (hover: hover) {
|
|
38
|
-
.navItem:hover {
|
|
39
|
-
color: var(--sv-color-text-primary);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.navItem:focus-visible {
|
|
44
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
45
|
-
outline-offset: -2px;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.navItemActive {
|
|
49
|
-
color: var(--sv-color-text-primary);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.navItemApps {
|
|
53
|
-
position: relative;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.navItemAppsOpen {
|
|
57
|
-
color: var(--sv-color-text-primary);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.navItemAppsOpen::before {
|
|
61
|
-
content: '';
|
|
62
|
-
position: absolute;
|
|
63
|
-
width: 48px;
|
|
64
|
-
height: 48px;
|
|
65
|
-
border-radius: var(--sv-radius-full);
|
|
66
|
-
background: var(--sv-color-surface-sunken);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.navItemAppsOpen svg,
|
|
70
|
-
.navItemAppsOpen img {
|
|
71
|
-
position: relative;
|
|
72
|
-
z-index: 1;
|
|
73
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import { MobileFooter } from './MobileFooter';
|
|
4
|
-
import { Icon } from '../Icon/Icon';
|
|
5
|
-
|
|
6
|
-
const homeIcon = { icon: <Icon name="house" size="md" aria-hidden />, label: 'Home', active: true };
|
|
7
|
-
const searchIcon = { icon: <Icon name="search" size="md" aria-hidden />, label: 'Search' };
|
|
8
|
-
const calendarIcon = { icon: <Icon name="calendar" size="md" aria-hidden />, label: 'Calendar' };
|
|
9
|
-
const activityIcon = { icon: <Icon name="activity" size="md" aria-hidden />, label: 'Activity' };
|
|
10
|
-
|
|
11
|
-
const meta = {
|
|
12
|
-
title: 'Components/MobileFooter',
|
|
13
|
-
component: MobileFooter,
|
|
14
|
-
parameters: {
|
|
15
|
-
layout: 'padded',
|
|
16
|
-
docs: {
|
|
17
|
-
description: {
|
|
18
|
-
component:
|
|
19
|
-
'Mobile shell footer nav row (RFC 0088). The centered launcher (onOpenApps) is always rendered; leftIcons/rightIcons take 1 or 2 items each, symmetric so the launcher stays centered. Presentational only, no data fetching.',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
args: {
|
|
24
|
-
onOpenApps: () => {},
|
|
25
|
-
leftIcons: [homeIcon],
|
|
26
|
-
rightIcons: [searchIcon],
|
|
27
|
-
},
|
|
28
|
-
} satisfies Meta<typeof MobileFooter>;
|
|
29
|
-
|
|
30
|
-
export default meta;
|
|
31
|
-
type Story = StoryObj<typeof meta>;
|
|
32
|
-
|
|
33
|
-
export const Default: Story = {};
|
|
34
|
-
|
|
35
|
-
export const FiveIcons: Story = {
|
|
36
|
-
args: {
|
|
37
|
-
leftIcons: [homeIcon, calendarIcon],
|
|
38
|
-
rightIcons: [searchIcon, activityIcon],
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const LauncherOpen: Story = {
|
|
43
|
-
render: function Render(args) {
|
|
44
|
-
const [open, setOpen] = useState(true);
|
|
45
|
-
return <MobileFooter {...args} launcherOpen={open} onOpenApps={() => setOpen((v) => !v)} />;
|
|
46
|
-
},
|
|
47
|
-
};
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import { Icon } from '../Icon/Icon';
|
|
3
|
-
import styles from './MobileFooter.module.css';
|
|
4
|
-
|
|
5
|
-
export interface FooterIcon {
|
|
6
|
-
icon: ReactNode;
|
|
7
|
-
label: string;
|
|
8
|
-
href?: string;
|
|
9
|
-
onClick?: () => void;
|
|
10
|
-
active?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface MobileFooterProps extends HTMLAttributes<HTMLElement> {
|
|
14
|
-
/** Opens the app drawer/launcher. Always rendered as the centered slot;
|
|
15
|
-
* not overridable. */
|
|
16
|
-
onOpenApps: () => void;
|
|
17
|
-
/** Custom launcher icon (e.g. a plugin-supplied image). Defaults to a
|
|
18
|
-
* generic grid icon. */
|
|
19
|
-
launcherIcon?: ReactNode;
|
|
20
|
-
/** Whether the launcher's own drawer is currently open (applies the
|
|
21
|
-
* pressed/active visual state). */
|
|
22
|
-
launcherOpen?: boolean;
|
|
23
|
-
/** 1 or 2 icons rendered to the left of the centered launcher. */
|
|
24
|
-
leftIcons: FooterIcon[];
|
|
25
|
-
/** 1 or 2 icons rendered to the right of the centered launcher. Must match
|
|
26
|
-
* `leftIcons.length` — the launcher stays visually centered, so a mismatch
|
|
27
|
-
* is flagged (dev-mode only, never thrown) rather than silently allowed. */
|
|
28
|
-
rightIcons: FooterIcon[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function FooterNavItem({ icon, label, href, onClick, active }: FooterIcon) {
|
|
32
|
-
const className = [styles.navItem, active ? styles.navItemActive : ''].filter(Boolean).join(' ');
|
|
33
|
-
if (href) {
|
|
34
|
-
return (
|
|
35
|
-
<a
|
|
36
|
-
href={href}
|
|
37
|
-
className={className}
|
|
38
|
-
aria-label={label}
|
|
39
|
-
aria-current={active ? 'page' : undefined}
|
|
40
|
-
>
|
|
41
|
-
{icon}
|
|
42
|
-
</a>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
return (
|
|
46
|
-
<button type="button" className={className} aria-label={label} onClick={onClick}>
|
|
47
|
-
{icon}
|
|
48
|
-
</button>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The mobile footer bar: a centered, fixed "Apps" launcher flanked by 1 or 2
|
|
54
|
-
* overridable icons on each side (3 or 5 total, matching standard
|
|
55
|
-
* iOS/Android bottom-nav conventions). Owns its own chrome — background,
|
|
56
|
-
* border-top, safe-area-aware bottom padding, and a 768px max-width (so it
|
|
57
|
-
* never stretches full-bleed if mounted outside an actual mobile viewport)
|
|
58
|
-
* — matching the runtime shell's own mobile footer (MobileNav) exactly,
|
|
59
|
-
* since this component is meant to replace that hand-rolled markup.
|
|
60
|
-
* Presentational only — no data fetching; the consumer owns the
|
|
61
|
-
* drawer/overlay `onOpenApps` opens. See RFC 0088 for the
|
|
62
|
-
* immutable/overridable boundary this enforces.
|
|
63
|
-
*/
|
|
64
|
-
export function MobileFooter({
|
|
65
|
-
onOpenApps,
|
|
66
|
-
launcherIcon,
|
|
67
|
-
launcherOpen,
|
|
68
|
-
leftIcons,
|
|
69
|
-
rightIcons,
|
|
70
|
-
className,
|
|
71
|
-
...rest
|
|
72
|
-
}: MobileFooterProps) {
|
|
73
|
-
if (process.env.NODE_ENV !== 'production' && leftIcons.length !== rightIcons.length) {
|
|
74
|
-
console.error(
|
|
75
|
-
`MobileFooter: leftIcons (${leftIcons.length}) and rightIcons (${rightIcons.length}) ` +
|
|
76
|
-
'must have the same length so the launcher stays visually centered.',
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const cls = [styles.footer, className].filter(Boolean).join(' ');
|
|
81
|
-
const launcherClassName = [
|
|
82
|
-
styles.navItem,
|
|
83
|
-
styles.navItemApps,
|
|
84
|
-
launcherOpen ? styles.navItemAppsOpen : '',
|
|
85
|
-
]
|
|
86
|
-
.filter(Boolean)
|
|
87
|
-
.join(' ');
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<nav {...rest} className={cls} aria-label="App navigation">
|
|
91
|
-
{leftIcons.map((item) => (
|
|
92
|
-
<FooterNavItem key={item.label} {...item} />
|
|
93
|
-
))}
|
|
94
|
-
<button
|
|
95
|
-
type="button"
|
|
96
|
-
className={launcherClassName}
|
|
97
|
-
aria-label="Apps"
|
|
98
|
-
aria-expanded={launcherOpen}
|
|
99
|
-
aria-haspopup="dialog"
|
|
100
|
-
onClick={onOpenApps}
|
|
101
|
-
>
|
|
102
|
-
{launcherIcon ?? <Icon name="grid-2x2" size="md" aria-hidden />}
|
|
103
|
-
</button>
|
|
104
|
-
{rightIcons.map((item) => (
|
|
105
|
-
<FooterNavItem key={item.label} {...item} />
|
|
106
|
-
))}
|
|
107
|
-
</nav>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
@@ -1,71 +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 { MobileFooter } from '../MobileFooter';
|
|
5
|
-
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
cleanup();
|
|
8
|
-
vi.unstubAllEnvs();
|
|
9
|
-
vi.restoreAllMocks();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const oneIcon = [{ icon: <span>icon</span>, label: 'Home' }];
|
|
13
|
-
const twoIcons = [
|
|
14
|
-
{ icon: <span>icon</span>, label: 'Home' },
|
|
15
|
-
{ icon: <span>icon</span>, label: 'Calendar' },
|
|
16
|
-
];
|
|
17
|
-
const leftTwo = [
|
|
18
|
-
{ icon: <span>icon</span>, label: 'Home' },
|
|
19
|
-
{ icon: <span>icon</span>, label: 'Calendar' },
|
|
20
|
-
];
|
|
21
|
-
const rightTwo = [
|
|
22
|
-
{ icon: <span>icon</span>, label: 'Search' },
|
|
23
|
-
{ icon: <span>icon</span>, label: 'Activity' },
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
describe('MobileFooter', () => {
|
|
27
|
-
it('always renders the centered launcher button', () => {
|
|
28
|
-
render(<MobileFooter onOpenApps={() => {}} leftIcons={oneIcon} rightIcons={oneIcon} />);
|
|
29
|
-
|
|
30
|
-
expect(screen.getByRole('button', { name: 'Apps' })).toBeDefined();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('calls onOpenApps when the launcher is pressed', () => {
|
|
34
|
-
const onOpenApps = vi.fn();
|
|
35
|
-
render(<MobileFooter onOpenApps={onOpenApps} leftIcons={oneIcon} rightIcons={oneIcon} />);
|
|
36
|
-
|
|
37
|
-
fireEvent.click(screen.getByRole('button', { name: 'Apps' }));
|
|
38
|
-
expect(onOpenApps).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('renders 1+1 and 2+2 icon layouts', () => {
|
|
42
|
-
const { rerender } = render(
|
|
43
|
-
<MobileFooter onOpenApps={() => {}} leftIcons={oneIcon} rightIcons={oneIcon} />,
|
|
44
|
-
);
|
|
45
|
-
expect(screen.getAllByRole('button').length).toBe(3);
|
|
46
|
-
|
|
47
|
-
rerender(<MobileFooter onOpenApps={() => {}} leftIcons={leftTwo} rightIcons={rightTwo} />);
|
|
48
|
-
expect(screen.getByLabelText('Calendar')).toBeDefined();
|
|
49
|
-
expect(screen.getByLabelText('Activity')).toBeDefined();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('logs a dev-mode console.error when left/right icon counts mismatch, but never throws', () => {
|
|
53
|
-
vi.stubEnv('NODE_ENV', 'development');
|
|
54
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
55
|
-
|
|
56
|
-
expect(() =>
|
|
57
|
-
render(<MobileFooter onOpenApps={() => {}} leftIcons={oneIcon} rightIcons={twoIcons} />),
|
|
58
|
-
).not.toThrow();
|
|
59
|
-
|
|
60
|
-
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('leftIcons'));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('does not call console.error in production even with mismatched counts', () => {
|
|
64
|
-
vi.stubEnv('NODE_ENV', 'production');
|
|
65
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
66
|
-
|
|
67
|
-
render(<MobileFooter onOpenApps={() => {}} leftIcons={oneIcon} rightIcons={twoIcons} />);
|
|
68
|
-
|
|
69
|
-
expect(errorSpy).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
.header {
|
|
2
|
-
display: flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
justify-content: space-between;
|
|
5
|
-
gap: var(--sv-space-3);
|
|
6
|
-
width: 100%;
|
|
7
|
-
/* Mobile-only component — never render wider than the platform's own
|
|
8
|
-
mobile breakpoint, even if a consumer accidentally mounts it in a wider
|
|
9
|
-
container (e.g. an un-gated preview). At real mobile widths this is a
|
|
10
|
-
no-op since the viewport itself is already narrower. */
|
|
11
|
-
max-width: 768px;
|
|
12
|
-
margin-inline: auto;
|
|
13
|
-
min-height: var(--sv-shell-header-height, 60px);
|
|
14
|
-
padding: var(--sv-space-3) var(--sv-space-4);
|
|
15
|
-
padding-top: max(var(--sv-space-3), env(safe-area-inset-top));
|
|
16
|
-
padding-right: max(var(--sv-space-4), env(safe-area-inset-right));
|
|
17
|
-
padding-left: max(var(--sv-space-4), env(safe-area-inset-left));
|
|
18
|
-
background-color: var(--sv-color-surface);
|
|
19
|
-
border-bottom: 1px solid var(--sv-color-border);
|
|
20
|
-
position: sticky;
|
|
21
|
-
top: 0;
|
|
22
|
-
z-index: 101;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.brandGroup {
|
|
26
|
-
display: flex;
|
|
27
|
-
align-items: center;
|
|
28
|
-
gap: var(--sv-space-3);
|
|
29
|
-
min-width: 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/* Matches the runtime shell's own .mobileBrandIcon (shell.module.css) — the
|
|
33
|
-
default logo shown when the consumer doesn't supply one. */
|
|
34
|
-
.defaultLogo {
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: center;
|
|
38
|
-
flex-shrink: 0;
|
|
39
|
-
width: 36px;
|
|
40
|
-
height: 36px;
|
|
41
|
-
border-radius: var(--sv-radius-md);
|
|
42
|
-
background-color: var(--sv-color-accent);
|
|
43
|
-
color: var(--sv-color-text-on-accent);
|
|
44
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
45
|
-
overflow: hidden;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.title {
|
|
49
|
-
font-size: var(--sv-font-size-md);
|
|
50
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
51
|
-
color: var(--sv-color-text-primary);
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
text-overflow: ellipsis;
|
|
54
|
-
white-space: nowrap;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.right {
|
|
58
|
-
display: flex;
|
|
59
|
-
align-items: center;
|
|
60
|
-
gap: var(--sv-space-2);
|
|
61
|
-
margin-left: auto;
|
|
62
|
-
flex-shrink: 0;
|
|
63
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { MobileHeader } from './MobileHeader';
|
|
3
|
-
import { Avatar } from '../Avatar/Avatar';
|
|
4
|
-
import { Icon } from '../Icon/Icon';
|
|
5
|
-
|
|
6
|
-
function Logo() {
|
|
7
|
-
return (
|
|
8
|
-
<span
|
|
9
|
-
aria-label="Home"
|
|
10
|
-
style={{
|
|
11
|
-
display: 'flex',
|
|
12
|
-
alignItems: 'center',
|
|
13
|
-
justifyContent: 'center',
|
|
14
|
-
width: 28,
|
|
15
|
-
height: 28,
|
|
16
|
-
borderRadius: 'var(--sv-radius-sm)',
|
|
17
|
-
background: 'var(--sv-color-accent)',
|
|
18
|
-
color: 'var(--sv-color-text-on-accent)',
|
|
19
|
-
fontWeight: 700,
|
|
20
|
-
fontSize: 13,
|
|
21
|
-
flexShrink: 0,
|
|
22
|
-
}}
|
|
23
|
-
>
|
|
24
|
-
S
|
|
25
|
-
</span>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const meta = {
|
|
30
|
-
title: 'Components/MobileHeader',
|
|
31
|
-
component: MobileHeader,
|
|
32
|
-
parameters: {
|
|
33
|
-
layout: 'padded',
|
|
34
|
-
docs: {
|
|
35
|
-
description: {
|
|
36
|
-
component:
|
|
37
|
-
'Mobile shell header content row (RFC 0088). Logo, bell, and avatarMenu are always rendered by the consumer — the optional title is the only overridable part. Presentational only, no data fetching. See Overview/Mobile Patterns for the full immutable/overridable boundary.',
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
args: {
|
|
42
|
-
logo: <Logo />,
|
|
43
|
-
bell: <Icon name="bell" size="md" aria-label="Notifications" />,
|
|
44
|
-
avatarMenu: <Avatar name="Jamie Doe" size="sm" />,
|
|
45
|
-
},
|
|
46
|
-
} satisfies Meta<typeof MobileHeader>;
|
|
47
|
-
|
|
48
|
-
export default meta;
|
|
49
|
-
type Story = StoryObj<typeof meta>;
|
|
50
|
-
|
|
51
|
-
export const Default: Story = {};
|
|
52
|
-
|
|
53
|
-
export const WithTitle: Story = {
|
|
54
|
-
args: {
|
|
55
|
-
title: 'Tasks',
|
|
56
|
-
},
|
|
57
|
-
};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import styles from './MobileHeader.module.css';
|
|
3
|
-
|
|
4
|
-
export interface MobileHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
/** The brand/logo element — already a complete, clickable link home (e.g. an
|
|
6
|
-
* instance logo wrapped in the consumer's own router `Link`). Always
|
|
7
|
-
* rendered. `MobileHeader` stays router-agnostic, so this is provided
|
|
8
|
-
* fully built rather than as a bare icon + href. Optional — omit it to get
|
|
9
|
-
* a default "S" badge (matching the runtime shell's own fallback brand
|
|
10
|
-
* icon when no instance logo is configured), sized and styled to the same
|
|
11
|
-
* 36px/radius-md convention a custom logo is expected to follow. */
|
|
12
|
-
logo?: ReactNode;
|
|
13
|
-
/** Optional contextual title rendered beside the logo (e.g. the active
|
|
14
|
-
* plugin's name). Absent by default — this is the one overridable part of
|
|
15
|
-
* the header. See RFC 0088. */
|
|
16
|
-
title?: string;
|
|
17
|
-
/** Notification bell — always rendered; not overridable.
|
|
18
|
-
* TODO: this is just an icon slot today (the consumer wires up its own
|
|
19
|
-
* click handler/content) — it doesn't yet accommodate a full notification
|
|
20
|
-
* feature (unread badge, dropdown panel, etc. — see the runtime shell's
|
|
21
|
-
* own NotificationBell). Deferred; revisit once a plugin actually needs
|
|
22
|
-
* more than a bare bell icon here. */
|
|
23
|
-
bell: ReactNode;
|
|
24
|
-
/** Avatar / account menu trigger — always rendered; not overridable. */
|
|
25
|
-
avatarMenu: ReactNode;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Default fallback logo — matches the runtime shell's own .mobileBrandIcon
|
|
29
|
-
* exactly (36px, radius-md, accent background) for the "no instance logo
|
|
30
|
-
* configured" case, just with a fixed "S" instead of the instance name's
|
|
31
|
-
* first letter (this component can't know the instance name — no data
|
|
32
|
-
* fetching, see the component's own doc comment). */
|
|
33
|
-
function DefaultLogo() {
|
|
34
|
-
return (
|
|
35
|
-
<span className={styles.defaultLogo} aria-hidden="true">
|
|
36
|
-
S
|
|
37
|
-
</span>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The mobile header bar: brand/logo (always shown), an optional contextual
|
|
43
|
-
* title, and a fixed bell + avatar-menu cluster. Owns its own chrome —
|
|
44
|
-
* background, border-bottom, sticky positioning, safe-area-aware padding,
|
|
45
|
-
* and a 768px max-width (so it never stretches full-bleed if mounted outside
|
|
46
|
-
* an actual mobile viewport) — matching the runtime shell's own mobile
|
|
47
|
-
* header exactly, since this component is meant to replace that hand-rolled
|
|
48
|
-
* markup. Presentational only — no data fetching; the consumer supplies
|
|
49
|
-
* `bell`/`avatarMenu` (and, optionally, `logo`) as already-wired components
|
|
50
|
-
* (their own sizing — 36px logo/avatar, 44px bell tap target — is the
|
|
51
|
-
* consumer's responsibility, matching the reference implementation in the
|
|
52
|
-
* runtime shell). See RFC 0088 for the immutable/overridable boundary this
|
|
53
|
-
* enforces.
|
|
54
|
-
*/
|
|
55
|
-
export function MobileHeader({
|
|
56
|
-
logo,
|
|
57
|
-
title,
|
|
58
|
-
bell,
|
|
59
|
-
avatarMenu,
|
|
60
|
-
className,
|
|
61
|
-
...rest
|
|
62
|
-
}: MobileHeaderProps) {
|
|
63
|
-
const cls = [styles.header, className].filter(Boolean).join(' ');
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div className={cls} {...rest}>
|
|
67
|
-
<div className={styles.brandGroup}>
|
|
68
|
-
{logo ?? <DefaultLogo />}
|
|
69
|
-
{title && <span className={styles.title}>{title}</span>}
|
|
70
|
-
</div>
|
|
71
|
-
<div className={styles.right}>
|
|
72
|
-
{bell}
|
|
73
|
-
{avatarMenu}
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import { cleanup, render, screen } from '@testing-library/react';
|
|
4
|
-
import { MobileHeader } from '../MobileHeader';
|
|
5
|
-
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
cleanup();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
describe('MobileHeader', () => {
|
|
11
|
-
it('always renders the logo, bell, and avatar menu slots', () => {
|
|
12
|
-
render(
|
|
13
|
-
<MobileHeader
|
|
14
|
-
logo={<a href="/">logo</a>}
|
|
15
|
-
bell={<span data-testid="bell">bell</span>}
|
|
16
|
-
avatarMenu={<span data-testid="avatar">avatar</span>}
|
|
17
|
-
/>,
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
expect(screen.getByText('logo')).toBeDefined();
|
|
21
|
-
expect(screen.getByTestId('bell')).toBeDefined();
|
|
22
|
-
expect(screen.getByTestId('avatar')).toBeDefined();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('renders no title by default', () => {
|
|
26
|
-
render(<MobileHeader logo={<a href="/">logo</a>} bell={<span />} avatarMenu={<span />} />);
|
|
27
|
-
|
|
28
|
-
expect(screen.queryByText(/tasks/i)).toBeNull();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('renders the optional title when provided', () => {
|
|
32
|
-
render(
|
|
33
|
-
<MobileHeader
|
|
34
|
-
logo={<a href="/">logo</a>}
|
|
35
|
-
title="Tasks"
|
|
36
|
-
bell={<span />}
|
|
37
|
-
avatarMenu={<span />}
|
|
38
|
-
/>,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
expect(screen.getByText('Tasks')).toBeDefined();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('renders a default "S" badge when logo is omitted', () => {
|
|
45
|
-
render(<MobileHeader bell={<span />} avatarMenu={<span />} />);
|
|
46
|
-
|
|
47
|
-
expect(screen.getByText('S')).toBeDefined();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('renders the supplied logo instead of the default badge when provided', () => {
|
|
51
|
-
render(
|
|
52
|
-
<MobileHeader logo={<a href="/">custom logo</a>} bell={<span />} avatarMenu={<span />} />,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
expect(screen.getByText('custom logo')).toBeDefined();
|
|
56
|
-
expect(screen.queryByText('S')).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
.nav {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-wrap: wrap;
|
|
4
|
-
gap: 0;
|
|
5
|
-
border-bottom: 1px solid var(--sv-color-border);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.link {
|
|
9
|
-
padding: var(--sv-space-2) var(--sv-space-3);
|
|
10
|
-
padding-bottom: calc(var(--sv-space-2) + 2px);
|
|
11
|
-
border-bottom: 2px solid transparent;
|
|
12
|
-
font-size: var(--sv-font-size-sm);
|
|
13
|
-
font-weight: var(--sv-font-weight-medium);
|
|
14
|
-
color: var(--sv-color-text-muted);
|
|
15
|
-
text-decoration: none;
|
|
16
|
-
white-space: nowrap;
|
|
17
|
-
transition: color 0.1s ease;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@media (hover: hover) {
|
|
21
|
-
.link:hover {
|
|
22
|
-
color: var(--sv-color-text-primary);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.link:focus-visible {
|
|
27
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
28
|
-
outline-offset: -2px;
|
|
29
|
-
border-radius: var(--sv-radius-sm);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.active {
|
|
33
|
-
color: var(--sv-color-text-primary);
|
|
34
|
-
font-weight: var(--sv-font-weight-semibold);
|
|
35
|
-
border-bottom-color: var(--sv-color-text-primary);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@media (max-width: 640px) {
|
|
39
|
-
.nav {
|
|
40
|
-
flex-wrap: nowrap;
|
|
41
|
-
overflow-x: auto;
|
|
42
|
-
scrollbar-width: none;
|
|
43
|
-
-webkit-overflow-scrolling: touch;
|
|
44
|
-
mask-image: linear-gradient(to right, black calc(100% - 32px), transparent 100%);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.nav::-webkit-scrollbar {
|
|
48
|
-
display: none;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Fragment, type ReactNode } from 'react';
|
|
2
|
-
import styles from './NavTabs.module.css';
|
|
3
|
-
|
|
4
|
-
export interface NavTabItem {
|
|
5
|
-
label: string;
|
|
6
|
-
href: string;
|
|
7
|
-
active?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Props to spread onto a consumer-supplied link element — keeps NavTabs' own
|
|
11
|
-
* class names and a11y attributes in sync with whatever the consumer renders. */
|
|
12
|
-
export interface NavTabLinkProps {
|
|
13
|
-
href: string;
|
|
14
|
-
className: string;
|
|
15
|
-
'aria-current': 'page' | undefined;
|
|
16
|
-
children: ReactNode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface NavTabsProps {
|
|
20
|
-
items: NavTabItem[];
|
|
21
|
-
className?: string;
|
|
22
|
-
'aria-label'?: string;
|
|
23
|
-
/** Render a tab as something other than a plain `<a href>` — e.g. Next's
|
|
24
|
-
* `<Link replace>` inside an overlay-shell plugin, where a full page
|
|
25
|
-
* navigation would break the dialog's history contract. Receives the item
|
|
26
|
-
* plus the class name/aria attributes NavTabs would otherwise apply. */
|
|
27
|
-
renderLink?: (item: NavTabItem, linkProps: NavTabLinkProps) => ReactNode;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function NavTabs({ items, className, 'aria-label': ariaLabel, renderLink }: NavTabsProps) {
|
|
31
|
-
return (
|
|
32
|
-
<nav
|
|
33
|
-
className={[styles.nav, className].filter(Boolean).join(' ')}
|
|
34
|
-
aria-label={ariaLabel ?? 'Page navigation'}
|
|
35
|
-
>
|
|
36
|
-
{items.map((item) => {
|
|
37
|
-
const linkProps: NavTabLinkProps = {
|
|
38
|
-
href: item.href,
|
|
39
|
-
className: [styles.link, item.active && styles.active].filter(Boolean).join(' '),
|
|
40
|
-
'aria-current': item.active ? 'page' : undefined,
|
|
41
|
-
children: item.label,
|
|
42
|
-
};
|
|
43
|
-
return (
|
|
44
|
-
<Fragment key={item.href}>
|
|
45
|
-
{renderLink ? (
|
|
46
|
-
renderLink(item, linkProps)
|
|
47
|
-
) : (
|
|
48
|
-
<a
|
|
49
|
-
href={linkProps.href}
|
|
50
|
-
className={linkProps.className}
|
|
51
|
-
aria-current={linkProps['aria-current']}
|
|
52
|
-
>
|
|
53
|
-
{linkProps.children}
|
|
54
|
-
</a>
|
|
55
|
-
)}
|
|
56
|
-
</Fragment>
|
|
57
|
-
);
|
|
58
|
-
})}
|
|
59
|
-
</nav>
|
|
60
|
-
);
|
|
61
|
-
}
|