@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,160 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { useMountTransition, usePrefersReducedMotion } from '../../motion';
|
|
5
|
-
import {
|
|
6
|
-
useOverlayFocusCapture,
|
|
7
|
-
useOverlayKeyboardTrap,
|
|
8
|
-
useOverlayScrollLock,
|
|
9
|
-
} from '../../overlay-shell';
|
|
10
|
-
import { OverlayHeader } from '../OverlayHeader/OverlayHeader';
|
|
11
|
-
import styles from './Dialog.module.css';
|
|
12
|
-
|
|
13
|
-
// Matches --sv-motion-duration-base (Dialog.module.css) — kept as a plain JS
|
|
14
|
-
// constant rather than read from the CSS custom property so the unmount timer
|
|
15
|
-
// and the CSS transition duration can't silently drift apart at build time;
|
|
16
|
-
// change both together if this value ever changes.
|
|
17
|
-
const MOTION_DURATION_MS = 250;
|
|
18
|
-
|
|
19
|
-
// undefined (the default, outside any Provider) means "no Dialog ancestor" —
|
|
20
|
-
// distinct from a real setter function, so useOverlaySecondRow can silently
|
|
21
|
-
// no-op when called outside a Dialog instead of throwing.
|
|
22
|
-
const OverlaySecondRowContext = createContext<((node: ReactNode | null) => void) | undefined>(
|
|
23
|
-
undefined,
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Lets content deep inside a `Dialog` (e.g. a plugin's own route layout,
|
|
28
|
-
* rendered several levels below wherever the `Dialog` itself is
|
|
29
|
-
* instantiated) supply the second-row content of the Dialog's mobile
|
|
30
|
-
* `OverlayHeader` — typically a tab strip. Solves the "double header"
|
|
31
|
-
* problem: without this, a plugin's own tab strip has no way to reach the
|
|
32
|
-
* Dialog's header and ends up rendered a second time, as its own sticky bar,
|
|
33
|
-
* inside the scrolling content area.
|
|
34
|
-
*
|
|
35
|
-
* A no-op when there is no enclosing `Dialog` (e.g. the same plugin layout
|
|
36
|
-
* also rendered on a plain, non-overlay route) — safe to call unconditionally.
|
|
37
|
-
* Returns whether an enclosing `Dialog` actually received the content, so a
|
|
38
|
-
* caller that also renders its own inline header/tab-strip copy for the
|
|
39
|
-
* no-Dialog case can hide that copy on mobile specifically when this
|
|
40
|
-
* returned `true` (the Dialog's own header is showing it instead there).
|
|
41
|
-
*
|
|
42
|
-
* ```tsx
|
|
43
|
-
* function AccountLayout({ children }) {
|
|
44
|
-
* const insideOverlay = useOverlaySecondRow(<nav>...tab strip...</nav>);
|
|
45
|
-
* return <div>{children}</div>;
|
|
46
|
-
* }
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export function useOverlaySecondRow(node: ReactNode | null): boolean {
|
|
50
|
-
const setSecondRow = useContext(OverlaySecondRowContext);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
if (!setSecondRow) return;
|
|
53
|
-
setSecondRow(node);
|
|
54
|
-
return () => setSecondRow(null);
|
|
55
|
-
}, [setSecondRow, node]);
|
|
56
|
-
return setSecondRow !== undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type DialogSize = 'sm' | 'md' | 'lg' | 'full';
|
|
60
|
-
|
|
61
|
-
export interface DialogProps {
|
|
62
|
-
/** Whether the dialog is shown. When false, nothing renders. */
|
|
63
|
-
open: boolean;
|
|
64
|
-
/** Called on Esc, scrim click, or the close button. */
|
|
65
|
-
onClose: () => void;
|
|
66
|
-
/** Panel size on desktop. Mobile always renders as a full-screen sheet. */
|
|
67
|
-
size?: DialogSize;
|
|
68
|
-
/** Accessible name for the dialog (sets `aria-label` on the panel). */
|
|
69
|
-
'aria-label'?: string;
|
|
70
|
-
/** On mobile: shown in the top bar alongside the close button so the title
|
|
71
|
-
* and dismiss affordance occupy the same row instead of stacking. */
|
|
72
|
-
title?: string;
|
|
73
|
-
children: ReactNode;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Dialog — a modal surface (scrim + panel) for overlay-shell plugins and any
|
|
78
|
-
* plugin that needs a dismissable layer. Router-agnostic: the caller decides
|
|
79
|
-
* what `onClose` does (the runtime's `@modal` slot wires it to `router.back()`).
|
|
80
|
-
*
|
|
81
|
-
* Behaviour: Esc and scrim-click dismiss; focus moves into the panel on open and
|
|
82
|
-
* is restored to the previously-focused element on close; Tab is trapped within
|
|
83
|
-
* the panel. Styling references `--sv-*` tokens only; on mobile the panel becomes
|
|
84
|
-
* a full-screen sheet regardless of `size`.
|
|
85
|
-
*
|
|
86
|
-
* Animated open/close: fade + scale on desktop, slide-up on mobile (matching
|
|
87
|
-
* the "feels like a page push" framing of the mobile sheet). The `open`/
|
|
88
|
-
* `onClose` API is unchanged — closing still stays mounted internally for the
|
|
89
|
-
* exit transition before actually unmounting; `prefers-reduced-motion: reduce`
|
|
90
|
-
* collapses both to near-instant.
|
|
91
|
-
*/
|
|
92
|
-
export function Dialog({
|
|
93
|
-
open,
|
|
94
|
-
onClose,
|
|
95
|
-
size = 'lg',
|
|
96
|
-
'aria-label': ariaLabel,
|
|
97
|
-
title,
|
|
98
|
-
children,
|
|
99
|
-
}: DialogProps) {
|
|
100
|
-
const panelRef = useRef<HTMLDivElement>(null);
|
|
101
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
102
|
-
const { mounted, phase } = useMountTransition(open, reducedMotion ? 0 : MOTION_DURATION_MS);
|
|
103
|
-
const [secondRow, setSecondRow] = useState<ReactNode | null>(null);
|
|
104
|
-
|
|
105
|
-
useOverlayScrollLock(mounted);
|
|
106
|
-
useOverlayFocusCapture(panelRef, open);
|
|
107
|
-
useOverlayKeyboardTrap(panelRef, open, onClose);
|
|
108
|
-
|
|
109
|
-
if (!mounted) return null;
|
|
110
|
-
const isOpenPhase = phase === 'open';
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
114
|
-
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
115
|
-
<div
|
|
116
|
-
className={[styles.scrim, isOpenPhase ? styles.scrimOpen : ''].filter(Boolean).join(' ')}
|
|
117
|
-
role="presentation"
|
|
118
|
-
onClick={(e) => {
|
|
119
|
-
if (e.target === e.currentTarget) onClose();
|
|
120
|
-
}}
|
|
121
|
-
onKeyDown={(e) => {
|
|
122
|
-
if (e.key === 'Escape') onClose();
|
|
123
|
-
}}
|
|
124
|
-
>
|
|
125
|
-
<div
|
|
126
|
-
ref={panelRef}
|
|
127
|
-
role="dialog"
|
|
128
|
-
aria-modal="true"
|
|
129
|
-
aria-label={ariaLabel ?? title}
|
|
130
|
-
tabIndex={-1}
|
|
131
|
-
className={[styles.panel, styles[size], isOpenPhase ? styles.panelOpen : '']
|
|
132
|
-
.filter(Boolean)
|
|
133
|
-
.join(' ')}
|
|
134
|
-
>
|
|
135
|
-
{/* Mobile: shared OverlayHeader (title + close in one row, hidden on
|
|
136
|
-
desktop via CSS — see .mobileHeader). secondRow is populated by a
|
|
137
|
-
descendant's useOverlaySecondRow call, e.g. a plugin's own tab
|
|
138
|
-
strip — see that hook's doc comment for why. */}
|
|
139
|
-
<OverlayHeader
|
|
140
|
-
title={title}
|
|
141
|
-
onClose={onClose}
|
|
142
|
-
secondRow={secondRow}
|
|
143
|
-
className={styles.mobileHeader}
|
|
144
|
-
/>
|
|
145
|
-
{/* Desktop: absolute close button (hidden on mobile via CSS). */}
|
|
146
|
-
<button type="button" className={styles.close} aria-label="Close" onClick={onClose}>
|
|
147
|
-
×
|
|
148
|
-
</button>
|
|
149
|
-
{/* The panel is a fixed-size box; only this region scrolls, so the
|
|
150
|
-
panel never resizes with its content and the close button stays
|
|
151
|
-
pinned. */}
|
|
152
|
-
<div className={styles.content}>
|
|
153
|
-
<OverlaySecondRowContext.Provider value={setSecondRow}>
|
|
154
|
-
{children}
|
|
155
|
-
</OverlaySecondRowContext.Provider>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
-
import { Dialog, useOverlaySecondRow } from '../Dialog';
|
|
5
|
-
|
|
6
|
-
// Dialog's exit animation reads prefers-reduced-motion via matchMedia, which
|
|
7
|
-
// jsdom does not implement. `matches: false` (motion enabled) exercises the
|
|
8
|
-
// normal animated path; the actual reduced-motion behaviour is covered by
|
|
9
|
-
// motion.ts's own tests.
|
|
10
|
-
function installMatchMedia() {
|
|
11
|
-
vi.stubGlobal(
|
|
12
|
-
'matchMedia',
|
|
13
|
-
vi.fn().mockImplementation((query: string) => ({
|
|
14
|
-
matches: false,
|
|
15
|
-
media: query,
|
|
16
|
-
addEventListener: vi.fn(),
|
|
17
|
-
removeEventListener: vi.fn(),
|
|
18
|
-
addListener: vi.fn(),
|
|
19
|
-
removeListener: vi.fn(),
|
|
20
|
-
dispatchEvent: vi.fn(),
|
|
21
|
-
})),
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('Dialog', () => {
|
|
26
|
-
beforeEach(installMatchMedia);
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
vi.unstubAllGlobals();
|
|
29
|
-
cleanup();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('renders nothing when closed', () => {
|
|
33
|
-
render(
|
|
34
|
-
<Dialog open={false} onClose={() => {}}>
|
|
35
|
-
Body
|
|
36
|
-
</Dialog>,
|
|
37
|
-
);
|
|
38
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('renders the panel with its accessible name when open', () => {
|
|
42
|
-
render(
|
|
43
|
-
<Dialog open onClose={() => {}} aria-label="Settings">
|
|
44
|
-
Body
|
|
45
|
-
</Dialog>,
|
|
46
|
-
);
|
|
47
|
-
const dialog = screen.getByRole('dialog', { name: 'Settings' });
|
|
48
|
-
expect(dialog.getAttribute('aria-modal')).toBe('true');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('calls onClose on Escape', () => {
|
|
52
|
-
const onClose = vi.fn();
|
|
53
|
-
render(
|
|
54
|
-
<Dialog open onClose={onClose}>
|
|
55
|
-
Body
|
|
56
|
-
</Dialog>,
|
|
57
|
-
);
|
|
58
|
-
// Keyboard handler is registered on document (not the dialog element) so
|
|
59
|
-
// screen readers and keyboard users can dismiss from anywhere in the page.
|
|
60
|
-
fireEvent.keyDown(document, { key: 'Escape' });
|
|
61
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('calls onClose on scrim click but not on panel click', () => {
|
|
65
|
-
const onClose = vi.fn();
|
|
66
|
-
render(
|
|
67
|
-
<Dialog open onClose={onClose} aria-label="Panel">
|
|
68
|
-
Body
|
|
69
|
-
</Dialog>,
|
|
70
|
-
);
|
|
71
|
-
fireEvent.click(screen.getByRole('dialog')); // panel — should not close
|
|
72
|
-
expect(onClose).not.toHaveBeenCalled();
|
|
73
|
-
const closeButtons = screen.getAllByRole('button', { name: 'Close' });
|
|
74
|
-
expect(closeButtons.length).toBeGreaterThan(0);
|
|
75
|
-
fireEvent.click(closeButtons[0] as HTMLElement);
|
|
76
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('applies the size class', () => {
|
|
80
|
-
render(
|
|
81
|
-
<Dialog open onClose={() => {}} size="full" aria-label="Big">
|
|
82
|
-
Body
|
|
83
|
-
</Dialog>,
|
|
84
|
-
);
|
|
85
|
-
expect(screen.getByRole('dialog').className).toContain('full');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('supports the sm size', () => {
|
|
89
|
-
render(
|
|
90
|
-
<Dialog open onClose={() => {}} size="sm" aria-label="Small">
|
|
91
|
-
Body
|
|
92
|
-
</Dialog>,
|
|
93
|
-
);
|
|
94
|
-
expect(screen.getByRole('dialog').className).toContain('sm');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('renders a descendant-supplied secondRow via useOverlaySecondRow', () => {
|
|
98
|
-
function TabStrip() {
|
|
99
|
-
useOverlaySecondRow(<nav>Tab strip</nav>);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
render(
|
|
103
|
-
<Dialog open onClose={() => {}} title="Account" aria-label="Account">
|
|
104
|
-
<TabStrip />
|
|
105
|
-
</Dialog>,
|
|
106
|
-
);
|
|
107
|
-
expect(screen.getByText('Tab strip')).toBeTruthy();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('removes the secondRow when the supplying descendant unmounts', () => {
|
|
111
|
-
function TabStrip() {
|
|
112
|
-
useOverlaySecondRow(<nav>Tab strip</nav>);
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
function Harness({ showTabs }: { showTabs: boolean }) {
|
|
116
|
-
return (
|
|
117
|
-
<Dialog open onClose={() => {}} title="Account" aria-label="Account">
|
|
118
|
-
{showTabs && <TabStrip />}
|
|
119
|
-
</Dialog>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
const { rerender } = render(<Harness showTabs />);
|
|
123
|
-
expect(screen.getByText('Tab strip')).toBeTruthy();
|
|
124
|
-
rerender(<Harness showTabs={false} />);
|
|
125
|
-
expect(screen.queryByText('Tab strip')).toBeNull();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('is a no-op when called outside a Dialog', () => {
|
|
129
|
-
function Standalone() {
|
|
130
|
-
useOverlaySecondRow(<nav>Tab strip</nav>);
|
|
131
|
-
return <span>Standalone content</span>;
|
|
132
|
-
}
|
|
133
|
-
render(<Standalone />);
|
|
134
|
-
expect(screen.getByText('Standalone content')).toBeTruthy();
|
|
135
|
-
expect(screen.queryByText('Tab strip')).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
.row {
|
|
2
|
-
display: flex;
|
|
3
|
-
align-items: flex-start;
|
|
4
|
-
gap: var(--sv-space-1);
|
|
5
|
-
position: relative;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/* Hover-reveal only where hover genuinely exists — on touch the handle would
|
|
9
|
-
otherwise never become visible (tap-generated hover doesn't count as a
|
|
10
|
-
sustained hover state, and :not(:hover) is always true with no hover
|
|
11
|
-
capability at all), so it stays visible by default there. */
|
|
12
|
-
@media (hover: hover) {
|
|
13
|
-
.row:not(:hover) .handle,
|
|
14
|
-
.row:not(:focus-within) .handle {
|
|
15
|
-
opacity: 0;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.handle {
|
|
20
|
-
flex-shrink: 0;
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
width: 24px;
|
|
25
|
-
height: 24px;
|
|
26
|
-
margin-top: 2px;
|
|
27
|
-
border: none;
|
|
28
|
-
background: none;
|
|
29
|
-
padding: 0;
|
|
30
|
-
border-radius: var(--sv-radius-sm);
|
|
31
|
-
color: var(--sv-color-text-muted);
|
|
32
|
-
cursor: grab;
|
|
33
|
-
opacity: 1;
|
|
34
|
-
transition: opacity 0.1s ease;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.handle:focus-visible {
|
|
38
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
39
|
-
outline-offset: 2px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.handle:active {
|
|
43
|
-
cursor: grabbing;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.icon {
|
|
47
|
-
pointer-events: none;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.content {
|
|
51
|
-
flex: 1;
|
|
52
|
-
min-width: 0;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.dragging {
|
|
56
|
-
opacity: 0.5;
|
|
57
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import styles from './DragHandleRow.module.css';
|
|
3
|
-
|
|
4
|
-
export interface DragHandleRowProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
/** Pass drag-listener and drag-control props from dnd-kit here. */
|
|
7
|
-
handleProps?: HTMLAttributes<HTMLButtonElement>;
|
|
8
|
-
isDragging?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* A row wrapper that surfaces a drag handle on the left.
|
|
13
|
-
* Consumers attach dnd-kit's useSortable listeners to `handleProps`.
|
|
14
|
-
*/
|
|
15
|
-
export function DragHandleRow({
|
|
16
|
-
children,
|
|
17
|
-
handleProps,
|
|
18
|
-
isDragging,
|
|
19
|
-
className,
|
|
20
|
-
...rest
|
|
21
|
-
}: DragHandleRowProps) {
|
|
22
|
-
return (
|
|
23
|
-
<div
|
|
24
|
-
{...rest}
|
|
25
|
-
className={[styles.row, isDragging ? styles.dragging : '', className]
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.join(' ')}
|
|
28
|
-
>
|
|
29
|
-
<button
|
|
30
|
-
type="button"
|
|
31
|
-
aria-label="Drag to reorder"
|
|
32
|
-
className={styles.handle}
|
|
33
|
-
tabIndex={-1}
|
|
34
|
-
{...handleProps}
|
|
35
|
-
>
|
|
36
|
-
<DragIcon />
|
|
37
|
-
</button>
|
|
38
|
-
<div className={styles.content}>{children}</div>
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function DragIcon() {
|
|
44
|
-
return (
|
|
45
|
-
<svg
|
|
46
|
-
width="14"
|
|
47
|
-
height="14"
|
|
48
|
-
viewBox="0 0 14 14"
|
|
49
|
-
fill="none"
|
|
50
|
-
aria-hidden="true"
|
|
51
|
-
className={styles.icon}
|
|
52
|
-
>
|
|
53
|
-
{/* Two columns of three dots */}
|
|
54
|
-
{[3, 7, 11].map((cy) =>
|
|
55
|
-
[4, 10].map((cx) => (
|
|
56
|
-
<circle key={`${cx}-${cy}`} cx={cx} cy={cy} r={1.2} fill="currentColor" />
|
|
57
|
-
)),
|
|
58
|
-
)}
|
|
59
|
-
</svg>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/* Drawer — bottom-sheet surface (RFC 0013). Shares scrim/shadow tokens with
|
|
2
|
-
* Dialog but slides up from the bottom instead of centering. The panel has no
|
|
3
|
-
* fixed height — it sizes to content, capped at 80dvh so some page context
|
|
4
|
-
* remains visible above. Safe-area-aware: bottom padding clears the home
|
|
5
|
-
* indicator in standalone/fullscreen mode. */
|
|
6
|
-
|
|
7
|
-
.scrim {
|
|
8
|
-
position: fixed;
|
|
9
|
-
inset: 0;
|
|
10
|
-
/* When the shell declares --sv-shell-footer-height (mobile nav bar), stop the
|
|
11
|
-
scrim above the footer so the panel never hides behind it. */
|
|
12
|
-
bottom: var(--sv-shell-footer-height, 0);
|
|
13
|
-
z-index: 100;
|
|
14
|
-
display: flex;
|
|
15
|
-
align-items: flex-end;
|
|
16
|
-
background: var(--sv-color-scrim);
|
|
17
|
-
/* Entrance/exit fade — same two-phase-mount pattern as Dialog (see motion.ts
|
|
18
|
-
and Drawer.tsx). */
|
|
19
|
-
opacity: 0;
|
|
20
|
-
transition: opacity var(--sv-motion-duration-base) var(--sv-motion-ease-out);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.scrimOpen {
|
|
24
|
-
opacity: 1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.panel {
|
|
28
|
-
width: 100%;
|
|
29
|
-
max-height: 80dvh;
|
|
30
|
-
overflow-y: auto;
|
|
31
|
-
overscroll-behavior: contain;
|
|
32
|
-
background: var(--sv-color-surface-raised);
|
|
33
|
-
border-top: 1px solid var(--sv-color-border);
|
|
34
|
-
border-radius: var(--sv-radius-3xl) var(--sv-radius-3xl) 0 0;
|
|
35
|
-
box-shadow: var(--sv-shadow-overlay);
|
|
36
|
-
/* Pull content above the home indicator / bottom bar in standalone mode. */
|
|
37
|
-
padding-bottom: env(safe-area-inset-bottom);
|
|
38
|
-
outline: none;
|
|
39
|
-
/* Slide up from below the viewport. Always a bottom sheet, so unlike Dialog
|
|
40
|
-
there's no desktop-vs-mobile fork here. */
|
|
41
|
-
transform: translateY(100%);
|
|
42
|
-
transition: transform var(--sv-motion-duration-base) var(--sv-motion-ease-out);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.panelOpen {
|
|
46
|
-
transform: translateY(0);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* 'half' snapHeight — a fixed height instead of the default content-sized cap
|
|
50
|
-
* above. Per the design-system's "half screen or less" convention for
|
|
51
|
-
* Drawer; content taller than this belongs in Sheet instead. */
|
|
52
|
-
.panelHalf {
|
|
53
|
-
height: 50dvh;
|
|
54
|
-
max-height: 50dvh;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* See Dialog.module.css's identical rule for why 0.01ms rather than 0. */
|
|
58
|
-
@media (prefers-reduced-motion: reduce) {
|
|
59
|
-
.scrim,
|
|
60
|
-
.panel {
|
|
61
|
-
transition-duration: 0.01ms;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/* Grab handle — the sole drag-initiation region for swipe-down-to-dismiss
|
|
66
|
-
* (see Drawer.tsx's doc comment for why not the whole panel). The visual bar
|
|
67
|
-
* (::before) stays the small, established 36x4px size; only the hit area
|
|
68
|
-
* grows to a comfortable touch target. */
|
|
69
|
-
.handle {
|
|
70
|
-
display: flex;
|
|
71
|
-
align-items: center;
|
|
72
|
-
justify-content: center;
|
|
73
|
-
width: 100%;
|
|
74
|
-
height: var(--sv-touch-target-min, 44px);
|
|
75
|
-
flex-shrink: 0;
|
|
76
|
-
cursor: grab;
|
|
77
|
-
/* JS-driven drag — the browser must not treat a press here as a
|
|
78
|
-
scroll/pan gesture that would fight it. */
|
|
79
|
-
touch-action: none;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.handle:active {
|
|
83
|
-
cursor: grabbing;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.handle::before {
|
|
87
|
-
content: '';
|
|
88
|
-
width: 36px;
|
|
89
|
-
height: 4px;
|
|
90
|
-
border-radius: var(--sv-radius-full);
|
|
91
|
-
background: var(--sv-color-border-strong);
|
|
92
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { expect, userEvent, within } from 'storybook/test';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { Button } from '../Button/Button';
|
|
5
|
-
import { Icon } from '../Icon/Icon';
|
|
6
|
-
import { Drawer } from './Drawer';
|
|
7
|
-
|
|
8
|
-
function DrawerDemo({ label = 'Navigation' }: { label?: string }) {
|
|
9
|
-
const [open, setOpen] = useState(false);
|
|
10
|
-
return (
|
|
11
|
-
<>
|
|
12
|
-
<Button onClick={() => setOpen(true)}>Open drawer</Button>
|
|
13
|
-
<Drawer open={open} onClose={() => setOpen(false)} aria-label={label}>
|
|
14
|
-
<ul style={{ listStyle: 'none', margin: 0, padding: '8px 0' }}>
|
|
15
|
-
{(['house', 'grid-2x2', 'settings', 'user'] as const).map((icon) => (
|
|
16
|
-
<li key={icon}>
|
|
17
|
-
<button
|
|
18
|
-
onClick={() => setOpen(false)}
|
|
19
|
-
style={{
|
|
20
|
-
display: 'flex',
|
|
21
|
-
alignItems: 'center',
|
|
22
|
-
gap: 12,
|
|
23
|
-
width: '100%',
|
|
24
|
-
padding: '12px 20px',
|
|
25
|
-
background: 'none',
|
|
26
|
-
border: 'none',
|
|
27
|
-
cursor: 'pointer',
|
|
28
|
-
fontSize: 15,
|
|
29
|
-
color: 'var(--sv-color-text-primary)',
|
|
30
|
-
fontFamily: 'system-ui',
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
<Icon name={icon} size="md" aria-hidden />
|
|
34
|
-
{icon.replace(/-/g, ' ')}
|
|
35
|
-
</button>
|
|
36
|
-
</li>
|
|
37
|
-
))}
|
|
38
|
-
</ul>
|
|
39
|
-
</Drawer>
|
|
40
|
-
</>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const meta = {
|
|
45
|
-
title: 'Components/Drawer',
|
|
46
|
-
component: Drawer,
|
|
47
|
-
parameters: {
|
|
48
|
-
layout: 'centered',
|
|
49
|
-
docs: {
|
|
50
|
-
description: {
|
|
51
|
-
component:
|
|
52
|
-
'Dismissable bottom-sheet panel. Used by the mobile shell for plugin navigation. Supports Esc, scrim-click, focus trap. Respects `env(safe-area-inset-bottom)`. Use the viewport addon at 375px to see the intended mobile context.',
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
viewport: { defaultViewport: 'mobile' },
|
|
56
|
-
},
|
|
57
|
-
} satisfies Meta<typeof Drawer>;
|
|
58
|
-
|
|
59
|
-
export default meta;
|
|
60
|
-
type Story = StoryObj<typeof meta>;
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
|
|
64
|
-
export const Default: Story = {
|
|
65
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
66
|
-
render: (_args) => <DrawerDemo />,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export const Closed: Story = {
|
|
70
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
71
|
-
render: (_args) => (
|
|
72
|
-
<Drawer open={false} onClose={() => {}} aria-label="Closed drawer">
|
|
73
|
-
<p>Never seen</p>
|
|
74
|
-
</Drawer>
|
|
75
|
-
),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
function DrawerWithTitleDemo() {
|
|
79
|
-
const [open, setOpen] = useState(false);
|
|
80
|
-
return (
|
|
81
|
-
<>
|
|
82
|
-
<Button onClick={() => setOpen(true)}>Open drawer</Button>
|
|
83
|
-
<Drawer open={open} onClose={() => setOpen(false)} title="Apps">
|
|
84
|
-
<ul style={{ listStyle: 'none', margin: 0, padding: '8px 0' }}>
|
|
85
|
-
{(['house', 'grid-2x2', 'settings', 'user'] as const).map((icon) => (
|
|
86
|
-
<li key={icon}>
|
|
87
|
-
<button
|
|
88
|
-
onClick={() => setOpen(false)}
|
|
89
|
-
style={{
|
|
90
|
-
display: 'flex',
|
|
91
|
-
alignItems: 'center',
|
|
92
|
-
gap: 12,
|
|
93
|
-
width: '100%',
|
|
94
|
-
padding: '12px 20px',
|
|
95
|
-
background: 'none',
|
|
96
|
-
border: 'none',
|
|
97
|
-
cursor: 'pointer',
|
|
98
|
-
fontSize: 15,
|
|
99
|
-
color: 'var(--sv-color-text-primary)',
|
|
100
|
-
fontFamily: 'system-ui',
|
|
101
|
-
}}
|
|
102
|
-
>
|
|
103
|
-
<Icon name={icon} size="md" aria-hidden />
|
|
104
|
-
{icon.replace(/-/g, ' ')}
|
|
105
|
-
</button>
|
|
106
|
-
</li>
|
|
107
|
-
))}
|
|
108
|
-
</ul>
|
|
109
|
-
</Drawer>
|
|
110
|
-
</>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Opt-in `title` prop renders a built-in `OverlayHeader` (title + close)
|
|
115
|
-
* below the grab handle — hidden by default (see the `Default` story), for
|
|
116
|
-
* consumers that want one instead of building their own header markup. */
|
|
117
|
-
export const WithTitle: Story = {
|
|
118
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
119
|
-
render: (_args) => <DrawerWithTitleDemo />,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/** Play function opens the drawer and asserts its list items are visible. */
|
|
123
|
-
export const OpenViaInteraction: Story = {
|
|
124
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
125
|
-
render: (_args) => <DrawerDemo label="Navigation menu" />,
|
|
126
|
-
play: async ({ canvasElement }) => {
|
|
127
|
-
const canvas = within(canvasElement);
|
|
128
|
-
await userEvent.click(canvas.getByRole('button', { name: /open drawer/i }));
|
|
129
|
-
const drawer = canvas.getByRole('navigation');
|
|
130
|
-
await expect(drawer).toBeVisible();
|
|
131
|
-
},
|
|
132
|
-
};
|