zabi-components 7.0.2 → 8.1.0
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/CHANGELOG.md +1924 -0
- package/README.md +287 -11
- package/THEME.md +149 -332
- package/THEMING.md +1329 -163
- package/dist/atoms/ActionPanel.svelte +24 -9
- package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
- package/dist/atoms/Avatar.svelte +104 -0
- package/dist/atoms/Avatar.svelte.d.ts +34 -0
- package/dist/atoms/Badge.svelte +124 -58
- package/dist/atoms/Badge.svelte.d.ts +17 -5
- package/dist/atoms/Button.svelte +212 -62
- package/dist/atoms/Button.svelte.d.ts +33 -2
- package/dist/atoms/Card.svelte +31 -28
- package/dist/atoms/Card.svelte.d.ts +5 -2
- package/dist/atoms/CardContent.svelte +10 -2
- package/dist/atoms/CardContent.svelte.d.ts +2 -0
- package/dist/atoms/CardFooter.svelte +10 -2
- package/dist/atoms/CardFooter.svelte.d.ts +2 -0
- package/dist/atoms/CardHeader.svelte +26 -6
- package/dist/atoms/CardHeader.svelte.d.ts +2 -0
- package/dist/atoms/Checkbox.svelte +52 -21
- package/dist/atoms/Checkbox.svelte.d.ts +6 -2
- package/dist/atoms/CodeBlock.svelte +35 -9
- package/dist/atoms/CodeBlock.svelte.d.ts +13 -3
- package/dist/atoms/ColorPicker.svelte +272 -52
- package/dist/atoms/ColorPicker.svelte.d.ts +6 -1
- package/dist/atoms/Container.svelte +17 -10
- package/dist/atoms/DateField.svelte +107 -0
- package/dist/atoms/DateField.svelte.d.ts +53 -0
- package/dist/atoms/Divider.svelte +4 -4
- package/dist/atoms/FeatureCard.svelte +4 -3
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +174 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +93 -32
- package/dist/atoms/Heading.svelte.d.ts +24 -4
- package/dist/atoms/IconButton.svelte +242 -53
- package/dist/atoms/IconButton.svelte.d.ts +49 -4
- package/dist/atoms/Input.svelte +267 -74
- package/dist/atoms/Input.svelte.d.ts +32 -2
- package/dist/atoms/List.svelte +13 -2
- package/dist/atoms/List.svelte.d.ts +2 -0
- package/dist/atoms/ListItem.svelte +44 -11
- package/dist/atoms/ListItem.svelte.d.ts +5 -0
- package/dist/atoms/ListItemLeading.svelte +4 -2
- package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
- package/dist/atoms/OptimizedImage.svelte +11 -2
- package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
- package/dist/atoms/Progress.svelte +31 -9
- package/dist/atoms/Progress.svelte.d.ts +6 -0
- package/dist/atoms/Radio.svelte +23 -3
- package/dist/atoms/Radio.svelte.d.ts +6 -2
- package/dist/atoms/Rating.svelte +494 -0
- package/dist/atoms/Rating.svelte.d.ts +56 -0
- package/dist/atoms/Select.svelte +475 -135
- package/dist/atoms/Select.svelte.d.ts +52 -2
- package/dist/atoms/SelectionControl.svelte +99 -18
- package/dist/atoms/SelectionControl.svelte.d.ts +6 -3
- package/dist/atoms/Skeleton.svelte +5 -3
- package/dist/atoms/Slider.svelte +309 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +152 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +140 -0
- package/dist/atoms/TemporalField.svelte.d.ts +32 -0
- package/dist/atoms/Text.svelte +56 -15
- package/dist/atoms/Text.svelte.d.ts +13 -4
- package/dist/atoms/Textarea.svelte +57 -45
- package/dist/atoms/Textarea.svelte.d.ts +14 -2
- package/dist/atoms/ThemeToggle.svelte +238 -56
- package/dist/atoms/ThemeToggle.svelte.d.ts +28 -3
- package/dist/atoms/TimeField.svelte +108 -0
- package/dist/atoms/TimeField.svelte.d.ts +54 -0
- package/dist/atoms/Toast.svelte +25 -10
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +60 -19
- package/dist/atoms/Toggle.svelte.d.ts +18 -2
- package/dist/atoms/Tooltip.svelte +583 -40
- package/dist/atoms/Tooltip.svelte.d.ts +26 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +17 -0
- package/dist/atoms/index.js +12 -0
- package/dist/atoms/selection-control.styles.d.ts +36 -5
- package/dist/atoms/selection-control.styles.js +40 -9
- package/dist/components/atoms/index.d.ts +17 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +36 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +83 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +46 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +53 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +6 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +15 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +28 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +36 -0
- package/dist/components/util/avatar.d.ts.map +1 -0
- package/dist/components/util/bottom-sheet.d.ts +30 -0
- package/dist/components/util/bottom-sheet.d.ts.map +1 -0
- package/dist/components/util/bottom-tab-bar.d.ts +35 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
- package/dist/components/util/calendar.d.ts +56 -0
- package/dist/components/util/calendar.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts +8 -0
- package/dist/components/util/cn.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +58 -0
- package/dist/components/util/collapsible.d.ts.map +1 -0
- package/dist/components/util/confirm-dialog.d.ts +12 -0
- package/dist/components/util/confirm-dialog.d.ts.map +1 -0
- package/dist/components/util/date.d.ts +96 -0
- package/dist/components/util/date.d.ts.map +1 -0
- package/dist/components/util/drawer.d.ts +13 -0
- package/dist/components/util/drawer.d.ts.map +1 -0
- package/dist/components/util/dropdown.d.ts +47 -0
- package/dist/components/util/dropdown.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +39 -0
- package/dist/components/util/field.d.ts.map +1 -0
- package/dist/components/util/fit-in-viewport.d.ts +201 -0
- package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +75 -2
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/hydration.d.ts +45 -0
- package/dist/components/util/hydration.d.ts.map +1 -0
- package/dist/components/util/keyboard-inset.d.ts +18 -0
- package/dist/components/util/keyboard-inset.d.ts.map +1 -0
- package/dist/components/util/media-grid.d.ts +51 -0
- package/dist/components/util/media-grid.d.ts.map +1 -0
- package/dist/components/util/media.d.ts +6 -0
- package/dist/components/util/media.d.ts.map +1 -0
- package/dist/components/util/overlay-chrome.d.ts +53 -0
- package/dist/components/util/overlay-chrome.d.ts.map +1 -0
- package/dist/components/util/overlay.d.ts +72 -0
- package/dist/components/util/overlay.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +215 -0
- package/dist/components/util/photo.d.ts.map +1 -0
- package/dist/components/util/portal.d.ts +23 -0
- package/dist/components/util/portal.d.ts.map +1 -0
- package/dist/components/util/pull-to-refresh.d.ts +27 -0
- package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
- package/dist/components/util/radio-keys.d.ts +25 -0
- package/dist/components/util/radio-keys.d.ts.map +1 -0
- package/dist/components/util/rating.d.ts +23 -0
- package/dist/components/util/rating.d.ts.map +1 -0
- package/dist/components/util/ready-made-strings.d.ts +77 -0
- package/dist/components/util/ready-made-strings.d.ts.map +1 -0
- package/dist/components/util/scroll-padding.d.ts +20 -0
- package/dist/components/util/scroll-padding.d.ts.map +1 -0
- package/dist/components/util/segmented-control.d.ts +16 -0
- package/dist/components/util/segmented-control.d.ts.map +1 -0
- package/dist/components/util/select.d.ts +41 -0
- package/dist/components/util/select.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +60 -0
- package/dist/components/util/sheet-drag.d.ts.map +1 -0
- package/dist/components/util/sidebar.d.ts +63 -0
- package/dist/components/util/sidebar.d.ts.map +1 -0
- package/dist/components/util/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/ssr-safe.d.ts +4 -4
- package/dist/components/util/ssr-safe.d.ts.map +1 -1
- package/dist/components/util/stepper.d.ts +40 -0
- package/dist/components/util/stepper.d.ts.map +1 -0
- package/dist/components/util/swipeable-list-item.d.ts +36 -0
- package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
- package/dist/components/util/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +93 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tooltip.d.ts +10 -0
- package/dist/components/util/tooltip.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +11 -0
- package/dist/components/util/top-navbar.d.ts.map +1 -0
- package/dist/components/util/touch-target.d.ts +31 -0
- package/dist/components/util/touch-target.d.ts.map +1 -0
- package/dist/components/util/zabi-strings.d.ts +133 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +169 -0
- package/dist/create-theme/index.d.ts +176 -0
- package/dist/create-theme/index.js +817 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +141 -0
- package/dist/create-theme/theme-data.js +1116 -0
- package/dist/index.d.ts +83 -3
- package/dist/index.js +60 -8
- package/dist/molecules/Alert.svelte +73 -66
- package/dist/molecules/Alert.svelte.d.ts +9 -1
- package/dist/molecules/AppBar.svelte +415 -0
- package/dist/molecules/AppBar.svelte.d.ts +69 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BottomSheet.svelte +540 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
- package/dist/molecules/BottomTabBar.svelte +297 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/Collapsible.svelte +243 -0
- package/dist/molecules/Collapsible.svelte.d.ts +44 -0
- package/dist/molecules/CollapsibleGroup.svelte +126 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +25 -5
- package/dist/molecules/ComponentDemo.svelte.d.ts +5 -0
- package/dist/molecules/ConfirmDialog.svelte +253 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
- package/dist/molecules/ContactForm.svelte +46 -19
- package/dist/molecules/ContactForm.svelte.d.ts +10 -0
- package/dist/molecules/Drawer.svelte +394 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +540 -50
- package/dist/molecules/Dropdown.svelte.d.ts +49 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +56 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/Form.svelte +25 -14
- package/dist/molecules/Form.svelte.d.ts +13 -5
- package/dist/molecules/FormField.svelte +11 -2
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +13 -3
- package/dist/molecules/Header.svelte.d.ts +2 -0
- package/dist/molecules/ImageUpload.svelte +422 -70
- package/dist/molecules/ImageUpload.svelte.d.ts +61 -5
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +340 -45
- package/dist/molecules/Modal.svelte.d.ts +59 -7
- package/dist/molecules/NavigationMenu.svelte +15 -3
- package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
- package/dist/molecules/NavigationMenuContent.svelte +91 -36
- package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuItem.svelte +10 -3
- package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuLink.svelte +10 -2
- package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuList.svelte +19 -4
- package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
- package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
- package/dist/molecules/Page.svelte +38 -2
- package/dist/molecules/Page.svelte.d.ts +13 -0
- package/dist/molecules/PhotoGrid.svelte +433 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
- package/dist/molecules/PhotoViewer.svelte +1116 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
- package/dist/molecules/PropsTable.svelte +26 -11
- package/dist/molecules/PropsTable.svelte.d.ts +5 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +57 -15
- package/dist/molecules/RadioGroup.svelte.d.ts +16 -3
- package/dist/molecules/Section.svelte +13 -5
- package/dist/molecules/Section.svelte.d.ts +3 -1
- package/dist/molecules/SegmentedControl.svelte +323 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
- package/dist/molecules/SidebarBrandHeader.svelte +24 -5
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +5 -0
- package/dist/molecules/SidebarFooter.svelte +39 -8
- package/dist/molecules/SidebarFooter.svelte.d.ts +5 -0
- package/dist/molecules/SidebarNavSection.svelte +12 -4
- package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
- package/dist/molecules/SlideUp.svelte +253 -30
- package/dist/molecules/SlideUp.svelte.d.ts +31 -4
- package/dist/molecules/SortableList.svelte +632 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +548 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +191 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +254 -24
- package/dist/molecules/Tabs.svelte.d.ts +15 -4
- package/dist/molecules/Toaster.svelte +352 -6
- package/dist/molecules/Toaster.svelte.d.ts +95 -2
- package/dist/molecules/ToasterToast.svelte +279 -62
- package/dist/molecules/ToasterToast.svelte.d.ts +11 -0
- package/dist/molecules/UnsavedChangesBar.svelte +212 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +46 -0
- package/dist/molecules/index.js +27 -0
- package/dist/molecules/toast-store.d.ts +53 -7
- package/dist/molecules/toast-store.js +21 -0
- package/dist/organisms/AppShell.svelte +196 -0
- package/dist/organisms/AppShell.svelte.d.ts +61 -0
- package/dist/organisms/SidebarAccountPanel.svelte +111 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +21 -0
- package/dist/organisms/SidebarNavigation.svelte +193 -69
- package/dist/organisms/SidebarNavigation.svelte.d.ts +31 -1
- package/dist/organisms/SidebarPanel.svelte +45 -16
- package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
- package/dist/organisms/SidebarShell.svelte +263 -0
- package/dist/organisms/SidebarShell.svelte.d.ts +106 -0
- package/dist/organisms/TopNavbar.svelte +329 -33
- package/dist/organisms/TopNavbar.svelte.d.ts +28 -0
- package/dist/organisms/index.d.ts +6 -0
- package/dist/organisms/index.js +4 -0
- package/dist/routes/lib/variant-utils.d.ts +26 -0
- package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
- package/dist/types/components.d.ts +4 -0
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +605 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/variants.ts +17 -1
- package/dist/util/app-shell.d.ts +27 -0
- package/dist/util/app-shell.js +72 -0
- package/dist/util/avatar.d.ts +35 -0
- package/dist/util/avatar.js +52 -0
- package/dist/util/bottom-sheet.d.ts +29 -0
- package/dist/util/bottom-sheet.js +43 -0
- package/dist/util/bottom-tab-bar.d.ts +34 -0
- package/dist/util/bottom-tab-bar.js +54 -0
- package/dist/util/calendar.d.ts +55 -0
- package/dist/util/calendar.js +74 -0
- package/dist/util/cn.d.ts +7 -0
- package/dist/util/cn.js +48 -0
- package/dist/util/collapsible.d.ts +57 -0
- package/dist/util/collapsible.js +23 -0
- package/dist/util/confirm-dialog.d.ts +11 -0
- package/dist/util/confirm-dialog.js +2 -0
- package/dist/util/date.d.ts +95 -0
- package/dist/util/date.js +237 -0
- package/dist/util/drawer.d.ts +12 -0
- package/dist/util/drawer.js +7 -0
- package/dist/util/dropdown.d.ts +46 -0
- package/dist/util/dropdown.js +21 -0
- package/dist/util/field.d.ts +38 -0
- package/dist/util/field.js +38 -0
- package/dist/util/fit-in-viewport.d.ts +200 -0
- package/dist/util/fit-in-viewport.js +399 -0
- package/dist/util/focus-utils.d.ts +75 -2
- package/dist/util/focus-utils.js +182 -9
- package/dist/util/hydration.d.ts +44 -0
- package/dist/util/hydration.js +58 -0
- package/dist/util/keyboard-inset.d.ts +17 -0
- package/dist/util/keyboard-inset.js +53 -0
- package/dist/util/media-grid.d.ts +50 -0
- package/dist/util/media-grid.js +81 -0
- package/dist/util/media.d.ts +5 -0
- package/dist/util/media.js +10 -0
- package/dist/util/overlay-chrome.d.ts +52 -0
- package/dist/util/overlay-chrome.js +56 -0
- package/dist/util/overlay.d.ts +71 -0
- package/dist/util/overlay.js +240 -0
- package/dist/util/photo.d.ts +214 -0
- package/dist/util/photo.js +206 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/pull-to-refresh.d.ts +26 -0
- package/dist/util/pull-to-refresh.js +30 -0
- package/dist/util/radio-keys.d.ts +24 -0
- package/dist/util/radio-keys.js +49 -0
- package/dist/util/rating.d.ts +22 -0
- package/dist/util/rating.js +26 -0
- package/dist/util/ready-made-strings.d.ts +76 -0
- package/dist/util/ready-made-strings.js +67 -0
- package/dist/util/scroll-padding.d.ts +19 -0
- package/dist/util/scroll-padding.js +44 -0
- package/dist/util/segmented-control.d.ts +15 -0
- package/dist/util/segmented-control.js +1 -0
- package/dist/util/select.d.ts +40 -0
- package/dist/util/select.js +63 -0
- package/dist/util/sheet-drag.d.ts +59 -0
- package/dist/util/sheet-drag.js +241 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/ssr-safe.d.ts +4 -4
- package/dist/util/stepper.d.ts +39 -0
- package/dist/util/stepper.js +38 -0
- package/dist/util/swipeable-list-item.d.ts +35 -0
- package/dist/util/swipeable-list-item.js +32 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +92 -0
- package/dist/util/toaster.js +75 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +10 -0
- package/dist/util/top-navbar.js +5 -0
- package/dist/util/touch-target.d.ts +30 -0
- package/dist/util/touch-target.js +30 -0
- package/dist/util/zabi-strings.d.ts +132 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +5 -2
- package/dist/zabi-components-theme-dark-only.css +574 -225
- package/dist/zabi-components-theme-dark.css +574 -225
- package/dist/zabi-components-theme-only.css +1680 -317
- package/dist/zabi-components-theme.css +1680 -317
- package/dist/zabi-components.css +4473 -1158
- package/docs/lucide-icons.md +41 -16
- package/docs/theme-imports.md +81 -11
- package/package.json +51 -29
- package/dist/lib/layout-width-tokens.d.ts +0 -17
- package/dist/lib/layout-width-tokens.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/List.d.ts +0 -3
- package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
- package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/index.d.ts +0 -4
- package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
- package/dist/lib/showcase/components-catalog.d.ts +0 -3
- package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
- package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
- package/dist/routes/lib/focus-utils.d.ts +0 -12
- package/dist/routes/lib/focus-utils.ts +0 -110
- package/dist/routes/lib/ssr-safe.ts +0 -58
- package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { SidebarDrawerCloseReason, SidebarTriggerContext } from "./SidebarShell.svelte";
|
|
1
2
|
import type { Snippet } from "svelte";
|
|
2
3
|
import type { Component } from "svelte";
|
|
3
4
|
import type { ButtonVariant, SizeVariant } from "../types/variants.js";
|
|
5
|
+
import { type SidebarNavigationStrings } from "../util/sidebar.js";
|
|
4
6
|
export interface SidebarNavigationItem {
|
|
5
7
|
id: string;
|
|
6
8
|
label: string;
|
|
@@ -21,6 +23,14 @@ interface Props {
|
|
|
21
23
|
items?: SidebarNavigationItem[];
|
|
22
24
|
currentPath?: string;
|
|
23
25
|
ariaLabel?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The words the sidebar says by itself, for another language: the
|
|
28
|
+
* names of its lists, the two lines shown when a search finds
|
|
29
|
+
* nothing, and those of the footer and the brand header in it.
|
|
30
|
+
*/
|
|
31
|
+
strings?: Partial<SidebarNavigationStrings>;
|
|
32
|
+
class?: string;
|
|
33
|
+
/** @deprecated use `class`. */
|
|
24
34
|
className?: string;
|
|
25
35
|
logoSrc?: string;
|
|
26
36
|
logoAlt?: string;
|
|
@@ -59,7 +69,27 @@ interface Props {
|
|
|
59
69
|
profilePanel?: Snippet;
|
|
60
70
|
/** Parent nav href to highlight when `currentPath` is a deeper leaf (e.g. category row). */
|
|
61
71
|
activePrimaryHref?: string;
|
|
72
|
+
/**
|
|
73
|
+
* What the sidebar is below the `lg` breakpoint. `drawer`: the rail is
|
|
74
|
+
* hidden there and the sidebar opens in a Drawer from the start edge.
|
|
75
|
+
* This and the six props after it are SidebarShell's; see there.
|
|
76
|
+
*/
|
|
77
|
+
mobile?: "none" | "drawer";
|
|
78
|
+
/** Whether the drawer is open. Bindable. */
|
|
79
|
+
isOpen?: boolean;
|
|
80
|
+
/** The button that opens the drawer, rendered in the rail's place below `lg`. */
|
|
81
|
+
trigger?: Snippet<[SidebarTriggerContext]>;
|
|
82
|
+
/** Heading of the drawer. Without it, `ariaLabel` is. */
|
|
83
|
+
drawerTitle?: string;
|
|
84
|
+
/** Accessible name of the drawer's close button. */
|
|
85
|
+
closeLabel?: string;
|
|
86
|
+
/** Fired when the drawer closes itself, with why. */
|
|
87
|
+
onclose?: (detail: {
|
|
88
|
+
reason: SidebarDrawerCloseReason;
|
|
89
|
+
}) => void;
|
|
90
|
+
/** Accessible name of the scrolling region between header and footer. */
|
|
91
|
+
label?: string;
|
|
62
92
|
}
|
|
63
|
-
declare const SidebarNavigation: Component<Props, {}, "isLightMode" | "searchValue">;
|
|
93
|
+
declare const SidebarNavigation: Component<Props, {}, "isOpen" | "isLightMode" | "searchValue">;
|
|
64
94
|
type SidebarNavigation = ReturnType<typeof SidebarNavigation>;
|
|
65
95
|
export default SidebarNavigation;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { zabiCommonStrings } from "../util/zabi-strings.js";
|
|
2
3
|
import Input from "../atoms/Input.svelte";
|
|
3
4
|
import Badge from "../atoms/Badge.svelte";
|
|
4
|
-
import
|
|
5
|
+
import Search from "@lucide/svelte/icons/search";
|
|
6
|
+
import X from "@lucide/svelte/icons/x";
|
|
5
7
|
import type { Component } from "svelte";
|
|
8
|
+
import { cn } from "../util/cn.js";
|
|
6
9
|
|
|
7
10
|
export interface SidebarPanelItem {
|
|
8
11
|
id: string;
|
|
@@ -13,6 +16,8 @@
|
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
interface Props {
|
|
19
|
+
class?: string;
|
|
20
|
+
/** @deprecated use `class`. */
|
|
16
21
|
className?: string;
|
|
17
22
|
widthClass?: string;
|
|
18
23
|
variant?: "plain" | "elevated";
|
|
@@ -35,17 +40,18 @@
|
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
let {
|
|
38
|
-
|
|
43
|
+
class: classAttr = "",
|
|
44
|
+
className: legacyClass = "",
|
|
39
45
|
widthClass = "w-80",
|
|
40
46
|
variant = "elevated",
|
|
41
47
|
ariaLabel = "Picker panel",
|
|
42
48
|
title = "Items",
|
|
43
49
|
subtitle = "Choose an item to continue",
|
|
44
50
|
showSearch = true,
|
|
45
|
-
searchPlaceholder
|
|
46
|
-
searchValue = $bindable
|
|
51
|
+
searchPlaceholder: searchPlaceholderGiven,
|
|
52
|
+
searchValue = $bindable<Exclude<Props["searchValue"], undefined>>(),
|
|
47
53
|
items = [],
|
|
48
|
-
selectedItemId = $bindable
|
|
54
|
+
selectedItemId = $bindable<Exclude<Props["selectedItemId"], undefined>>(),
|
|
49
55
|
emptyStateTitle = "No matches",
|
|
50
56
|
emptyStateDescription = "Try a different keyword.",
|
|
51
57
|
emptyStateActionLabel = "",
|
|
@@ -57,6 +63,26 @@
|
|
|
57
63
|
...restProps
|
|
58
64
|
}: Props = $props();
|
|
59
65
|
|
|
66
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
67
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
68
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
69
|
+
// here instead: at once, for the server and the first render, and again
|
|
70
|
+
// whenever a parent hands back `undefined`.
|
|
71
|
+
const applyDefaults = () => {
|
|
72
|
+
if (searchValue === undefined) searchValue = "";
|
|
73
|
+
if (selectedItemId === undefined) selectedItemId = "";
|
|
74
|
+
};
|
|
75
|
+
applyDefaults();
|
|
76
|
+
$effect.pre(applyDefaults);
|
|
77
|
+
|
|
78
|
+
/** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
|
|
79
|
+
const common = zabiCommonStrings();
|
|
80
|
+
const searchPlaceholder = $derived(searchPlaceholderGiven ?? common().search);
|
|
81
|
+
|
|
82
|
+
/** `class` is the public prop; `className` is a deprecated alias.
|
|
83
|
+
* Both are merged here so existing call sites keep working. */
|
|
84
|
+
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
85
|
+
|
|
60
86
|
const normalizedSearchTerm = $derived(searchValue.trim().toLowerCase());
|
|
61
87
|
const filteredItems = $derived(
|
|
62
88
|
normalizedSearchTerm
|
|
@@ -71,19 +97,22 @@
|
|
|
71
97
|
const containerClasses = $derived.by(() => {
|
|
72
98
|
const resolvedWidthClass = widthClass.trim() || "w-80";
|
|
73
99
|
const shell = isElevated
|
|
74
|
-
? "rounded-
|
|
75
|
-
: "rounded-
|
|
76
|
-
return `${resolvedWidthClass} shrink-0 p-5 ${shell} ${className}
|
|
100
|
+
? "rounded-container border border-border bg-card text-headline shadow-sm ring-1 ring-border"
|
|
101
|
+
: "rounded-container border border-border bg-card text-headline shadow-sm";
|
|
102
|
+
return cn(`${resolvedWidthClass} shrink-0 p-5 ${shell} ${className}`);
|
|
77
103
|
});
|
|
78
104
|
|
|
79
105
|
function getItemClasses(itemId: string): string {
|
|
80
106
|
const isActive = selectedItemId === itemId;
|
|
81
107
|
const baseClasses =
|
|
82
|
-
"focus-ring focus-ring--nav w-full cursor-pointer rounded-
|
|
108
|
+
"focus-ring focus-ring--nav w-full cursor-pointer rounded-control px-3 py-2 text-left transition-colors duration-150";
|
|
83
109
|
if (isActive) {
|
|
84
|
-
|
|
110
|
+
// The fill, the shadow and the ring are all dropped in forced
|
|
111
|
+
// colours; an outline is drawn there, so the selected item keeps
|
|
112
|
+
// a shape of its own.
|
|
113
|
+
return `${baseClasses} bg-nav-menu-active text-inherit shadow-sm ring-1 ring-border forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2`;
|
|
85
114
|
}
|
|
86
|
-
return `${baseClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-
|
|
115
|
+
return `${baseClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-surface-active`;
|
|
87
116
|
}
|
|
88
117
|
|
|
89
118
|
function handleSelect(item: SidebarPanelItem): void {
|
|
@@ -116,7 +145,7 @@
|
|
|
116
145
|
</div>
|
|
117
146
|
<button
|
|
118
147
|
type="button"
|
|
119
|
-
class="-mr-0.5 -mt-0.5 inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-
|
|
148
|
+
class="-mr-0.5 -mt-0.5 inline-flex size-9 pointer-coarse:min-h-11 pointer-coarse:min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-control text-nav-menu-item transition-colors hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active focus-ring focus-ring--nav"
|
|
120
149
|
aria-label={closeLabel}
|
|
121
150
|
onclick={handleClose}
|
|
122
151
|
>
|
|
@@ -137,7 +166,7 @@
|
|
|
137
166
|
bind:value={searchValue}
|
|
138
167
|
placeholder={searchPlaceholder}
|
|
139
168
|
aria-label={searchPlaceholder}
|
|
140
|
-
class="w-full min-w-0 min-h-10 rounded-
|
|
169
|
+
class="w-full min-w-0 min-h-10 pointer-coarse:min-h-11 rounded-container border-transparent !bg-transparent py-2 pl-10 text-sm ring-1 ring-input-border hover:!bg-nav-menu-hover focus:!bg-transparent focus-ring focus-ring--nav"
|
|
141
170
|
/>
|
|
142
171
|
</div>
|
|
143
172
|
{/if}
|
|
@@ -154,7 +183,7 @@
|
|
|
154
183
|
aria-selected={selectedItemId === item.id}
|
|
155
184
|
>
|
|
156
185
|
<span class="flex items-center justify-between gap-3">
|
|
157
|
-
<span class="flex min-w-0 items-start gap-
|
|
186
|
+
<span class="flex min-w-0 items-start gap-3">
|
|
158
187
|
{#if item.icon}
|
|
159
188
|
{@const Icon = item.icon}
|
|
160
189
|
<span
|
|
@@ -189,14 +218,14 @@
|
|
|
189
218
|
</ul>
|
|
190
219
|
{:else}
|
|
191
220
|
<div
|
|
192
|
-
class="rounded-
|
|
221
|
+
class="rounded-container border border-dashed border-border bg-transparent px-4 py-4"
|
|
193
222
|
>
|
|
194
223
|
<h4 class="text-sm font-semibold text-headline">{emptyStateTitle}</h4>
|
|
195
224
|
<p class="mt-1 text-sm leading-relaxed text-description">{emptyStateDescription}</p>
|
|
196
225
|
{#if emptyStateActionLabel.trim() && onEmptyStateAction}
|
|
197
226
|
<button
|
|
198
227
|
type="button"
|
|
199
|
-
class="mt-3 inline-flex min-h-10 cursor-pointer items-center rounded-
|
|
228
|
+
class="mt-3 inline-flex min-h-10 pointer-coarse:min-h-11 cursor-pointer items-center rounded-control bg-action-primary px-3 py-2 text-sm font-medium text-action-primary transition-colors hover:bg-action-primary-hover focus-ring focus-ring--nav"
|
|
200
229
|
onclick={handleEmptyStateAction}
|
|
201
230
|
>
|
|
202
231
|
{emptyStateActionLabel.trim()}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { zabiCommonStrings } from "../util/zabi-strings.js";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
5
|
+
import Drawer from "../molecules/Drawer.svelte";
|
|
6
|
+
import { cn } from "../util/cn.js";
|
|
7
|
+
import type { DrawerCloseReason } from "../util/drawer.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The chrome of a sidebar and nothing else: width, surface, collapse
|
|
11
|
+
* behaviour, the inset its regions share, and a scroll container for the
|
|
12
|
+
* middle.
|
|
13
|
+
*
|
|
14
|
+
* `SidebarNavigation` is a fully-specified sidebar driven by ~44 props —
|
|
15
|
+
* excellent when your sidebar looks like that one, useless when it does
|
|
16
|
+
* not, because the parts are not reachable. This is the same shell with
|
|
17
|
+
* the regions left open, so you can drop in `SidebarBrandHeader`,
|
|
18
|
+
* `SidebarNavSection` and `SidebarFooter` (or your own markup) and skip
|
|
19
|
+
* the props you would never set.
|
|
20
|
+
*
|
|
21
|
+
* `SidebarNavigation` is built on it, so the two cannot drift apart.
|
|
22
|
+
*
|
|
23
|
+
* ```svelte
|
|
24
|
+
* <SidebarShell mode="collapsed">
|
|
25
|
+
* {#snippet header({ collapsed })}
|
|
26
|
+
* <SidebarBrandHeader {collapsed} brandName="Zabi" />
|
|
27
|
+
* {/snippet}
|
|
28
|
+
* <SidebarNavSection title="Main" sectionKey="main">…</SidebarNavSection>
|
|
29
|
+
* {#snippet footer({ collapsed, insetX })}
|
|
30
|
+
* <SidebarFooter {collapsed} className={insetX} />
|
|
31
|
+
* {/snippet}
|
|
32
|
+
* </SidebarShell>
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* A sidebar is 266px wide, which a phone does not have beside its
|
|
36
|
+
* content. With `mobile="drawer"` the rail is only there from the `lg`
|
|
37
|
+
* breakpoint (1024px) up; below it the same regions open in a Drawer from
|
|
38
|
+
* the start edge, with the Drawer's focus trap, Escape and backdrop:
|
|
39
|
+
*
|
|
40
|
+
* ```svelte
|
|
41
|
+
* <SidebarShell mobile="drawer" bind:isOpen drawerTitle="Menu">
|
|
42
|
+
* {#snippet trigger({ props, toggle })}
|
|
43
|
+
* <IconButton label="Menu" onclick={toggle} {...props}><Menu /></IconButton>
|
|
44
|
+
* {/snippet}
|
|
45
|
+
* …
|
|
46
|
+
* </SidebarShell>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* `trigger` is rendered where the rail would be, below `lg` only; leave
|
|
50
|
+
* it out and open the drawer from a button of your own by setting
|
|
51
|
+
* `isOpen`. The drawer closes when a link in it is followed.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/** What the `trigger` snippet gets. */
|
|
55
|
+
export interface SidebarTriggerContext {
|
|
56
|
+
isOpen: boolean;
|
|
57
|
+
toggle: () => void;
|
|
58
|
+
/** Spread on the button: it says what the button opens, and whether it is open. */
|
|
59
|
+
props: { "aria-haspopup": "dialog"; "aria-expanded": boolean };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** What closed the drawer: the user, a link that was followed, or the screen reaching `lg`. */
|
|
63
|
+
export type SidebarDrawerCloseReason = DrawerCloseReason | "navigate" | "resize";
|
|
64
|
+
|
|
65
|
+
/** Passed to every region so it can match the shell's collapse and inset. */
|
|
66
|
+
export interface SidebarRegionContext {
|
|
67
|
+
collapsed: boolean;
|
|
68
|
+
/** Horizontal padding utility the shell's own regions use. */
|
|
69
|
+
insetX: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// `children` here is a snippet that takes the region context; the one in
|
|
73
|
+
// `HTMLAttributes` takes nothing, and the two together accepted neither.
|
|
74
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "onclose" | "children"> & {
|
|
75
|
+
mode?: "expanded" | "collapsed";
|
|
76
|
+
/** `rail` attaches to the viewport edge; `card` is a floating panel. */
|
|
77
|
+
layout?: "rail" | "card";
|
|
78
|
+
ariaLabel?: string;
|
|
79
|
+
/** Accessible name of the scrolling region between header and footer. */
|
|
80
|
+
label?: string;
|
|
81
|
+
/**
|
|
82
|
+
* What the sidebar is below the `lg` breakpoint. `none`: the rail, as
|
|
83
|
+
* everywhere. `drawer`: the rail is hidden there and its regions open
|
|
84
|
+
* in a Drawer from the start edge instead.
|
|
85
|
+
*/
|
|
86
|
+
mobile?: "none" | "drawer";
|
|
87
|
+
/** Whether the drawer is open. Bindable. Only used with `mobile="drawer"`. */
|
|
88
|
+
isOpen?: boolean;
|
|
89
|
+
/** Heading of the drawer, and its accessible name. Without it, `ariaLabel` is. */
|
|
90
|
+
drawerTitle?: string;
|
|
91
|
+
/** Accessible name of the drawer's close button. */
|
|
92
|
+
closeLabel?: string;
|
|
93
|
+
/** Fired when the drawer closes itself, with why. */
|
|
94
|
+
onclose?: (detail: { reason: SidebarDrawerCloseReason }) => void;
|
|
95
|
+
/**
|
|
96
|
+
* The button that opens the drawer, rendered in the rail's place below
|
|
97
|
+
* `lg`. Optional: a button anywhere that sets `isOpen` does the same.
|
|
98
|
+
*/
|
|
99
|
+
trigger?: Snippet<[SidebarTriggerContext]>;
|
|
100
|
+
class?: string;
|
|
101
|
+
/** @deprecated use `class`. */
|
|
102
|
+
className?: string;
|
|
103
|
+
/** Brand row, search — anything above the scrolling nav area. */
|
|
104
|
+
header?: Snippet<[SidebarRegionContext]>;
|
|
105
|
+
/** The scrolling middle. */
|
|
106
|
+
children?: Snippet<[SidebarRegionContext]>;
|
|
107
|
+
/** Account row, logout, theme toggle. Outside the scroll container. */
|
|
108
|
+
footer?: Snippet<[SidebarRegionContext]>;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let {
|
|
112
|
+
mode = "expanded",
|
|
113
|
+
layout = "rail",
|
|
114
|
+
ariaLabel = "Sidebar navigation",
|
|
115
|
+
label = "Navigation links",
|
|
116
|
+
mobile = "none",
|
|
117
|
+
isOpen = $bindable<Exclude<Props["isOpen"], undefined>>(),
|
|
118
|
+
drawerTitle = undefined,
|
|
119
|
+
closeLabel: closeLabelGiven,
|
|
120
|
+
onclose,
|
|
121
|
+
trigger,
|
|
122
|
+
class: classAttr = "",
|
|
123
|
+
className: legacyClass = "",
|
|
124
|
+
header,
|
|
125
|
+
children,
|
|
126
|
+
footer,
|
|
127
|
+
...restProps
|
|
128
|
+
}: Props = $props();
|
|
129
|
+
|
|
130
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
131
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
132
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
133
|
+
// here instead: at once, for the server and the first render, and again
|
|
134
|
+
// whenever a parent hands back `undefined`.
|
|
135
|
+
const applyDefaults = () => {
|
|
136
|
+
if (isOpen === undefined) isOpen = false;
|
|
137
|
+
};
|
|
138
|
+
applyDefaults();
|
|
139
|
+
$effect.pre(applyDefaults);
|
|
140
|
+
|
|
141
|
+
/** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
|
|
142
|
+
const common = zabiCommonStrings();
|
|
143
|
+
const closeLabel = $derived(closeLabelGiven ?? common().close);
|
|
144
|
+
|
|
145
|
+
const className = $derived(cn(classAttr, legacyClass));
|
|
146
|
+
const isCollapsed = $derived(mode === "collapsed");
|
|
147
|
+
const isCard = $derived(layout === "card");
|
|
148
|
+
|
|
149
|
+
const insetX = $derived(isCollapsed ? "px-2" : "px-4");
|
|
150
|
+
|
|
151
|
+
const containerClasses = $derived.by(() => {
|
|
152
|
+
const widthClass = isCollapsed ? "w-[72px]" : "w-[266px]";
|
|
153
|
+
const railSurface = "border-r border-border bg-background text-headline";
|
|
154
|
+
const cardSurface =
|
|
155
|
+
"border-r border-border bg-background text-headline shadow-sm";
|
|
156
|
+
const surfaceClasses = isCard ? cardSurface : railSurface;
|
|
157
|
+
const verticalPad = isCard ? "py-4" : "py-5";
|
|
158
|
+
// `flex` and `max-lg:hidden` must not both apply below `lg`: with
|
|
159
|
+
// the drawer mode the rail is a flex box from `lg` up only.
|
|
160
|
+
const display = asDrawer ? "hidden lg:flex" : "flex";
|
|
161
|
+
const baseClasses = `${display} h-full min-h-0 max-h-full flex-col overflow-visible ${verticalPad}`;
|
|
162
|
+
|
|
163
|
+
return cn(`${baseClasses} ${widthClass} ${surfaceClasses} ${className}`);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const region = $derived({ collapsed: isCollapsed, insetX });
|
|
167
|
+
|
|
168
|
+
const asDrawer = $derived(mobile === "drawer");
|
|
169
|
+
/** In the drawer the sidebar is always spelled out, and the drawer's own padding is the inset. */
|
|
170
|
+
const drawerRegion: SidebarRegionContext = { collapsed: false, insetX: "px-0" };
|
|
171
|
+
|
|
172
|
+
function closeDrawer(reason: SidebarDrawerCloseReason) {
|
|
173
|
+
if (!isOpen) return;
|
|
174
|
+
isOpen = false;
|
|
175
|
+
onclose?.({ reason });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// From `lg` up the rail is there: a drawer left open would cover it.
|
|
179
|
+
$effect(() => {
|
|
180
|
+
if (!asDrawer || typeof window.matchMedia !== "function") return;
|
|
181
|
+
const wide = window.matchMedia("(min-width: 1024px)");
|
|
182
|
+
const onChange = () => {
|
|
183
|
+
if (wide.matches) closeDrawer("resize");
|
|
184
|
+
};
|
|
185
|
+
onChange();
|
|
186
|
+
wide.addEventListener("change", onChange);
|
|
187
|
+
return () => wide.removeEventListener("change", onChange);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
/** A link in the drawer was followed: the page it leads to is behind the drawer. */
|
|
191
|
+
function handleDrawerClick(event: MouseEvent) {
|
|
192
|
+
if (!(event.target instanceof Element)) return;
|
|
193
|
+
if (event.target.closest("a[href]")) closeDrawer("navigate");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const triggerContext = $derived<SidebarTriggerContext>({
|
|
197
|
+
isOpen,
|
|
198
|
+
toggle: () => (isOpen ? closeDrawer("close-button") : (isOpen = true)),
|
|
199
|
+
props: { "aria-haspopup": "dialog", "aria-expanded": isOpen },
|
|
200
|
+
});
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
{#snippet regions(context: SidebarRegionContext, inDrawer: boolean)}
|
|
204
|
+
<div class={cn("flex w-full min-w-0 flex-col", !inDrawer && "min-h-0 flex-1")}>
|
|
205
|
+
{#if header}
|
|
206
|
+
<div class={cn("flex w-full shrink-0 flex-col gap-5 pb-3", context.insetX)}>
|
|
207
|
+
{@render header(context)}
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
|
|
211
|
+
<!-- In the drawer the drawer's body scrolls, so this region does not. -->
|
|
212
|
+
<div
|
|
213
|
+
class={cn(
|
|
214
|
+
"flex min-w-0 flex-col gap-3 pt-4 pb-2",
|
|
215
|
+
!inDrawer &&
|
|
216
|
+
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden overscroll-y-contain",
|
|
217
|
+
context.insetX,
|
|
218
|
+
)}
|
|
219
|
+
role="region"
|
|
220
|
+
aria-label={label}
|
|
221
|
+
>
|
|
222
|
+
{@render children?.(context)}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{#if footer}
|
|
227
|
+
{@render footer(context)}
|
|
228
|
+
{/if}
|
|
229
|
+
{/snippet}
|
|
230
|
+
|
|
231
|
+
<nav class={containerClasses} aria-label={ariaLabel} {...restProps}>
|
|
232
|
+
{@render regions(region, false)}
|
|
233
|
+
</nav>
|
|
234
|
+
|
|
235
|
+
{#if asDrawer}
|
|
236
|
+
{#if trigger}
|
|
237
|
+
<div class="lg:hidden" data-sidebar-trigger>
|
|
238
|
+
{@render trigger(triggerContext)}
|
|
239
|
+
</div>
|
|
240
|
+
{/if}
|
|
241
|
+
<!-- The same regions, in a Drawer. Rendered only while it is open, so
|
|
242
|
+
nothing of the sidebar is in the page twice until then, and the server
|
|
243
|
+
renders the rail alone. -->
|
|
244
|
+
<Drawer
|
|
245
|
+
bind:isOpen
|
|
246
|
+
side="start"
|
|
247
|
+
size="sm"
|
|
248
|
+
title={drawerTitle || ariaLabel}
|
|
249
|
+
{closeLabel}
|
|
250
|
+
onclose={(detail) => onclose?.(detail)}
|
|
251
|
+
class="lg:hidden"
|
|
252
|
+
>
|
|
253
|
+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
|
|
254
|
+
<nav
|
|
255
|
+
class="flex w-full min-w-0 flex-col text-headline"
|
|
256
|
+
aria-label={ariaLabel}
|
|
257
|
+
data-sidebar-drawer
|
|
258
|
+
onclick={handleDrawerClick}
|
|
259
|
+
>
|
|
260
|
+
{@render regions(drawerRegion, true)}
|
|
261
|
+
</nav>
|
|
262
|
+
</Drawer>
|
|
263
|
+
{/if}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { DrawerCloseReason } from "../util/drawer.js";
|
|
4
|
+
/**
|
|
5
|
+
* The chrome of a sidebar and nothing else: width, surface, collapse
|
|
6
|
+
* behaviour, the inset its regions share, and a scroll container for the
|
|
7
|
+
* middle.
|
|
8
|
+
*
|
|
9
|
+
* `SidebarNavigation` is a fully-specified sidebar driven by ~44 props —
|
|
10
|
+
* excellent when your sidebar looks like that one, useless when it does
|
|
11
|
+
* not, because the parts are not reachable. This is the same shell with
|
|
12
|
+
* the regions left open, so you can drop in `SidebarBrandHeader`,
|
|
13
|
+
* `SidebarNavSection` and `SidebarFooter` (or your own markup) and skip
|
|
14
|
+
* the props you would never set.
|
|
15
|
+
*
|
|
16
|
+
* `SidebarNavigation` is built on it, so the two cannot drift apart.
|
|
17
|
+
*
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <SidebarShell mode="collapsed">
|
|
20
|
+
* {#snippet header({ collapsed })}
|
|
21
|
+
* <SidebarBrandHeader {collapsed} brandName="Zabi" />
|
|
22
|
+
* {/snippet}
|
|
23
|
+
* <SidebarNavSection title="Main" sectionKey="main">…</SidebarNavSection>
|
|
24
|
+
* {#snippet footer({ collapsed, insetX })}
|
|
25
|
+
* <SidebarFooter {collapsed} className={insetX} />
|
|
26
|
+
* {/snippet}
|
|
27
|
+
* </SidebarShell>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* A sidebar is 266px wide, which a phone does not have beside its
|
|
31
|
+
* content. With `mobile="drawer"` the rail is only there from the `lg`
|
|
32
|
+
* breakpoint (1024px) up; below it the same regions open in a Drawer from
|
|
33
|
+
* the start edge, with the Drawer's focus trap, Escape and backdrop:
|
|
34
|
+
*
|
|
35
|
+
* ```svelte
|
|
36
|
+
* <SidebarShell mobile="drawer" bind:isOpen drawerTitle="Menu">
|
|
37
|
+
* {#snippet trigger({ props, toggle })}
|
|
38
|
+
* <IconButton label="Menu" onclick={toggle} {...props}><Menu /></IconButton>
|
|
39
|
+
* {/snippet}
|
|
40
|
+
* …
|
|
41
|
+
* </SidebarShell>
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* `trigger` is rendered where the rail would be, below `lg` only; leave
|
|
45
|
+
* it out and open the drawer from a button of your own by setting
|
|
46
|
+
* `isOpen`. The drawer closes when a link in it is followed.
|
|
47
|
+
*/
|
|
48
|
+
/** What the `trigger` snippet gets. */
|
|
49
|
+
export interface SidebarTriggerContext {
|
|
50
|
+
isOpen: boolean;
|
|
51
|
+
toggle: () => void;
|
|
52
|
+
/** Spread on the button: it says what the button opens, and whether it is open. */
|
|
53
|
+
props: {
|
|
54
|
+
"aria-haspopup": "dialog";
|
|
55
|
+
"aria-expanded": boolean;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** What closed the drawer: the user, a link that was followed, or the screen reaching `lg`. */
|
|
59
|
+
export type SidebarDrawerCloseReason = DrawerCloseReason | "navigate" | "resize";
|
|
60
|
+
/** Passed to every region so it can match the shell's collapse and inset. */
|
|
61
|
+
export interface SidebarRegionContext {
|
|
62
|
+
collapsed: boolean;
|
|
63
|
+
/** Horizontal padding utility the shell's own regions use. */
|
|
64
|
+
insetX: string;
|
|
65
|
+
}
|
|
66
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "onclose" | "children"> & {
|
|
67
|
+
mode?: "expanded" | "collapsed";
|
|
68
|
+
/** `rail` attaches to the viewport edge; `card` is a floating panel. */
|
|
69
|
+
layout?: "rail" | "card";
|
|
70
|
+
ariaLabel?: string;
|
|
71
|
+
/** Accessible name of the scrolling region between header and footer. */
|
|
72
|
+
label?: string;
|
|
73
|
+
/**
|
|
74
|
+
* What the sidebar is below the `lg` breakpoint. `none`: the rail, as
|
|
75
|
+
* everywhere. `drawer`: the rail is hidden there and its regions open
|
|
76
|
+
* in a Drawer from the start edge instead.
|
|
77
|
+
*/
|
|
78
|
+
mobile?: "none" | "drawer";
|
|
79
|
+
/** Whether the drawer is open. Bindable. Only used with `mobile="drawer"`. */
|
|
80
|
+
isOpen?: boolean;
|
|
81
|
+
/** Heading of the drawer, and its accessible name. Without it, `ariaLabel` is. */
|
|
82
|
+
drawerTitle?: string;
|
|
83
|
+
/** Accessible name of the drawer's close button. */
|
|
84
|
+
closeLabel?: string;
|
|
85
|
+
/** Fired when the drawer closes itself, with why. */
|
|
86
|
+
onclose?: (detail: {
|
|
87
|
+
reason: SidebarDrawerCloseReason;
|
|
88
|
+
}) => void;
|
|
89
|
+
/**
|
|
90
|
+
* The button that opens the drawer, rendered in the rail's place below
|
|
91
|
+
* `lg`. Optional: a button anywhere that sets `isOpen` does the same.
|
|
92
|
+
*/
|
|
93
|
+
trigger?: Snippet<[SidebarTriggerContext]>;
|
|
94
|
+
class?: string;
|
|
95
|
+
/** @deprecated use `class`. */
|
|
96
|
+
className?: string;
|
|
97
|
+
/** Brand row, search — anything above the scrolling nav area. */
|
|
98
|
+
header?: Snippet<[SidebarRegionContext]>;
|
|
99
|
+
/** The scrolling middle. */
|
|
100
|
+
children?: Snippet<[SidebarRegionContext]>;
|
|
101
|
+
/** Account row, logout, theme toggle. Outside the scroll container. */
|
|
102
|
+
footer?: Snippet<[SidebarRegionContext]>;
|
|
103
|
+
};
|
|
104
|
+
declare const SidebarShell: import("svelte").Component<Props, {}, "isOpen">;
|
|
105
|
+
type SidebarShell = ReturnType<typeof SidebarShell>;
|
|
106
|
+
export default SidebarShell;
|