@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,357 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import { Popover } from './Popover';
|
|
4
|
-
import { Button } from '../Button/Button';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'Components/Popover',
|
|
8
|
-
component: Popover,
|
|
9
|
-
parameters: {
|
|
10
|
-
layout: 'centered',
|
|
11
|
-
docs: {
|
|
12
|
-
description: {
|
|
13
|
-
component:
|
|
14
|
-
'Floating panel anchored to a trigger, below it by default. Non-modal: closes on outside click or Escape. `align` controls which trigger edge the panel aligns to; `width` accepts a fixed px number or `"trigger"` to match the trigger\'s own rendered width. Automatically flips to open upward instead of downward if the panel wouldn\'t fit below the trigger within the viewport.',
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
args: {
|
|
19
|
-
open: false,
|
|
20
|
-
onClose: () => {},
|
|
21
|
-
trigger: <button type="button">Trigger</button>,
|
|
22
|
-
'aria-label': 'Popover',
|
|
23
|
-
children: null,
|
|
24
|
-
},
|
|
25
|
-
} satisfies Meta<typeof Popover>;
|
|
26
|
-
|
|
27
|
-
export default meta;
|
|
28
|
-
type Story = StoryObj<typeof meta>;
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
const rowStyle: React.CSSProperties = {
|
|
33
|
-
display: 'flex',
|
|
34
|
-
alignItems: 'center',
|
|
35
|
-
gap: 12,
|
|
36
|
-
padding: '10px 16px',
|
|
37
|
-
fontFamily: 'var(--sv-font-family)',
|
|
38
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
39
|
-
color: 'var(--sv-color-text-primary)',
|
|
40
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
41
|
-
cursor: 'pointer',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/** Notification bell popover — right-aligned, 288px. */
|
|
45
|
-
export const Notifications: Story = {
|
|
46
|
-
render: (_args) => {
|
|
47
|
-
const [open, setOpen] = useState(false);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div style={{ padding: 40 }}>
|
|
51
|
-
<Popover
|
|
52
|
-
open={open}
|
|
53
|
-
onClose={() => setOpen(false)}
|
|
54
|
-
align="right"
|
|
55
|
-
aria-label="Notifications"
|
|
56
|
-
trigger={
|
|
57
|
-
<Button variant="ghost" onClick={() => setOpen((o) => !o)} aria-label="Notifications">
|
|
58
|
-
🔔
|
|
59
|
-
</Button>
|
|
60
|
-
}
|
|
61
|
-
>
|
|
62
|
-
<div
|
|
63
|
-
style={{
|
|
64
|
-
display: 'flex',
|
|
65
|
-
alignItems: 'center',
|
|
66
|
-
justifyContent: 'space-between',
|
|
67
|
-
padding: '12px 16px',
|
|
68
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
69
|
-
fontFamily: 'var(--sv-font-family)',
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
<span
|
|
73
|
-
style={{
|
|
74
|
-
fontWeight: 'var(--sv-font-weight-semibold)',
|
|
75
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
76
|
-
color: 'var(--sv-color-text-primary)',
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
Notifications
|
|
80
|
-
</span>
|
|
81
|
-
<button
|
|
82
|
-
type="button"
|
|
83
|
-
style={{
|
|
84
|
-
background: 'none',
|
|
85
|
-
border: 'none',
|
|
86
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
87
|
-
color: 'var(--sv-color-text-muted)',
|
|
88
|
-
cursor: 'pointer',
|
|
89
|
-
fontFamily: 'var(--sv-font-family)',
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
Mark all read
|
|
93
|
-
</button>
|
|
94
|
-
</div>
|
|
95
|
-
<div style={rowStyle}>
|
|
96
|
-
<span style={{ fontSize: 20 }}>📦</span>
|
|
97
|
-
<div style={{ flex: 1 }}>
|
|
98
|
-
<div style={{ fontWeight: 'var(--sv-font-weight-medium)' }}>Plugin updated</div>
|
|
99
|
-
<div
|
|
100
|
-
style={{
|
|
101
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
102
|
-
color: 'var(--sv-color-text-muted)',
|
|
103
|
-
}}
|
|
104
|
-
>
|
|
105
|
-
1 hour ago
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
<div style={rowStyle}>
|
|
110
|
-
<span style={{ fontSize: 20 }}>✉️</span>
|
|
111
|
-
<div style={{ flex: 1 }}>
|
|
112
|
-
<div style={{ fontWeight: 'var(--sv-font-weight-medium)' }}>Invite accepted</div>
|
|
113
|
-
<div
|
|
114
|
-
style={{
|
|
115
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
116
|
-
color: 'var(--sv-color-text-muted)',
|
|
117
|
-
}}
|
|
118
|
-
>
|
|
119
|
-
2 min ago
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</Popover>
|
|
124
|
-
</div>
|
|
125
|
-
);
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/** User menu — right-aligned with identity header and nav rows. */
|
|
130
|
-
export const UserMenu: Story = {
|
|
131
|
-
render: (_args) => {
|
|
132
|
-
const [open, setOpen] = useState(false);
|
|
133
|
-
|
|
134
|
-
const menuRowStyle: React.CSSProperties = {
|
|
135
|
-
...rowStyle,
|
|
136
|
-
gap: 10,
|
|
137
|
-
cursor: 'default',
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<div style={{ padding: 40 }}>
|
|
142
|
-
<Popover
|
|
143
|
-
open={open}
|
|
144
|
-
onClose={() => setOpen(false)}
|
|
145
|
-
align="right"
|
|
146
|
-
aria-label="User menu"
|
|
147
|
-
trigger={
|
|
148
|
-
<button
|
|
149
|
-
type="button"
|
|
150
|
-
onClick={() => setOpen((o) => !o)}
|
|
151
|
-
style={{
|
|
152
|
-
width: 32,
|
|
153
|
-
height: 32,
|
|
154
|
-
borderRadius: '50%',
|
|
155
|
-
background: 'var(--sv-color-accent)',
|
|
156
|
-
color: 'var(--sv-color-text-on-accent)',
|
|
157
|
-
border: 'none',
|
|
158
|
-
cursor: 'pointer',
|
|
159
|
-
fontFamily: 'var(--sv-font-family)',
|
|
160
|
-
fontWeight: 600,
|
|
161
|
-
fontSize: 13,
|
|
162
|
-
}}
|
|
163
|
-
aria-label="Open user menu"
|
|
164
|
-
>
|
|
165
|
-
KB
|
|
166
|
-
</button>
|
|
167
|
-
}
|
|
168
|
-
>
|
|
169
|
-
<div
|
|
170
|
-
style={{
|
|
171
|
-
padding: '12px 16px',
|
|
172
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
173
|
-
fontFamily: 'var(--sv-font-family)',
|
|
174
|
-
}}
|
|
175
|
-
>
|
|
176
|
-
<div
|
|
177
|
-
style={{
|
|
178
|
-
fontWeight: 'var(--sv-font-weight-semibold)',
|
|
179
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
180
|
-
color: 'var(--sv-color-text-primary)',
|
|
181
|
-
}}
|
|
182
|
-
>
|
|
183
|
-
Kasun Benthara
|
|
184
|
-
</div>
|
|
185
|
-
<div
|
|
186
|
-
style={{
|
|
187
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
188
|
-
color: 'var(--sv-color-text-muted)',
|
|
189
|
-
}}
|
|
190
|
-
>
|
|
191
|
-
kasun@openfs.io
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
{['Account', 'Console', 'Sign out'].map((item) => (
|
|
195
|
-
<div key={item} style={menuRowStyle}>
|
|
196
|
-
{item}
|
|
197
|
-
</div>
|
|
198
|
-
))}
|
|
199
|
-
</Popover>
|
|
200
|
-
</div>
|
|
201
|
-
);
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/** Left-aligned variant. */
|
|
206
|
-
export const LeftAligned: Story = {
|
|
207
|
-
render: (_args) => {
|
|
208
|
-
const [open, setOpen] = useState(false);
|
|
209
|
-
return (
|
|
210
|
-
<div style={{ padding: 40 }}>
|
|
211
|
-
<Popover
|
|
212
|
-
open={open}
|
|
213
|
-
onClose={() => setOpen(false)}
|
|
214
|
-
align="left"
|
|
215
|
-
aria-label="Options"
|
|
216
|
-
trigger={<Button onClick={() => setOpen((o) => !o)}>Open (left-aligned)</Button>}
|
|
217
|
-
>
|
|
218
|
-
<div
|
|
219
|
-
style={{
|
|
220
|
-
padding: 16,
|
|
221
|
-
fontFamily: 'var(--sv-font-family)',
|
|
222
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
223
|
-
color: 'var(--sv-color-text-muted)',
|
|
224
|
-
}}
|
|
225
|
-
>
|
|
226
|
-
Panel aligns to the left edge of the trigger.
|
|
227
|
-
</div>
|
|
228
|
-
</Popover>
|
|
229
|
-
</div>
|
|
230
|
-
);
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
/** `panelStyle` overrides the panel's own chrome (square corners here) —
|
|
235
|
-
* an escape hatch for compact pickers where the default rounded panel
|
|
236
|
-
* doesn't fit the content (e.g. a small colour-swatch grid). */
|
|
237
|
-
export const SquareCorners: Story = {
|
|
238
|
-
render: (_args) => {
|
|
239
|
-
const [open, setOpen] = useState(false);
|
|
240
|
-
return (
|
|
241
|
-
<div style={{ padding: 40 }}>
|
|
242
|
-
<Popover
|
|
243
|
-
open={open}
|
|
244
|
-
onClose={() => setOpen(false)}
|
|
245
|
-
align="left"
|
|
246
|
-
width={160}
|
|
247
|
-
panelStyle={{ borderRadius: 0 }}
|
|
248
|
-
aria-label="Colour"
|
|
249
|
-
trigger={<Button onClick={() => setOpen((o) => !o)}>Open (square panel)</Button>}
|
|
250
|
-
>
|
|
251
|
-
<div
|
|
252
|
-
style={{
|
|
253
|
-
padding: 16,
|
|
254
|
-
fontFamily: 'var(--sv-font-family)',
|
|
255
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
256
|
-
color: 'var(--sv-color-text-muted)',
|
|
257
|
-
}}
|
|
258
|
-
>
|
|
259
|
-
No rounded corners.
|
|
260
|
-
</div>
|
|
261
|
-
</Popover>
|
|
262
|
-
</div>
|
|
263
|
-
);
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
/** `width="trigger"` matches the panel's width to the trigger's own rendered
|
|
268
|
-
* width — for a dropdown under a full-width form field, a fixed px value
|
|
269
|
-
* would either overflow or leave a gap depending on the field's own width. */
|
|
270
|
-
export const MatchesTriggerWidth: Story = {
|
|
271
|
-
render: (_args) => {
|
|
272
|
-
const [open, setOpen] = useState(false);
|
|
273
|
-
return (
|
|
274
|
-
<div style={{ padding: 40, width: 360 }}>
|
|
275
|
-
<Popover
|
|
276
|
-
open={open}
|
|
277
|
-
onClose={() => setOpen(false)}
|
|
278
|
-
align="left"
|
|
279
|
-
width="trigger"
|
|
280
|
-
aria-label="Set due date"
|
|
281
|
-
trigger={
|
|
282
|
-
<button
|
|
283
|
-
type="button"
|
|
284
|
-
onClick={() => setOpen((o) => !o)}
|
|
285
|
-
style={{
|
|
286
|
-
width: '100%',
|
|
287
|
-
boxSizing: 'border-box',
|
|
288
|
-
textAlign: 'left',
|
|
289
|
-
padding: '10px 14px',
|
|
290
|
-
border: '1px solid var(--sv-color-border)',
|
|
291
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
292
|
-
background: 'var(--sv-color-surface-raised)',
|
|
293
|
-
fontFamily: 'var(--sv-font-family)',
|
|
294
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
295
|
-
color: 'var(--sv-color-text-primary)',
|
|
296
|
-
cursor: 'pointer',
|
|
297
|
-
}}
|
|
298
|
-
>
|
|
299
|
-
No due date
|
|
300
|
-
</button>
|
|
301
|
-
}
|
|
302
|
-
>
|
|
303
|
-
<div
|
|
304
|
-
style={{
|
|
305
|
-
padding: 16,
|
|
306
|
-
fontFamily: 'var(--sv-font-family)',
|
|
307
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
308
|
-
color: 'var(--sv-color-text-muted)',
|
|
309
|
-
}}
|
|
310
|
-
>
|
|
311
|
-
Panel width matches the 360px trigger above exactly, instead of a fixed pixel value that
|
|
312
|
-
would overflow or leave a gap at other trigger widths.
|
|
313
|
-
</div>
|
|
314
|
-
</Popover>
|
|
315
|
-
</div>
|
|
316
|
-
);
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
/** Flips to open upward automatically when the panel wouldn't fit below the
|
|
321
|
-
* trigger within the viewport — no prop needed, this is always on. */
|
|
322
|
-
export const OpensUpward: Story = {
|
|
323
|
-
render: (_args) => {
|
|
324
|
-
const [open, setOpen] = useState(false);
|
|
325
|
-
return (
|
|
326
|
-
<div
|
|
327
|
-
style={{
|
|
328
|
-
height: '70vh',
|
|
329
|
-
display: 'flex',
|
|
330
|
-
alignItems: 'flex-end',
|
|
331
|
-
justifyContent: 'center',
|
|
332
|
-
paddingBottom: 24,
|
|
333
|
-
}}
|
|
334
|
-
>
|
|
335
|
-
<Popover
|
|
336
|
-
open={open}
|
|
337
|
-
onClose={() => setOpen(false)}
|
|
338
|
-
align="left"
|
|
339
|
-
aria-label="Options"
|
|
340
|
-
trigger={<Button onClick={() => setOpen((o) => !o)}>Open (near viewport bottom)</Button>}
|
|
341
|
-
>
|
|
342
|
-
<div
|
|
343
|
-
style={{
|
|
344
|
-
padding: 16,
|
|
345
|
-
fontFamily: 'var(--sv-font-family)',
|
|
346
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
347
|
-
color: 'var(--sv-color-text-muted)',
|
|
348
|
-
}}
|
|
349
|
-
>
|
|
350
|
-
This trigger sits near the bottom of the viewport, so the panel opens upward instead of
|
|
351
|
-
getting clipped below.
|
|
352
|
-
</div>
|
|
353
|
-
</Popover>
|
|
354
|
-
</div>
|
|
355
|
-
);
|
|
356
|
-
},
|
|
357
|
-
};
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
useEffect,
|
|
5
|
-
useLayoutEffect,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
type CSSProperties,
|
|
9
|
-
type ReactElement,
|
|
10
|
-
type ReactNode,
|
|
11
|
-
} from 'react';
|
|
12
|
-
import styles from './Popover.module.css';
|
|
13
|
-
|
|
14
|
-
export interface PopoverProps {
|
|
15
|
-
/** The element that opens the popover — rendered as-is; caller wires its onClick. */
|
|
16
|
-
trigger: ReactElement;
|
|
17
|
-
open: boolean;
|
|
18
|
-
onClose: () => void;
|
|
19
|
-
/** Which edge of the trigger the panel aligns to. Default: 'right'. */
|
|
20
|
-
align?: 'left' | 'right';
|
|
21
|
-
/** Panel width in px, or `'trigger'` to match the trigger element's own
|
|
22
|
-
* rendered width exactly (e.g. a dropdown under a full-width form field,
|
|
23
|
-
* where a fixed pixel width would either overflow or leave a gap
|
|
24
|
-
* depending on viewport size). Default: 288. */
|
|
25
|
-
width?: number | 'trigger';
|
|
26
|
-
/**
|
|
27
|
-
* Inline style overrides for the panel, merged after `width`. Escape hatch
|
|
28
|
-
* for callers that need to override the panel's own chrome (e.g. no
|
|
29
|
-
* rounded corners for a compact swatch picker) — inline styles always win
|
|
30
|
-
* regardless of CSS module load order, unlike passing an extra className.
|
|
31
|
-
*/
|
|
32
|
-
panelStyle?: CSSProperties;
|
|
33
|
-
'aria-label': string;
|
|
34
|
-
children: ReactNode;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Matches the gap already applied via .panel's `calc(100% + var(--sv-space-2))`
|
|
38
|
-
// offset — used here in plain px since layout math needs a number, not a
|
|
39
|
-
// CSS custom property.
|
|
40
|
-
const TRIGGER_GAP = 8;
|
|
41
|
-
// Breathing room kept between the panel and the viewport edge it's closest
|
|
42
|
-
// to — without this, a panel that just barely fits ends up flush against
|
|
43
|
-
// the edge (e.g. touching a mobile footer nav bar with no gap at all). Kept
|
|
44
|
-
// small deliberately: this subtracts directly from the space available
|
|
45
|
-
// before the internal-scroll cap kicks in (see the layout effect below), so
|
|
46
|
-
// a larger value here trades "more edge clearance" for "more panels that
|
|
47
|
-
// would otherwise fit exactly get an unwanted scrollbar instead" — a bad
|
|
48
|
-
// trade for a purely cosmetic gap.
|
|
49
|
-
const VIEWPORT_MARGIN = 4;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Popover — floating panel anchored to a trigger element, below it by
|
|
53
|
-
* default.
|
|
54
|
-
*
|
|
55
|
-
* Positioning is `position: absolute` inside a `position: relative` wrapper —
|
|
56
|
-
* no floating-ui dependency. Supports left/right alignment against the trigger
|
|
57
|
-
* edge, and automatically opens on whichever side (above or below the
|
|
58
|
-
* trigger) has more room within the viewport.
|
|
59
|
-
*
|
|
60
|
-
* If the panel doesn't fit on *either* side as the trigger currently sits —
|
|
61
|
-
* common for a tall panel (e.g. a full calendar month) nested partway down a
|
|
62
|
-
* long scrollable mobile sheet — the trigger's scrollable ancestor is
|
|
63
|
-
* scrolled so the trigger sits at the top of it first, maximizing the room
|
|
64
|
-
* available below, then the panel opens downward against that. This mirrors
|
|
65
|
-
* how most mobile date pickers behave (scroll the page, not the picker) and
|
|
66
|
-
* avoids the panel needing its own internal scroll in the vast majority of
|
|
67
|
-
* cases. If it's still too tall even after that (a genuinely tiny viewport,
|
|
68
|
-
* or an unusually tall panel), it's capped to whatever room actually exists
|
|
69
|
-
* and scrolls internally as a last resort, rather than rendering past the
|
|
70
|
-
* edge of the viewport with no way to reach the rest of it.
|
|
71
|
-
*
|
|
72
|
-
* Closes on outside click or Escape. Does not trap focus (it is non-modal).
|
|
73
|
-
*/
|
|
74
|
-
export function Popover({
|
|
75
|
-
trigger,
|
|
76
|
-
open,
|
|
77
|
-
onClose,
|
|
78
|
-
align = 'right',
|
|
79
|
-
width = 288,
|
|
80
|
-
panelStyle,
|
|
81
|
-
'aria-label': ariaLabel,
|
|
82
|
-
children,
|
|
83
|
-
}: PopoverProps) {
|
|
84
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
85
|
-
const panelRef = useRef<HTMLDivElement>(null);
|
|
86
|
-
const [triggerWidth, setTriggerWidth] = useState<number | null>(null);
|
|
87
|
-
// Whether the panel opens upward instead of downward. Defaults to false
|
|
88
|
-
// (today's only behavior) so nothing shifts before this can actually be
|
|
89
|
-
// measured.
|
|
90
|
-
const [openUpward, setOpenUpward] = useState(false);
|
|
91
|
-
// Caps the panel to whatever room actually exists on its chosen side, only
|
|
92
|
-
// when the panel is taller than that — null leaves it at its natural
|
|
93
|
-
// height, unconstrained, for the common case where it already fits.
|
|
94
|
-
const [maxHeight, setMaxHeight] = useState<number | null>(null);
|
|
95
|
-
|
|
96
|
-
// width="trigger": measure the trigger's own rendered width and keep the
|
|
97
|
-
// panel matching it, including live resizes (e.g. rotating the device, or
|
|
98
|
-
// the trigger's own width changing for any other reason).
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
if (width !== 'trigger') return;
|
|
101
|
-
const el = containerRef.current?.firstElementChild as HTMLElement | null;
|
|
102
|
-
if (!el) return;
|
|
103
|
-
function recompute() {
|
|
104
|
-
if (el) setTriggerWidth(el.getBoundingClientRect().width);
|
|
105
|
-
}
|
|
106
|
-
recompute();
|
|
107
|
-
const ro = new ResizeObserver(recompute);
|
|
108
|
-
ro.observe(el);
|
|
109
|
-
return () => ro.disconnect();
|
|
110
|
-
}, [width]);
|
|
111
|
-
|
|
112
|
-
// Collision detection: once open, measure the room available above and
|
|
113
|
-
// below the trigger within the viewport. If neither side has enough for
|
|
114
|
-
// the panel as things currently sit, scroll the trigger's scrollable
|
|
115
|
-
// ancestor so the trigger sits at the top of it (scrollIntoView with
|
|
116
|
-
// behavior: 'instant' applies synchronously, so re-measuring right after
|
|
117
|
-
// reflects the new position with no extra frame to wait for) — this is
|
|
118
|
-
// usually enough on its own to make the panel fit below. Whatever's left
|
|
119
|
-
// after that: open on whichever side has more room, and only if it still
|
|
120
|
-
// doesn't fit, cap the panel's height to that room (.panel's overflow-y:
|
|
121
|
-
// auto then lets it scroll internally as a last resort, rather than
|
|
122
|
-
// extending past the viewport edge with no way to reach the clipped part).
|
|
123
|
-
// Runs in a layout effect (before paint) so there's no visible flash of
|
|
124
|
-
// "opens one way, then snaps to the other" — measured and corrected within
|
|
125
|
-
// the same commit the browser paints.
|
|
126
|
-
useLayoutEffect(() => {
|
|
127
|
-
if (!open) {
|
|
128
|
-
setOpenUpward(false);
|
|
129
|
-
setMaxHeight(null);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const container = containerRef.current;
|
|
133
|
-
const panel = panelRef.current;
|
|
134
|
-
if (!container || !panel) return;
|
|
135
|
-
|
|
136
|
-
// Arrow function expression, not a hoisted declaration — TypeScript only
|
|
137
|
-
// preserves the non-null narrowing above for a closure defined after it.
|
|
138
|
-
const measure = () => {
|
|
139
|
-
const triggerRect = container.getBoundingClientRect();
|
|
140
|
-
const panelHeight = panel.getBoundingClientRect().height;
|
|
141
|
-
return {
|
|
142
|
-
panelHeight,
|
|
143
|
-
spaceBelow: window.innerHeight - triggerRect.bottom - TRIGGER_GAP - VIEWPORT_MARGIN,
|
|
144
|
-
spaceAbove: triggerRect.top - TRIGGER_GAP - VIEWPORT_MARGIN,
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
let { panelHeight, spaceBelow, spaceAbove } = measure();
|
|
149
|
-
if (panelHeight > spaceBelow && panelHeight > spaceAbove) {
|
|
150
|
-
container.scrollIntoView({ block: 'start', behavior: 'instant' });
|
|
151
|
-
({ panelHeight, spaceBelow, spaceAbove } = measure());
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const upward = panelHeight > spaceBelow && spaceAbove > spaceBelow;
|
|
155
|
-
setOpenUpward(upward);
|
|
156
|
-
const available = upward ? spaceAbove : spaceBelow;
|
|
157
|
-
setMaxHeight(panelHeight > available ? Math.max(available, 0) : null);
|
|
158
|
-
// children included: content that changes the panel's height (e.g. a
|
|
159
|
-
// calendar switching months to a row with fewer/more weeks) should
|
|
160
|
-
// re-run this measurement, not just the open/close transition itself.
|
|
161
|
-
}, [open, children]);
|
|
162
|
-
|
|
163
|
-
// Outside-click dismissal
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (!open) return;
|
|
166
|
-
function handleMouseDown(e: MouseEvent) {
|
|
167
|
-
if (!containerRef.current?.contains(e.target as Node)) {
|
|
168
|
-
onClose();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
document.addEventListener('mousedown', handleMouseDown);
|
|
172
|
-
return () => document.removeEventListener('mousedown', handleMouseDown);
|
|
173
|
-
}, [open, onClose]);
|
|
174
|
-
|
|
175
|
-
// Escape key dismissal
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
if (!open) return;
|
|
178
|
-
function handleKeyDown(e: KeyboardEvent) {
|
|
179
|
-
if (e.key === 'Escape') onClose();
|
|
180
|
-
}
|
|
181
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
182
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
183
|
-
}, [open, onClose]);
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<div ref={containerRef} className={styles.container}>
|
|
187
|
-
{trigger}
|
|
188
|
-
{open && (
|
|
189
|
-
<div
|
|
190
|
-
ref={panelRef}
|
|
191
|
-
role="dialog"
|
|
192
|
-
aria-label={ariaLabel}
|
|
193
|
-
aria-modal={false}
|
|
194
|
-
className={[styles.panel, styles[align], openUpward ? styles.upward : '']
|
|
195
|
-
.filter(Boolean)
|
|
196
|
-
.join(' ')}
|
|
197
|
-
style={{
|
|
198
|
-
width: width === 'trigger' ? (triggerWidth ?? undefined) : width,
|
|
199
|
-
maxHeight: maxHeight ?? undefined,
|
|
200
|
-
...panelStyle,
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
{children}
|
|
204
|
-
</div>
|
|
205
|
-
)}
|
|
206
|
-
</div>
|
|
207
|
-
);
|
|
208
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
-
import { Popover } from '../Popover';
|
|
5
|
-
|
|
6
|
-
afterEach(cleanup);
|
|
7
|
-
|
|
8
|
-
const trigger = <button type="button">Open</button>;
|
|
9
|
-
|
|
10
|
-
describe('Popover', () => {
|
|
11
|
-
it('renders the trigger element', () => {
|
|
12
|
-
render(
|
|
13
|
-
<Popover open={false} onClose={() => {}} aria-label="Menu" trigger={trigger}>
|
|
14
|
-
Content
|
|
15
|
-
</Popover>,
|
|
16
|
-
);
|
|
17
|
-
expect(screen.getByRole('button', { name: 'Open' })).toBeDefined();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('does not render the panel when closed', () => {
|
|
21
|
-
render(
|
|
22
|
-
<Popover open={false} onClose={() => {}} aria-label="Menu" trigger={trigger}>
|
|
23
|
-
Content
|
|
24
|
-
</Popover>,
|
|
25
|
-
);
|
|
26
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('renders the panel with children when open', () => {
|
|
30
|
-
render(
|
|
31
|
-
<Popover open={true} onClose={() => {}} aria-label="Menu" trigger={trigger}>
|
|
32
|
-
Panel content
|
|
33
|
-
</Popover>,
|
|
34
|
-
);
|
|
35
|
-
expect(screen.getByRole('dialog', { name: 'Menu' })).toBeDefined();
|
|
36
|
-
expect(screen.getByText('Panel content')).toBeDefined();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('applies the right alignment class by default', () => {
|
|
40
|
-
render(
|
|
41
|
-
<Popover open={true} onClose={() => {}} aria-label="Menu" trigger={trigger}>
|
|
42
|
-
Content
|
|
43
|
-
</Popover>,
|
|
44
|
-
);
|
|
45
|
-
expect(screen.getByRole('dialog').className).toContain('right');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('applies the left alignment class when specified', () => {
|
|
49
|
-
render(
|
|
50
|
-
<Popover open={true} onClose={() => {}} align="left" aria-label="Menu" trigger={trigger}>
|
|
51
|
-
Content
|
|
52
|
-
</Popover>,
|
|
53
|
-
);
|
|
54
|
-
expect(screen.getByRole('dialog').className).toContain('left');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('calls onClose when Escape is pressed', () => {
|
|
58
|
-
const onClose = vi.fn();
|
|
59
|
-
render(
|
|
60
|
-
<Popover open={true} onClose={onClose} aria-label="Menu" trigger={trigger}>
|
|
61
|
-
Content
|
|
62
|
-
</Popover>,
|
|
63
|
-
);
|
|
64
|
-
fireEvent.keyDown(document, { key: 'Escape' });
|
|
65
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('calls onClose when clicking outside the container', () => {
|
|
69
|
-
const onClose = vi.fn();
|
|
70
|
-
render(
|
|
71
|
-
<Popover open={true} onClose={onClose} aria-label="Menu" trigger={trigger}>
|
|
72
|
-
Content
|
|
73
|
-
</Popover>,
|
|
74
|
-
);
|
|
75
|
-
fireEvent.mouseDown(document.body);
|
|
76
|
-
expect(onClose).toHaveBeenCalledOnce();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('does not call onClose when clicking inside the panel', () => {
|
|
80
|
-
const onClose = vi.fn();
|
|
81
|
-
render(
|
|
82
|
-
<Popover open={true} onClose={onClose} aria-label="Menu" trigger={trigger}>
|
|
83
|
-
<button type="button">Inside</button>
|
|
84
|
-
</Popover>,
|
|
85
|
-
);
|
|
86
|
-
fireEvent.mouseDown(screen.getByRole('button', { name: 'Inside' }));
|
|
87
|
-
expect(onClose).not.toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('applies the provided width as inline style', () => {
|
|
91
|
-
render(
|
|
92
|
-
<Popover open={true} onClose={() => {}} aria-label="Menu" width={320} trigger={trigger}>
|
|
93
|
-
Content
|
|
94
|
-
</Popover>,
|
|
95
|
-
);
|
|
96
|
-
expect((screen.getByRole('dialog') as HTMLElement).style.width).toBe('320px');
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
.track {
|
|
2
|
-
width: 100%;
|
|
3
|
-
height: 8px;
|
|
4
|
-
border-radius: var(--sv-radius-full);
|
|
5
|
-
background: var(--sv-color-surface-sunken);
|
|
6
|
-
overflow: hidden;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.fill {
|
|
10
|
-
height: 100%;
|
|
11
|
-
border-radius: var(--sv-radius-full);
|
|
12
|
-
background: var(--sv-color-accent);
|
|
13
|
-
transition: width 0.2s ease;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@media (prefers-reduced-motion: reduce) {
|
|
17
|
-
.fill {
|
|
18
|
-
transition: none;
|
|
19
|
-
}
|
|
20
|
-
}
|