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
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext, onDestroy, setContext, untrack, type Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import ChevronDown from "@lucide/svelte/icons/chevron-down";
|
|
5
|
+
import { cn } from "../util/cn.js";
|
|
6
|
+
import { generateId, isBrowser } from "../util/ssr-safe.js";
|
|
7
|
+
import {
|
|
8
|
+
COLLAPSIBLE_GROUP,
|
|
9
|
+
type CollapsibleGroupContext,
|
|
10
|
+
type CollapsibleGroupMember,
|
|
11
|
+
type CollapsibleHeadingLevel,
|
|
12
|
+
type CollapsibleTriggerProps,
|
|
13
|
+
type CollapsibleTriggerState,
|
|
14
|
+
} from "../util/collapsible.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A trigger, then a panel of its own that it opens. It is not for a "show
|
|
18
|
+
* all" under a list: there the control comes after the content, and what
|
|
19
|
+
* it shows is more items of a list that is already on the screen. For
|
|
20
|
+
* that, put a `Button` with `aria-expanded` and `aria-controls` under the
|
|
21
|
+
* list and let the list draw the rest (Z-127).
|
|
22
|
+
*
|
|
23
|
+
* Other attributes (`id`, `data-*`, ...) land on the host `<div>`.
|
|
24
|
+
*/
|
|
25
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
|
|
26
|
+
/** Extra classes for the host element. */
|
|
27
|
+
class?: string;
|
|
28
|
+
/** Extra classes for the default trigger (a `<summary>`). */
|
|
29
|
+
triggerClass?: string;
|
|
30
|
+
/** Extra classes for the panel. */
|
|
31
|
+
panelClass?: string;
|
|
32
|
+
/** Whether the panel is shown; supports `bind:open`. */
|
|
33
|
+
open?: boolean;
|
|
34
|
+
/** Called when the user toggles the panel, or a group closes it. */
|
|
35
|
+
onopenchange?: (open: boolean) => void;
|
|
36
|
+
/**
|
|
37
|
+
* The trigger cannot be activated. The panel keeps its current state:
|
|
38
|
+
* a group does not close a disabled panel either.
|
|
39
|
+
*/
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Text of the default trigger, a full-width header with a chevron. It is
|
|
43
|
+
* a native `<details>` / `<summary>`, so it opens and closes without
|
|
44
|
+
* scripts: before hydration, and for a visitor who has none.
|
|
45
|
+
*/
|
|
46
|
+
title?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Your own header, in place of the default trigger. Spread the first
|
|
49
|
+
* argument on a `<button>`; the second carries the open state. This
|
|
50
|
+
* path needs scripts: the button and the `hidden` panel are yours to
|
|
51
|
+
* wire, and nothing opens until the page has hydrated. Use `title` for
|
|
52
|
+
* content that must open without scripts.
|
|
53
|
+
*/
|
|
54
|
+
trigger?: Snippet<[CollapsibleTriggerProps, CollapsibleTriggerState]>;
|
|
55
|
+
/** Wraps the default trigger in `<h1>`–`<h6>`. No heading when omitted. */
|
|
56
|
+
headingLevel?: CollapsibleHeadingLevel;
|
|
57
|
+
/**
|
|
58
|
+
* Closed content stays in the DOM, inside the closed `<details>`, so a
|
|
59
|
+
* form inside it keeps its values and still submits them: closed
|
|
60
|
+
* details content is part of the form. Set this to remove the content
|
|
61
|
+
* while closed instead.
|
|
62
|
+
*/
|
|
63
|
+
unmountOnClose?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Gives the panel `role="region"`, which makes it a landmark. Defaults
|
|
66
|
+
* to true only inside a single-open `CollapsibleGroup`.
|
|
67
|
+
*/
|
|
68
|
+
region?: boolean;
|
|
69
|
+
children?: Snippet;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
let {
|
|
73
|
+
class: className = "",
|
|
74
|
+
triggerClass = "",
|
|
75
|
+
panelClass = "",
|
|
76
|
+
open = $bindable<Exclude<Props["open"], undefined>>(),
|
|
77
|
+
onopenchange,
|
|
78
|
+
disabled = false,
|
|
79
|
+
title,
|
|
80
|
+
trigger,
|
|
81
|
+
headingLevel,
|
|
82
|
+
unmountOnClose = false,
|
|
83
|
+
region,
|
|
84
|
+
children,
|
|
85
|
+
...restProps
|
|
86
|
+
}: Props = $props();
|
|
87
|
+
|
|
88
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
89
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
90
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
91
|
+
// here instead: at once, for the server and the first render, and again
|
|
92
|
+
// whenever a parent hands back `undefined`.
|
|
93
|
+
const applyDefaults = () => {
|
|
94
|
+
if (open === undefined) open = false;
|
|
95
|
+
};
|
|
96
|
+
applyDefaults();
|
|
97
|
+
$effect.pre(applyDefaults);
|
|
98
|
+
|
|
99
|
+
const triggerId = generateId("collapsible-trigger");
|
|
100
|
+
const panelId = generateId("collapsible-panel");
|
|
101
|
+
|
|
102
|
+
const group = getContext<CollapsibleGroupContext | undefined>(COLLAPSIBLE_GROUP);
|
|
103
|
+
// A Collapsible inside this panel is not a member of the group around it:
|
|
104
|
+
// opening it must not close the panel it lives in.
|
|
105
|
+
setContext(COLLAPSIBLE_GROUP, undefined);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* True for a panel that asked to start open in a single-open group that
|
|
109
|
+
* already had one. It renders closed from the first render, the server's
|
|
110
|
+
* included, and `open` is brought in line after mount. Nobody changed
|
|
111
|
+
* anything, so `onopenchange` is not called for it.
|
|
112
|
+
*/
|
|
113
|
+
let startClosed = $state(false);
|
|
114
|
+
/** What is rendered. Everything below reads this, not `open`. */
|
|
115
|
+
const isOpen = $derived(open && !startClosed);
|
|
116
|
+
|
|
117
|
+
let panelElement: HTMLDivElement | undefined = $state();
|
|
118
|
+
|
|
119
|
+
function setOpen(next: boolean) {
|
|
120
|
+
if (isOpen === next) return;
|
|
121
|
+
open = next;
|
|
122
|
+
onopenchange?.(next);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toggle() {
|
|
126
|
+
if (disabled) return;
|
|
127
|
+
setOpen(!isOpen);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const member: CollapsibleGroupMember = {
|
|
131
|
+
triggerId,
|
|
132
|
+
// Spelled out rather than `isOpen`: the group asks while this
|
|
133
|
+
// component is still initialising, before `startClosed` is known, and
|
|
134
|
+
// on the server a derived value keeps its first answer.
|
|
135
|
+
isOpen: () => open && !startClosed,
|
|
136
|
+
isDisabled: () => disabled,
|
|
137
|
+
close: () => setOpen(false),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (group) {
|
|
141
|
+
const registration = group.register(member);
|
|
142
|
+
startClosed = registration.startClosed;
|
|
143
|
+
onDestroy(registration.unregister);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The bound prop cannot be written while the component initialises, so
|
|
147
|
+
// the settled state is written back here, once.
|
|
148
|
+
$effect(() => {
|
|
149
|
+
if (!startClosed) return;
|
|
150
|
+
open = false;
|
|
151
|
+
startClosed = false;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// An effect rather than a call in `toggle`, so a panel opened from outside
|
|
155
|
+
// through `bind:open` closes its siblings as well. Untracked, because the
|
|
156
|
+
// group reads the siblings' state: tracking that would re-run this effect
|
|
157
|
+
// when a sibling opens, and the two would close each other.
|
|
158
|
+
$effect(() => {
|
|
159
|
+
if (isOpen) untrack(() => group?.opened(member));
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// A panel can close while focus is inside it: the parent sets `open`, or
|
|
163
|
+
// a group closes it. Hidden content cannot hold focus, which would fall
|
|
164
|
+
// to `<body>`, so the trigger takes it. This runs before the panel is
|
|
165
|
+
// hidden, while the focused element can still be found; focus is not
|
|
166
|
+
// touched in any other case.
|
|
167
|
+
$effect.pre(() => {
|
|
168
|
+
if (isOpen || !panelElement) return;
|
|
169
|
+
const panel = panelElement;
|
|
170
|
+
untrack(() => {
|
|
171
|
+
if (panel.contains(document.activeElement)) {
|
|
172
|
+
document.getElementById(triggerId)?.focus();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const triggerProps = $derived({
|
|
178
|
+
id: triggerId,
|
|
179
|
+
type: "button",
|
|
180
|
+
"aria-expanded": isOpen,
|
|
181
|
+
"aria-controls": panelId,
|
|
182
|
+
disabled,
|
|
183
|
+
onclick: toggle,
|
|
184
|
+
"data-collapsible-trigger": "",
|
|
185
|
+
} satisfies CollapsibleTriggerProps);
|
|
186
|
+
|
|
187
|
+
let detailsElement: HTMLDetailsElement | undefined = $state();
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* What only the server writes on the `<details>`: the `open` attribute and
|
|
191
|
+
* a mark that says the element came from the server. The client never
|
|
192
|
+
* writes `open` through the template: hydration would set it from the
|
|
193
|
+
* state and undo a toggle the visitor made before the scripts arrived.
|
|
194
|
+
* The effects below adopt that choice, then keep `open` in line.
|
|
195
|
+
*/
|
|
196
|
+
const SERVER_MARK = "data-collapsible-ssr";
|
|
197
|
+
const serverAttributes = () =>
|
|
198
|
+
isBrowser() ? {} : { open: isOpen, [SERVER_MARK]: "" };
|
|
199
|
+
|
|
200
|
+
// Once, after mount: a panel toggled before hydration keeps that state.
|
|
201
|
+
$effect(() => {
|
|
202
|
+
const element = detailsElement;
|
|
203
|
+
if (!element?.hasAttribute(SERVER_MARK)) return;
|
|
204
|
+
element.removeAttribute(SERVER_MARK);
|
|
205
|
+
untrack(() => {
|
|
206
|
+
if (!disabled && element.open !== isOpen) setOpen(element.open);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// `open` follows the state: an `open` bound from outside, a group closing
|
|
211
|
+
// a sibling. Runs after the adoption above on the first pass.
|
|
212
|
+
$effect(() => {
|
|
213
|
+
const element = detailsElement;
|
|
214
|
+
if (element && element.open !== isOpen) element.open = isOpen;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The browser's `toggle` event is where a change made on the `<details>`
|
|
219
|
+
* enters: a click on the summary, Enter or Space, find-in-page opening it,
|
|
220
|
+
* or the native exclusive group closing it. It also fires, a task later,
|
|
221
|
+
* for a change this component made itself; `setOpen` then sees no change.
|
|
222
|
+
*/
|
|
223
|
+
function handleToggle() {
|
|
224
|
+
const element = detailsElement;
|
|
225
|
+
if (!element) return;
|
|
226
|
+
if (disabled) {
|
|
227
|
+
// Find-in-page can still open a disabled panel: undo it.
|
|
228
|
+
if (element.open !== isOpen) element.open = isOpen;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
setOpen(element.open);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** A summary cannot be disabled, so its click is cancelled instead. */
|
|
235
|
+
function handleSummaryClick(event: MouseEvent) {
|
|
236
|
+
if (disabled) event.preventDefault();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Native exclusivity (the `name` attribute) for a single-open group. A
|
|
241
|
+
* disabled panel stays out of it: the group neither closes nor counts it.
|
|
242
|
+
*/
|
|
243
|
+
const detailsName = $derived(disabled ? undefined : group?.exclusiveName);
|
|
244
|
+
|
|
245
|
+
const rowClass =
|
|
246
|
+
"m-0 flex items-center justify-between gap-2 px-3 py-2 text-sm font-medium pointer-coarse:min-h-11";
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* A region is a landmark, and a page of them is noise (the WAI-ARIA
|
|
250
|
+
* accordion pattern warns against it). In a single-open group at most one
|
|
251
|
+
* panel is exposed at a time, so a region is safe there. Everywhere else
|
|
252
|
+
* the panel is a plain named group unless the caller asks for a region.
|
|
253
|
+
*/
|
|
254
|
+
const panelRole = $derived(
|
|
255
|
+
(region ?? (group ? !group.multiple : false)) ? "region" : "group",
|
|
256
|
+
);
|
|
257
|
+
</script>
|
|
258
|
+
|
|
259
|
+
{#snippet defaultRow()}
|
|
260
|
+
<span class="min-w-0 flex-1">{title}</span>
|
|
261
|
+
<ChevronDown
|
|
262
|
+
size={16}
|
|
263
|
+
aria-hidden="true"
|
|
264
|
+
class={cn(
|
|
265
|
+
"shrink-0 text-description transition-transform duration-150 motion-reduce:transition-none",
|
|
266
|
+
isOpen && "rotate-180",
|
|
267
|
+
)}
|
|
268
|
+
/>
|
|
269
|
+
{/snippet}
|
|
270
|
+
|
|
271
|
+
{#snippet panel()}
|
|
272
|
+
<div
|
|
273
|
+
bind:this={panelElement}
|
|
274
|
+
id={panelId}
|
|
275
|
+
role={panelRole}
|
|
276
|
+
aria-labelledby={triggerId}
|
|
277
|
+
hidden={trigger ? !isOpen : undefined}
|
|
278
|
+
class={cn(!trigger && "px-3 pb-3 pt-1", panelClass)}
|
|
279
|
+
>
|
|
280
|
+
{#if isOpen || !unmountOnClose}
|
|
281
|
+
{@render children?.()}
|
|
282
|
+
{/if}
|
|
283
|
+
</div>
|
|
284
|
+
{/snippet}
|
|
285
|
+
|
|
286
|
+
<div
|
|
287
|
+
class={cn(className)}
|
|
288
|
+
data-state={isOpen ? "open" : "closed"}
|
|
289
|
+
data-disabled={disabled ? "" : undefined}
|
|
290
|
+
{...restProps}
|
|
291
|
+
>
|
|
292
|
+
{#if trigger}
|
|
293
|
+
{@render trigger(triggerProps, { open: isOpen, disabled })}
|
|
294
|
+
<!-- `hidden` is `display: none`: closed content takes no focus and is not
|
|
295
|
+
in the accessibility tree. The element itself stays, so `aria-controls`
|
|
296
|
+
always points at something. -->
|
|
297
|
+
{@render panel()}
|
|
298
|
+
{:else}
|
|
299
|
+
<!-- A native disclosure: the browser opens and closes it, so it works
|
|
300
|
+
before hydration and without scripts. `open` follows `isOpen` (see
|
|
301
|
+
`serverAttributes`); user changes come back through `toggle`. -->
|
|
302
|
+
<details
|
|
303
|
+
bind:this={detailsElement}
|
|
304
|
+
{...serverAttributes()}
|
|
305
|
+
name={detailsName}
|
|
306
|
+
ontoggle={handleToggle}
|
|
307
|
+
>
|
|
308
|
+
<summary
|
|
309
|
+
id={triggerId}
|
|
310
|
+
aria-controls={panelId}
|
|
311
|
+
aria-disabled={disabled ? "true" : undefined}
|
|
312
|
+
tabindex={disabled ? -1 : undefined}
|
|
313
|
+
data-collapsible-trigger=""
|
|
314
|
+
onclick={handleSummaryClick}
|
|
315
|
+
class={cn(
|
|
316
|
+
"focus-ring block w-full cursor-pointer list-none rounded-control text-start text-sm font-medium text-headline transition-colors duration-150 hover:bg-surface-hover active:bg-surface-active motion-reduce:transition-none [&::-webkit-details-marker]:hidden",
|
|
317
|
+
disabled &&
|
|
318
|
+
"pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent active:bg-transparent",
|
|
319
|
+
triggerClass,
|
|
320
|
+
)}
|
|
321
|
+
>
|
|
322
|
+
<!-- One branch per level, not `<svelte:element>`: hydration takes
|
|
323
|
+
a dynamic element out and puts it back, which blurs the trigger
|
|
324
|
+
inside it if the user had already tabbed to it. The heading goes
|
|
325
|
+
inside the summary: a summary inside a heading is not valid. The
|
|
326
|
+
flex row is on the inner element, not on the summary, which
|
|
327
|
+
older Safari does not lay out as flex. -->
|
|
328
|
+
{#if headingLevel === 1}
|
|
329
|
+
<h1 class={rowClass}>{@render defaultRow()}</h1>
|
|
330
|
+
{:else if headingLevel === 2}
|
|
331
|
+
<h2 class={rowClass}>{@render defaultRow()}</h2>
|
|
332
|
+
{:else if headingLevel === 3}
|
|
333
|
+
<h3 class={rowClass}>{@render defaultRow()}</h3>
|
|
334
|
+
{:else if headingLevel === 4}
|
|
335
|
+
<h4 class={rowClass}>{@render defaultRow()}</h4>
|
|
336
|
+
{:else if headingLevel === 5}
|
|
337
|
+
<h5 class={rowClass}>{@render defaultRow()}</h5>
|
|
338
|
+
{:else if headingLevel === 6}
|
|
339
|
+
<h6 class={rowClass}>{@render defaultRow()}</h6>
|
|
340
|
+
{:else}
|
|
341
|
+
<span class={rowClass}>{@render defaultRow()}</span>
|
|
342
|
+
{/if}
|
|
343
|
+
</summary>
|
|
344
|
+
{@render panel()}
|
|
345
|
+
</details>
|
|
346
|
+
{/if}
|
|
347
|
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { type CollapsibleHeadingLevel, type CollapsibleTriggerProps, type CollapsibleTriggerState } from "../util/collapsible.js";
|
|
4
|
+
/**
|
|
5
|
+
* A trigger, then a panel of its own that it opens. It is not for a "show
|
|
6
|
+
* all" under a list: there the control comes after the content, and what
|
|
7
|
+
* it shows is more items of a list that is already on the screen. For
|
|
8
|
+
* that, put a `Button` with `aria-expanded` and `aria-controls` under the
|
|
9
|
+
* list and let the list draw the rest (Z-127).
|
|
10
|
+
*
|
|
11
|
+
* Other attributes (`id`, `data-*`, ...) land on the host `<div>`.
|
|
12
|
+
*/
|
|
13
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
|
|
14
|
+
/** Extra classes for the host element. */
|
|
15
|
+
class?: string;
|
|
16
|
+
/** Extra classes for the default trigger (a `<summary>`). */
|
|
17
|
+
triggerClass?: string;
|
|
18
|
+
/** Extra classes for the panel. */
|
|
19
|
+
panelClass?: string;
|
|
20
|
+
/** Whether the panel is shown; supports `bind:open`. */
|
|
21
|
+
open?: boolean;
|
|
22
|
+
/** Called when the user toggles the panel, or a group closes it. */
|
|
23
|
+
onopenchange?: (open: boolean) => void;
|
|
24
|
+
/**
|
|
25
|
+
* The trigger cannot be activated. The panel keeps its current state:
|
|
26
|
+
* a group does not close a disabled panel either.
|
|
27
|
+
*/
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Text of the default trigger, a full-width header with a chevron. It is
|
|
31
|
+
* a native `<details>` / `<summary>`, so it opens and closes without
|
|
32
|
+
* scripts: before hydration, and for a visitor who has none.
|
|
33
|
+
*/
|
|
34
|
+
title?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Your own header, in place of the default trigger. Spread the first
|
|
37
|
+
* argument on a `<button>`; the second carries the open state. This
|
|
38
|
+
* path needs scripts: the button and the `hidden` panel are yours to
|
|
39
|
+
* wire, and nothing opens until the page has hydrated. Use `title` for
|
|
40
|
+
* content that must open without scripts.
|
|
41
|
+
*/
|
|
42
|
+
trigger?: Snippet<[CollapsibleTriggerProps, CollapsibleTriggerState]>;
|
|
43
|
+
/** Wraps the default trigger in `<h1>`–`<h6>`. No heading when omitted. */
|
|
44
|
+
headingLevel?: CollapsibleHeadingLevel;
|
|
45
|
+
/**
|
|
46
|
+
* Closed content stays in the DOM, inside the closed `<details>`, so a
|
|
47
|
+
* form inside it keeps its values and still submits them: closed
|
|
48
|
+
* details content is part of the form. Set this to remove the content
|
|
49
|
+
* while closed instead.
|
|
50
|
+
*/
|
|
51
|
+
unmountOnClose?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Gives the panel `role="region"`, which makes it a landmark. Defaults
|
|
54
|
+
* to true only inside a single-open `CollapsibleGroup`.
|
|
55
|
+
*/
|
|
56
|
+
region?: boolean;
|
|
57
|
+
children?: Snippet;
|
|
58
|
+
};
|
|
59
|
+
declare const Collapsible: import("svelte").Component<Props, {}, "open">;
|
|
60
|
+
type Collapsible = ReturnType<typeof Collapsible>;
|
|
61
|
+
export default Collapsible;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setContext, untrack, type Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import { cn } from "../util/cn.js";
|
|
5
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
6
|
+
import {
|
|
7
|
+
COLLAPSIBLE_GROUP,
|
|
8
|
+
nextTriggerIndex,
|
|
9
|
+
type CollapsibleGroupContext,
|
|
10
|
+
type CollapsibleGroupMember,
|
|
11
|
+
} from "../util/collapsible.js";
|
|
12
|
+
|
|
13
|
+
/** Other attributes (`id`, `aria-label`, `data-*`, ...) land on the host `<div>`. */
|
|
14
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
|
|
15
|
+
/** Extra classes for the host element. */
|
|
16
|
+
class?: string;
|
|
17
|
+
/** Lets several panels be open at once. By default opening one closes the others. */
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
/** The `Collapsible` components to coordinate. */
|
|
20
|
+
children?: Snippet;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
class: className = "",
|
|
25
|
+
multiple = false,
|
|
26
|
+
onkeydown,
|
|
27
|
+
children,
|
|
28
|
+
...restProps
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
let hostElement: HTMLDivElement | undefined = $state();
|
|
32
|
+
|
|
33
|
+
/** Names the native exclusive accordion of a single-open group. */
|
|
34
|
+
const exclusiveName = generateId("collapsible-group");
|
|
35
|
+
|
|
36
|
+
/** In the order the Collapsibles were created. */
|
|
37
|
+
const members = new Set<CollapsibleGroupMember>();
|
|
38
|
+
|
|
39
|
+
/** A disabled panel keeps its state, so it neither holds the one open slot nor loses it. */
|
|
40
|
+
function holdsTheSlot(member: CollapsibleGroupMember): boolean {
|
|
41
|
+
return member.isOpen() && !member.isDisabled();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function closeOthers(keep: CollapsibleGroupMember) {
|
|
45
|
+
for (const member of members) {
|
|
46
|
+
if (member !== keep && holdsTheSlot(member)) member.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setContext<CollapsibleGroupContext>(COLLAPSIBLE_GROUP, {
|
|
51
|
+
get multiple() {
|
|
52
|
+
return multiple;
|
|
53
|
+
},
|
|
54
|
+
get exclusiveName() {
|
|
55
|
+
return multiple ? undefined : exclusiveName;
|
|
56
|
+
},
|
|
57
|
+
register(member) {
|
|
58
|
+
// Settled here, while the members initialise, so the first render
|
|
59
|
+
// (the server's included) already shows one open panel.
|
|
60
|
+
const startClosed =
|
|
61
|
+
!multiple &&
|
|
62
|
+
holdsTheSlot(member) &&
|
|
63
|
+
[...members].some(holdsTheSlot);
|
|
64
|
+
members.add(member);
|
|
65
|
+
return { unregister: () => members.delete(member), startClosed };
|
|
66
|
+
},
|
|
67
|
+
opened(member) {
|
|
68
|
+
// A disabled panel is outside the one-open rule in both
|
|
69
|
+
// directions: it is not closed, and it closes nothing.
|
|
70
|
+
if (!multiple && !member.isDisabled()) closeOthers(member);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Going from `multiple` back to single: the first open panel stays.
|
|
75
|
+
$effect(() => {
|
|
76
|
+
if (multiple) return;
|
|
77
|
+
untrack(() => {
|
|
78
|
+
const first = [...members].find(holdsTheSlot);
|
|
79
|
+
if (first) closeOthers(first);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The optional keys of the accordion pattern, between this group's own
|
|
85
|
+
* header buttons. A trigger nested in a panel belongs to no group, or to
|
|
86
|
+
* an inner one, and is left alone; Tab is never touched.
|
|
87
|
+
*/
|
|
88
|
+
function handleKeydown(
|
|
89
|
+
event: KeyboardEvent & { currentTarget: EventTarget & HTMLDivElement },
|
|
90
|
+
) {
|
|
91
|
+
onkeydown?.(event);
|
|
92
|
+
if (event.defaultPrevented) return;
|
|
93
|
+
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
|
|
94
|
+
const target = event.target;
|
|
95
|
+
if (!(target instanceof HTMLElement) || !hostElement) return;
|
|
96
|
+
|
|
97
|
+
const ids = new Set([...members].map((member) => member.triggerId));
|
|
98
|
+
if (!ids.has(target.id)) return;
|
|
99
|
+
|
|
100
|
+
// Read from the DOM so the order is the visual one. A disabled button
|
|
101
|
+
// (or a summary marked `aria-disabled`) cannot take focus, so it is skipped.
|
|
102
|
+
const triggers = Array.from(
|
|
103
|
+
hostElement.querySelectorAll<HTMLElement>("[data-collapsible-trigger]"),
|
|
104
|
+
).filter(
|
|
105
|
+
(element) =>
|
|
106
|
+
ids.has(element.id) &&
|
|
107
|
+
!(element as HTMLButtonElement).disabled &&
|
|
108
|
+
element.getAttribute("aria-disabled") !== "true",
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const next = nextTriggerIndex(
|
|
112
|
+
event.key,
|
|
113
|
+
triggers.indexOf(target),
|
|
114
|
+
triggers.length,
|
|
115
|
+
);
|
|
116
|
+
if (next === null) return;
|
|
117
|
+
|
|
118
|
+
// Also keeps the arrow keys from scrolling the page under the header.
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
triggers[next]?.focus();
|
|
121
|
+
}
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<!-- The listener only moves focus between buttons that are already in the
|
|
125
|
+
tab order; the group itself is not interactive. -->
|
|
126
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
127
|
+
<div
|
|
128
|
+
bind:this={hostElement}
|
|
129
|
+
class={cn("flex flex-col", className)}
|
|
130
|
+
data-collapsible-group=""
|
|
131
|
+
data-multiple={multiple ? "" : undefined}
|
|
132
|
+
{...restProps}
|
|
133
|
+
onkeydown={handleKeydown}
|
|
134
|
+
>
|
|
135
|
+
{@render children?.()}
|
|
136
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
/** Other attributes (`id`, `aria-label`, `data-*`, ...) land on the host `<div>`. */
|
|
4
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
|
|
5
|
+
/** Extra classes for the host element. */
|
|
6
|
+
class?: string;
|
|
7
|
+
/** Lets several panels be open at once. By default opening one closes the others. */
|
|
8
|
+
multiple?: boolean;
|
|
9
|
+
/** The `Collapsible` components to coordinate. */
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
};
|
|
12
|
+
declare const CollapsibleGroup: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type CollapsibleGroup = ReturnType<typeof CollapsibleGroup>;
|
|
14
|
+
export default CollapsibleGroup;
|
|
@@ -1,15 +1,23 @@
|
|
|
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 CodeBlock from "../atoms/CodeBlock.svelte";
|
|
3
5
|
import Card from "../atoms/Card.svelte";
|
|
4
6
|
import CardHeader from "../atoms/CardHeader.svelte";
|
|
5
7
|
import CardContent from "../atoms/CardContent.svelte";
|
|
6
8
|
import { cn } from "../util/cn.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_COMPONENT_DEMO_STRINGS,
|
|
11
|
+
type ComponentDemoStrings,
|
|
12
|
+
} from "../util/ready-made-strings.js";
|
|
7
13
|
|
|
8
14
|
interface Props {
|
|
9
15
|
title: string;
|
|
10
16
|
description?: string;
|
|
11
17
|
code: string;
|
|
12
18
|
language?: string;
|
|
19
|
+
/** The words of the switch between the preview and the code. */
|
|
20
|
+
strings?: Partial<ComponentDemoStrings>;
|
|
13
21
|
class?: string;
|
|
14
22
|
/** @deprecated use `class`. */
|
|
15
23
|
className?: string;
|
|
@@ -20,6 +28,7 @@
|
|
|
20
28
|
description = "",
|
|
21
29
|
code,
|
|
22
30
|
language = "svelte",
|
|
31
|
+
strings,
|
|
23
32
|
class: classAttr = "",
|
|
24
33
|
className: legacyClass = "",
|
|
25
34
|
children,
|
|
@@ -31,6 +40,9 @@
|
|
|
31
40
|
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
32
41
|
|
|
33
42
|
let showCode = $state(false);
|
|
43
|
+
|
|
44
|
+
const provided = zabiStringsFor("componentDemo");
|
|
45
|
+
const text = $derived(mergeStrings(DEFAULT_COMPONENT_DEMO_STRINGS, provided(), (strings as Partial<ComponentDemoStrings> | undefined)));
|
|
34
46
|
</script>
|
|
35
47
|
|
|
36
48
|
<div class="relative">
|
|
@@ -44,8 +56,8 @@
|
|
|
44
56
|
<CardContent>
|
|
45
57
|
<button
|
|
46
58
|
onclick={() => (showCode = !showCode)}
|
|
47
|
-
class="absolute top-8 right-8 z-10 flex cursor-pointer items-center gap-2 rounded-control px-3 py-2 text-sm text-description transition-colors duration-200 hover:bg-surface-hover hover:text-body"
|
|
48
|
-
aria-label={showCode ?
|
|
59
|
+
class="absolute top-8 right-8 z-10 flex cursor-pointer items-center gap-2 rounded-control px-3 py-2 pointer-coarse:min-h-11 text-sm text-description transition-colors duration-200 hover:bg-surface-hover hover:text-body active:bg-surface-active"
|
|
60
|
+
aria-label={showCode ? text.showPreview : text.showCode}
|
|
49
61
|
>
|
|
50
62
|
{#if showCode}
|
|
51
63
|
<svg
|
|
@@ -67,7 +79,7 @@
|
|
|
67
79
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
68
80
|
/>
|
|
69
81
|
</svg>
|
|
70
|
-
|
|
82
|
+
{text.preview}
|
|
71
83
|
{:else}
|
|
72
84
|
<svg
|
|
73
85
|
class="w-4 h-4"
|
|
@@ -82,7 +94,7 @@
|
|
|
82
94
|
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
|
|
83
95
|
/>
|
|
84
96
|
</svg>
|
|
85
|
-
|
|
97
|
+
{text.code}
|
|
86
98
|
{/if}
|
|
87
99
|
</button>
|
|
88
100
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { type ComponentDemoStrings } from "../util/ready-made-strings.js";
|
|
1
2
|
interface Props {
|
|
2
3
|
title: string;
|
|
3
4
|
description?: string;
|
|
4
5
|
code: string;
|
|
5
6
|
language?: string;
|
|
7
|
+
/** The words of the switch between the preview and the code. */
|
|
8
|
+
strings?: Partial<ComponentDemoStrings>;
|
|
6
9
|
class?: string;
|
|
7
10
|
/** @deprecated use `class`. */
|
|
8
11
|
className?: string;
|