@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,100 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
import { EmptyState } from '../EmptyState/EmptyState';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* `'checking'` covers the moment before the caller's own async status check
|
|
8
|
-
* resolves — same transitional-state pattern `EncryptionSection`'s local
|
|
9
|
-
* `LocalState` uses, so this gate never flashes an empty state before the
|
|
10
|
-
* real answer is known. `'no-device-auth'` is the hard-block case (RFC 0093
|
|
11
|
-
* §5, epic task 1.22): the environment supports the tier, but this device
|
|
12
|
-
* has no passcode, fingerprint, or face unlock configured, so a key cannot
|
|
13
|
-
* be created here at all — distinct from `'not-set-up'`, where it can.
|
|
14
|
-
*/
|
|
15
|
-
export type DeviceStorageKeyGateStatus =
|
|
16
|
-
'checking' | 'unsupported' | 'no-device-auth' | 'not-set-up' | 'set-up';
|
|
17
|
-
|
|
18
|
-
export interface DeviceStorageKeyGateProps {
|
|
19
|
-
children: ReactNode;
|
|
20
|
-
/** Defaults to a generic "This app" phrasing — pass the plugin's own name. */
|
|
21
|
-
surfaceName?: string;
|
|
22
|
-
/**
|
|
23
|
-
* The caller's own `getDeviceStorageKeyStatus()` result (from
|
|
24
|
-
* `@sovereignfs/sdk/device-only-storage`) — taken as a plain status rather
|
|
25
|
-
* than computed inside this component, so `@sovereignfs/ui` never depends
|
|
26
|
-
* on `@sovereignfs/sdk` (same boundary `DeviceOnlyGate`'s `available` prop
|
|
27
|
-
* draws — this package must stay usable standalone, outside the plugin
|
|
28
|
-
* runtime — RFC 0073).
|
|
29
|
-
*/
|
|
30
|
-
status: DeviceStorageKeyGateStatus;
|
|
31
|
-
/**
|
|
32
|
-
* Rendered under the "not set up" message — typically a link to Account →
|
|
33
|
-
* Security. Not built in here: this package depends on no router, so the
|
|
34
|
-
* caller supplies its own navigable element (a plain `<a>`, a framework
|
|
35
|
-
* `<Link>`, whatever the host app uses).
|
|
36
|
-
*/
|
|
37
|
-
setupAction?: ReactNode;
|
|
38
|
-
className?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* DeviceStorageKeyGate — blocks a `device-only`-tier plugin's content until
|
|
43
|
-
* the user has set up their Device Storage Key (RFC 0093 §2, epic task
|
|
44
|
-
* 1.22). Distinct from `DeviceOnlyGate`: that one checks whether the *tier
|
|
45
|
-
* itself* is available on this surface (native shell, secure-storage bridge
|
|
46
|
-
* present); this one checks whether the user has actually completed the
|
|
47
|
-
* one-time, per-device enrollment that tier depends on. A `device-only`
|
|
48
|
-
* plugin typically wants both — `DeviceOnlyGate` outermost (no point
|
|
49
|
-
* checking enrollment on a surface that cannot offer the tier at all), this
|
|
50
|
-
* gate inside it.
|
|
51
|
-
*
|
|
52
|
-
* Enrollment is centralized and decoupled from any single plugin (RFC 0093
|
|
53
|
-
* §2): a plugin that finds no key set up does not run its own enrollment
|
|
54
|
-
* ceremony inline. It stops here and points the user at Account → Security —
|
|
55
|
-
* the one place enrollment happens for every `device-only` plugin on this
|
|
56
|
-
* device — via `setupAction`, the same opt-in, self-applied pattern
|
|
57
|
-
* `OfflineGate`/`DeviceOnlyGate` already use for their own surfaces.
|
|
58
|
-
*/
|
|
59
|
-
export function DeviceStorageKeyGate({
|
|
60
|
-
children,
|
|
61
|
-
surfaceName,
|
|
62
|
-
status,
|
|
63
|
-
setupAction,
|
|
64
|
-
className,
|
|
65
|
-
}: DeviceStorageKeyGateProps) {
|
|
66
|
-
if (status === 'checking') return null;
|
|
67
|
-
if (status === 'set-up') return <>{children}</>;
|
|
68
|
-
|
|
69
|
-
if (status === 'unsupported') {
|
|
70
|
-
return (
|
|
71
|
-
<EmptyState
|
|
72
|
-
icon="smartphone"
|
|
73
|
-
heading="Phone only"
|
|
74
|
-
description={`${surfaceName ?? 'This app'} needs a security feature (WebAuthn PRF and Origin Private File System support) that isn't available here.`}
|
|
75
|
-
className={className}
|
|
76
|
-
/>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (status === 'no-device-auth') {
|
|
81
|
-
return (
|
|
82
|
-
<EmptyState
|
|
83
|
-
icon="alert-triangle"
|
|
84
|
-
heading="Set up a device passcode"
|
|
85
|
-
description={`${surfaceName ?? 'This app'} needs a passcode, fingerprint, or face unlock set up on this device before its Device Storage Key can be created. Set one up in your device's system settings, then come back.`}
|
|
86
|
-
className={className}
|
|
87
|
-
/>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<EmptyState
|
|
93
|
-
icon="lock"
|
|
94
|
-
heading="Set up your Device Storage Key"
|
|
95
|
-
description={`${surfaceName ?? 'This app'} keeps its data only on this device, protected by a Device Storage Key you set up once in Account → Security.`}
|
|
96
|
-
action={setupAction}
|
|
97
|
-
className={className}
|
|
98
|
-
/>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
@@ -1,119 +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 { DeviceStorageKeyGate } from '../DeviceStorageKeyGate';
|
|
5
|
-
|
|
6
|
-
describe('DeviceStorageKeyGate', () => {
|
|
7
|
-
afterEach(cleanup);
|
|
8
|
-
|
|
9
|
-
it('renders nothing while checking', () => {
|
|
10
|
-
const { container } = render(
|
|
11
|
-
<DeviceStorageKeyGate status="checking">
|
|
12
|
-
<p>Notes content</p>
|
|
13
|
-
</DeviceStorageKeyGate>,
|
|
14
|
-
);
|
|
15
|
-
expect(container.firstChild).toBeNull();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('renders children unchanged once set up', () => {
|
|
19
|
-
render(
|
|
20
|
-
<DeviceStorageKeyGate status="set-up">
|
|
21
|
-
<p>Notes content</p>
|
|
22
|
-
</DeviceStorageKeyGate>,
|
|
23
|
-
);
|
|
24
|
-
expect(screen.getByText('Notes content')).toBeDefined();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('shows a "Phone only"-style message when the environment is unsupported', () => {
|
|
28
|
-
render(
|
|
29
|
-
<DeviceStorageKeyGate status="unsupported">
|
|
30
|
-
<p>Notes content</p>
|
|
31
|
-
</DeviceStorageKeyGate>,
|
|
32
|
-
);
|
|
33
|
-
expect(screen.queryByText('Notes content')).toBeNull();
|
|
34
|
-
expect(screen.getByText('Phone only')).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('shows the setup message and prompt when not-set-up', () => {
|
|
38
|
-
render(
|
|
39
|
-
<DeviceStorageKeyGate status="not-set-up">
|
|
40
|
-
<p>Notes content</p>
|
|
41
|
-
</DeviceStorageKeyGate>,
|
|
42
|
-
);
|
|
43
|
-
expect(screen.queryByText('Notes content')).toBeNull();
|
|
44
|
-
expect(screen.getByText('Set up your Device Storage Key')).toBeDefined();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('names the surface in the not-set-up message when provided', () => {
|
|
48
|
-
render(
|
|
49
|
-
<DeviceStorageKeyGate status="not-set-up" surfaceName="Notes">
|
|
50
|
-
<p>Notes content</p>
|
|
51
|
-
</DeviceStorageKeyGate>,
|
|
52
|
-
);
|
|
53
|
-
expect(screen.getByText(/Notes keeps its data only on this device/)).toBeDefined();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('falls back to generic phrasing with no surface name', () => {
|
|
57
|
-
render(
|
|
58
|
-
<DeviceStorageKeyGate status="not-set-up">
|
|
59
|
-
<p>Notes content</p>
|
|
60
|
-
</DeviceStorageKeyGate>,
|
|
61
|
-
);
|
|
62
|
-
expect(screen.getByText(/This app keeps its data only on this device/)).toBeDefined();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('renders the caller-supplied setupAction when not-set-up', () => {
|
|
66
|
-
render(
|
|
67
|
-
<DeviceStorageKeyGate
|
|
68
|
-
status="not-set-up"
|
|
69
|
-
setupAction={<a href="/account/security">Go to Account → Security</a>}
|
|
70
|
-
>
|
|
71
|
-
<p>Notes content</p>
|
|
72
|
-
</DeviceStorageKeyGate>,
|
|
73
|
-
);
|
|
74
|
-
expect(screen.getByRole('link', { name: 'Go to Account → Security' })).toBeDefined();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('renders no setupAction when unsupported, even if one is passed', () => {
|
|
78
|
-
render(
|
|
79
|
-
<DeviceStorageKeyGate
|
|
80
|
-
status="unsupported"
|
|
81
|
-
setupAction={<a href="/account/security">Go to Account → Security</a>}
|
|
82
|
-
>
|
|
83
|
-
<p>Notes content</p>
|
|
84
|
-
</DeviceStorageKeyGate>,
|
|
85
|
-
);
|
|
86
|
-
expect(screen.queryByRole('link')).toBeNull();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('shows the hard-block message when no-device-auth', () => {
|
|
90
|
-
render(
|
|
91
|
-
<DeviceStorageKeyGate status="no-device-auth">
|
|
92
|
-
<p>Notes content</p>
|
|
93
|
-
</DeviceStorageKeyGate>,
|
|
94
|
-
);
|
|
95
|
-
expect(screen.queryByText('Notes content')).toBeNull();
|
|
96
|
-
expect(screen.getByText('Set up a device passcode')).toBeDefined();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('names the surface in the no-device-auth message when provided', () => {
|
|
100
|
-
render(
|
|
101
|
-
<DeviceStorageKeyGate status="no-device-auth" surfaceName="Notes">
|
|
102
|
-
<p>Notes content</p>
|
|
103
|
-
</DeviceStorageKeyGate>,
|
|
104
|
-
);
|
|
105
|
-
expect(screen.getByText(/Notes needs a passcode, fingerprint, or face unlock/)).toBeDefined();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('renders no setupAction when no-device-auth, even if one is passed — fixing this is a device settings change, not a Sovereign link', () => {
|
|
109
|
-
render(
|
|
110
|
-
<DeviceStorageKeyGate
|
|
111
|
-
status="no-device-auth"
|
|
112
|
-
setupAction={<a href="/account/security">Go to Account → Security</a>}
|
|
113
|
-
>
|
|
114
|
-
<p>Notes content</p>
|
|
115
|
-
</DeviceStorageKeyGate>,
|
|
116
|
-
);
|
|
117
|
-
expect(screen.queryByRole('link')).toBeNull();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/* Dialog — scrim + centered panel. Tokens only (z-index is structural, not a
|
|
2
|
-
* theming value, so it has no token).
|
|
3
|
-
*
|
|
4
|
-
* Each size is a FIXED box (width AND height) — the panel never grows or
|
|
5
|
-
* shrinks with its content, so switching views inside it (e.g. overlay-plugin
|
|
6
|
-
* tabs) keeps the dialog the exact same size. Overflow scrolls inside `.content`
|
|
7
|
-
* while the panel and its close button stay put. The scrim's padding is the
|
|
8
|
-
* fixed margin `lg` leaves around the viewport; `sm`/`md` are centered and cap
|
|
9
|
-
* to that available space on short screens. On mobile every size becomes a
|
|
10
|
-
* full-screen sheet.
|
|
11
|
-
*
|
|
12
|
-
* `--sv-dialog-inset-left` lets a host inset the scrim past a fixed left nav
|
|
13
|
-
* rail (e.g. the platform sidebar) so the overlay's left margin is measured
|
|
14
|
-
* from the rail, not the viewport edge, and the rail stays visible/usable.
|
|
15
|
-
* `--sv-dialog-inset-top` (RFC 0013) mirrors this on the vertical axis: the
|
|
16
|
-
* shell sets it to the mobile header height so the header stays visible and
|
|
17
|
-
* interactive above the dialog sheet. Defaults to 0 for standalone use. */
|
|
18
|
-
|
|
19
|
-
.scrim {
|
|
20
|
-
position: fixed;
|
|
21
|
-
top: var(--sv-dialog-inset-top, 0);
|
|
22
|
-
right: 0;
|
|
23
|
-
bottom: 0;
|
|
24
|
-
left: 0;
|
|
25
|
-
z-index: 100;
|
|
26
|
-
display: flex;
|
|
27
|
-
align-items: center;
|
|
28
|
-
justify-content: center;
|
|
29
|
-
/* left edge always 0 so the backdrop covers the full viewport incl. sidebar.
|
|
30
|
-
padding-left offsets the panel's available area to start at the rail edge. */
|
|
31
|
-
padding: var(--sv-space-8);
|
|
32
|
-
padding-left: calc(var(--sv-dialog-inset-left, 0px) + var(--sv-space-8));
|
|
33
|
-
background: var(--sv-color-scrim);
|
|
34
|
-
/* Entrance/exit fade. The Dialog component drives this via a two-phase
|
|
35
|
-
mount (see motion.ts) — .scrimOpen is added one frame after mount so this
|
|
36
|
-
transition has somewhere to animate from, and removed (while staying
|
|
37
|
-
mounted) for the exit before the component actually unmounts. */
|
|
38
|
-
opacity: 0;
|
|
39
|
-
transition: opacity var(--sv-motion-duration-base) var(--sv-motion-ease-out);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.scrimOpen {
|
|
43
|
-
opacity: 1;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.panel {
|
|
47
|
-
position: relative;
|
|
48
|
-
display: flex;
|
|
49
|
-
flex-direction: column;
|
|
50
|
-
overflow: hidden;
|
|
51
|
-
background: var(--sv-color-surface-raised);
|
|
52
|
-
color: var(--sv-color-text-primary);
|
|
53
|
-
border: 1px solid var(--sv-color-border);
|
|
54
|
-
border-radius: var(--sv-radius-lg);
|
|
55
|
-
box-shadow: var(--sv-shadow-overlay);
|
|
56
|
-
/* Desktop entrance: fade + gentle scale-up. Same two-phase-mount pattern as
|
|
57
|
-
.scrim above. Mobile overrides this to a slide-up instead (below). */
|
|
58
|
-
opacity: 0;
|
|
59
|
-
transform: scale(0.96);
|
|
60
|
-
transition:
|
|
61
|
-
opacity var(--sv-motion-duration-base) var(--sv-motion-ease-out),
|
|
62
|
-
transform var(--sv-motion-duration-base) var(--sv-motion-ease-out);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.panelOpen {
|
|
66
|
-
opacity: 1;
|
|
67
|
-
transform: scale(1);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* prefers-reduced-motion: collapse both fade/scale and slide to near-instant.
|
|
71
|
-
Not literally 0 — a 0ms transition can skip applying entirely in some
|
|
72
|
-
engines, and the JS unmount timer (Dialog.tsx's MOTION_DURATION_MS,
|
|
73
|
-
switched to ~0 via usePrefersReducedMotion) needs a transition to still be
|
|
74
|
-
"present" for the visual state to actually reach its end value in time. */
|
|
75
|
-
@media (prefers-reduced-motion: reduce) {
|
|
76
|
-
.scrim,
|
|
77
|
-
.panel {
|
|
78
|
-
transition-duration: 0.01ms;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Sizing audit: reviewed padding/radius against this scale's other
|
|
83
|
-
surfaces. --sv-radius-lg (shared with Card via .panel below) left
|
|
84
|
-
unchanged for the same blast-radius reason as Card.module.css. */
|
|
85
|
-
.content {
|
|
86
|
-
flex: 1 1 auto;
|
|
87
|
-
overflow-y: auto;
|
|
88
|
-
overflow-x: hidden;
|
|
89
|
-
padding: var(--sv-space-6);
|
|
90
|
-
/* Disable elastic overscroll within the dialog: `none` stops both scroll-
|
|
91
|
-
chaining to the parent and the iOS rubber-band bounce inside the panel.
|
|
92
|
-
`contain` only prevents chaining — the panel itself still bounces, which
|
|
93
|
-
reveals white panel-background between OverlayHeader and the sticky tab
|
|
94
|
-
strip on iOS PWA. A dialog is a bounded surface where bounce is a visual
|
|
95
|
-
bug, not a feature. */
|
|
96
|
-
overscroll-behavior: none;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/* Fixed sizes. `max-*: 100%` caps to the scrim's content box (viewport minus
|
|
100
|
-
* margin) so a size never overflows a small viewport. */
|
|
101
|
-
.sm {
|
|
102
|
-
width: 24rem;
|
|
103
|
-
height: 28rem;
|
|
104
|
-
max-width: 100%;
|
|
105
|
-
max-height: 100%;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.md {
|
|
109
|
-
width: 36rem;
|
|
110
|
-
height: 42rem;
|
|
111
|
-
max-width: 100%;
|
|
112
|
-
max-height: 100%;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.lg,
|
|
116
|
-
.full {
|
|
117
|
-
width: 100%;
|
|
118
|
-
height: 100%;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.close {
|
|
122
|
-
position: absolute;
|
|
123
|
-
top: var(--sv-space-3);
|
|
124
|
-
right: var(--sv-space-3);
|
|
125
|
-
z-index: 1;
|
|
126
|
-
display: inline-flex;
|
|
127
|
-
align-items: center;
|
|
128
|
-
justify-content: center;
|
|
129
|
-
width: var(--sv-space-8);
|
|
130
|
-
height: var(--sv-space-8);
|
|
131
|
-
border: none;
|
|
132
|
-
border-radius: var(--sv-radius-md);
|
|
133
|
-
background: transparent;
|
|
134
|
-
color: var(--sv-color-text-muted);
|
|
135
|
-
font-size: var(--sv-font-size-xl);
|
|
136
|
-
line-height: 1;
|
|
137
|
-
cursor: pointer;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
@media (hover: hover) {
|
|
141
|
-
.close:hover {
|
|
142
|
-
background: var(--sv-color-surface-sunken);
|
|
143
|
-
color: var(--sv-color-text-primary);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.close:focus-visible {
|
|
148
|
-
outline: 2px solid var(--sv-color-focus-ring);
|
|
149
|
-
outline-offset: 2px;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* Hidden on desktop; shown only on mobile where it replaces the close button.
|
|
153
|
-
* OverlayHeader owns its own background/border/padding — this wrapper class
|
|
154
|
-
* only toggles visibility per breakpoint. */
|
|
155
|
-
.mobileHeader {
|
|
156
|
-
display: none;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/* Mobile: full-screen sheet (RFC 0001 §3, breakpoint unified to 768px per
|
|
160
|
-
* RFC 0013). Every size fills the scrim's available area (which is already
|
|
161
|
-
* inset from the top by --sv-dialog-inset-top when a fixed header is shown).
|
|
162
|
-
* The panel therefore fills height: 100% of the remaining space, not 100dvh. */
|
|
163
|
-
@media (max-width: 768px) {
|
|
164
|
-
/* On mobile the overlay feels like a full-page push, not a floating modal.
|
|
165
|
-
Remove the dim scrim and panel chrome so it's indistinguishable from a
|
|
166
|
-
regular page render. router.back() still closes it correctly.
|
|
167
|
-
bottom stops the scrim above the footer nav (matching Drawer) instead of
|
|
168
|
-
running the full viewport height — the panel used to extend under the
|
|
169
|
-
footer with only .content's own padding compensating for it. */
|
|
170
|
-
.scrim {
|
|
171
|
-
padding: 0;
|
|
172
|
-
background: transparent;
|
|
173
|
-
bottom: var(--sv-shell-footer-height, 0);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.panel,
|
|
177
|
-
.sm,
|
|
178
|
-
.md,
|
|
179
|
-
.lg,
|
|
180
|
-
.full {
|
|
181
|
-
width: 100%;
|
|
182
|
-
height: 100%;
|
|
183
|
-
max-width: none;
|
|
184
|
-
max-height: none;
|
|
185
|
-
border: none;
|
|
186
|
-
border-radius: 0;
|
|
187
|
-
box-shadow: none;
|
|
188
|
-
background: var(--sv-color-surface);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/* Mobile entrance is a slide-up, not the desktop fade+scale — matches "page
|
|
192
|
-
push" framing above. No opacity change (the panel is opaque full-screen
|
|
193
|
-
content, not a floating box), so only transform needs a transition here. */
|
|
194
|
-
.panel {
|
|
195
|
-
opacity: 1;
|
|
196
|
-
transform: translateY(100%);
|
|
197
|
-
transition: transform var(--sv-motion-duration-base) var(--sv-motion-ease-out);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.panelOpen {
|
|
201
|
-
transform: translateY(0);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/* Desktop close button is replaced by OverlayHeader on mobile. */
|
|
205
|
-
.close {
|
|
206
|
-
display: none;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/* Title + close button in one solid row, via the shared OverlayHeader
|
|
210
|
-
component. Being a flex sibling before .content means it never scrolls —
|
|
211
|
-
the content area scrolls below it.
|
|
212
|
-
.content itself needs no mobile override here any more: it used to zero
|
|
213
|
-
out padding-top so a plugin's hand-rolled sticky header (rendered as
|
|
214
|
-
.content's own first child, right below OverlayHeader) would sit flush
|
|
215
|
-
against it. That case no longer exists — a plugin with a tab strip/
|
|
216
|
-
secondary nav now hands it to OverlayHeader's own secondRow slot
|
|
217
|
-
(useOverlaySecondRow) instead of rendering it inside .content; Account
|
|
218
|
-
and Console both migrated to this (mobile design-system plan, Phase
|
|
219
|
-
C1b). With .content's first child back to being genuine page content, it
|
|
220
|
-
needs the same breathing room from the header that .content's own base
|
|
221
|
-
padding (var(--sv-space-6) on every side) already provides everywhere
|
|
222
|
-
else. */
|
|
223
|
-
.mobileHeader {
|
|
224
|
-
display: block;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
@@ -1,147 +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 { Dialog, useOverlaySecondRow } from './Dialog';
|
|
6
|
-
|
|
7
|
-
// Controlled wrapper so the play function can open/inspect the dialog.
|
|
8
|
-
function DialogDemo({
|
|
9
|
-
size = 'lg',
|
|
10
|
-
label = 'Example dialog',
|
|
11
|
-
}: {
|
|
12
|
-
size?: 'sm' | 'md' | 'lg' | 'full';
|
|
13
|
-
label?: string;
|
|
14
|
-
}) {
|
|
15
|
-
const [open, setOpen] = useState(false);
|
|
16
|
-
return (
|
|
17
|
-
<>
|
|
18
|
-
<Button onClick={() => setOpen(true)}>Open {size} dialog</Button>
|
|
19
|
-
<Dialog open={open} onClose={() => setOpen(false)} size={size} aria-label={label}>
|
|
20
|
-
<div style={{ padding: 24, fontFamily: 'system-ui' }}>
|
|
21
|
-
<h2
|
|
22
|
-
style={{
|
|
23
|
-
fontSize: 18,
|
|
24
|
-
fontWeight: 600,
|
|
25
|
-
color: 'var(--sv-color-text-primary)',
|
|
26
|
-
marginBottom: 12,
|
|
27
|
-
}}
|
|
28
|
-
>
|
|
29
|
-
{label}
|
|
30
|
-
</h2>
|
|
31
|
-
<p style={{ color: 'var(--sv-color-text-muted)', marginBottom: 24 }}>
|
|
32
|
-
This is a <strong>{size}</strong> dialog. Press Esc or click the scrim to dismiss.
|
|
33
|
-
</p>
|
|
34
|
-
<Button variant="secondary" onClick={() => setOpen(false)}>
|
|
35
|
-
Close
|
|
36
|
-
</Button>
|
|
37
|
-
</div>
|
|
38
|
-
</Dialog>
|
|
39
|
-
</>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const meta = {
|
|
44
|
-
title: 'Components/Dialog',
|
|
45
|
-
component: Dialog,
|
|
46
|
-
parameters: {
|
|
47
|
-
layout: 'centered',
|
|
48
|
-
docs: {
|
|
49
|
-
description: {
|
|
50
|
-
component:
|
|
51
|
-
'Modal surface (scrim + panel). Router-agnostic — caller provides `onClose`. Supports Esc, scrim-click, focus trap and focus restoration. Sizes: `sm` / `md` / `lg` / `full`. Mobile always renders as a full-screen sheet.',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
} satisfies Meta<typeof Dialog>;
|
|
56
|
-
|
|
57
|
-
export default meta;
|
|
58
|
-
type Story = StoryObj<typeof meta>;
|
|
59
|
-
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
export const Small: Story = {
|
|
63
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
64
|
-
render: (_args) => <DialogDemo size="sm" label="Small dialog" />,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const Medium: Story = {
|
|
68
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
69
|
-
render: (_args) => <DialogDemo size="md" label="Medium dialog" />,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export const Large: Story = {
|
|
73
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
74
|
-
render: (_args) => <DialogDemo size="lg" label="Large dialog" />,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const Closed: Story = {
|
|
78
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
79
|
-
render: (_args) => (
|
|
80
|
-
<Dialog open={false} onClose={() => {}} aria-label="Closed dialog">
|
|
81
|
-
<p>Never seen</p>
|
|
82
|
-
</Dialog>
|
|
83
|
-
),
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/** Play function opens the dialog and asserts it is visible. */
|
|
87
|
-
export const OpenViaInteraction: Story = {
|
|
88
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
89
|
-
render: (_args) => <DialogDemo size="md" label="Interaction test dialog" />,
|
|
90
|
-
play: async ({ canvasElement }) => {
|
|
91
|
-
const canvas = within(canvasElement);
|
|
92
|
-
const trigger = canvas.getByRole('button', { name: /open md dialog/i });
|
|
93
|
-
await userEvent.click(trigger);
|
|
94
|
-
const dialog = canvas.getByRole('dialog');
|
|
95
|
-
await expect(dialog).toBeVisible();
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// A stand-in for a plugin's own route layout — several component layers below
|
|
100
|
-
// wherever <Dialog> itself is instantiated, exactly like the real
|
|
101
|
-
// AccountLayout/ConsoleLayout usage this hook was built for.
|
|
102
|
-
function NestedTabStrip() {
|
|
103
|
-
const insideOverlay = useOverlaySecondRow(
|
|
104
|
-
<div style={{ display: 'flex', gap: 16, padding: '0 16px' }}>
|
|
105
|
-
{['Profile', 'Security', 'Preferences'].map((tab) => (
|
|
106
|
-
<span key={tab} style={{ fontSize: 14, color: 'var(--sv-color-text-primary)' }}>
|
|
107
|
-
{tab}
|
|
108
|
-
</span>
|
|
109
|
-
))}
|
|
110
|
-
</div>,
|
|
111
|
-
);
|
|
112
|
-
return (
|
|
113
|
-
<p style={{ color: 'var(--sv-color-text-muted)', fontSize: 13 }}>
|
|
114
|
-
useOverlaySecondRow found a Dialog ancestor: <strong>{String(insideOverlay)}</strong>. Switch
|
|
115
|
-
the viewport toolbar to a mobile width to see the tab strip render inside the Dialog's own
|
|
116
|
-
mobile OverlayHeader instead of here.
|
|
117
|
-
</p>
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Demonstrates useOverlaySecondRow — solves the "double header" problem for
|
|
123
|
-
* overlay-shell plugins (Console, Account): a deeply-nested layout hands its
|
|
124
|
-
* tab strip up to the enclosing Dialog's mobile OverlayHeader instead of
|
|
125
|
-
* rendering a second header bar as ordinary content. Only visible at mobile
|
|
126
|
-
* widths, where Dialog's own OverlayHeader takes over; at desktop widths the
|
|
127
|
-
* secondRow prop has no visible effect (OverlayHeader is desktop-hidden).
|
|
128
|
-
*/
|
|
129
|
-
export const WithOverlaySecondRow: Story = {
|
|
130
|
-
args: { open: false, onClose: () => {}, children: null },
|
|
131
|
-
render: (_args) => {
|
|
132
|
-
function Demo() {
|
|
133
|
-
const [open, setOpen] = useState(false);
|
|
134
|
-
return (
|
|
135
|
-
<>
|
|
136
|
-
<Button onClick={() => setOpen(true)}>Open dialog with tab strip</Button>
|
|
137
|
-
<Dialog open={open} onClose={() => setOpen(false)} title="Account" aria-label="Account">
|
|
138
|
-
<div style={{ padding: 24, fontFamily: 'system-ui' }}>
|
|
139
|
-
<NestedTabStrip />
|
|
140
|
-
</div>
|
|
141
|
-
</Dialog>
|
|
142
|
-
</>
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return <Demo />;
|
|
146
|
-
},
|
|
147
|
-
};
|