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,26 +1,64 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
3
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
2
4
|
import ThemeToggle from "../atoms/ThemeToggle.svelte";
|
|
5
|
+
import IconButton from "../atoms/IconButton.svelte";
|
|
6
|
+
import ExternalLink from "@lucide/svelte/icons/external-link";
|
|
7
|
+
import Menu from "@lucide/svelte/icons/menu";
|
|
8
|
+
import X from "@lucide/svelte/icons/x";
|
|
3
9
|
import type { Component, Snippet } from "svelte";
|
|
10
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
11
|
+
import { cn } from "../util/cn.js";
|
|
12
|
+
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
13
|
+
import { DEFAULT_TOP_NAVBAR_STRINGS, type TopNavbarStrings } from "../util/top-navbar.js";
|
|
14
|
+
import { isInsideToastRegion } from "../util/focus-utils.js";
|
|
4
15
|
|
|
5
16
|
export interface TopNavbarNavItem {
|
|
6
17
|
label: string;
|
|
7
18
|
href: string;
|
|
8
19
|
icon?: Component<{ size?: number; class?: string }>;
|
|
9
20
|
iconFilled?: Component<{ size?: number; class?: string }>;
|
|
21
|
+
/** Marks the link and opens it in a new tab. Defaults to true for absolute URLs. */
|
|
22
|
+
external?: boolean;
|
|
10
23
|
}
|
|
11
24
|
|
|
25
|
+
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
26
|
+
|
|
12
27
|
interface Props {
|
|
13
28
|
brand?: string;
|
|
14
29
|
brandHref?: string;
|
|
15
30
|
/** `aria-label` on `<nav>` when multiple nav landmarks exist. */
|
|
16
31
|
ariaLabel?: string;
|
|
17
32
|
showThemeToggle?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* How the theme toggle steps: `"two"` flips light and dark, `"three"`
|
|
35
|
+
* goes through system, light and dark. As `modes` of ThemeToggle.
|
|
36
|
+
*/
|
|
37
|
+
themeModes?: "two" | "three";
|
|
38
|
+
/**
|
|
39
|
+
* The `localStorage` key the theme choice is kept under; `null` keeps
|
|
40
|
+
* nothing. As `storageKey` of ThemeToggle, whose default applies when
|
|
41
|
+
* this is left out.
|
|
42
|
+
*/
|
|
43
|
+
themeStorageKey?: string | null;
|
|
44
|
+
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
45
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
46
|
+
/** The words the bar says by itself: the menu button's two names and the note on a link that opens a new tab. */
|
|
47
|
+
strings?: Partial<TopNavbarStrings>;
|
|
48
|
+
class?: string;
|
|
49
|
+
/** @deprecated use `class`. */
|
|
18
50
|
className?: string;
|
|
19
51
|
/** Slim mode: only the `nav` region (no full chrome bar). */
|
|
20
52
|
embedded?: boolean;
|
|
21
53
|
items?: TopNavbarNavItem[];
|
|
22
54
|
navVariant?: "header" | "sidebar";
|
|
23
55
|
currentPath?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Where the links move from the phone menu into the bar: at this
|
|
58
|
+
* breakpoint and wider they sit in the row. Raise it when the row has
|
|
59
|
+
* more items than fit at 768px.
|
|
60
|
+
*/
|
|
61
|
+
collapseAt?: TopNavbarCollapseAt;
|
|
24
62
|
preventNavigation?: boolean;
|
|
25
63
|
onclick?: (event: Event) => void;
|
|
26
64
|
}
|
|
@@ -30,11 +68,17 @@
|
|
|
30
68
|
brandHref,
|
|
31
69
|
ariaLabel,
|
|
32
70
|
showThemeToggle = true,
|
|
33
|
-
|
|
71
|
+
themeModes = "two",
|
|
72
|
+
themeStorageKey,
|
|
73
|
+
themeLabels,
|
|
74
|
+
strings,
|
|
75
|
+
class: classAttr = "",
|
|
76
|
+
className: legacyClass = "",
|
|
34
77
|
embedded = false,
|
|
35
78
|
items = [],
|
|
36
79
|
navVariant = "header",
|
|
37
80
|
currentPath = "",
|
|
81
|
+
collapseAt = "md",
|
|
38
82
|
preventNavigation = false,
|
|
39
83
|
onclick,
|
|
40
84
|
nav,
|
|
@@ -42,12 +86,141 @@
|
|
|
42
86
|
...restProps
|
|
43
87
|
}: Props & { nav?: Snippet; actions?: Snippet } = $props();
|
|
44
88
|
|
|
89
|
+
/** `class` is the public prop; `className` is a deprecated alias.
|
|
90
|
+
* Both are merged here so existing call sites keep working. */
|
|
91
|
+
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
92
|
+
|
|
93
|
+
const mobileMenuId = generateId("topnavbar-menu");
|
|
94
|
+
|
|
95
|
+
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
96
|
+
const provided = zabiStringsFor("topNavbar");
|
|
97
|
+
const text = $derived(mergeStrings(DEFAULT_TOP_NAVBAR_STRINGS, provided(), strings));
|
|
98
|
+
|
|
45
99
|
let isMenuOpen = $state(false);
|
|
100
|
+
let navElement = $state<HTMLElement | null>(null);
|
|
101
|
+
let panelElement = $state<HTMLElement | null>(null);
|
|
102
|
+
let menuButtonHolder = $state<HTMLElement | null>(null);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The classes that move the links between the row and the menu, written
|
|
106
|
+
* out for each breakpoint: Tailwind only generates a class it can read in
|
|
107
|
+
* the source, so `${collapseAt}:hidden` would produce nothing. `query` is
|
|
108
|
+
* the same breakpoint for the script (Tailwind's defaults).
|
|
109
|
+
*/
|
|
110
|
+
const COLLAPSE: Record<
|
|
111
|
+
TopNavbarCollapseAt,
|
|
112
|
+
{ row: string; menu: string; brand: string; query: string }
|
|
113
|
+
> = {
|
|
114
|
+
sm: { row: "hidden sm:block", menu: "sm:hidden", brand: "sm:shrink-0", query: "(min-width: 40rem)" },
|
|
115
|
+
md: { row: "hidden md:block", menu: "md:hidden", brand: "md:shrink-0", query: "(min-width: 48rem)" },
|
|
116
|
+
lg: { row: "hidden lg:block", menu: "lg:hidden", brand: "lg:shrink-0", query: "(min-width: 64rem)" },
|
|
117
|
+
xl: { row: "hidden xl:block", menu: "xl:hidden", brand: "xl:shrink-0", query: "(min-width: 80rem)" },
|
|
118
|
+
};
|
|
119
|
+
const collapse = $derived(COLLAPSE[collapseAt] ?? COLLAPSE.md);
|
|
46
120
|
|
|
47
121
|
function toggleMenu() {
|
|
48
122
|
isMenuOpen = !isMenuOpen;
|
|
49
123
|
}
|
|
50
124
|
|
|
125
|
+
function focusMenuButton() {
|
|
126
|
+
menuButtonHolder?.querySelector("button")?.focus();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The menu is a disclosure, not a dialog: nothing is trapped or locked, so
|
|
131
|
+
* it has to get out of the way by itself whenever the user has moved on.
|
|
132
|
+
*/
|
|
133
|
+
function handleWindowKeydown(event: KeyboardEvent) {
|
|
134
|
+
// A menu inside the panel (a Dropdown, say) closes on Escape first.
|
|
135
|
+
if (event.key !== "Escape" || !isMenuOpen || event.defaultPrevented) return;
|
|
136
|
+
const focusInside = !!panelElement?.contains(document.activeElement);
|
|
137
|
+
isMenuOpen = false;
|
|
138
|
+
// The link that had focus is gone; without this, focus falls to <body>.
|
|
139
|
+
if (focusInside) focusMenuButton();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** A toast lies over the menu; a press on it, or focus in it, is not leaving the bar. */
|
|
143
|
+
function isOutside(event: Event): boolean {
|
|
144
|
+
if (isInsideToastRegion(event.target)) return false;
|
|
145
|
+
return !!navElement && !(event.target instanceof Node && navElement.contains(event.target));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A press outside the bar closes the menu once it is complete (the click),
|
|
150
|
+
* not when it starts. The open menu pushes the page down, so closing it
|
|
151
|
+
* on the way down moved the page back up under the pointer and the
|
|
152
|
+
* control that was pressed never got its click.
|
|
153
|
+
*/
|
|
154
|
+
let pressing = false;
|
|
155
|
+
|
|
156
|
+
function handleOutsideClick(event: MouseEvent) {
|
|
157
|
+
if (isMenuOpen && isOutside(event)) isMenuOpen = false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Focus that leaves the bar by the keyboard or from a script. A press
|
|
162
|
+
* moves focus too, between its `mousedown` and its click (after the
|
|
163
|
+
* finger has lifted, on a touch screen); that one is left to the click.
|
|
164
|
+
*/
|
|
165
|
+
function handleOutsideFocus(event: FocusEvent) {
|
|
166
|
+
if (isMenuOpen && !pressing && isOutside(event)) isMenuOpen = false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Following a link in the menu: the page under it is about to change. */
|
|
170
|
+
function handlePanelClick(event: MouseEvent) {
|
|
171
|
+
if (event.target instanceof Element && event.target.closest("a[href]")) {
|
|
172
|
+
isMenuOpen = false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// The route changed some other way (history, a link elsewhere on the page).
|
|
177
|
+
let lastPath: string | undefined;
|
|
178
|
+
$effect(() => {
|
|
179
|
+
if (lastPath !== undefined && currentPath !== lastPath) isMenuOpen = false;
|
|
180
|
+
lastPath = currentPath;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Past the breakpoint the menu button is gone, so a menu left open could
|
|
184
|
+
// not be closed, and it would still be open on the way back down.
|
|
185
|
+
$effect(() => {
|
|
186
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
187
|
+
const wide = window.matchMedia(collapse.query);
|
|
188
|
+
const close = () => {
|
|
189
|
+
if (wide.matches) isMenuOpen = false;
|
|
190
|
+
};
|
|
191
|
+
wide.addEventListener("change", close);
|
|
192
|
+
return () => wide.removeEventListener("change", close);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The menu scrolls on its own only when it is taller than the room under
|
|
197
|
+
* the bar. A scrolling box clips whatever reaches outside it, and a
|
|
198
|
+
* Dropdown among the `actions` opens past the end of the menu: when the
|
|
199
|
+
* menu fits, that has to stay visible, as it was before the menu could
|
|
200
|
+
* scroll.
|
|
201
|
+
*/
|
|
202
|
+
let panelScrolls = $state(false);
|
|
203
|
+
$effect(() => {
|
|
204
|
+
const panel = panelElement;
|
|
205
|
+
const content = panel?.firstElementChild;
|
|
206
|
+
if (!panel || !content) {
|
|
207
|
+
panelScrolls = false;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const measure = () => {
|
|
211
|
+
const room = parseFloat(getComputedStyle(panel).maxHeight);
|
|
212
|
+
panelScrolls = Number.isFinite(room) && content.getBoundingClientRect().height > room;
|
|
213
|
+
};
|
|
214
|
+
measure();
|
|
215
|
+
window.addEventListener("resize", measure);
|
|
216
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
217
|
+
observer?.observe(content);
|
|
218
|
+
return () => {
|
|
219
|
+
window.removeEventListener("resize", measure);
|
|
220
|
+
observer?.disconnect();
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
|
|
51
224
|
function handleNavLinkClick(event: MouseEvent) {
|
|
52
225
|
if (preventNavigation) {
|
|
53
226
|
event.preventDefault();
|
|
@@ -57,6 +230,12 @@
|
|
|
57
230
|
}
|
|
58
231
|
}
|
|
59
232
|
|
|
233
|
+
/** Absolute URLs leave the site, so they are marked and open in a new tab.
|
|
234
|
+
* `external` overrides that for same-origin links that leave the app. */
|
|
235
|
+
function isExternal(item: TopNavbarNavItem): boolean {
|
|
236
|
+
return item.external ?? (item.href.startsWith("http://") || item.href.startsWith("https://"));
|
|
237
|
+
}
|
|
238
|
+
|
|
60
239
|
/** App routes: prefix match except `/` and absolute URLs (exact match). */
|
|
61
240
|
function isNavItemActive(href: string): boolean {
|
|
62
241
|
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
@@ -74,20 +253,65 @@
|
|
|
74
253
|
: "flex flex-col gap-1 md:flex-row md:items-center md:justify-between";
|
|
75
254
|
});
|
|
76
255
|
|
|
77
|
-
|
|
78
|
-
|
|
256
|
+
/** Where a list of links is drawn: in the bar, in the phone menu, or alone (`embedded`). */
|
|
257
|
+
type NavListPlace = "row" | "menu" | "embedded";
|
|
258
|
+
|
|
259
|
+
function getListClasses(place: NavListPlace): string {
|
|
260
|
+
if (place === "embedded" || navVariant === "sidebar") return ulClasses;
|
|
261
|
+
// The row only shows at the breakpoint and wider, and the menu only
|
|
262
|
+
// below it, so neither needs a breakpoint of its own.
|
|
263
|
+
return place === "row"
|
|
264
|
+
? "flex flex-row items-center justify-between gap-1"
|
|
265
|
+
: "flex flex-col gap-1";
|
|
79
266
|
}
|
|
80
267
|
|
|
81
|
-
function
|
|
82
|
-
|
|
268
|
+
function getNavItemClasses(place: NavListPlace = "embedded"): string {
|
|
269
|
+
const base =
|
|
270
|
+
"flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer";
|
|
271
|
+
if (place === "row") return `${base} w-auto`;
|
|
272
|
+
if (place === "menu") return `${base} w-full`;
|
|
273
|
+
return `${base} w-full md:w-auto`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The active state is resolved here rather than with
|
|
278
|
+
* `aria-[current=page]:text-nav-menu-item-active`.
|
|
279
|
+
*
|
|
280
|
+
* That variant was dead: the semantic utilities are hand-written in
|
|
281
|
+
* app.css and sit outside Tailwind's cascade layer, so an unlayered
|
|
282
|
+
* `.text-nav-menu-item` beats anything Tailwind generates for a variant of
|
|
283
|
+
* the same property. The fill happened to survive only because no plain
|
|
284
|
+
* `bg-*` class competed with it — so the selected tab painted its pill but
|
|
285
|
+
* kept the idle label colour (#27272a where #3c52ba was intended), which
|
|
286
|
+
* is exactly the "active state does almost nothing" failure the sidebar
|
|
287
|
+
* had.
|
|
288
|
+
*
|
|
289
|
+
* Plain classes chosen in JS cannot lose that way.
|
|
290
|
+
*/
|
|
291
|
+
function getIconContainerClasses(isActive: boolean): string {
|
|
292
|
+
const base =
|
|
293
|
+
"focus-ring focus-ring--nav group/nav-item flex flex-col items-center justify-center overflow-clip relative rounded-pill shrink-0 transition-colors duration-200 outline-none";
|
|
294
|
+
return isActive
|
|
295
|
+
? // Forced colours drop the fill and recolour the label: the
|
|
296
|
+
// current item keeps an inset outline there, which is drawn.
|
|
297
|
+
cn(
|
|
298
|
+
base,
|
|
299
|
+
"bg-nav-menu-active text-nav-menu-item-active forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2",
|
|
300
|
+
)
|
|
301
|
+
: cn(
|
|
302
|
+
base,
|
|
303
|
+
"text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active",
|
|
304
|
+
);
|
|
83
305
|
}
|
|
84
306
|
|
|
85
307
|
function getStateLayerClasses(): string {
|
|
86
|
-
return "box-border flex gap-1 h-10 items-center px-4 py-2 relative shrink-0";
|
|
308
|
+
return "box-border flex gap-1 h-10 pointer-coarse:h-11 items-center px-4 py-2 relative shrink-0";
|
|
87
309
|
}
|
|
88
310
|
|
|
89
311
|
function getLabelClasses(): string {
|
|
90
|
-
|
|
312
|
+
// `whitespace-pre` used to sit beside `text-nowrap` — same property,
|
|
313
|
+
// and it preserved literal whitespace as a side effect.
|
|
314
|
+
return "font-medium leading-4 relative shrink-0 text-center text-nowrap tracking-wide text-xs text-inherit";
|
|
91
315
|
}
|
|
92
316
|
|
|
93
317
|
function getIconWrapperClasses(): string {
|
|
@@ -95,16 +319,20 @@
|
|
|
95
319
|
}
|
|
96
320
|
</script>
|
|
97
321
|
|
|
98
|
-
{#snippet defaultNavList()}
|
|
99
|
-
<ul class="{
|
|
322
|
+
{#snippet defaultNavList(place: NavListPlace)}
|
|
323
|
+
<ul class="{getListClasses(place)} list-none m-0 p-0">
|
|
100
324
|
{#each items as item (item.href)}
|
|
101
325
|
{@const isActive = isNavItemActive(item.href)}
|
|
102
|
-
|
|
326
|
+
{@const external = isExternal(item)}
|
|
327
|
+
<li class={getNavItemClasses(place)}>
|
|
328
|
+
<!-- In the menu the link is the whole row, not a pill as wide as its label. -->
|
|
103
329
|
<a
|
|
104
330
|
href={item.href}
|
|
105
|
-
class={getIconContainerClasses()}
|
|
331
|
+
class={cn(getIconContainerClasses(isActive), place === "menu" && "w-full")}
|
|
106
332
|
onclick={handleNavLinkClick}
|
|
107
333
|
aria-current={isActive ? "page" : undefined}
|
|
334
|
+
target={external ? "_blank" : undefined}
|
|
335
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
108
336
|
>
|
|
109
337
|
<div class={getStateLayerClasses()}>
|
|
110
338
|
{#if item.iconFilled && isActive}
|
|
@@ -118,9 +346,17 @@
|
|
|
118
346
|
<Icon size={16} class="w-4 h-4" />
|
|
119
347
|
</div>
|
|
120
348
|
{/if}
|
|
121
|
-
<
|
|
349
|
+
<span class={getLabelClasses()}>
|
|
122
350
|
{item.label}
|
|
123
|
-
</
|
|
351
|
+
</span>
|
|
352
|
+
{#if external}
|
|
353
|
+
<ExternalLink
|
|
354
|
+
size={12}
|
|
355
|
+
class="shrink-0 text-current opacity-70"
|
|
356
|
+
aria-hidden="true"
|
|
357
|
+
/>
|
|
358
|
+
<span class="sr-only">{text.opensInNewTab}</span>
|
|
359
|
+
{/if}
|
|
124
360
|
</div>
|
|
125
361
|
</a>
|
|
126
362
|
</li>
|
|
@@ -128,81 +364,115 @@
|
|
|
128
364
|
</ul>
|
|
129
365
|
{/snippet}
|
|
130
366
|
|
|
367
|
+
<svelte:window
|
|
368
|
+
onkeydown={handleWindowKeydown}
|
|
369
|
+
onmousedowncapture={() => (pressing = true)}
|
|
370
|
+
onmouseupcapture={() => (pressing = false)}
|
|
371
|
+
onpointercancelcapture={() => (pressing = false)}
|
|
372
|
+
onclickcapture={handleOutsideClick}
|
|
373
|
+
onfocusin={handleOutsideFocus}
|
|
374
|
+
/>
|
|
375
|
+
|
|
131
376
|
{#if embedded}
|
|
132
377
|
<nav class={className} aria-label={ariaLabel} {...restProps}>
|
|
133
378
|
{#if nav}
|
|
134
379
|
{@render nav()}
|
|
135
380
|
{:else}
|
|
136
|
-
{@render defaultNavList()}
|
|
381
|
+
{@render defaultNavList("embedded")}
|
|
137
382
|
{/if}
|
|
138
383
|
</nav>
|
|
139
384
|
{:else}
|
|
140
385
|
<nav
|
|
141
|
-
|
|
386
|
+
bind:this={navElement}
|
|
387
|
+
class={cn("border-b border-border bg-background sticky top-0 z-sticky", className)}
|
|
142
388
|
aria-label={ariaLabel}
|
|
143
389
|
{...restProps}
|
|
144
390
|
>
|
|
145
391
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
146
392
|
<div class="flex justify-between items-center h-16">
|
|
147
|
-
|
|
393
|
+
<!-- Beside the menu button the brand gives way and is cut short,
|
|
394
|
+
so the button stays on screen however long the name or large the
|
|
395
|
+
text. In the row it keeps its full width, as before. -->
|
|
396
|
+
<div class={cn("flex min-w-0", collapse.brand)}>
|
|
148
397
|
{#if brandHref}
|
|
149
398
|
<a
|
|
150
399
|
href={brandHref}
|
|
151
|
-
class="focus-ring text-xl font-bold text-headline hover:
|
|
400
|
+
class="focus-ring block min-w-0 truncate text-xl font-bold text-headline transition-colors hover:text-link active:text-link pointer-coarse:min-h-11 pointer-coarse:leading-[2.75rem]"
|
|
152
401
|
>
|
|
153
402
|
{brand}
|
|
154
403
|
</a>
|
|
155
404
|
{:else}
|
|
156
|
-
<span class="text-xl font-bold text-headline">{brand}</span>
|
|
405
|
+
<span class="block min-w-0 truncate text-xl font-bold text-headline">{brand}</span>
|
|
157
406
|
{/if}
|
|
158
407
|
</div>
|
|
159
408
|
|
|
160
|
-
<div class=
|
|
161
|
-
<div class="ml-10 flex items-
|
|
409
|
+
<div class={collapse.row}>
|
|
410
|
+
<div class="ml-10 flex items-center space-x-4">
|
|
162
411
|
{#if nav}
|
|
163
412
|
{@render nav()}
|
|
164
413
|
{:else if items.length > 0}
|
|
165
|
-
{@render defaultNavList()}
|
|
414
|
+
{@render defaultNavList("row")}
|
|
166
415
|
{/if}
|
|
167
416
|
</div>
|
|
168
417
|
</div>
|
|
169
418
|
|
|
170
|
-
<div class=
|
|
419
|
+
<div class={collapse.row}>
|
|
171
420
|
<div class="ml-4 flex items-center space-x-4">
|
|
172
421
|
{@render actions?.()}
|
|
173
422
|
{#if showThemeToggle}
|
|
174
|
-
<ThemeToggle />
|
|
423
|
+
<ThemeToggle modes={themeModes} storageKey={themeStorageKey} labels={themeLabels} />
|
|
175
424
|
{/if}
|
|
176
425
|
</div>
|
|
177
426
|
</div>
|
|
178
427
|
|
|
179
|
-
<div class="
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
428
|
+
<div bind:this={menuButtonHolder} class={cn("ms-2 shrink-0", collapse.menu)}>
|
|
429
|
+
<!--
|
|
430
|
+
Was a bare ☰ glyph in a text-2xl span: font-dependent,
|
|
431
|
+
off the icon scale, and with no disclosure semantics, so
|
|
432
|
+
a screen reader could not tell the menu was open.
|
|
433
|
+
-->
|
|
434
|
+
<IconButton
|
|
435
|
+
variant="ghost"
|
|
436
|
+
label={isMenuOpen ? text.closeMenu : text.openMenu}
|
|
183
437
|
onclick={toggleMenu}
|
|
184
|
-
aria-
|
|
438
|
+
aria-expanded={isMenuOpen}
|
|
439
|
+
aria-controls={isMenuOpen ? mobileMenuId : undefined}
|
|
185
440
|
>
|
|
186
|
-
|
|
187
|
-
|
|
441
|
+
{#if isMenuOpen}
|
|
442
|
+
<X size={20} />
|
|
443
|
+
{:else}
|
|
444
|
+
<Menu size={20} />
|
|
445
|
+
{/if}
|
|
446
|
+
</IconButton>
|
|
188
447
|
</div>
|
|
189
448
|
</div>
|
|
190
449
|
|
|
450
|
+
<!-- `aria-controls` above names this panel only while it exists.
|
|
451
|
+
Keeping it in the DOM, hidden, would render `nav` and `actions` a
|
|
452
|
+
second time on every page for every desktop visitor: duplicate ids
|
|
453
|
+
from the caller's snippets, and a second ThemeToggle mounted. -->
|
|
191
454
|
{#if isMenuOpen}
|
|
192
|
-
|
|
455
|
+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
|
|
456
|
+
<div
|
|
457
|
+
bind:this={panelElement}
|
|
458
|
+
class={cn("topnavbar-menu", collapse.menu)}
|
|
459
|
+
id={mobileMenuId}
|
|
460
|
+
data-scrolls={panelScrolls ? "" : undefined}
|
|
461
|
+
onclick={handlePanelClick}
|
|
462
|
+
>
|
|
193
463
|
<div
|
|
194
464
|
class="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-border"
|
|
195
465
|
>
|
|
196
466
|
{#if nav}
|
|
197
467
|
{@render nav()}
|
|
198
468
|
{:else if items.length > 0}
|
|
199
|
-
{@render defaultNavList()}
|
|
469
|
+
{@render defaultNavList("menu")}
|
|
200
470
|
{/if}
|
|
201
471
|
<div class="pt-4">
|
|
202
472
|
{@render actions?.()}
|
|
203
473
|
{#if showThemeToggle}
|
|
204
474
|
<div class="mt-2">
|
|
205
|
-
<ThemeToggle />
|
|
475
|
+
<ThemeToggle modes={themeModes} storageKey={themeStorageKey} labels={themeLabels} />
|
|
206
476
|
</div>
|
|
207
477
|
{/if}
|
|
208
478
|
</div>
|
|
@@ -212,3 +482,29 @@
|
|
|
212
482
|
</div>
|
|
213
483
|
</nav>
|
|
214
484
|
{/if}
|
|
485
|
+
|
|
486
|
+
<style>
|
|
487
|
+
/*
|
|
488
|
+
* The bar is sticky, so an open menu taller than the screen could never be
|
|
489
|
+
* scrolled into view: the page moved under it and its last control stayed
|
|
490
|
+
* below the fold. It takes what is left of the screen under the 4rem bar
|
|
491
|
+
* (and its 1px border) and scrolls on its own, without handing the scroll
|
|
492
|
+
* on to the page when it reaches an end. `dvh` follows a phone's
|
|
493
|
+
* collapsing address bar; `vh` is for browsers without it. In a web app
|
|
494
|
+
* installed to the home screen the bar starts below the status bar, so
|
|
495
|
+
* that inset comes off as well, or the menu's end would be under the
|
|
496
|
+
* bottom of the screen by as much.
|
|
497
|
+
*
|
|
498
|
+
* Only a menu that does not fit scrolls (`data-scrolls`, set by the
|
|
499
|
+
* script): a scrolling box would clip a Dropdown that opens out of it.
|
|
500
|
+
*/
|
|
501
|
+
.topnavbar-menu {
|
|
502
|
+
max-height: calc(100vh - 4rem - 1px - env(safe-area-inset-top, 0px));
|
|
503
|
+
max-height: calc(100dvh - 4rem - 1px - env(safe-area-inset-top, 0px));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.topnavbar-menu[data-scrolls] {
|
|
507
|
+
overflow-y: auto;
|
|
508
|
+
overscroll-behavior: contain;
|
|
509
|
+
}
|
|
510
|
+
</style>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Component, Snippet } from "svelte";
|
|
2
|
+
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
3
|
+
import { type TopNavbarStrings } from "../util/top-navbar.js";
|
|
2
4
|
export interface TopNavbarNavItem {
|
|
3
5
|
label: string;
|
|
4
6
|
href: string;
|
|
@@ -10,19 +12,45 @@ export interface TopNavbarNavItem {
|
|
|
10
12
|
size?: number;
|
|
11
13
|
class?: string;
|
|
12
14
|
}>;
|
|
15
|
+
/** Marks the link and opens it in a new tab. Defaults to true for absolute URLs. */
|
|
16
|
+
external?: boolean;
|
|
13
17
|
}
|
|
18
|
+
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
14
19
|
interface Props {
|
|
15
20
|
brand?: string;
|
|
16
21
|
brandHref?: string;
|
|
17
22
|
/** `aria-label` on `<nav>` when multiple nav landmarks exist. */
|
|
18
23
|
ariaLabel?: string;
|
|
19
24
|
showThemeToggle?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* How the theme toggle steps: `"two"` flips light and dark, `"three"`
|
|
27
|
+
* goes through system, light and dark. As `modes` of ThemeToggle.
|
|
28
|
+
*/
|
|
29
|
+
themeModes?: "two" | "three";
|
|
30
|
+
/**
|
|
31
|
+
* The `localStorage` key the theme choice is kept under; `null` keeps
|
|
32
|
+
* nothing. As `storageKey` of ThemeToggle, whose default applies when
|
|
33
|
+
* this is left out.
|
|
34
|
+
*/
|
|
35
|
+
themeStorageKey?: string | null;
|
|
36
|
+
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
37
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
38
|
+
/** The words the bar says by itself: the menu button's two names and the note on a link that opens a new tab. */
|
|
39
|
+
strings?: Partial<TopNavbarStrings>;
|
|
40
|
+
class?: string;
|
|
41
|
+
/** @deprecated use `class`. */
|
|
20
42
|
className?: string;
|
|
21
43
|
/** Slim mode: only the `nav` region (no full chrome bar). */
|
|
22
44
|
embedded?: boolean;
|
|
23
45
|
items?: TopNavbarNavItem[];
|
|
24
46
|
navVariant?: "header" | "sidebar";
|
|
25
47
|
currentPath?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Where the links move from the phone menu into the bar: at this
|
|
50
|
+
* breakpoint and wider they sit in the row. Raise it when the row has
|
|
51
|
+
* more items than fit at 768px.
|
|
52
|
+
*/
|
|
53
|
+
collapseAt?: TopNavbarCollapseAt;
|
|
26
54
|
preventNavigation?: boolean;
|
|
27
55
|
onclick?: (event: Event) => void;
|
|
28
56
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export { default as TopNavbar } from './TopNavbar.svelte';
|
|
2
2
|
export { default as SidebarNavigation } from './SidebarNavigation.svelte';
|
|
3
|
+
export { default as SidebarShell } from './SidebarShell.svelte';
|
|
3
4
|
export { default as SidebarAccountPanel } from './SidebarAccountPanel.svelte';
|
|
4
5
|
export { default as SidebarPanel } from './SidebarPanel.svelte';
|
|
6
|
+
export { default as AppShell } from './AppShell.svelte';
|
|
7
|
+
export { DEFAULT_TOP_NAVBAR_STRINGS } from '../util/top-navbar.js';
|
|
8
|
+
export type { TopNavbarStrings } from '../util/top-navbar.js';
|
|
9
|
+
export { DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS, DEFAULT_SIDEBAR_NAVIGATION_STRINGS, } from '../util/sidebar.js';
|
|
10
|
+
export type { SidebarAccountPanelStrings, SidebarNavigationStrings } from '../util/sidebar.js';
|
package/dist/organisms/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export { default as TopNavbar } from './TopNavbar.svelte';
|
|
2
2
|
export { default as SidebarNavigation } from './SidebarNavigation.svelte';
|
|
3
|
+
export { default as SidebarShell } from './SidebarShell.svelte';
|
|
3
4
|
export { default as SidebarAccountPanel } from './SidebarAccountPanel.svelte';
|
|
4
5
|
export { default as SidebarPanel } from './SidebarPanel.svelte';
|
|
6
|
+
export { default as AppShell } from './AppShell.svelte';
|
|
7
|
+
export { DEFAULT_TOP_NAVBAR_STRINGS } from '../util/top-navbar.js';
|
|
8
|
+
export { DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS, DEFAULT_SIDEBAR_NAVIGATION_STRINGS, } from '../util/sidebar.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variant class helpers — semantic design tokens only (see src/app.css).
|
|
3
|
+
*/
|
|
4
|
+
import type { SemanticVariant } from '../../types/variants.js';
|
|
5
|
+
export type Variant = SemanticVariant;
|
|
6
|
+
/**
|
|
7
|
+
* Border + focus ring classes for text inputs (Input, Textarea).
|
|
8
|
+
*/
|
|
9
|
+
export declare function getInputVariantClasses(variant: Variant): string;
|
|
10
|
+
/**
|
|
11
|
+
* Card surfaces — border + background using semantic tokens.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getCardVariantClasses(variant: Variant): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generic variant classes for borders, text, or backgrounds.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getVariantClasses(variant: Variant, type: 'border' | 'text' | 'bg'): string;
|
|
18
|
+
export declare function getAllVariantClasses(variant: Variant): {
|
|
19
|
+
border: string;
|
|
20
|
+
text: string;
|
|
21
|
+
bg: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Create a variant class map for use in reactive statements
|
|
25
|
+
*/
|
|
26
|
+
export declare function createVariantClassMap<T extends string>(variants: T[], prefix: string): Record<T, string>;
|