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,54 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { type SwipeableListItemAction, type SwipeableListItemStrings } from "../util/swipeable-list-item.js";
|
|
4
|
+
/**
|
|
5
|
+
* A row with one or two actions behind its inline end, such as Delete on
|
|
6
|
+
* a draft. A finger swipes the row towards the inline start to show them;
|
|
7
|
+
* a swipe only shows them, and an action runs when its button is pressed.
|
|
8
|
+
*
|
|
9
|
+
* The swipe is a shortcut and never the only way. The row has a "more"
|
|
10
|
+
* button at its end that opens the same actions for a mouse, a keyboard,
|
|
11
|
+
* a switch or a screen reader, and they are ordinary buttons once open.
|
|
12
|
+
* Escape or a press elsewhere closes the row. In a list (`ul`, `ol`,
|
|
13
|
+
* `List`) one row is open at a time.
|
|
14
|
+
*
|
|
15
|
+
* It is the row's content and a group of buttons, not a list item: put
|
|
16
|
+
* it inside your own `li` or `ListItem`.
|
|
17
|
+
*
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <ul>
|
|
20
|
+
* {#each drafts as draft (draft.id)}
|
|
21
|
+
* <li>
|
|
22
|
+
* <SwipeableListItem
|
|
23
|
+
* actions={[{ id: "delete", label: "Delete", tone: "danger", onselect: () => remove(draft) }]}
|
|
24
|
+
* >
|
|
25
|
+
* {draft.title}
|
|
26
|
+
* </SwipeableListItem>
|
|
27
|
+
* </li>
|
|
28
|
+
* {/each}
|
|
29
|
+
* </ul>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "children"> & {
|
|
33
|
+
/** One or two actions, shown at the inline end. */
|
|
34
|
+
actions: SwipeableListItemAction[];
|
|
35
|
+
/** Whether the actions are shown. Bindable. */
|
|
36
|
+
open?: boolean;
|
|
37
|
+
/** Runs when the row opens or closes, by whatever means. */
|
|
38
|
+
onopenchange?: (open: boolean) => void;
|
|
39
|
+
/**
|
|
40
|
+
* The button at the end of the row that opens the actions without a
|
|
41
|
+
* swipe. Without it the swipe is the only way this component offers,
|
|
42
|
+
* and the app has to give another: the same actions on the page the
|
|
43
|
+
* row leads to, or in a menu (WCAG 2.5.1, 2.5.7).
|
|
44
|
+
*/
|
|
45
|
+
showMoreButton?: boolean;
|
|
46
|
+
/** The words the row says by itself, for another language. */
|
|
47
|
+
strings?: Partial<SwipeableListItemStrings>;
|
|
48
|
+
class?: string;
|
|
49
|
+
/** The row's content. */
|
|
50
|
+
children?: Snippet;
|
|
51
|
+
};
|
|
52
|
+
declare const SwipeableListItem: import("svelte").Component<Props, {}, "open">;
|
|
53
|
+
type SwipeableListItem = ReturnType<typeof SwipeableListItem>;
|
|
54
|
+
export default SwipeableListItem;
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import { generateId } from "../util/ssr-safe.js";
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
|
+
import {
|
|
6
|
+
afterFrame,
|
|
7
|
+
measureIndicator,
|
|
8
|
+
observeSizes,
|
|
9
|
+
type IndicatorBox,
|
|
10
|
+
} from "../util/sliding-indicator.js";
|
|
5
11
|
|
|
6
12
|
interface Props {
|
|
7
13
|
/** Extra classes for the host element. */
|
|
@@ -14,6 +20,13 @@
|
|
|
14
20
|
/** Selected tab id; supports `bind:activeTab`. */
|
|
15
21
|
activeTab?: string;
|
|
16
22
|
variant?: "default" | "pills";
|
|
23
|
+
/**
|
|
24
|
+
* The tabs share the row equally. Meant for two or three tabs on a
|
|
25
|
+
* phone. A label too long for its share wraps between words; when a
|
|
26
|
+
* word does not fit, the tabs keep their words whole and the row
|
|
27
|
+
* scrolls sideways instead.
|
|
28
|
+
*/
|
|
29
|
+
fullWidth?: boolean;
|
|
17
30
|
onclick?: (event: Event) => void;
|
|
18
31
|
onkeydown?: (event: Event) => void;
|
|
19
32
|
children?: Snippet<[{ activeTab: string }]>;
|
|
@@ -22,12 +35,24 @@
|
|
|
22
35
|
let {
|
|
23
36
|
class: className = "",
|
|
24
37
|
tabs = [],
|
|
25
|
-
activeTab = $bindable
|
|
38
|
+
activeTab = $bindable<Exclude<Props["activeTab"], undefined>>(),
|
|
26
39
|
variant = "default",
|
|
40
|
+
fullWidth = false,
|
|
27
41
|
children,
|
|
28
42
|
...restProps
|
|
29
43
|
}: Props = $props();
|
|
30
44
|
|
|
45
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
46
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
47
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
48
|
+
// here instead: at once, for the server and the first render, and again
|
|
49
|
+
// whenever a parent hands back `undefined`.
|
|
50
|
+
const applyDefaults = () => {
|
|
51
|
+
if (activeTab === undefined) activeTab = "";
|
|
52
|
+
};
|
|
53
|
+
applyDefaults();
|
|
54
|
+
$effect.pre(applyDefaults);
|
|
55
|
+
|
|
31
56
|
const tabsBaseId = generateId("tabs");
|
|
32
57
|
|
|
33
58
|
/** Button refs keyed by tab id — roving tabindex needs focus to follow selection. */
|
|
@@ -36,8 +61,151 @@
|
|
|
36
61
|
function selectTab(tabId: string, moveFocus = false) {
|
|
37
62
|
activeTab = tabId;
|
|
38
63
|
if (moveFocus) {
|
|
39
|
-
|
|
64
|
+
// The list brings the tab into view itself (`handleFocusIn`), with
|
|
65
|
+
// room for the fade; the browser's own jump would come first.
|
|
66
|
+
tabElements[tabId]?.focus({ preventScroll: true });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* When the tabs do not fit, the list scrolls sideways. It used to do
|
|
72
|
+
* neither that nor wrap: in a 160px card the third tab ended 94px outside
|
|
73
|
+
* it. Each edge with more tabs beyond it fades out, so a cut-off label
|
|
74
|
+
* reads as "there is more" and not as a mistake.
|
|
75
|
+
*/
|
|
76
|
+
let listElement = $state<HTMLDivElement | null>(null);
|
|
77
|
+
/** Physical sides, since a mask is drawn in physical directions. */
|
|
78
|
+
let fadeLeft = $state(false);
|
|
79
|
+
let fadeRight = $state(false);
|
|
80
|
+
|
|
81
|
+
/** Room kept between a revealed tab and the edge: the fade, and the tab's focus ring. */
|
|
82
|
+
const REVEAL_MARGIN = 32;
|
|
83
|
+
|
|
84
|
+
function updateFades() {
|
|
85
|
+
const list = listElement;
|
|
86
|
+
if (!list) return;
|
|
87
|
+
const hidden = list.scrollWidth - list.clientWidth;
|
|
88
|
+
if (hidden <= 1) {
|
|
89
|
+
fadeLeft = false;
|
|
90
|
+
fadeRight = false;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Right to left, `scrollLeft` runs from 0 down to minus the hidden width.
|
|
94
|
+
const rtl = getComputedStyle(list).direction === "rtl";
|
|
95
|
+
const fromLeft = rtl ? hidden + list.scrollLeft : list.scrollLeft;
|
|
96
|
+
fadeLeft = fromLeft > 1;
|
|
97
|
+
fadeRight = fromLeft < hidden - 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Scrolls the list, and only the list, until `tab` is clear of both edges. */
|
|
101
|
+
function reveal(tab: HTMLElement | undefined, animate: boolean) {
|
|
102
|
+
const list = listElement;
|
|
103
|
+
if (!list || !tab || list.scrollWidth - list.clientWidth <= 1) return;
|
|
104
|
+
const box = list.getBoundingClientRect();
|
|
105
|
+
const item = tab.getBoundingClientRect();
|
|
106
|
+
let delta = 0;
|
|
107
|
+
if (item.left < box.left + REVEAL_MARGIN) {
|
|
108
|
+
delta = item.left - box.left - REVEAL_MARGIN;
|
|
109
|
+
} else if (item.right > box.right - REVEAL_MARGIN) {
|
|
110
|
+
delta = item.right - box.right + REVEAL_MARGIN;
|
|
111
|
+
}
|
|
112
|
+
if (delta === 0) return;
|
|
113
|
+
const still =
|
|
114
|
+
!animate ||
|
|
115
|
+
(typeof window.matchMedia === "function" &&
|
|
116
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
117
|
+
// `scrollBy` with a behaviour is missing from older engines and from jsdom.
|
|
118
|
+
if (typeof list.scrollBy === "function") {
|
|
119
|
+
list.scrollBy({ left: delta, behavior: still ? "instant" : "smooth" });
|
|
120
|
+
} else {
|
|
121
|
+
list.scrollLeft += delta;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The selected tab is brought into view when the list mounts and when the
|
|
126
|
+
// selection changes: at once the first time, so the page does not open on
|
|
127
|
+
// a moving list.
|
|
128
|
+
let revealedOnce = false;
|
|
129
|
+
$effect(() => {
|
|
130
|
+
const tab = tabElements[activeTab];
|
|
131
|
+
if (!listElement) return;
|
|
132
|
+
reveal(tab, revealedOnce);
|
|
133
|
+
revealedOnce = true;
|
|
134
|
+
updateFades();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// The fades follow the list's own size and the number of tabs.
|
|
138
|
+
$effect(() => {
|
|
139
|
+
const list = listElement;
|
|
140
|
+
void tabs.length;
|
|
141
|
+
if (!list) return;
|
|
142
|
+
updateFades();
|
|
143
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
144
|
+
const observer = new ResizeObserver(updateFades);
|
|
145
|
+
observer.observe(list);
|
|
146
|
+
return () => observer.disconnect();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The sliding indicator: the underline, or the pill. Until a box has been
|
|
151
|
+
* measured (on the server, before hydration, with no layout) it is null and
|
|
152
|
+
* the selected tab draws its own mark; with a box, that mark moves to the
|
|
153
|
+
* indicator, which slides between tabs. It lives in the scrolling list and
|
|
154
|
+
* is placed against the list's content, so it stays on its tab as the list
|
|
155
|
+
* scrolls.
|
|
156
|
+
*/
|
|
157
|
+
let indicator = $state<IndicatorBox | null>(null);
|
|
158
|
+
/** Off for the first placement, so the indicator appears in place and does not fly in. */
|
|
159
|
+
let indicatorMotion = $state(false);
|
|
160
|
+
|
|
161
|
+
function measure() {
|
|
162
|
+
const tab = tabElements[activeTab];
|
|
163
|
+
indicator = listElement && tab ? measureIndicator(listElement, tab) : null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$effect(() => {
|
|
167
|
+
void [tabs, activeTab, variant, fullWidth, listElement];
|
|
168
|
+
measure();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// The tabs resize with a loaded font, zoomed text or a wider row.
|
|
172
|
+
$effect(() => {
|
|
173
|
+
const list = listElement;
|
|
174
|
+
void tabs.length;
|
|
175
|
+
if (!list) return;
|
|
176
|
+
return observeSizes([list, ...Object.values(tabElements)], measure);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
$effect(() => {
|
|
180
|
+
if (!indicator || indicatorMotion) return;
|
|
181
|
+
return afterFrame(() => (indicatorMotion = true));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
/** The underline is the bottom 2px of the tab's box; the pill is all of it. */
|
|
185
|
+
const indicatorStyle = $derived.by(() => {
|
|
186
|
+
if (!indicator) return "";
|
|
187
|
+
const { x, y, width, height } = indicator;
|
|
188
|
+
const line = variant === "pills" ? height : 2;
|
|
189
|
+
const top = variant === "pills" ? y : y + height - 2;
|
|
190
|
+
return `transform: translate(${x}px, ${top}px); width: ${width}px; height: ${line}px;`;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Arrow keys and Tab move focus to a tab that may be out of view. Keyboard
|
|
195
|
+
* focus only: a press focuses the tab on the way down, and moving the list
|
|
196
|
+
* then would take the tab from under the pointer before the click lands.
|
|
197
|
+
* A pressed tab is revealed once it is selected, by the effect above.
|
|
198
|
+
*/
|
|
199
|
+
function handleFocusIn(event: FocusEvent) {
|
|
200
|
+
const tab = (event.target as HTMLElement | null)?.closest<HTMLElement>('[role="tab"]');
|
|
201
|
+
if (!tab) return;
|
|
202
|
+
let fromKeyboard = true;
|
|
203
|
+
try {
|
|
204
|
+
fromKeyboard = tab.matches(":focus-visible");
|
|
205
|
+
} catch {
|
|
206
|
+
// An engine without `:focus-visible`: treat every focus as the keyboard's.
|
|
40
207
|
}
|
|
208
|
+
if (fromKeyboard) reveal(tab, true);
|
|
41
209
|
}
|
|
42
210
|
|
|
43
211
|
function getEnabledTabs() {
|
|
@@ -91,27 +259,92 @@
|
|
|
91
259
|
}
|
|
92
260
|
// Enter/Space: native <button> click already selects the tab.
|
|
93
261
|
}
|
|
262
|
+
|
|
263
|
+
const TAB_BASE =
|
|
264
|
+
"focus-ring relative cursor-pointer border-b-2 px-4 py-2 pointer-coarse:min-h-11 text-sm font-medium transition-colors focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed";
|
|
265
|
+
// A tab keeps its width and the list scrolls; sharing the row, it may shrink and its label wrap.
|
|
266
|
+
const TAB_NATURAL = "shrink-0 whitespace-nowrap";
|
|
267
|
+
// `min-w-min` is the tab's longest word: a label wraps between words, never
|
|
268
|
+
// inside one, and when the words do not fit the shares the row scrolls like
|
|
269
|
+
// any other.
|
|
270
|
+
const TAB_SHARED = "min-w-min flex-1 basis-0 text-center";
|
|
271
|
+
// A disabled tab is never the selected one, so only this arm carries the disabled state.
|
|
272
|
+
const TAB_IDLE =
|
|
273
|
+
"border-transparent text-description hover:border-border-medium hover:text-body active:bg-surface-active disabled:opacity-50 disabled:hover:border-transparent disabled:hover:text-description disabled:active:bg-transparent";
|
|
274
|
+
const TAB_SELECTED = "border-brand-500 text-body active:bg-surface-active";
|
|
275
|
+
// The pill has a fill of its own, so its pressed state is a further step of
|
|
276
|
+
// that fill; a translucent tint in its place would be a weaker fill, not a
|
|
277
|
+
// stronger one. It is the pressed role, not the hover step: the hover step
|
|
278
|
+
// is 1.24:1 from the resting fill in light, under the 1.25:1 a pressed fill
|
|
279
|
+
// is held to. The label darkens with it (`.text-link:active` in app.css)
|
|
280
|
+
// and keeps 4.89:1 on the pressed fill.
|
|
281
|
+
const TAB_SELECTED_PILL =
|
|
282
|
+
"border-brand-500 bg-action-primary-subtle text-link active:bg-action-primary-subtle-active";
|
|
283
|
+
|
|
284
|
+
// With the indicator the tab keeps its label colour and its pressed fill
|
|
285
|
+
// only. Forced colours drop backgrounds, so the underline of the tab itself
|
|
286
|
+
// stays there and the indicator is hidden.
|
|
287
|
+
const TAB_SELECTED_MOVED =
|
|
288
|
+
"border-brand-500 not-forced-colors:border-transparent text-body active:bg-surface-active";
|
|
289
|
+
const TAB_SELECTED_PILL_MOVED =
|
|
290
|
+
"border-brand-500 not-forced-colors:border-transparent text-link active:bg-action-primary-subtle-active";
|
|
291
|
+
|
|
292
|
+
function tabClasses(tabId: string): string {
|
|
293
|
+
const base = `${TAB_BASE} ${fullWidth ? TAB_SHARED : TAB_NATURAL}`;
|
|
294
|
+
if (activeTab !== tabId) return `${base} ${TAB_IDLE}`;
|
|
295
|
+
if (indicator) {
|
|
296
|
+
return `${base} ${variant === "pills" ? TAB_SELECTED_PILL_MOVED : TAB_SELECTED_MOVED}`;
|
|
297
|
+
}
|
|
298
|
+
return `${base} ${variant === "pills" ? TAB_SELECTED_PILL : TAB_SELECTED}`;
|
|
299
|
+
}
|
|
94
300
|
</script>
|
|
95
301
|
|
|
96
302
|
<div class={cn("tabs-container", className)}>
|
|
303
|
+
<!-- The tablist keeps the line under the tabs, and the tabs scroll in a
|
|
304
|
+
box of their own inside it, so the line stays put. That box is
|
|
305
|
+
presentational: the tabs are still the tablist's tabs. -->
|
|
306
|
+
<!-- `flow-root`: the inner box's negative margin stays inside this one,
|
|
307
|
+
so the row is as tall as its tabs and its line, as it was. -->
|
|
97
308
|
<div
|
|
98
|
-
class="
|
|
309
|
+
class="flow-root border-b border-border"
|
|
99
310
|
role="tablist"
|
|
100
311
|
tabindex="-1"
|
|
101
312
|
onkeydown={handleKeydown}
|
|
102
313
|
>
|
|
314
|
+
<!-- `tabindex="-1"`: a scrolling box is otherwise a Tab stop of its own. -->
|
|
315
|
+
<div
|
|
316
|
+
bind:this={listElement}
|
|
317
|
+
class="tabs-list relative flex"
|
|
318
|
+
role="presentation"
|
|
319
|
+
tabindex="-1"
|
|
320
|
+
data-fade-left={fadeLeft ? "" : undefined}
|
|
321
|
+
data-fade-right={fadeRight ? "" : undefined}
|
|
322
|
+
onfocusin={handleFocusIn}
|
|
323
|
+
onscroll={updateFades}
|
|
324
|
+
>
|
|
325
|
+
{#if indicator}
|
|
326
|
+
<!-- Under the tabs (they are positioned and come later), and never in their way. -->
|
|
327
|
+
<span
|
|
328
|
+
class={cn(
|
|
329
|
+
"pointer-events-none absolute top-0 left-0 forced-colors:hidden",
|
|
330
|
+
variant === "pills" ? "bg-action-primary-subtle border-b-2 border-brand-500" : "bg-brand-500",
|
|
331
|
+
// The underline must not overshoot; the pill may spring.
|
|
332
|
+
indicatorMotion &&
|
|
333
|
+
"transition-[transform,width,height] duration-(--duration-moderate) motion-reduce:transition-none",
|
|
334
|
+
indicatorMotion && (variant === "pills" ? "ease-spring" : "ease-standard"),
|
|
335
|
+
)}
|
|
336
|
+
aria-hidden="true"
|
|
337
|
+
data-indicator={variant}
|
|
338
|
+
style={indicatorStyle}
|
|
339
|
+
></span>
|
|
340
|
+
{/if}
|
|
103
341
|
{#each tabs as tab (tab.id)}
|
|
104
342
|
<button
|
|
105
343
|
bind:this={tabElements[tab.id]}
|
|
106
344
|
type="button"
|
|
107
345
|
role="tab"
|
|
108
346
|
id={getTabId(tab.id)}
|
|
109
|
-
class=
|
|
110
|
-
tab.id
|
|
111
|
-
? variant === 'pills'
|
|
112
|
-
? 'border-brand-500 bg-brand-100 text-brand-700'
|
|
113
|
-
: 'border-brand-500 text-body'
|
|
114
|
-
: 'border-transparent text-description hover:border-border-medium hover:text-body'}"
|
|
347
|
+
class={tabClasses(tab.id)}
|
|
115
348
|
onclick={() => selectTab(tab.id)}
|
|
116
349
|
disabled={tab.disabled}
|
|
117
350
|
aria-selected={activeTab === tab.id}
|
|
@@ -122,6 +355,7 @@
|
|
|
122
355
|
</button>
|
|
123
356
|
{/each}
|
|
124
357
|
</div>
|
|
358
|
+
</div>
|
|
125
359
|
|
|
126
360
|
<div
|
|
127
361
|
class="mt-4"
|
|
@@ -132,3 +366,70 @@
|
|
|
132
366
|
{@render children?.({ activeTab })}
|
|
133
367
|
</div>
|
|
134
368
|
</div>
|
|
369
|
+
|
|
370
|
+
<style>
|
|
371
|
+
/*
|
|
372
|
+
* The list scrolls sideways when its tabs do not fit, with no scrollbar:
|
|
373
|
+
* the fade says there is more. A scroll container clips what is outside
|
|
374
|
+
* it, and a tab's focus ring is 4px outside the tab, so the list is 4px
|
|
375
|
+
* larger on every side than the row it fills and pulled back by the same
|
|
376
|
+
* amount. A list that fits looks exactly as it did.
|
|
377
|
+
*/
|
|
378
|
+
.tabs-list {
|
|
379
|
+
--tabs-ring-room: 4px;
|
|
380
|
+
margin: calc(var(--tabs-ring-room) * -1);
|
|
381
|
+
padding: var(--tabs-ring-room);
|
|
382
|
+
overflow-x: auto;
|
|
383
|
+
overflow-y: hidden;
|
|
384
|
+
overscroll-behavior-x: contain;
|
|
385
|
+
scrollbar-width: none;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.tabs-list::-webkit-scrollbar {
|
|
389
|
+
display: none;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/*
|
|
393
|
+
* A mask, not a gradient laid over the tabs: it fades them into whatever
|
|
394
|
+
* surface is behind, and nothing sits on top of a tab or its focus ring.
|
|
395
|
+
* Only an edge with more tabs beyond it fades, and a list that fits has no
|
|
396
|
+
* mask at all. Left and right are physical here; the script works out
|
|
397
|
+
* which is which in a right-to-left layout.
|
|
398
|
+
*/
|
|
399
|
+
.tabs-list[data-fade-left],
|
|
400
|
+
.tabs-list[data-fade-right] {
|
|
401
|
+
--tabs-fade-left: 0px;
|
|
402
|
+
--tabs-fade-right: 0px;
|
|
403
|
+
-webkit-mask-image: linear-gradient(
|
|
404
|
+
to right,
|
|
405
|
+
transparent,
|
|
406
|
+
black var(--tabs-fade-left),
|
|
407
|
+
black calc(100% - var(--tabs-fade-right)),
|
|
408
|
+
transparent
|
|
409
|
+
);
|
|
410
|
+
mask-image: linear-gradient(
|
|
411
|
+
to right,
|
|
412
|
+
transparent,
|
|
413
|
+
black var(--tabs-fade-left),
|
|
414
|
+
black calc(100% - var(--tabs-fade-right)),
|
|
415
|
+
transparent
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.tabs-list[data-fade-left] {
|
|
420
|
+
--tabs-fade-left: 1.5rem;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.tabs-list[data-fade-right] {
|
|
424
|
+
--tabs-fade-right: 1.5rem;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* Forced colours: a half-transparent label is harder to read than a cut-off one. */
|
|
428
|
+
@media (forced-colors: active) {
|
|
429
|
+
.tabs-list[data-fade-left],
|
|
430
|
+
.tabs-list[data-fade-right] {
|
|
431
|
+
-webkit-mask-image: none;
|
|
432
|
+
mask-image: none;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
</style>
|
|
@@ -10,6 +10,13 @@ interface Props {
|
|
|
10
10
|
/** Selected tab id; supports `bind:activeTab`. */
|
|
11
11
|
activeTab?: string;
|
|
12
12
|
variant?: "default" | "pills";
|
|
13
|
+
/**
|
|
14
|
+
* The tabs share the row equally. Meant for two or three tabs on a
|
|
15
|
+
* phone. A label too long for its share wraps between words; when a
|
|
16
|
+
* word does not fit, the tabs keep their words whole and the row
|
|
17
|
+
* scrolls sideways instead.
|
|
18
|
+
*/
|
|
19
|
+
fullWidth?: boolean;
|
|
13
20
|
onclick?: (event: Event) => void;
|
|
14
21
|
onkeydown?: (event: Event) => void;
|
|
15
22
|
children?: Snippet<[{
|