zabi-components 8.0.0 → 9.0.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 +2935 -0
- package/README.md +243 -10
- package/THEME.md +162 -332
- package/THEMING.md +1419 -259
- package/dist/atoms/ActionPanel.svelte +15 -5
- package/dist/atoms/Avatar.svelte +120 -0
- package/dist/atoms/Avatar.svelte.d.ts +46 -0
- package/dist/atoms/Badge.svelte +71 -13
- package/dist/atoms/Badge.svelte.d.ts +8 -2
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +217 -30
- package/dist/atoms/Button.svelte.d.ts +33 -4
- package/dist/atoms/Card.svelte +182 -26
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/CardHeader.svelte +16 -4
- package/dist/atoms/Checkbox.svelte +81 -21
- package/dist/atoms/Checkbox.svelte.d.ts +31 -2
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeBlock.svelte +20 -2
- package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +260 -49
- package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
- package/dist/atoms/Container.svelte +16 -8
- package/dist/atoms/DateField.svelte +145 -0
- package/dist/atoms/DateField.svelte.d.ts +81 -0
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +176 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +63 -5
- package/dist/atoms/Heading.svelte.d.ts +14 -0
- package/dist/atoms/IconButton.svelte +294 -26
- package/dist/atoms/IconButton.svelte.d.ts +62 -4
- package/dist/atoms/Input.svelte +259 -53
- package/dist/atoms/Input.svelte.d.ts +33 -2
- package/dist/atoms/List.svelte +5 -1
- package/dist/atoms/ListItem.svelte +70 -13
- package/dist/atoms/ListItem.svelte.d.ts +11 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +99 -19
- package/dist/atoms/Progress.svelte.d.ts +15 -1
- package/dist/atoms/Radio.svelte +51 -3
- package/dist/atoms/Radio.svelte.d.ts +30 -2
- package/dist/atoms/Rating.svelte +620 -0
- package/dist/atoms/Rating.svelte.d.ts +77 -0
- package/dist/atoms/Select.svelte +438 -98
- package/dist/atoms/Select.svelte.d.ts +61 -3
- package/dist/atoms/SelectionControl.svelte +193 -23
- package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
- package/dist/atoms/Skeleton.svelte +114 -18
- package/dist/atoms/Skeleton.svelte.d.ts +30 -3
- package/dist/atoms/SkeletonGroup.svelte +43 -0
- package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
- package/dist/atoms/Slider.svelte +344 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Sparkline.svelte +109 -0
- package/dist/atoms/Sparkline.svelte.d.ts +27 -0
- package/dist/atoms/SpeechBubble.svelte +187 -0
- package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +151 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +472 -0
- package/dist/atoms/TemporalField.svelte.d.ts +48 -0
- package/dist/atoms/Text.svelte +23 -11
- package/dist/atoms/Text.svelte.d.ts +6 -3
- package/dist/atoms/Textarea.svelte +150 -44
- package/dist/atoms/Textarea.svelte.d.ts +35 -2
- package/dist/atoms/ThemeToggle.svelte +223 -44
- package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +151 -0
- package/dist/atoms/TimeField.svelte.d.ts +85 -0
- package/dist/atoms/Toast.svelte +22 -8
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +98 -14
- package/dist/atoms/Toggle.svelte.d.ts +26 -2
- package/dist/atoms/Tooltip.svelte +528 -24
- package/dist/atoms/Tooltip.svelte.d.ts +18 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +38 -0
- package/dist/atoms/index.js +29 -0
- package/dist/atoms/selection-control.styles.d.ts +47 -5
- package/dist/atoms/selection-control.styles.js +52 -10
- package/dist/components/atoms/index.d.ts +38 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +47 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +104 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map.d.ts +11 -0
- package/dist/components/map.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +51 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +83 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +10 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +24 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +79 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +39 -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 +41 -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/chart.d.ts +140 -0
- package/dist/components/util/chart.d.ts.map +1 -0
- package/dist/components/util/chip.d.ts +66 -0
- package/dist/components/util/chip.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts.map +1 -1
- package/dist/components/util/code-field.d.ts +63 -0
- package/dist/components/util/code-field.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +64 -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/display-voice.d.ts +10 -0
- package/dist/components/util/display-voice.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-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +45 -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 +77 -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/map-view.d.ts +144 -0
- package/dist/components/util/map-view.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 +234 -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/progress.d.ts +19 -0
- package/dist/components/util/progress.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-edge.d.ts +33 -0
- package/dist/components/util/scroll-edge.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 +84 -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/skeleton.d.ts +15 -0
- package/dist/components/util/skeleton.d.ts.map +1 -0
- package/dist/components/util/sliding-indicator.d.ts +25 -0
- package/dist/components/util/sliding-indicator.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/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/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.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 +133 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tone-surface.d.ts +34 -0
- package/dist/components/util/tone-surface.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 +35 -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 +142 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +173 -0
- package/dist/create-theme/index.d.ts +190 -0
- package/dist/create-theme/index.js +1034 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +238 -0
- package/dist/create-theme/theme-data.js +2644 -0
- package/dist/index.d.ts +104 -2
- package/dist/index.js +75 -2
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +63 -16
- package/dist/molecules/Alert.svelte.d.ts +14 -1
- package/dist/molecules/AppBar.svelte +674 -0
- package/dist/molecules/AppBar.svelte.d.ts +131 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +608 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
- package/dist/molecules/BottomTabBar.svelte +403 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +347 -0
- package/dist/molecules/Collapsible.svelte.d.ts +61 -0
- package/dist/molecules/CollapsibleGroup.svelte +136 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +16 -4
- package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
- package/dist/molecules/ConfirmDialog.svelte +257 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
- package/dist/molecules/ContactForm.svelte +34 -17
- package/dist/molecules/ContactForm.svelte.d.ts +8 -0
- package/dist/molecules/Drawer.svelte +415 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +517 -48
- package/dist/molecules/Dropdown.svelte.d.ts +47 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +55 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/FormField.svelte +9 -1
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +492 -69
- package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
- package/dist/molecules/LineChart.svelte +459 -0
- package/dist/molecules/LineChart.svelte.d.ts +53 -0
- package/dist/molecules/MapView.svelte +565 -0
- package/dist/molecules/MapView.svelte.d.ts +88 -0
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +293 -72
- package/dist/molecules/Modal.svelte.d.ts +51 -2
- package/dist/molecules/NavigationMenuContent.svelte +58 -1
- package/dist/molecules/NavigationMenuItem.svelte +0 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuList.svelte +10 -3
- package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
- package/dist/molecules/Page.svelte +32 -2
- package/dist/molecules/Page.svelte.d.ts +11 -0
- package/dist/molecules/PhotoGrid.svelte +523 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
- package/dist/molecules/PhotoViewer.svelte +1213 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/PropsTable.svelte +22 -10
- package/dist/molecules/PropsTable.svelte.d.ts +3 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +72 -18
- package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
- package/dist/molecules/Section.svelte +1 -1
- package/dist/molecules/SegmentedControl.svelte +456 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
- package/dist/molecules/SidebarBrandHeader.svelte +11 -1
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
- package/dist/molecules/SidebarFooter.svelte +30 -7
- package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SlideUp.svelte +220 -61
- package/dist/molecules/SlideUp.svelte.d.ts +26 -0
- package/dist/molecules/SortableList.svelte +633 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +562 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +250 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +310 -9
- package/dist/molecules/Tabs.svelte.d.ts +7 -0
- package/dist/molecules/Toaster.svelte +514 -5
- package/dist/molecules/Toaster.svelte.d.ts +153 -2
- package/dist/molecules/ToasterToast.svelte +495 -77
- package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
- package/dist/molecules/UnsavedChangesBar.svelte +223 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +51 -0
- package/dist/molecules/index.js +31 -0
- package/dist/molecules/toast-store.d.ts +83 -7
- package/dist/molecules/toast-store.js +25 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +696 -0
- package/dist/organisms/AppShell.svelte.d.ts +157 -0
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarAccountPanel.svelte +107 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
- package/dist/organisms/SidebarNavigation.svelte +111 -43
- package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
- package/dist/organisms/SidebarPanel.svelte +34 -17
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +164 -14
- package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
- package/dist/organisms/TopNavbar.svelte +353 -39
- package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +10 -0
- package/dist/organisms/index.js +7 -0
- package/dist/types/components.d.ts +21 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +1093 -26
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +28 -1
- package/dist/util/app-shell.d.ts +78 -0
- package/dist/util/app-shell.js +97 -0
- package/dist/util/avatar.d.ts +38 -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 +40 -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/chart.d.ts +139 -0
- package/dist/util/chart.js +305 -0
- package/dist/util/chip.d.ts +65 -0
- package/dist/util/chip.js +58 -0
- package/dist/util/cn.js +28 -2
- package/dist/util/code-field.d.ts +62 -0
- package/dist/util/code-field.js +112 -0
- package/dist/util/collapsible.d.ts +63 -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/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -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-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +44 -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 +77 -2
- package/dist/util/focus-utils.js +186 -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/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -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 +233 -0
- package/dist/util/photo.js +209 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -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-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -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 +83 -0
- package/dist/util/sheet-drag.js +311 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/skeleton.d.ts +14 -0
- package/dist/util/skeleton.js +22 -0
- package/dist/util/sliding-indicator.d.ts +24 -0
- package/dist/util/sliding-indicator.js +44 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- 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/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +132 -0
- package/dist/util/toaster.js +130 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +34 -0
- package/dist/util/top-navbar.js +28 -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 +141 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +11 -2
- package/dist/zabi-components-theme-dark-only.css +870 -258
- package/dist/zabi-components-theme-dark.css +870 -258
- package/dist/zabi-components-theme-only.css +2419 -368
- package/dist/zabi-components-theme.css +2419 -368
- package/dist/zabi-components.css +6032 -711
- package/docs/lucide-icons.md +40 -17
- package/docs/theme-imports.md +81 -11
- package/package.json +45 -8
|
@@ -1,24 +1,61 @@
|
|
|
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";
|
|
3
5
|
import IconButton from "../atoms/IconButton.svelte";
|
|
4
|
-
import
|
|
6
|
+
import BottomSheet from "../molecules/BottomSheet.svelte";
|
|
7
|
+
import TopNavbarSheetContent from "./TopNavbarSheetContent.svelte";
|
|
8
|
+
import ExternalLink from "@lucide/svelte/icons/external-link";
|
|
9
|
+
import Menu from "@lucide/svelte/icons/menu";
|
|
10
|
+
import X from "@lucide/svelte/icons/x";
|
|
5
11
|
import type { Component, Snippet } from "svelte";
|
|
6
12
|
import { generateId } from "../util/ssr-safe.js";
|
|
13
|
+
import { getAppShell } from "../util/app-shell.js";
|
|
7
14
|
import { cn } from "../util/cn.js";
|
|
15
|
+
import { resolveScrolledUnder, watchScrollEdge, type ScrollEdgeMode } from "../util/scroll-edge.js";
|
|
16
|
+
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
17
|
+
import { DEFAULT_TOP_NAVBAR_STRINGS, type TopNavbarStrings } from "../util/top-navbar.js";
|
|
18
|
+
import { isInsideToastRegion } from "../util/focus-utils.js";
|
|
8
19
|
|
|
9
20
|
export interface TopNavbarNavItem {
|
|
10
21
|
label: string;
|
|
11
22
|
href: string;
|
|
12
23
|
icon?: Component<{ size?: number; class?: string }>;
|
|
13
24
|
iconFilled?: Component<{ size?: number; class?: string }>;
|
|
25
|
+
/** Marks the link and opens it in a new tab. Defaults to true for absolute URLs. */
|
|
26
|
+
external?: boolean;
|
|
14
27
|
}
|
|
15
28
|
|
|
29
|
+
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* How the links open below `collapseAt`: `sheet` slides a modal sheet up
|
|
33
|
+
* from the bottom of the screen; `inline` opens a panel under the bar,
|
|
34
|
+
* which pushes the page down (the menu of 8.1 and earlier).
|
|
35
|
+
*/
|
|
36
|
+
export type TopNavbarMenu = "sheet" | "inline";
|
|
37
|
+
|
|
16
38
|
interface Props {
|
|
17
39
|
brand?: string;
|
|
18
40
|
brandHref?: string;
|
|
19
41
|
/** `aria-label` on `<nav>` when multiple nav landmarks exist. */
|
|
20
42
|
ariaLabel?: string;
|
|
21
43
|
showThemeToggle?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* How the theme toggle steps: `"two"` flips light and dark, `"three"`
|
|
46
|
+
* goes through system, light and dark. As `modes` of ThemeToggle.
|
|
47
|
+
*/
|
|
48
|
+
themeModes?: "two" | "three";
|
|
49
|
+
/**
|
|
50
|
+
* The `localStorage` key the theme choice is kept under; `null` keeps
|
|
51
|
+
* nothing. As `storageKey` of ThemeToggle, whose default applies when
|
|
52
|
+
* this is left out.
|
|
53
|
+
*/
|
|
54
|
+
themeStorageKey?: string | null;
|
|
55
|
+
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
56
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
57
|
+
/** The words the bar says by itself: the menu button's two names, the name of the menu sheet and the note on a link that opens a new tab. */
|
|
58
|
+
strings?: Partial<TopNavbarStrings>;
|
|
22
59
|
class?: string;
|
|
23
60
|
/** @deprecated use `class`. */
|
|
24
61
|
className?: string;
|
|
@@ -27,8 +64,28 @@
|
|
|
27
64
|
items?: TopNavbarNavItem[];
|
|
28
65
|
navVariant?: "header" | "sidebar";
|
|
29
66
|
currentPath?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Where the links move from the phone menu into the bar: at this
|
|
69
|
+
* breakpoint and wider they sit in the row. Raise it when the row has
|
|
70
|
+
* more items than fit at 768px.
|
|
71
|
+
*/
|
|
72
|
+
collapseAt?: TopNavbarCollapseAt;
|
|
73
|
+
/**
|
|
74
|
+
* How the links open below `collapseAt`. `sheet` (the default) is a
|
|
75
|
+
* bottom sheet: a modal dialog that is as tall as its content, up to
|
|
76
|
+
* the full screen below the status bar, and scrolls inside. `inline` is a panel under the
|
|
77
|
+
* bar, a disclosure that pushes the page down and traps nothing.
|
|
78
|
+
*/
|
|
79
|
+
menu?: TopNavbarMenu;
|
|
30
80
|
preventNavigation?: boolean;
|
|
31
81
|
onclick?: (event: Event) => void;
|
|
82
|
+
/**
|
|
83
|
+
* When the bar shows the glass and the hairline. `auto` follows the
|
|
84
|
+
* scroll position: flush at the top, glass once content is under it
|
|
85
|
+
* (and while the inline phone menu is open). `always` is glass all the time,
|
|
86
|
+
* `never` flush all the time.
|
|
87
|
+
*/
|
|
88
|
+
scrollEdge?: ScrollEdgeMode;
|
|
32
89
|
}
|
|
33
90
|
|
|
34
91
|
let {
|
|
@@ -36,14 +93,21 @@
|
|
|
36
93
|
brandHref,
|
|
37
94
|
ariaLabel,
|
|
38
95
|
showThemeToggle = true,
|
|
96
|
+
themeModes = "two",
|
|
97
|
+
themeStorageKey,
|
|
98
|
+
themeLabels,
|
|
99
|
+
strings,
|
|
39
100
|
class: classAttr = "",
|
|
40
101
|
className: legacyClass = "",
|
|
41
102
|
embedded = false,
|
|
42
103
|
items = [],
|
|
43
104
|
navVariant = "header",
|
|
44
105
|
currentPath = "",
|
|
106
|
+
collapseAt = "md",
|
|
107
|
+
menu = "sheet",
|
|
45
108
|
preventNavigation = false,
|
|
46
109
|
onclick,
|
|
110
|
+
scrollEdge = "auto",
|
|
47
111
|
nav,
|
|
48
112
|
actions,
|
|
49
113
|
...restProps
|
|
@@ -55,18 +119,156 @@
|
|
|
55
119
|
|
|
56
120
|
const mobileMenuId = generateId("topnavbar-menu");
|
|
57
121
|
|
|
122
|
+
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
123
|
+
const provided = zabiStringsFor("topNavbar");
|
|
124
|
+
const text = $derived(mergeStrings<Required<TopNavbarStrings>>(DEFAULT_TOP_NAVBAR_STRINGS, provided(), strings));
|
|
125
|
+
|
|
58
126
|
let isMenuOpen = $state(false);
|
|
127
|
+
const sheet = $derived(menu !== "inline");
|
|
128
|
+
/** The inline panel is open: only then do the window listeners below have anything to close. */
|
|
129
|
+
const inlineOpen = $derived(isMenuOpen && !sheet);
|
|
130
|
+
|
|
131
|
+
/** Content is under the bar: from the shell if there is one, else from the window. */
|
|
132
|
+
const shell = getAppShell();
|
|
133
|
+
let ownScrolled = $state(false);
|
|
134
|
+
$effect(() => {
|
|
135
|
+
if (shell || scrollEdge !== "auto" || embedded) return;
|
|
136
|
+
return watchScrollEdge(null, (edge) => (ownScrolled = edge.top));
|
|
137
|
+
});
|
|
138
|
+
// An open inline menu sits on the material, whatever the scroll position.
|
|
139
|
+
// The sheet covers the page and the bar under its backdrop: the bar stays as it was.
|
|
140
|
+
const scrolledUnder = $derived(
|
|
141
|
+
resolveScrolledUnder(
|
|
142
|
+
scrollEdge,
|
|
143
|
+
inlineOpen || (shell ? shell.scrolledTop : ownScrolled),
|
|
144
|
+
),
|
|
145
|
+
);
|
|
146
|
+
let navElement = $state<HTMLElement | null>(null);
|
|
147
|
+
let panelElement = $state<HTMLElement | null>(null);
|
|
148
|
+
let menuButtonHolder = $state<HTMLElement | null>(null);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The classes that move the links between the row and the menu, written
|
|
152
|
+
* out for each breakpoint: Tailwind only generates a class it can read in
|
|
153
|
+
* the source, so `${collapseAt}:hidden` would produce nothing. `query` is
|
|
154
|
+
* the same breakpoint for the script (Tailwind's defaults).
|
|
155
|
+
*/
|
|
156
|
+
const COLLAPSE: Record<
|
|
157
|
+
TopNavbarCollapseAt,
|
|
158
|
+
{ row: string; menu: string; brand: string; query: string }
|
|
159
|
+
> = {
|
|
160
|
+
sm: { row: "hidden sm:block", menu: "sm:hidden", brand: "sm:shrink-0", query: "(min-width: 40rem)" },
|
|
161
|
+
md: { row: "hidden md:block", menu: "md:hidden", brand: "md:shrink-0", query: "(min-width: 48rem)" },
|
|
162
|
+
lg: { row: "hidden lg:block", menu: "lg:hidden", brand: "lg:shrink-0", query: "(min-width: 64rem)" },
|
|
163
|
+
xl: { row: "hidden xl:block", menu: "xl:hidden", brand: "xl:shrink-0", query: "(min-width: 80rem)" },
|
|
164
|
+
};
|
|
165
|
+
const collapse = $derived(COLLAPSE[collapseAt] ?? COLLAPSE.md);
|
|
59
166
|
|
|
60
167
|
function toggleMenu() {
|
|
61
168
|
isMenuOpen = !isMenuOpen;
|
|
62
169
|
}
|
|
63
170
|
|
|
171
|
+
function focusMenuButton() {
|
|
172
|
+
menuButtonHolder?.querySelector("button")?.focus();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The inline menu is a disclosure, not a dialog: nothing is trapped or
|
|
177
|
+
* locked, so it has to get out of the way by itself whenever the user has
|
|
178
|
+
* moved on. The sheet is a dialog and does all of this itself.
|
|
179
|
+
*/
|
|
64
180
|
function handleWindowKeydown(event: KeyboardEvent) {
|
|
65
|
-
|
|
181
|
+
// A menu inside the panel (a Dropdown, say) closes on Escape first.
|
|
182
|
+
if (event.key !== "Escape" || !inlineOpen || event.defaultPrevented) return;
|
|
183
|
+
const focusInside = !!panelElement?.contains(document.activeElement);
|
|
184
|
+
isMenuOpen = false;
|
|
185
|
+
// The link that had focus is gone; without this, focus falls to <body>.
|
|
186
|
+
if (focusInside) focusMenuButton();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** A toast lies over the menu; a press on it, or focus in it, is not leaving the bar. */
|
|
190
|
+
function isOutside(event: Event): boolean {
|
|
191
|
+
if (isInsideToastRegion(event.target)) return false;
|
|
192
|
+
return !!navElement && !(event.target instanceof Node && navElement.contains(event.target));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* A press outside the bar closes the menu once it is complete (the click),
|
|
197
|
+
* not when it starts. The open menu pushes the page down, so closing it
|
|
198
|
+
* on the way down moved the page back up under the pointer and the
|
|
199
|
+
* control that was pressed never got its click.
|
|
200
|
+
*/
|
|
201
|
+
let pressing = false;
|
|
202
|
+
|
|
203
|
+
function handleOutsideClick(event: MouseEvent) {
|
|
204
|
+
if (inlineOpen && isOutside(event)) isMenuOpen = false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Focus that leaves the bar by the keyboard or from a script. A press
|
|
209
|
+
* moves focus too, between its `mousedown` and its click (after the
|
|
210
|
+
* finger has lifted, on a touch screen); that one is left to the click.
|
|
211
|
+
*/
|
|
212
|
+
function handleOutsideFocus(event: FocusEvent) {
|
|
213
|
+
if (inlineOpen && !pressing && isOutside(event)) isMenuOpen = false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Following a link in the menu or the sheet: the page under it is about to change. */
|
|
217
|
+
function handlePanelClick(event: MouseEvent) {
|
|
218
|
+
if (event.target instanceof Element && event.target.closest("a[href]")) {
|
|
66
219
|
isMenuOpen = false;
|
|
67
220
|
}
|
|
68
221
|
}
|
|
69
222
|
|
|
223
|
+
// The route changed some other way (history, a link elsewhere on the page).
|
|
224
|
+
let lastPath: string | undefined;
|
|
225
|
+
$effect(() => {
|
|
226
|
+
if (lastPath !== undefined && currentPath !== lastPath) isMenuOpen = false;
|
|
227
|
+
lastPath = currentPath;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Past the breakpoint the menu button is gone, so a menu left open could
|
|
231
|
+
// not be closed, and it would still be open on the way back down. For the
|
|
232
|
+
// sheet, closing also lets go of the scroll lock and the inert page.
|
|
233
|
+
$effect(() => {
|
|
234
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
235
|
+
const wide = window.matchMedia(collapse.query);
|
|
236
|
+
const close = () => {
|
|
237
|
+
if (wide.matches) isMenuOpen = false;
|
|
238
|
+
};
|
|
239
|
+
wide.addEventListener("change", close);
|
|
240
|
+
return () => wide.removeEventListener("change", close);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The menu scrolls on its own only when it is taller than the room under
|
|
245
|
+
* the bar. A scrolling box clips whatever reaches outside it, and a
|
|
246
|
+
* Dropdown among the `actions` opens past the end of the menu: when the
|
|
247
|
+
* menu fits, that has to stay visible, as it was before the menu could
|
|
248
|
+
* scroll.
|
|
249
|
+
*/
|
|
250
|
+
let panelScrolls = $state(false);
|
|
251
|
+
$effect(() => {
|
|
252
|
+
const panel = panelElement;
|
|
253
|
+
const content = panel?.firstElementChild;
|
|
254
|
+
if (!panel || !content) {
|
|
255
|
+
panelScrolls = false;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const measure = () => {
|
|
259
|
+
const room = parseFloat(getComputedStyle(panel).maxHeight);
|
|
260
|
+
panelScrolls = Number.isFinite(room) && content.getBoundingClientRect().height > room;
|
|
261
|
+
};
|
|
262
|
+
measure();
|
|
263
|
+
window.addEventListener("resize", measure);
|
|
264
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
265
|
+
observer?.observe(content);
|
|
266
|
+
return () => {
|
|
267
|
+
window.removeEventListener("resize", measure);
|
|
268
|
+
observer?.disconnect();
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
|
|
70
272
|
function handleNavLinkClick(event: MouseEvent) {
|
|
71
273
|
if (preventNavigation) {
|
|
72
274
|
event.preventDefault();
|
|
@@ -76,9 +278,10 @@
|
|
|
76
278
|
}
|
|
77
279
|
}
|
|
78
280
|
|
|
79
|
-
/** Absolute URLs leave the site, so they are marked and open in a new tab.
|
|
80
|
-
|
|
81
|
-
|
|
281
|
+
/** Absolute URLs leave the site, so they are marked and open in a new tab.
|
|
282
|
+
* `external` overrides that for same-origin links that leave the app. */
|
|
283
|
+
function isExternal(item: TopNavbarNavItem): boolean {
|
|
284
|
+
return item.external ?? (item.href.startsWith("http://") || item.href.startsWith("https://"));
|
|
82
285
|
}
|
|
83
286
|
|
|
84
287
|
/** App routes: prefix match except `/` and absolute URLs (exact match). */
|
|
@@ -98,8 +301,24 @@
|
|
|
98
301
|
: "flex flex-col gap-1 md:flex-row md:items-center md:justify-between";
|
|
99
302
|
});
|
|
100
303
|
|
|
101
|
-
|
|
102
|
-
|
|
304
|
+
/** Where a list of links is drawn: in the bar, in the phone menu, or alone (`embedded`). */
|
|
305
|
+
type NavListPlace = "row" | "menu" | "sheet" | "embedded";
|
|
306
|
+
|
|
307
|
+
function getListClasses(place: NavListPlace): string {
|
|
308
|
+
if (place === "embedded" || navVariant === "sidebar") return ulClasses;
|
|
309
|
+
// The row only shows at the breakpoint and wider, and the menu only
|
|
310
|
+
// below it, so neither needs a breakpoint of its own.
|
|
311
|
+
return place === "row"
|
|
312
|
+
? "flex flex-row items-center justify-between gap-1"
|
|
313
|
+
: "flex flex-col gap-1";
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function getNavItemClasses(place: NavListPlace = "embedded"): string {
|
|
317
|
+
const base =
|
|
318
|
+
"flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer";
|
|
319
|
+
if (place === "row") return `${base} w-auto`;
|
|
320
|
+
if (place === "menu" || place === "sheet") return `${base} w-full`;
|
|
321
|
+
return `${base} w-full md:w-auto`;
|
|
103
322
|
}
|
|
104
323
|
|
|
105
324
|
/**
|
|
@@ -119,23 +338,30 @@
|
|
|
119
338
|
*/
|
|
120
339
|
function getIconContainerClasses(isActive: boolean): string {
|
|
121
340
|
const base =
|
|
122
|
-
"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-
|
|
341
|
+
"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-(--duration-moderate) ease-standard outline-none";
|
|
123
342
|
return isActive
|
|
124
|
-
?
|
|
343
|
+
? // Forced colours drop the fill and recolour the label: the
|
|
344
|
+
// current item keeps an inset outline there, which is drawn.
|
|
345
|
+
cn(
|
|
346
|
+
base,
|
|
347
|
+
"bg-nav-menu-active font-semibold text-nav-menu-item-active forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2",
|
|
348
|
+
)
|
|
125
349
|
: cn(
|
|
126
350
|
base,
|
|
127
|
-
"text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover",
|
|
351
|
+
"font-medium text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active",
|
|
128
352
|
);
|
|
129
353
|
}
|
|
130
354
|
|
|
131
|
-
function getStateLayerClasses(): string {
|
|
132
|
-
|
|
355
|
+
function getStateLayerClasses(place: NavListPlace): string {
|
|
356
|
+
// A row of the sheet is a touch target on every pointer: 44px.
|
|
357
|
+
const height = place === "sheet" ? "h-11" : "h-10 pointer-coarse:h-11";
|
|
358
|
+
return `box-border flex gap-1 ${height} items-center px-4 py-2 relative shrink-0`;
|
|
133
359
|
}
|
|
134
360
|
|
|
135
361
|
function getLabelClasses(): string {
|
|
136
362
|
// `whitespace-pre` used to sit beside `text-nowrap` — same property,
|
|
137
363
|
// and it preserved literal whitespace as a side effect.
|
|
138
|
-
return "
|
|
364
|
+
return "leading-5 relative shrink-0 text-center text-nowrap text-sm text-inherit";
|
|
139
365
|
}
|
|
140
366
|
|
|
141
367
|
function getIconWrapperClasses(): string {
|
|
@@ -143,21 +369,22 @@
|
|
|
143
369
|
}
|
|
144
370
|
</script>
|
|
145
371
|
|
|
146
|
-
{#snippet defaultNavList()}
|
|
147
|
-
<ul class="{
|
|
372
|
+
{#snippet defaultNavList(place: NavListPlace)}
|
|
373
|
+
<ul class="{getListClasses(place)} list-none m-0 p-0">
|
|
148
374
|
{#each items as item (item.href)}
|
|
149
375
|
{@const isActive = isNavItemActive(item.href)}
|
|
150
|
-
{@const external = isExternal(item
|
|
151
|
-
<li class={getNavItemClasses()}>
|
|
376
|
+
{@const external = isExternal(item)}
|
|
377
|
+
<li class={getNavItemClasses(place)}>
|
|
378
|
+
<!-- In the menu and the sheet the link is the whole row, not a pill as wide as its label. -->
|
|
152
379
|
<a
|
|
153
380
|
href={item.href}
|
|
154
|
-
class={getIconContainerClasses(isActive)}
|
|
381
|
+
class={cn(getIconContainerClasses(isActive), (place === "menu" || place === "sheet") && "w-full")}
|
|
155
382
|
onclick={handleNavLinkClick}
|
|
156
383
|
aria-current={isActive ? "page" : undefined}
|
|
157
384
|
target={external ? "_blank" : undefined}
|
|
158
385
|
rel={external ? "noopener noreferrer" : undefined}
|
|
159
386
|
>
|
|
160
|
-
<div class={getStateLayerClasses()}>
|
|
387
|
+
<div class={getStateLayerClasses(place)}>
|
|
161
388
|
{#if item.iconFilled && isActive}
|
|
162
389
|
{@const Icon = item.iconFilled}
|
|
163
390
|
<div class={getIconWrapperClasses()}>
|
|
@@ -178,7 +405,7 @@
|
|
|
178
405
|
class="shrink-0 text-current opacity-70"
|
|
179
406
|
aria-hidden="true"
|
|
180
407
|
/>
|
|
181
|
-
<span class="sr-only">
|
|
408
|
+
<span class="sr-only">{text.opensInNewTab}</span>
|
|
182
409
|
{/if}
|
|
183
410
|
</div>
|
|
184
411
|
</a>
|
|
@@ -187,70 +414,93 @@
|
|
|
187
414
|
</ul>
|
|
188
415
|
{/snippet}
|
|
189
416
|
|
|
190
|
-
|
|
417
|
+
{#snippet sheetLinks()}
|
|
418
|
+
{#if nav}
|
|
419
|
+
{@render nav()}
|
|
420
|
+
{:else}
|
|
421
|
+
{@render defaultNavList("sheet")}
|
|
422
|
+
{/if}
|
|
423
|
+
{/snippet}
|
|
424
|
+
|
|
425
|
+
<svelte:window
|
|
426
|
+
onkeydown={handleWindowKeydown}
|
|
427
|
+
onmousedowncapture={() => (pressing = true)}
|
|
428
|
+
onmouseupcapture={() => (pressing = false)}
|
|
429
|
+
onpointercancelcapture={() => (pressing = false)}
|
|
430
|
+
onclickcapture={handleOutsideClick}
|
|
431
|
+
onfocusin={handleOutsideFocus}
|
|
432
|
+
/>
|
|
191
433
|
|
|
192
434
|
{#if embedded}
|
|
193
435
|
<nav class={className} aria-label={ariaLabel} {...restProps}>
|
|
194
436
|
{#if nav}
|
|
195
437
|
{@render nav()}
|
|
196
438
|
{:else}
|
|
197
|
-
{@render defaultNavList()}
|
|
439
|
+
{@render defaultNavList("embedded")}
|
|
198
440
|
{/if}
|
|
199
441
|
</nav>
|
|
200
442
|
{:else}
|
|
201
443
|
<nav
|
|
202
|
-
|
|
444
|
+
bind:this={navElement}
|
|
445
|
+
data-scrolled-under={String(scrolledUnder)}
|
|
446
|
+
class={cn("material-bar sticky top-0 z-sticky", className)}
|
|
203
447
|
aria-label={ariaLabel}
|
|
204
448
|
{...restProps}
|
|
205
449
|
>
|
|
206
450
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
207
451
|
<div class="flex justify-between items-center h-16">
|
|
208
|
-
|
|
452
|
+
<!-- Beside the menu button the brand gives way and is cut short,
|
|
453
|
+
so the button stays on screen however long the name or large the
|
|
454
|
+
text. In the row it keeps its full width, as before. -->
|
|
455
|
+
<div class={cn("flex min-w-0", collapse.brand)}>
|
|
209
456
|
{#if brandHref}
|
|
210
457
|
<a
|
|
211
458
|
href={brandHref}
|
|
212
|
-
class="focus-ring text-xl font-bold text-headline transition-colors hover:text-link"
|
|
459
|
+
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]"
|
|
213
460
|
>
|
|
214
461
|
{brand}
|
|
215
462
|
</a>
|
|
216
463
|
{:else}
|
|
217
|
-
<span class="text-xl font-bold text-headline">{brand}</span>
|
|
464
|
+
<span class="block min-w-0 truncate text-xl font-bold text-headline">{brand}</span>
|
|
218
465
|
{/if}
|
|
219
466
|
</div>
|
|
220
467
|
|
|
221
|
-
<div class=
|
|
468
|
+
<div class={collapse.row}>
|
|
222
469
|
<div class="ml-10 flex items-center space-x-4">
|
|
223
470
|
{#if nav}
|
|
224
471
|
{@render nav()}
|
|
225
472
|
{:else if items.length > 0}
|
|
226
|
-
{@render defaultNavList()}
|
|
473
|
+
{@render defaultNavList("row")}
|
|
227
474
|
{/if}
|
|
228
475
|
</div>
|
|
229
476
|
</div>
|
|
230
477
|
|
|
231
|
-
<div class=
|
|
478
|
+
<div class={collapse.row}>
|
|
232
479
|
<div class="ml-4 flex items-center space-x-4">
|
|
233
480
|
{@render actions?.()}
|
|
234
481
|
{#if showThemeToggle}
|
|
235
|
-
<ThemeToggle />
|
|
482
|
+
<ThemeToggle modes={themeModes} storageKey={themeStorageKey} labels={themeLabels} />
|
|
236
483
|
{/if}
|
|
237
484
|
</div>
|
|
238
485
|
</div>
|
|
239
486
|
|
|
240
|
-
<div class="
|
|
487
|
+
<div bind:this={menuButtonHolder} class={cn("ms-2 shrink-0", collapse.menu)}>
|
|
241
488
|
<!--
|
|
242
489
|
Was a bare ☰ glyph in a text-2xl span: font-dependent,
|
|
243
490
|
off the icon scale, and with no disclosure semantics, so
|
|
244
491
|
a screen reader could not tell the menu was open.
|
|
245
492
|
-->
|
|
493
|
+
<!-- The sheet is a dialog the button opens; the page under its
|
|
494
|
+
backdrop is inert, so this button is never the one that closes it. -->
|
|
246
495
|
<IconButton
|
|
247
496
|
variant="ghost"
|
|
248
|
-
label={
|
|
497
|
+
label={inlineOpen ? text.closeMenu : text.openMenu}
|
|
249
498
|
onclick={toggleMenu}
|
|
499
|
+
aria-haspopup={sheet ? "dialog" : undefined}
|
|
250
500
|
aria-expanded={isMenuOpen}
|
|
251
|
-
aria-controls={mobileMenuId}
|
|
501
|
+
aria-controls={isMenuOpen ? mobileMenuId : undefined}
|
|
252
502
|
>
|
|
253
|
-
{#if
|
|
503
|
+
{#if inlineOpen}
|
|
254
504
|
<X size={20} />
|
|
255
505
|
{:else}
|
|
256
506
|
<Menu size={20} />
|
|
@@ -259,21 +509,32 @@
|
|
|
259
509
|
</div>
|
|
260
510
|
</div>
|
|
261
511
|
|
|
262
|
-
|
|
263
|
-
|
|
512
|
+
<!-- `aria-controls` above names this panel only while it exists.
|
|
513
|
+
Keeping it in the DOM, hidden, would render `nav` and `actions` a
|
|
514
|
+
second time on every page for every desktop visitor: duplicate ids
|
|
515
|
+
from the caller's snippets, and a second ThemeToggle mounted. -->
|
|
516
|
+
{#if inlineOpen}
|
|
517
|
+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
|
|
518
|
+
<div
|
|
519
|
+
bind:this={panelElement}
|
|
520
|
+
class={cn("topnavbar-menu", collapse.menu)}
|
|
521
|
+
id={mobileMenuId}
|
|
522
|
+
data-scrolls={panelScrolls ? "" : undefined}
|
|
523
|
+
onclick={handlePanelClick}
|
|
524
|
+
>
|
|
264
525
|
<div
|
|
265
|
-
class="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-border"
|
|
526
|
+
class="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-border-weak"
|
|
266
527
|
>
|
|
267
528
|
{#if nav}
|
|
268
529
|
{@render nav()}
|
|
269
530
|
{:else if items.length > 0}
|
|
270
|
-
{@render defaultNavList()}
|
|
531
|
+
{@render defaultNavList("menu")}
|
|
271
532
|
{/if}
|
|
272
533
|
<div class="pt-4">
|
|
273
534
|
{@render actions?.()}
|
|
274
535
|
{#if showThemeToggle}
|
|
275
536
|
<div class="mt-2">
|
|
276
|
-
<ThemeToggle />
|
|
537
|
+
<ThemeToggle modes={themeModes} storageKey={themeStorageKey} labels={themeLabels} />
|
|
277
538
|
</div>
|
|
278
539
|
{/if}
|
|
279
540
|
</div>
|
|
@@ -282,4 +543,57 @@
|
|
|
282
543
|
{/if}
|
|
283
544
|
</div>
|
|
284
545
|
</nav>
|
|
546
|
+
|
|
547
|
+
<!-- Below `collapseAt` the links open in a sheet from the bottom. It is
|
|
548
|
+
portalled to the body: the bar is sticky and glass, and a fixed child of a
|
|
549
|
+
filtered box is placed against that box. It renders nothing until
|
|
550
|
+
it is open, so `nav` and `actions` are not drawn a second time. -->
|
|
551
|
+
{#if sheet}
|
|
552
|
+
<BottomSheet
|
|
553
|
+
bind:isOpen={isMenuOpen}
|
|
554
|
+
fit
|
|
555
|
+
fitMax="full"
|
|
556
|
+
title={text.menuTitle}
|
|
557
|
+
id={mobileMenuId}
|
|
558
|
+
data-topnavbar-sheet=""
|
|
559
|
+
>
|
|
560
|
+
<TopNavbarSheetContent
|
|
561
|
+
links={nav || items.length > 0 ? sheetLinks : undefined}
|
|
562
|
+
navLabel={ariaLabel ?? text.menuTitle}
|
|
563
|
+
{actions}
|
|
564
|
+
{showThemeToggle}
|
|
565
|
+
{themeModes}
|
|
566
|
+
{themeStorageKey}
|
|
567
|
+
{themeLabels}
|
|
568
|
+
themeHeading={text.themeHeading}
|
|
569
|
+
onclick={handlePanelClick}
|
|
570
|
+
/>
|
|
571
|
+
</BottomSheet>
|
|
572
|
+
{/if}
|
|
285
573
|
{/if}
|
|
574
|
+
|
|
575
|
+
<style>
|
|
576
|
+
/*
|
|
577
|
+
* The bar is sticky, so an open menu taller than the screen could never be
|
|
578
|
+
* scrolled into view: the page moved under it and its last control stayed
|
|
579
|
+
* below the fold. It takes what is left of the screen under the 4rem bar
|
|
580
|
+
* and scrolls on its own, without handing the scroll
|
|
581
|
+
* on to the page when it reaches an end. `dvh` follows a phone's
|
|
582
|
+
* collapsing address bar; `vh` is for browsers without it. In a web app
|
|
583
|
+
* installed to the home screen the bar starts below the status bar, so
|
|
584
|
+
* that inset comes off as well, or the menu's end would be under the
|
|
585
|
+
* bottom of the screen by as much.
|
|
586
|
+
*
|
|
587
|
+
* Only a menu that does not fit scrolls (`data-scrolls`, set by the
|
|
588
|
+
* script): a scrolling box would clip a Dropdown that opens out of it.
|
|
589
|
+
*/
|
|
590
|
+
.topnavbar-menu {
|
|
591
|
+
max-height: calc(100vh - 4rem - env(safe-area-inset-top, 0px));
|
|
592
|
+
max-height: calc(100dvh - 4rem - env(safe-area-inset-top, 0px));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.topnavbar-menu[data-scrolls] {
|
|
596
|
+
overflow-y: auto;
|
|
597
|
+
overscroll-behavior: contain;
|
|
598
|
+
}
|
|
599
|
+
</style>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Component, Snippet } from "svelte";
|
|
2
|
+
import { type ScrollEdgeMode } from "../util/scroll-edge.js";
|
|
3
|
+
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
4
|
+
import { type TopNavbarStrings } from "../util/top-navbar.js";
|
|
2
5
|
export interface TopNavbarNavItem {
|
|
3
6
|
label: string;
|
|
4
7
|
href: string;
|
|
@@ -10,13 +13,37 @@ export interface TopNavbarNavItem {
|
|
|
10
13
|
size?: number;
|
|
11
14
|
class?: string;
|
|
12
15
|
}>;
|
|
16
|
+
/** Marks the link and opens it in a new tab. Defaults to true for absolute URLs. */
|
|
17
|
+
external?: boolean;
|
|
13
18
|
}
|
|
19
|
+
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
20
|
+
/**
|
|
21
|
+
* How the links open below `collapseAt`: `sheet` slides a modal sheet up
|
|
22
|
+
* from the bottom of the screen; `inline` opens a panel under the bar,
|
|
23
|
+
* which pushes the page down (the menu of 8.1 and earlier).
|
|
24
|
+
*/
|
|
25
|
+
export type TopNavbarMenu = "sheet" | "inline";
|
|
14
26
|
interface Props {
|
|
15
27
|
brand?: string;
|
|
16
28
|
brandHref?: string;
|
|
17
29
|
/** `aria-label` on `<nav>` when multiple nav landmarks exist. */
|
|
18
30
|
ariaLabel?: string;
|
|
19
31
|
showThemeToggle?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* How the theme toggle steps: `"two"` flips light and dark, `"three"`
|
|
34
|
+
* goes through system, light and dark. As `modes` of ThemeToggle.
|
|
35
|
+
*/
|
|
36
|
+
themeModes?: "two" | "three";
|
|
37
|
+
/**
|
|
38
|
+
* The `localStorage` key the theme choice is kept under; `null` keeps
|
|
39
|
+
* nothing. As `storageKey` of ThemeToggle, whose default applies when
|
|
40
|
+
* this is left out.
|
|
41
|
+
*/
|
|
42
|
+
themeStorageKey?: string | null;
|
|
43
|
+
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
44
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
45
|
+
/** The words the bar says by itself: the menu button's two names, the name of the menu sheet and the note on a link that opens a new tab. */
|
|
46
|
+
strings?: Partial<TopNavbarStrings>;
|
|
20
47
|
class?: string;
|
|
21
48
|
/** @deprecated use `class`. */
|
|
22
49
|
className?: string;
|
|
@@ -25,8 +52,28 @@ interface Props {
|
|
|
25
52
|
items?: TopNavbarNavItem[];
|
|
26
53
|
navVariant?: "header" | "sidebar";
|
|
27
54
|
currentPath?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Where the links move from the phone menu into the bar: at this
|
|
57
|
+
* breakpoint and wider they sit in the row. Raise it when the row has
|
|
58
|
+
* more items than fit at 768px.
|
|
59
|
+
*/
|
|
60
|
+
collapseAt?: TopNavbarCollapseAt;
|
|
61
|
+
/**
|
|
62
|
+
* How the links open below `collapseAt`. `sheet` (the default) is a
|
|
63
|
+
* bottom sheet: a modal dialog that is as tall as its content, up to
|
|
64
|
+
* the full screen below the status bar, and scrolls inside. `inline` is a panel under the
|
|
65
|
+
* bar, a disclosure that pushes the page down and traps nothing.
|
|
66
|
+
*/
|
|
67
|
+
menu?: TopNavbarMenu;
|
|
28
68
|
preventNavigation?: boolean;
|
|
29
69
|
onclick?: (event: Event) => void;
|
|
70
|
+
/**
|
|
71
|
+
* When the bar shows the glass and the hairline. `auto` follows the
|
|
72
|
+
* scroll position: flush at the top, glass once content is under it
|
|
73
|
+
* (and while the inline phone menu is open). `always` is glass all the time,
|
|
74
|
+
* `never` flush all the time.
|
|
75
|
+
*/
|
|
76
|
+
scrollEdge?: ScrollEdgeMode;
|
|
30
77
|
}
|
|
31
78
|
type $$ComponentProps = Props & {
|
|
32
79
|
nav?: Snippet;
|