@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,925 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import { Card } from '../components/Card/Card';
|
|
4
|
-
import {
|
|
5
|
-
useCarouselRouteSync,
|
|
6
|
-
useCommitOnEnterOrBlur,
|
|
7
|
-
useDoubleTapHandler,
|
|
8
|
-
useIsMobile,
|
|
9
|
-
useIsOffline,
|
|
10
|
-
useLongPress,
|
|
11
|
-
useOfflineTileState,
|
|
12
|
-
useResponsiveLayout,
|
|
13
|
-
useSingleOrDoubleTap,
|
|
14
|
-
useSnapCarousel,
|
|
15
|
-
useSwipeReveal,
|
|
16
|
-
} from '../hooks';
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Shared primitives (mirrors MobilePatterns.stories.tsx's doc styling)
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
const ff = 'var(--sv-font-family)';
|
|
23
|
-
|
|
24
|
-
function Heading({ level = 2, children }: { level?: 1 | 2 | 3; children: React.ReactNode }) {
|
|
25
|
-
const sizes: Record<number, string> = { 1: '1.75rem', 2: '1.125rem', 3: '0.9375rem' };
|
|
26
|
-
const weights: Record<number, number> = { 1: 700, 2: 600, 3: 600 };
|
|
27
|
-
const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
|
|
28
|
-
return (
|
|
29
|
-
<Tag
|
|
30
|
-
style={{
|
|
31
|
-
fontFamily: ff,
|
|
32
|
-
fontSize: sizes[level],
|
|
33
|
-
fontWeight: weights[level],
|
|
34
|
-
color: 'var(--sv-color-text-primary)',
|
|
35
|
-
margin: 0,
|
|
36
|
-
lineHeight: 1.3,
|
|
37
|
-
}}
|
|
38
|
-
>
|
|
39
|
-
{children}
|
|
40
|
-
</Tag>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function Body({ children }: { children: React.ReactNode }) {
|
|
45
|
-
return (
|
|
46
|
-
<p
|
|
47
|
-
style={{
|
|
48
|
-
fontFamily: ff,
|
|
49
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
50
|
-
color: 'var(--sv-color-text-muted)',
|
|
51
|
-
margin: '0.5rem 0 0',
|
|
52
|
-
lineHeight: 1.6,
|
|
53
|
-
}}
|
|
54
|
-
>
|
|
55
|
-
{children}
|
|
56
|
-
</p>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function SectionHeader({ title, subtitle }: { title: string; subtitle?: string }) {
|
|
61
|
-
return (
|
|
62
|
-
<div
|
|
63
|
-
style={{
|
|
64
|
-
paddingBottom: 'var(--sv-space-3)',
|
|
65
|
-
borderBottom: '2px solid var(--sv-color-accent)',
|
|
66
|
-
marginBottom: 'var(--sv-space-5)',
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
69
|
-
<Heading level={2}>{title}</Heading>
|
|
70
|
-
{subtitle && <Body>{subtitle}</Body>}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function Callout({
|
|
76
|
-
type = 'info',
|
|
77
|
-
children,
|
|
78
|
-
}: {
|
|
79
|
-
type?: 'info' | 'warn' | 'tip';
|
|
80
|
-
children: React.ReactNode;
|
|
81
|
-
}) {
|
|
82
|
-
const map = {
|
|
83
|
-
info: {
|
|
84
|
-
bg: 'var(--sv-color-info-surface)',
|
|
85
|
-
border: 'var(--sv-color-info-border)',
|
|
86
|
-
text: 'var(--sv-color-info-text)',
|
|
87
|
-
label: 'ℹ',
|
|
88
|
-
},
|
|
89
|
-
warn: {
|
|
90
|
-
bg: 'var(--sv-color-warning-surface)',
|
|
91
|
-
border: 'var(--sv-color-warning-border)',
|
|
92
|
-
text: 'var(--sv-color-warning-text)',
|
|
93
|
-
label: '⚠',
|
|
94
|
-
},
|
|
95
|
-
tip: {
|
|
96
|
-
bg: 'var(--sv-color-success-surface)',
|
|
97
|
-
border: 'var(--sv-color-success-border)',
|
|
98
|
-
text: 'var(--sv-color-success-text)',
|
|
99
|
-
label: '✓',
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
const c = map[type];
|
|
103
|
-
return (
|
|
104
|
-
<div
|
|
105
|
-
style={{
|
|
106
|
-
background: c.bg,
|
|
107
|
-
border: `1px solid ${c.border}`,
|
|
108
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
109
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
110
|
-
display: 'flex',
|
|
111
|
-
gap: 'var(--sv-space-2)',
|
|
112
|
-
alignItems: 'flex-start',
|
|
113
|
-
marginBottom: 'var(--sv-space-4)',
|
|
114
|
-
}}
|
|
115
|
-
>
|
|
116
|
-
<span style={{ color: c.text, fontWeight: 700, flexShrink: 0, fontSize: '0.875rem' }}>
|
|
117
|
-
{c.label}
|
|
118
|
-
</span>
|
|
119
|
-
<p
|
|
120
|
-
style={{
|
|
121
|
-
fontFamily: ff,
|
|
122
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
123
|
-
color: c.text,
|
|
124
|
-
margin: 0,
|
|
125
|
-
lineHeight: 1.55,
|
|
126
|
-
}}
|
|
127
|
-
>
|
|
128
|
-
{children}
|
|
129
|
-
</p>
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function CodeBlock({ children }: { children: string }) {
|
|
135
|
-
return (
|
|
136
|
-
<pre
|
|
137
|
-
style={{
|
|
138
|
-
fontFamily: 'var(--sv-font-family-mono)',
|
|
139
|
-
fontSize: '0.75rem',
|
|
140
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
141
|
-
border: '1px solid var(--sv-color-border)',
|
|
142
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
143
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
144
|
-
marginTop: 'var(--sv-space-3)',
|
|
145
|
-
overflowX: 'auto',
|
|
146
|
-
color: 'var(--sv-color-text-primary)',
|
|
147
|
-
lineHeight: 1.6,
|
|
148
|
-
}}
|
|
149
|
-
>
|
|
150
|
-
{children}
|
|
151
|
-
</pre>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function EventLog({ events }: { events: string[] }) {
|
|
156
|
-
return (
|
|
157
|
-
<div
|
|
158
|
-
style={{
|
|
159
|
-
marginTop: 'var(--sv-space-3)',
|
|
160
|
-
fontFamily: 'var(--sv-font-family-mono)',
|
|
161
|
-
fontSize: '0.75rem',
|
|
162
|
-
color: 'var(--sv-color-text-muted)',
|
|
163
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
164
|
-
border: '1px solid var(--sv-color-border)',
|
|
165
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
166
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
167
|
-
minHeight: '2.5em',
|
|
168
|
-
}}
|
|
169
|
-
>
|
|
170
|
-
{events.length === 0 ? '(no events yet)' : events.slice(-5).join('\n')}
|
|
171
|
-
</div>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ---------------------------------------------------------------------------
|
|
176
|
-
// Interactive demos
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
|
|
179
|
-
function LongPressDemo() {
|
|
180
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
181
|
-
const log = (msg: string) =>
|
|
182
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
183
|
-
const longPress = useLongPress({ onLongPress: () => log('onLongPress fired') });
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<div>
|
|
187
|
-
<div
|
|
188
|
-
{...longPress}
|
|
189
|
-
style={{
|
|
190
|
-
...longPress.style,
|
|
191
|
-
padding: 'var(--sv-space-5)',
|
|
192
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
193
|
-
border: '1px dashed var(--sv-color-border-strong)',
|
|
194
|
-
background: 'var(--sv-color-surface)',
|
|
195
|
-
textAlign: 'center',
|
|
196
|
-
fontFamily: ff,
|
|
197
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
198
|
-
color: 'var(--sv-color-text-muted)',
|
|
199
|
-
cursor: 'default',
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
Touch and hold (real touch input only — see note below)
|
|
203
|
-
</div>
|
|
204
|
-
<EventLog events={events} />
|
|
205
|
-
<Callout type="info">
|
|
206
|
-
The hook only arms for <code>pointerType === 'touch'</code> — a desktop mouse
|
|
207
|
-
click never starts the timer, so mouse text-selection is never disabled. Test this on a real
|
|
208
|
-
touch device or with devtools' device toolbar (which synthesizes real touch pointer
|
|
209
|
-
events), not a plain mouse click in this preview.
|
|
210
|
-
</Callout>
|
|
211
|
-
</div>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function DoubleTapDemo() {
|
|
216
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
217
|
-
const log = (msg: string) =>
|
|
218
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
219
|
-
|
|
220
|
-
const handleSwatchTap = useDoubleTapHandler<React.MouseEvent>(() => log('double-tap: swatch'));
|
|
221
|
-
const handleTitleTap = useSingleOrDoubleTap<React.MouseEvent>(
|
|
222
|
-
() => log('single tap: would navigate'),
|
|
223
|
-
() => log('double-tap: would rename'),
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
return (
|
|
227
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-3)' }}>
|
|
228
|
-
<div style={{ display: 'flex', gap: 'var(--sv-space-3)', alignItems: 'center' }}>
|
|
229
|
-
<button
|
|
230
|
-
type="button"
|
|
231
|
-
onClick={handleSwatchTap}
|
|
232
|
-
style={{
|
|
233
|
-
width: 40,
|
|
234
|
-
height: 40,
|
|
235
|
-
borderRadius: 'var(--sv-radius-full)',
|
|
236
|
-
background: 'var(--sv-color-accent)',
|
|
237
|
-
border: 'none',
|
|
238
|
-
cursor: 'pointer',
|
|
239
|
-
}}
|
|
240
|
-
aria-label="Double-tap demo swatch"
|
|
241
|
-
/>
|
|
242
|
-
<span
|
|
243
|
-
style={{
|
|
244
|
-
fontFamily: ff,
|
|
245
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
246
|
-
color: 'var(--sv-color-text-muted)',
|
|
247
|
-
}}
|
|
248
|
-
>
|
|
249
|
-
useDoubleTapHandler — nothing to preempt, double-click/double-tap fires immediately
|
|
250
|
-
</span>
|
|
251
|
-
</div>
|
|
252
|
-
<div style={{ display: 'flex', gap: 'var(--sv-space-3)', alignItems: 'center' }}>
|
|
253
|
-
<button
|
|
254
|
-
type="button"
|
|
255
|
-
onClick={handleTitleTap}
|
|
256
|
-
style={{
|
|
257
|
-
padding: 'var(--sv-space-2) var(--sv-space-4)',
|
|
258
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
259
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
260
|
-
border: '1px solid var(--sv-color-border)',
|
|
261
|
-
fontFamily: ff,
|
|
262
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
263
|
-
cursor: 'pointer',
|
|
264
|
-
}}
|
|
265
|
-
>
|
|
266
|
-
List title
|
|
267
|
-
</button>
|
|
268
|
-
<span
|
|
269
|
-
style={{
|
|
270
|
-
fontFamily: ff,
|
|
271
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
272
|
-
color: 'var(--sv-color-text-muted)',
|
|
273
|
-
}}
|
|
274
|
-
>
|
|
275
|
-
useSingleOrDoubleTap — single tap deferred ~350ms so a double can preempt it
|
|
276
|
-
</span>
|
|
277
|
-
</div>
|
|
278
|
-
<EventLog events={events} />
|
|
279
|
-
</div>
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function CommitOnEnterOrBlurDemo() {
|
|
284
|
-
const [value, setValue] = useState('');
|
|
285
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
286
|
-
const log = (msg: string) =>
|
|
287
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
288
|
-
const commitHandlers = useCommitOnEnterOrBlur(() => log(`commit: "${value}"`));
|
|
289
|
-
|
|
290
|
-
return (
|
|
291
|
-
<div>
|
|
292
|
-
<input
|
|
293
|
-
value={value}
|
|
294
|
-
onChange={(e) => setValue(e.target.value)}
|
|
295
|
-
{...commitHandlers}
|
|
296
|
-
placeholder="Type, then press Enter or click away…"
|
|
297
|
-
style={{
|
|
298
|
-
width: '100%',
|
|
299
|
-
boxSizing: 'border-box',
|
|
300
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
301
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
302
|
-
border: '1px solid var(--sv-color-border-strong)',
|
|
303
|
-
background: 'var(--sv-color-surface)',
|
|
304
|
-
color: 'var(--sv-color-text-primary)',
|
|
305
|
-
fontFamily: ff,
|
|
306
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
307
|
-
}}
|
|
308
|
-
/>
|
|
309
|
-
<EventLog events={events} />
|
|
310
|
-
<Callout type="info">
|
|
311
|
-
Tries to reproduce the bug this hook fixes: on iOS, tap into the field, type something, then
|
|
312
|
-
dismiss the keyboard via the native Done/checkmark toolbar instead of the Return key — both
|
|
313
|
-
now log a commit here, where an Enter-only handler would silently drop the checkmark path.
|
|
314
|
-
</Callout>
|
|
315
|
-
</div>
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function IsMobileDemo() {
|
|
320
|
-
const isMobile = useIsMobile();
|
|
321
|
-
return (
|
|
322
|
-
<div
|
|
323
|
-
style={{
|
|
324
|
-
padding: 'var(--sv-space-4)',
|
|
325
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
326
|
-
background: isMobile ? 'var(--sv-color-success-surface)' : 'var(--sv-color-info-surface)',
|
|
327
|
-
color: isMobile ? 'var(--sv-color-success-text)' : 'var(--sv-color-info-text)',
|
|
328
|
-
fontFamily: ff,
|
|
329
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
330
|
-
fontWeight: 600,
|
|
331
|
-
textAlign: 'center',
|
|
332
|
-
}}
|
|
333
|
-
>
|
|
334
|
-
useIsMobile() → {String(isMobile)} — resize the preview or switch the viewport toolbar to see
|
|
335
|
-
this flip at 768px.
|
|
336
|
-
</div>
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function IsOfflineDemo() {
|
|
341
|
-
const isOffline = useIsOffline();
|
|
342
|
-
return (
|
|
343
|
-
<div
|
|
344
|
-
style={{
|
|
345
|
-
padding: 'var(--sv-space-4)',
|
|
346
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
347
|
-
background: isOffline
|
|
348
|
-
? 'var(--sv-color-warning-surface)'
|
|
349
|
-
: 'var(--sv-color-success-surface)',
|
|
350
|
-
color: isOffline ? 'var(--sv-color-warning-text)' : 'var(--sv-color-success-text)',
|
|
351
|
-
fontFamily: ff,
|
|
352
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
353
|
-
fontWeight: 600,
|
|
354
|
-
textAlign: 'center',
|
|
355
|
-
}}
|
|
356
|
-
>
|
|
357
|
-
useIsOffline() → {String(isOffline)} — toggle DevTools > Network > Offline (or your OS's
|
|
358
|
-
airplane mode) to see this flip.
|
|
359
|
-
</div>
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function OfflineTileStateDemo() {
|
|
364
|
-
const [tier, setTier] = useState<'none' | 'offline-first' | 'device-only'>('device-only');
|
|
365
|
-
const [deviceOnlyAvailable, setDeviceOnlyAvailable] = useState(false);
|
|
366
|
-
const state = useOfflineTileState(tier === 'none' ? undefined : tier, deviceOnlyAvailable);
|
|
367
|
-
|
|
368
|
-
return (
|
|
369
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-3)' }}>
|
|
370
|
-
<div
|
|
371
|
-
style={{ display: 'flex', gap: 'var(--sv-space-4)', alignItems: 'center', fontFamily: ff }}
|
|
372
|
-
>
|
|
373
|
-
<label style={{ fontSize: 'var(--sv-font-size-sm)' }}>
|
|
374
|
-
offline tier:{' '}
|
|
375
|
-
<select value={tier} onChange={(e) => setTier(e.target.value as typeof tier)}>
|
|
376
|
-
<option value="none">(none)</option>
|
|
377
|
-
<option value="offline-first">offline-first</option>
|
|
378
|
-
<option value="device-only">device-only</option>
|
|
379
|
-
</select>
|
|
380
|
-
</label>
|
|
381
|
-
<label style={{ fontSize: 'var(--sv-font-size-sm)' }}>
|
|
382
|
-
<input
|
|
383
|
-
type="checkbox"
|
|
384
|
-
checked={deviceOnlyAvailable}
|
|
385
|
-
onChange={(e) => setDeviceOnlyAvailable(e.target.checked)}
|
|
386
|
-
/>{' '}
|
|
387
|
-
deviceOnlyAvailable
|
|
388
|
-
</label>
|
|
389
|
-
</div>
|
|
390
|
-
<div
|
|
391
|
-
style={{
|
|
392
|
-
padding: 'var(--sv-space-4)',
|
|
393
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
394
|
-
background:
|
|
395
|
-
state === 'connectivity-dimmed'
|
|
396
|
-
? 'var(--sv-color-warning-surface)'
|
|
397
|
-
: state === 'capability-restricted'
|
|
398
|
-
? 'var(--sv-color-info-surface)'
|
|
399
|
-
: 'var(--sv-color-success-surface)',
|
|
400
|
-
color:
|
|
401
|
-
state === 'connectivity-dimmed'
|
|
402
|
-
? 'var(--sv-color-warning-text)'
|
|
403
|
-
: state === 'capability-restricted'
|
|
404
|
-
? 'var(--sv-color-info-text)'
|
|
405
|
-
: 'var(--sv-color-success-text)',
|
|
406
|
-
fontFamily: ff,
|
|
407
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
408
|
-
fontWeight: 600,
|
|
409
|
-
textAlign: 'center',
|
|
410
|
-
}}
|
|
411
|
-
>
|
|
412
|
-
useOfflineTileState() → {String(state)}
|
|
413
|
-
</div>
|
|
414
|
-
<p
|
|
415
|
-
style={{
|
|
416
|
-
margin: 0,
|
|
417
|
-
fontFamily: ff,
|
|
418
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
419
|
-
color: 'var(--sv-color-text-muted)',
|
|
420
|
-
}}
|
|
421
|
-
>
|
|
422
|
-
connectivity-dimmed also reacts live to DevTools > Network > Offline — try it with
|
|
423
|
-
tier set to (none).
|
|
424
|
-
</p>
|
|
425
|
-
</div>
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function SwipeRevealDemo() {
|
|
430
|
-
const [open, setOpen] = useState(false);
|
|
431
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
432
|
-
const log = (msg: string) =>
|
|
433
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
434
|
-
const { rowRef, handlers } = useSwipeReveal({
|
|
435
|
-
revealWidth: 88,
|
|
436
|
-
open,
|
|
437
|
-
onOpen: () => {
|
|
438
|
-
setOpen(true);
|
|
439
|
-
log('opened');
|
|
440
|
-
},
|
|
441
|
-
onClose: () => {
|
|
442
|
-
setOpen(false);
|
|
443
|
-
log('closed');
|
|
444
|
-
},
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
return (
|
|
448
|
-
<div>
|
|
449
|
-
<div
|
|
450
|
-
style={{
|
|
451
|
-
position: 'relative',
|
|
452
|
-
overflow: 'hidden',
|
|
453
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
454
|
-
border: '1px solid var(--sv-color-border)',
|
|
455
|
-
}}
|
|
456
|
-
>
|
|
457
|
-
<div
|
|
458
|
-
style={{
|
|
459
|
-
position: 'absolute',
|
|
460
|
-
inset: 0,
|
|
461
|
-
display: 'flex',
|
|
462
|
-
justifyContent: 'flex-end',
|
|
463
|
-
}}
|
|
464
|
-
>
|
|
465
|
-
<button
|
|
466
|
-
type="button"
|
|
467
|
-
onClick={() => {
|
|
468
|
-
setOpen(false);
|
|
469
|
-
log('Delete tapped');
|
|
470
|
-
}}
|
|
471
|
-
style={{
|
|
472
|
-
width: 88,
|
|
473
|
-
border: 'none',
|
|
474
|
-
background: 'var(--sv-color-error-surface)',
|
|
475
|
-
color: 'var(--sv-color-error-text)',
|
|
476
|
-
fontFamily: ff,
|
|
477
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
478
|
-
fontWeight: 600,
|
|
479
|
-
cursor: 'pointer',
|
|
480
|
-
}}
|
|
481
|
-
>
|
|
482
|
-
Delete
|
|
483
|
-
</button>
|
|
484
|
-
</div>
|
|
485
|
-
<div
|
|
486
|
-
ref={rowRef}
|
|
487
|
-
{...handlers}
|
|
488
|
-
style={{
|
|
489
|
-
position: 'relative',
|
|
490
|
-
padding: 'var(--sv-space-4)',
|
|
491
|
-
background: 'var(--sv-color-surface)',
|
|
492
|
-
fontFamily: ff,
|
|
493
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
494
|
-
color: 'var(--sv-color-text-primary)',
|
|
495
|
-
touchAction: 'pan-y',
|
|
496
|
-
transform: open ? 'translateX(-88px)' : undefined,
|
|
497
|
-
transition: 'transform 150ms ease-out',
|
|
498
|
-
}}
|
|
499
|
-
>
|
|
500
|
-
Drag me left to reveal Delete
|
|
501
|
-
</div>
|
|
502
|
-
</div>
|
|
503
|
-
<EventLog events={events} />
|
|
504
|
-
<Callout type="info">
|
|
505
|
-
Drag with a mouse in this preview, or touch on a real device/device toolbar. The hook only
|
|
506
|
-
writes an imperative transform during the drag itself — the CSS transition above only takes
|
|
507
|
-
over once the gesture resolves to open or closed on release.
|
|
508
|
-
</Callout>
|
|
509
|
-
</div>
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function SnapCarouselDemo() {
|
|
514
|
-
const slides = ['Lists', 'Groceries', 'Errands'];
|
|
515
|
-
const [index, setIndex] = useState(0);
|
|
516
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
517
|
-
const log = (msg: string) =>
|
|
518
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
519
|
-
const { scrollRef, scrollToIndex } = useSnapCarousel({
|
|
520
|
-
itemCount: slides.length,
|
|
521
|
-
onSettle: (i) => {
|
|
522
|
-
setIndex(i);
|
|
523
|
-
log(`settled on slide ${i}`);
|
|
524
|
-
},
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
return (
|
|
528
|
-
<div>
|
|
529
|
-
<div
|
|
530
|
-
ref={scrollRef}
|
|
531
|
-
style={{
|
|
532
|
-
display: 'flex',
|
|
533
|
-
overflowX: 'auto',
|
|
534
|
-
scrollSnapType: 'x mandatory',
|
|
535
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
536
|
-
border: '1px solid var(--sv-color-border)',
|
|
537
|
-
}}
|
|
538
|
-
>
|
|
539
|
-
{slides.map((label, i) => (
|
|
540
|
-
<div
|
|
541
|
-
key={label}
|
|
542
|
-
style={{
|
|
543
|
-
flex: '0 0 100%',
|
|
544
|
-
scrollSnapAlign: 'start',
|
|
545
|
-
padding: 'var(--sv-space-6)',
|
|
546
|
-
textAlign: 'center',
|
|
547
|
-
fontFamily: ff,
|
|
548
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
549
|
-
color: 'var(--sv-color-text-primary)',
|
|
550
|
-
background:
|
|
551
|
-
i === index ? 'var(--sv-color-surface-sunken)' : 'var(--sv-color-surface)',
|
|
552
|
-
}}
|
|
553
|
-
>
|
|
554
|
-
{label}
|
|
555
|
-
</div>
|
|
556
|
-
))}
|
|
557
|
-
</div>
|
|
558
|
-
<div style={{ display: 'flex', gap: 'var(--sv-space-2)', marginTop: 'var(--sv-space-3)' }}>
|
|
559
|
-
{slides.map((label, i) => (
|
|
560
|
-
<button
|
|
561
|
-
key={label}
|
|
562
|
-
type="button"
|
|
563
|
-
onClick={() => scrollToIndex(i)}
|
|
564
|
-
style={{
|
|
565
|
-
padding: 'var(--sv-space-1) var(--sv-space-3)',
|
|
566
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
567
|
-
border: '1px solid var(--sv-color-border)',
|
|
568
|
-
background: i === index ? 'var(--sv-color-accent)' : 'var(--sv-color-surface)',
|
|
569
|
-
color:
|
|
570
|
-
i === index ? 'var(--sv-color-text-on-accent)' : 'var(--sv-color-text-primary)',
|
|
571
|
-
fontFamily: ff,
|
|
572
|
-
fontSize: '0.75rem',
|
|
573
|
-
cursor: 'pointer',
|
|
574
|
-
}}
|
|
575
|
-
>
|
|
576
|
-
Slide {i}
|
|
577
|
-
</button>
|
|
578
|
-
))}
|
|
579
|
-
</div>
|
|
580
|
-
<EventLog events={events} />
|
|
581
|
-
<Callout type="info">
|
|
582
|
-
Swipe or scroll the strip above horizontally, or use the buttons. Settle detection is
|
|
583
|
-
debounced (120ms default) rather than tied to the <code>scrollend</code> event, which
|
|
584
|
-
pre-17.4 iOS Safari/WKWebView doesn't support.
|
|
585
|
-
</Callout>
|
|
586
|
-
</div>
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function ResponsiveLayoutDemo() {
|
|
591
|
-
const { isMobile, value } = useResponsiveLayout({
|
|
592
|
-
web: (
|
|
593
|
-
<div
|
|
594
|
-
style={{
|
|
595
|
-
padding: 'var(--sv-space-6)',
|
|
596
|
-
textAlign: 'center',
|
|
597
|
-
fontFamily: ff,
|
|
598
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
599
|
-
color: 'var(--sv-color-text-primary)',
|
|
600
|
-
}}
|
|
601
|
-
>
|
|
602
|
-
Web tree — a three-column layout, for example.
|
|
603
|
-
</div>
|
|
604
|
-
),
|
|
605
|
-
mobile: (
|
|
606
|
-
<div
|
|
607
|
-
style={{
|
|
608
|
-
padding: 'var(--sv-space-6)',
|
|
609
|
-
textAlign: 'center',
|
|
610
|
-
fontFamily: ff,
|
|
611
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
612
|
-
color: 'var(--sv-color-text-primary)',
|
|
613
|
-
}}
|
|
614
|
-
>
|
|
615
|
-
Mobile tree — an entirely different component, not a CSS squeeze.
|
|
616
|
-
</div>
|
|
617
|
-
),
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
return (
|
|
621
|
-
<div>
|
|
622
|
-
<div
|
|
623
|
-
style={{
|
|
624
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
625
|
-
border: '1px solid var(--sv-color-border)',
|
|
626
|
-
background: 'var(--sv-color-surface)',
|
|
627
|
-
}}
|
|
628
|
-
>
|
|
629
|
-
{value}
|
|
630
|
-
</div>
|
|
631
|
-
<Callout type="info">
|
|
632
|
-
Currently showing the <strong>{isMobile ? 'mobile' : 'web'}</strong> tree. Resize the
|
|
633
|
-
Storybook viewport (or the toolbar's viewport picker) below 768px to see it swap — only
|
|
634
|
-
the active side is ever mounted, the other tree doesn't exist in the DOM at all.
|
|
635
|
-
</Callout>
|
|
636
|
-
</div>
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function CarouselRouteSyncDemo() {
|
|
641
|
-
const PATHS = ['/tasks', '/tasks/starred', '/tasks/groceries'];
|
|
642
|
-
const LABELS = ['Lists', 'Starred', 'Groceries'];
|
|
643
|
-
const [pathname, setPathname] = useState(PATHS[0] as string);
|
|
644
|
-
const [events, setEvents] = useState<string[]>([]);
|
|
645
|
-
const log = (msg: string) =>
|
|
646
|
-
setEvents((e) => [...e, `${new Date().toLocaleTimeString()} — ${msg}`]);
|
|
647
|
-
|
|
648
|
-
const { activeIndex, onSettle } = useCarouselRouteSync({
|
|
649
|
-
indexForPathname: (p) => Math.max(0, PATHS.indexOf(p)),
|
|
650
|
-
pathForIndex: (i) => PATHS[i] ?? '/tasks',
|
|
651
|
-
pathname,
|
|
652
|
-
onNavigate: (path) => {
|
|
653
|
-
log(`onNavigate("${path}") — simulating router.replace`);
|
|
654
|
-
setPathname(path);
|
|
655
|
-
},
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
return (
|
|
659
|
-
<div>
|
|
660
|
-
<div style={{ display: 'flex', gap: 'var(--sv-space-2)', marginBottom: 'var(--sv-space-3)' }}>
|
|
661
|
-
{LABELS.map((label, i) => (
|
|
662
|
-
<button
|
|
663
|
-
key={label}
|
|
664
|
-
type="button"
|
|
665
|
-
onClick={() => {
|
|
666
|
-
log(`swipe settled on "${label}" (index ${i})`);
|
|
667
|
-
onSettle(i);
|
|
668
|
-
}}
|
|
669
|
-
style={{
|
|
670
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
671
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
672
|
-
border: '1px solid var(--sv-color-border)',
|
|
673
|
-
background: i === activeIndex ? 'var(--sv-color-accent)' : 'var(--sv-color-surface)',
|
|
674
|
-
color:
|
|
675
|
-
i === activeIndex
|
|
676
|
-
? 'var(--sv-color-text-on-accent)'
|
|
677
|
-
: 'var(--sv-color-text-primary)',
|
|
678
|
-
fontFamily: ff,
|
|
679
|
-
fontSize: '0.8rem',
|
|
680
|
-
cursor: 'pointer',
|
|
681
|
-
}}
|
|
682
|
-
>
|
|
683
|
-
{label}
|
|
684
|
-
</button>
|
|
685
|
-
))}
|
|
686
|
-
<button
|
|
687
|
-
type="button"
|
|
688
|
-
onClick={() => {
|
|
689
|
-
const other = PATHS[(activeIndex + 1) % PATHS.length] as string;
|
|
690
|
-
log(`external nav to "${other}" (e.g. a tapped <Link>, browser back/forward)`);
|
|
691
|
-
setPathname(other);
|
|
692
|
-
}}
|
|
693
|
-
style={{
|
|
694
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
695
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
696
|
-
border: '1px dashed var(--sv-color-border)',
|
|
697
|
-
background: 'var(--sv-color-surface)',
|
|
698
|
-
color: 'var(--sv-color-text-primary)',
|
|
699
|
-
fontFamily: ff,
|
|
700
|
-
fontSize: '0.8rem',
|
|
701
|
-
cursor: 'pointer',
|
|
702
|
-
}}
|
|
703
|
-
>
|
|
704
|
-
Simulate external nav
|
|
705
|
-
</button>
|
|
706
|
-
</div>
|
|
707
|
-
<EventLog events={events} />
|
|
708
|
-
<Callout type="info">
|
|
709
|
-
This demo fakes a router with local <code>useState</code> — in a real app, pass{' '}
|
|
710
|
-
<code>next/navigation</code>'s <code>usePathname()</code> as <code>pathname</code> and{' '}
|
|
711
|
-
<code>router.replace</code> as <code>onNavigate</code>. Note the "swipe settled"
|
|
712
|
-
buttons never log a redundant external-nav resync afterwards — that's the isInternalNav
|
|
713
|
-
dance being handled internally.
|
|
714
|
-
</Callout>
|
|
715
|
-
</div>
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// ---------------------------------------------------------------------------
|
|
720
|
-
// Main story component
|
|
721
|
-
// ---------------------------------------------------------------------------
|
|
722
|
-
|
|
723
|
-
function InteractionHooksDoc() {
|
|
724
|
-
return (
|
|
725
|
-
<div
|
|
726
|
-
style={{
|
|
727
|
-
padding: 'var(--sv-space-6)',
|
|
728
|
-
background: 'var(--sv-color-surface)',
|
|
729
|
-
minHeight: '100vh',
|
|
730
|
-
fontFamily: ff,
|
|
731
|
-
maxWidth: 760,
|
|
732
|
-
}}
|
|
733
|
-
>
|
|
734
|
-
<div style={{ marginBottom: 'var(--sv-space-8)' }}>
|
|
735
|
-
<Heading level={1}>Interaction Hooks</Heading>
|
|
736
|
-
<Body>
|
|
737
|
-
Touch gesture handling, carried once in the design system so no plugin has to rediscover
|
|
738
|
-
the failure modes. Per the DS-first principle, build gesture handling here — not in a
|
|
739
|
-
plugin. See{' '}
|
|
740
|
-
<code style={{ fontFamily: 'var(--sv-font-family-mono)', fontSize: '0.8em' }}>
|
|
741
|
-
docs/design-system.md
|
|
742
|
-
</code>{' '}
|
|
743
|
-
for the full written reference.
|
|
744
|
-
</Body>
|
|
745
|
-
</div>
|
|
746
|
-
|
|
747
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
748
|
-
<SectionHeader
|
|
749
|
-
title="useLongPress"
|
|
750
|
-
subtitle="Touch-and-hold with movement tolerance, pointercancel handling, OS-callout suppression, and time-boxed click suppression."
|
|
751
|
-
/>
|
|
752
|
-
<Card padding="md">
|
|
753
|
-
<LongPressDemo />
|
|
754
|
-
</Card>
|
|
755
|
-
<CodeBlock>{`const longPress = useLongPress({ onLongPress: () => setBulkMode(true) });
|
|
756
|
-
<div {...longPress}>Hold to select</div>`}</CodeBlock>
|
|
757
|
-
</section>
|
|
758
|
-
|
|
759
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
760
|
-
<SectionHeader
|
|
761
|
-
title="useDoubleTapHandler / useSingleOrDoubleTap"
|
|
762
|
-
subtitle="Desktop double-click reports e.detail === 2 natively; touch needs timing-based detection instead."
|
|
763
|
-
/>
|
|
764
|
-
<Card padding="md">
|
|
765
|
-
<DoubleTapDemo />
|
|
766
|
-
</Card>
|
|
767
|
-
<Callout type="tip">
|
|
768
|
-
Use <code style={{ fontFamily: 'var(--sv-font-family-mono)' }}>useDoubleTapHandler</code>{' '}
|
|
769
|
-
when the single tap has nothing to cancel. Use{' '}
|
|
770
|
-
<code style={{ fontFamily: 'var(--sv-font-family-mono)' }}>useSingleOrDoubleTap</code>{' '}
|
|
771
|
-
only when the single tap has a default action (e.g. navigation) a following double-tap
|
|
772
|
-
must be able to preempt — every single tap through it pays the ~350ms wait.
|
|
773
|
-
</Callout>
|
|
774
|
-
</section>
|
|
775
|
-
|
|
776
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
777
|
-
<SectionHeader
|
|
778
|
-
title="useCommitOnEnterOrBlur"
|
|
779
|
-
subtitle="Unifies Enter-to-commit with iOS's native keyboard accessory toolbar, whose Done/checkmark only ever fires a blur, never a keydown."
|
|
780
|
-
/>
|
|
781
|
-
<Card padding="md">
|
|
782
|
-
<CommitOnEnterOrBlurDemo />
|
|
783
|
-
</Card>
|
|
784
|
-
<CodeBlock>{`const commitHandlers = useCommitOnEnterOrBlur(() => createTask(title));
|
|
785
|
-
<input value={title} onChange={...} {...commitHandlers} />`}</CodeBlock>
|
|
786
|
-
</section>
|
|
787
|
-
|
|
788
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
789
|
-
<SectionHeader
|
|
790
|
-
title="useIsMobile"
|
|
791
|
-
subtitle="SSR-safe viewport check against the platform's single documented breakpoint (768px)."
|
|
792
|
-
/>
|
|
793
|
-
<Card padding="md">
|
|
794
|
-
<IsMobileDemo />
|
|
795
|
-
</Card>
|
|
796
|
-
<CodeBlock>{`import { useIsMobile, MOBILE_BREAKPOINT_PX } from '@sovereignfs/ui';
|
|
797
|
-
|
|
798
|
-
const isMobile = useIsMobile(); // defaults to MOBILE_BREAKPOINT_PX (768)`}</CodeBlock>
|
|
799
|
-
</section>
|
|
800
|
-
|
|
801
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
802
|
-
<SectionHeader
|
|
803
|
-
title="useIsOffline"
|
|
804
|
-
subtitle="SSR-safe navigator.onLine tracking, shared by OfflineBanner, the login page, and Console/Account's offline gate (research 0012)."
|
|
805
|
-
/>
|
|
806
|
-
<Card padding="md">
|
|
807
|
-
<IsOfflineDemo />
|
|
808
|
-
</Card>
|
|
809
|
-
<CodeBlock>{`import { useIsOffline } from '@sovereignfs/ui';
|
|
810
|
-
|
|
811
|
-
const isOffline = useIsOffline();`}</CodeBlock>
|
|
812
|
-
</section>
|
|
813
|
-
|
|
814
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
815
|
-
<SectionHeader
|
|
816
|
-
title="useOfflineTileState"
|
|
817
|
-
subtitle="Which of the two app-tile states, if any, applies given a plugin's declared offline tier — connectivity-dimmed (no tier, offline right now) vs. capability-restricted (device-only, unavailable here), never conflated (research 0012)."
|
|
818
|
-
/>
|
|
819
|
-
<Card padding="md">
|
|
820
|
-
<OfflineTileStateDemo />
|
|
821
|
-
</Card>
|
|
822
|
-
<CodeBlock>{`import { useOfflineTileState } from '@sovereignfs/ui';
|
|
823
|
-
|
|
824
|
-
// deviceOnlyAvailable comes from isDeviceOnlyTierAvailable() in
|
|
825
|
-
// @sovereignfs/sdk/device-client — passed in, not computed here, so
|
|
826
|
-
// @sovereignfs/ui never depends on @sovereignfs/sdk.
|
|
827
|
-
const state = useOfflineTileState(plugin.offline, deviceOnlyAvailable);
|
|
828
|
-
// 'connectivity-dimmed' | 'capability-restricted' | null`}</CodeBlock>
|
|
829
|
-
</section>
|
|
830
|
-
|
|
831
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
832
|
-
<SectionHeader
|
|
833
|
-
title="useSwipeReveal"
|
|
834
|
-
subtitle="Horizontal swipe-to-reveal for a row's Done/Delete actions — extracted from sovereign-tasks' two independent hand-rolled implementations (RFC 0079)."
|
|
835
|
-
/>
|
|
836
|
-
<Card padding="md">
|
|
837
|
-
<SwipeRevealDemo />
|
|
838
|
-
</Card>
|
|
839
|
-
<CodeBlock>{`const { rowRef, handlers } = useSwipeReveal({
|
|
840
|
-
revealWidth: 88,
|
|
841
|
-
open,
|
|
842
|
-
onOpen: () => setOpen(true),
|
|
843
|
-
onClose: () => setOpen(false),
|
|
844
|
-
});
|
|
845
|
-
<div ref={rowRef} {...handlers} style={{ transform: open ? 'translateX(-88px)' : undefined }}>
|
|
846
|
-
Row content
|
|
847
|
-
</div>`}</CodeBlock>
|
|
848
|
-
</section>
|
|
849
|
-
|
|
850
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
851
|
-
<SectionHeader
|
|
852
|
-
title="useSnapCarousel"
|
|
853
|
-
subtitle="Debounced settled-slide detection over a native scroll-snap container — extracted from sovereign-tasks and sovereign-shopper's independently duplicated carousels (RFC 0079)."
|
|
854
|
-
/>
|
|
855
|
-
<Card padding="md">
|
|
856
|
-
<SnapCarouselDemo />
|
|
857
|
-
</Card>
|
|
858
|
-
<CodeBlock>{`const { scrollRef, scrollToIndex } = useSnapCarousel({
|
|
859
|
-
itemCount: slides.length,
|
|
860
|
-
onSettle: (index) => setActiveIndex(index),
|
|
861
|
-
});
|
|
862
|
-
<div ref={scrollRef} style={{ display: 'flex', overflowX: 'auto', scrollSnapType: 'x mandatory' }}>
|
|
863
|
-
{slides.map((slide) => <div style={{ scrollSnapAlign: 'start' }}>{slide}</div>)}
|
|
864
|
-
</div>`}</CodeBlock>
|
|
865
|
-
</section>
|
|
866
|
-
|
|
867
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
868
|
-
<SectionHeader
|
|
869
|
-
title="useResponsiveLayout"
|
|
870
|
-
subtitle="Picks between a web and mobile value for the current viewport — formalizes the 'render an entirely different tree below a breakpoint' fork plugins have hand-rolled per plugin. See also the ResponsiveSurface component for the equivalent as plain JSX."
|
|
871
|
-
/>
|
|
872
|
-
<Card padding="md">
|
|
873
|
-
<ResponsiveLayoutDemo />
|
|
874
|
-
</Card>
|
|
875
|
-
<CodeBlock>{`const { isMobile, value } = useResponsiveLayout({ web: <WebShell />, mobile: <MobileShell /> });
|
|
876
|
-
// or, as a component:
|
|
877
|
-
<ResponsiveSurface web={<WebShell />} mobile={<MobileShell />} />`}</CodeBlock>
|
|
878
|
-
</section>
|
|
879
|
-
|
|
880
|
-
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
881
|
-
<SectionHeader
|
|
882
|
-
title="useCarouselRouteSync"
|
|
883
|
-
subtitle="Router-agnostic pathname↔slide-index sync for SwipableMobileCarousel — centralizes the isInternalNav dance both sovereign-tasks and sovereign-shopper hand-roll identically today."
|
|
884
|
-
/>
|
|
885
|
-
<Card padding="md">
|
|
886
|
-
<CarouselRouteSyncDemo />
|
|
887
|
-
</Card>
|
|
888
|
-
<CodeBlock>{`const { activeIndex, onSettle } = useCarouselRouteSync({
|
|
889
|
-
indexForPathname: (p) => indexForPathname(p, lists), // your plugin's own logic, unchanged
|
|
890
|
-
pathForIndex: (i) => pathForIndex(i, lists),
|
|
891
|
-
pathname: usePathname(),
|
|
892
|
-
onNavigate: (path) => router.replace(path, { scroll: false }),
|
|
893
|
-
});
|
|
894
|
-
<SwipableMobileCarousel activeIndex={activeIndex} onSettle={onSettle} aria-label="Task lists">
|
|
895
|
-
...
|
|
896
|
-
</SwipableMobileCarousel>`}</CodeBlock>
|
|
897
|
-
</section>
|
|
898
|
-
</div>
|
|
899
|
-
);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// ---------------------------------------------------------------------------
|
|
903
|
-
|
|
904
|
-
const meta = {
|
|
905
|
-
title: 'Overview/Interaction Hooks',
|
|
906
|
-
component: InteractionHooksDoc,
|
|
907
|
-
parameters: {
|
|
908
|
-
layout: 'fullscreen',
|
|
909
|
-
docs: {
|
|
910
|
-
description: {
|
|
911
|
-
component:
|
|
912
|
-
'Reference and live demos for the touch-gesture hooks exported from @sovereignfs/ui. Long-press requires real touch input to trigger — use the devtools device toolbar or a physical device.',
|
|
913
|
-
},
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
} satisfies Meta<typeof InteractionHooksDoc>;
|
|
917
|
-
|
|
918
|
-
export default meta;
|
|
919
|
-
type Story = StoryObj<typeof meta>;
|
|
920
|
-
|
|
921
|
-
export const Default: Story = {};
|
|
922
|
-
|
|
923
|
-
export const MobileView: Story = {
|
|
924
|
-
parameters: { viewport: { defaultViewport: 'mobile' } },
|
|
925
|
-
};
|