zabi-components 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2935 -0
- package/README.md +243 -10
- package/THEME.md +162 -332
- package/THEMING.md +1419 -259
- package/dist/atoms/ActionPanel.svelte +15 -5
- package/dist/atoms/Avatar.svelte +120 -0
- package/dist/atoms/Avatar.svelte.d.ts +46 -0
- package/dist/atoms/Badge.svelte +71 -13
- package/dist/atoms/Badge.svelte.d.ts +8 -2
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +217 -30
- package/dist/atoms/Button.svelte.d.ts +33 -4
- package/dist/atoms/Card.svelte +182 -26
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/CardHeader.svelte +16 -4
- package/dist/atoms/Checkbox.svelte +81 -21
- package/dist/atoms/Checkbox.svelte.d.ts +31 -2
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeBlock.svelte +20 -2
- package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +260 -49
- package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
- package/dist/atoms/Container.svelte +16 -8
- package/dist/atoms/DateField.svelte +145 -0
- package/dist/atoms/DateField.svelte.d.ts +81 -0
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +176 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +63 -5
- package/dist/atoms/Heading.svelte.d.ts +14 -0
- package/dist/atoms/IconButton.svelte +294 -26
- package/dist/atoms/IconButton.svelte.d.ts +62 -4
- package/dist/atoms/Input.svelte +259 -53
- package/dist/atoms/Input.svelte.d.ts +33 -2
- package/dist/atoms/List.svelte +5 -1
- package/dist/atoms/ListItem.svelte +70 -13
- package/dist/atoms/ListItem.svelte.d.ts +11 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +99 -19
- package/dist/atoms/Progress.svelte.d.ts +15 -1
- package/dist/atoms/Radio.svelte +51 -3
- package/dist/atoms/Radio.svelte.d.ts +30 -2
- package/dist/atoms/Rating.svelte +620 -0
- package/dist/atoms/Rating.svelte.d.ts +77 -0
- package/dist/atoms/Select.svelte +438 -98
- package/dist/atoms/Select.svelte.d.ts +61 -3
- package/dist/atoms/SelectionControl.svelte +193 -23
- package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
- package/dist/atoms/Skeleton.svelte +114 -18
- package/dist/atoms/Skeleton.svelte.d.ts +30 -3
- package/dist/atoms/SkeletonGroup.svelte +43 -0
- package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
- package/dist/atoms/Slider.svelte +344 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Sparkline.svelte +109 -0
- package/dist/atoms/Sparkline.svelte.d.ts +27 -0
- package/dist/atoms/SpeechBubble.svelte +187 -0
- package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +151 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +472 -0
- package/dist/atoms/TemporalField.svelte.d.ts +48 -0
- package/dist/atoms/Text.svelte +23 -11
- package/dist/atoms/Text.svelte.d.ts +6 -3
- package/dist/atoms/Textarea.svelte +150 -44
- package/dist/atoms/Textarea.svelte.d.ts +35 -2
- package/dist/atoms/ThemeToggle.svelte +223 -44
- package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +151 -0
- package/dist/atoms/TimeField.svelte.d.ts +85 -0
- package/dist/atoms/Toast.svelte +22 -8
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +98 -14
- package/dist/atoms/Toggle.svelte.d.ts +26 -2
- package/dist/atoms/Tooltip.svelte +528 -24
- package/dist/atoms/Tooltip.svelte.d.ts +18 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +38 -0
- package/dist/atoms/index.js +29 -0
- package/dist/atoms/selection-control.styles.d.ts +47 -5
- package/dist/atoms/selection-control.styles.js +52 -10
- package/dist/components/atoms/index.d.ts +38 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +47 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +104 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map.d.ts +11 -0
- package/dist/components/map.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +51 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +83 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +10 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +24 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +79 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +39 -0
- package/dist/components/util/avatar.d.ts.map +1 -0
- package/dist/components/util/bottom-sheet.d.ts +30 -0
- package/dist/components/util/bottom-sheet.d.ts.map +1 -0
- package/dist/components/util/bottom-tab-bar.d.ts +41 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
- package/dist/components/util/calendar.d.ts +56 -0
- package/dist/components/util/calendar.d.ts.map +1 -0
- package/dist/components/util/chart.d.ts +140 -0
- package/dist/components/util/chart.d.ts.map +1 -0
- package/dist/components/util/chip.d.ts +66 -0
- package/dist/components/util/chip.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts.map +1 -1
- package/dist/components/util/code-field.d.ts +63 -0
- package/dist/components/util/code-field.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +64 -0
- package/dist/components/util/collapsible.d.ts.map +1 -0
- package/dist/components/util/confirm-dialog.d.ts +12 -0
- package/dist/components/util/confirm-dialog.d.ts.map +1 -0
- package/dist/components/util/date.d.ts +96 -0
- package/dist/components/util/date.d.ts.map +1 -0
- package/dist/components/util/display-voice.d.ts +10 -0
- package/dist/components/util/display-voice.d.ts.map +1 -0
- package/dist/components/util/drawer.d.ts +13 -0
- package/dist/components/util/drawer.d.ts.map +1 -0
- package/dist/components/util/dropdown.d.ts +47 -0
- package/dist/components/util/dropdown.d.ts.map +1 -0
- package/dist/components/util/field-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +45 -0
- package/dist/components/util/field.d.ts.map +1 -0
- package/dist/components/util/fit-in-viewport.d.ts +201 -0
- package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +77 -2
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/hydration.d.ts +45 -0
- package/dist/components/util/hydration.d.ts.map +1 -0
- package/dist/components/util/keyboard-inset.d.ts +18 -0
- package/dist/components/util/keyboard-inset.d.ts.map +1 -0
- package/dist/components/util/map-view.d.ts +144 -0
- package/dist/components/util/map-view.d.ts.map +1 -0
- package/dist/components/util/media-grid.d.ts +51 -0
- package/dist/components/util/media-grid.d.ts.map +1 -0
- package/dist/components/util/media.d.ts +6 -0
- package/dist/components/util/media.d.ts.map +1 -0
- package/dist/components/util/overlay-chrome.d.ts +53 -0
- package/dist/components/util/overlay-chrome.d.ts.map +1 -0
- package/dist/components/util/overlay.d.ts +72 -0
- package/dist/components/util/overlay.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +234 -0
- package/dist/components/util/photo.d.ts.map +1 -0
- package/dist/components/util/portal.d.ts +23 -0
- package/dist/components/util/portal.d.ts.map +1 -0
- package/dist/components/util/progress.d.ts +19 -0
- package/dist/components/util/progress.d.ts.map +1 -0
- package/dist/components/util/pull-to-refresh.d.ts +27 -0
- package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
- package/dist/components/util/radio-keys.d.ts +25 -0
- package/dist/components/util/radio-keys.d.ts.map +1 -0
- package/dist/components/util/rating.d.ts +23 -0
- package/dist/components/util/rating.d.ts.map +1 -0
- package/dist/components/util/ready-made-strings.d.ts +77 -0
- package/dist/components/util/ready-made-strings.d.ts.map +1 -0
- package/dist/components/util/scroll-edge.d.ts +33 -0
- package/dist/components/util/scroll-edge.d.ts.map +1 -0
- package/dist/components/util/scroll-padding.d.ts +20 -0
- package/dist/components/util/scroll-padding.d.ts.map +1 -0
- package/dist/components/util/segmented-control.d.ts +16 -0
- package/dist/components/util/segmented-control.d.ts.map +1 -0
- package/dist/components/util/select.d.ts +41 -0
- package/dist/components/util/select.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +84 -0
- package/dist/components/util/sheet-drag.d.ts.map +1 -0
- package/dist/components/util/sidebar.d.ts +63 -0
- package/dist/components/util/sidebar.d.ts.map +1 -0
- package/dist/components/util/skeleton.d.ts +15 -0
- package/dist/components/util/skeleton.d.ts.map +1 -0
- package/dist/components/util/sliding-indicator.d.ts +25 -0
- package/dist/components/util/sliding-indicator.d.ts.map +1 -0
- package/dist/components/util/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/stepper.d.ts +40 -0
- package/dist/components/util/stepper.d.ts.map +1 -0
- package/dist/components/util/swipeable-list-item.d.ts +36 -0
- package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
- package/dist/components/util/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.d.ts.map +1 -0
- package/dist/components/util/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +133 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tone-surface.d.ts +34 -0
- package/dist/components/util/tone-surface.d.ts.map +1 -0
- package/dist/components/util/tooltip.d.ts +10 -0
- package/dist/components/util/tooltip.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +35 -0
- package/dist/components/util/top-navbar.d.ts.map +1 -0
- package/dist/components/util/touch-target.d.ts +31 -0
- package/dist/components/util/touch-target.d.ts.map +1 -0
- package/dist/components/util/zabi-strings.d.ts +142 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +173 -0
- package/dist/create-theme/index.d.ts +190 -0
- package/dist/create-theme/index.js +1034 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +238 -0
- package/dist/create-theme/theme-data.js +2644 -0
- package/dist/index.d.ts +104 -2
- package/dist/index.js +75 -2
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +63 -16
- package/dist/molecules/Alert.svelte.d.ts +14 -1
- package/dist/molecules/AppBar.svelte +674 -0
- package/dist/molecules/AppBar.svelte.d.ts +131 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +608 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
- package/dist/molecules/BottomTabBar.svelte +403 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +347 -0
- package/dist/molecules/Collapsible.svelte.d.ts +61 -0
- package/dist/molecules/CollapsibleGroup.svelte +136 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +16 -4
- package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
- package/dist/molecules/ConfirmDialog.svelte +257 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
- package/dist/molecules/ContactForm.svelte +34 -17
- package/dist/molecules/ContactForm.svelte.d.ts +8 -0
- package/dist/molecules/Drawer.svelte +415 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +517 -48
- package/dist/molecules/Dropdown.svelte.d.ts +47 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +55 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/FormField.svelte +9 -1
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +492 -69
- package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
- package/dist/molecules/LineChart.svelte +459 -0
- package/dist/molecules/LineChart.svelte.d.ts +53 -0
- package/dist/molecules/MapView.svelte +565 -0
- package/dist/molecules/MapView.svelte.d.ts +88 -0
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +293 -72
- package/dist/molecules/Modal.svelte.d.ts +51 -2
- package/dist/molecules/NavigationMenuContent.svelte +58 -1
- package/dist/molecules/NavigationMenuItem.svelte +0 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuList.svelte +10 -3
- package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
- package/dist/molecules/Page.svelte +32 -2
- package/dist/molecules/Page.svelte.d.ts +11 -0
- package/dist/molecules/PhotoGrid.svelte +523 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
- package/dist/molecules/PhotoViewer.svelte +1213 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/PropsTable.svelte +22 -10
- package/dist/molecules/PropsTable.svelte.d.ts +3 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +72 -18
- package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
- package/dist/molecules/Section.svelte +1 -1
- package/dist/molecules/SegmentedControl.svelte +456 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
- package/dist/molecules/SidebarBrandHeader.svelte +11 -1
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
- package/dist/molecules/SidebarFooter.svelte +30 -7
- package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SlideUp.svelte +220 -61
- package/dist/molecules/SlideUp.svelte.d.ts +26 -0
- package/dist/molecules/SortableList.svelte +633 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +562 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +250 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +310 -9
- package/dist/molecules/Tabs.svelte.d.ts +7 -0
- package/dist/molecules/Toaster.svelte +514 -5
- package/dist/molecules/Toaster.svelte.d.ts +153 -2
- package/dist/molecules/ToasterToast.svelte +495 -77
- package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
- package/dist/molecules/UnsavedChangesBar.svelte +223 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +51 -0
- package/dist/molecules/index.js +31 -0
- package/dist/molecules/toast-store.d.ts +83 -7
- package/dist/molecules/toast-store.js +25 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +696 -0
- package/dist/organisms/AppShell.svelte.d.ts +157 -0
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarAccountPanel.svelte +107 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
- package/dist/organisms/SidebarNavigation.svelte +111 -43
- package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
- package/dist/organisms/SidebarPanel.svelte +34 -17
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +164 -14
- package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
- package/dist/organisms/TopNavbar.svelte +353 -39
- package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +10 -0
- package/dist/organisms/index.js +7 -0
- package/dist/types/components.d.ts +21 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +1093 -26
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +28 -1
- package/dist/util/app-shell.d.ts +78 -0
- package/dist/util/app-shell.js +97 -0
- package/dist/util/avatar.d.ts +38 -0
- package/dist/util/avatar.js +52 -0
- package/dist/util/bottom-sheet.d.ts +29 -0
- package/dist/util/bottom-sheet.js +43 -0
- package/dist/util/bottom-tab-bar.d.ts +40 -0
- package/dist/util/bottom-tab-bar.js +54 -0
- package/dist/util/calendar.d.ts +55 -0
- package/dist/util/calendar.js +74 -0
- package/dist/util/chart.d.ts +139 -0
- package/dist/util/chart.js +305 -0
- package/dist/util/chip.d.ts +65 -0
- package/dist/util/chip.js +58 -0
- package/dist/util/cn.js +28 -2
- package/dist/util/code-field.d.ts +62 -0
- package/dist/util/code-field.js +112 -0
- package/dist/util/collapsible.d.ts +63 -0
- package/dist/util/collapsible.js +23 -0
- package/dist/util/confirm-dialog.d.ts +11 -0
- package/dist/util/confirm-dialog.js +2 -0
- package/dist/util/date.d.ts +95 -0
- package/dist/util/date.js +237 -0
- package/dist/util/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -0
- package/dist/util/drawer.d.ts +12 -0
- package/dist/util/drawer.js +7 -0
- package/dist/util/dropdown.d.ts +46 -0
- package/dist/util/dropdown.js +21 -0
- package/dist/util/field-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +44 -0
- package/dist/util/field.js +38 -0
- package/dist/util/fit-in-viewport.d.ts +200 -0
- package/dist/util/fit-in-viewport.js +399 -0
- package/dist/util/focus-utils.d.ts +77 -2
- package/dist/util/focus-utils.js +186 -9
- package/dist/util/hydration.d.ts +44 -0
- package/dist/util/hydration.js +58 -0
- package/dist/util/keyboard-inset.d.ts +17 -0
- package/dist/util/keyboard-inset.js +53 -0
- package/dist/util/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -0
- package/dist/util/media-grid.d.ts +50 -0
- package/dist/util/media-grid.js +81 -0
- package/dist/util/media.d.ts +5 -0
- package/dist/util/media.js +10 -0
- package/dist/util/overlay-chrome.d.ts +52 -0
- package/dist/util/overlay-chrome.js +56 -0
- package/dist/util/overlay.d.ts +71 -0
- package/dist/util/overlay.js +240 -0
- package/dist/util/photo.d.ts +233 -0
- package/dist/util/photo.js +209 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -0
- package/dist/util/pull-to-refresh.d.ts +26 -0
- package/dist/util/pull-to-refresh.js +30 -0
- package/dist/util/radio-keys.d.ts +24 -0
- package/dist/util/radio-keys.js +49 -0
- package/dist/util/rating.d.ts +22 -0
- package/dist/util/rating.js +26 -0
- package/dist/util/ready-made-strings.d.ts +76 -0
- package/dist/util/ready-made-strings.js +67 -0
- package/dist/util/scroll-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -0
- package/dist/util/scroll-padding.d.ts +19 -0
- package/dist/util/scroll-padding.js +44 -0
- package/dist/util/segmented-control.d.ts +15 -0
- package/dist/util/segmented-control.js +1 -0
- package/dist/util/select.d.ts +40 -0
- package/dist/util/select.js +63 -0
- package/dist/util/sheet-drag.d.ts +83 -0
- package/dist/util/sheet-drag.js +311 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/skeleton.d.ts +14 -0
- package/dist/util/skeleton.js +22 -0
- package/dist/util/sliding-indicator.d.ts +24 -0
- package/dist/util/sliding-indicator.js +44 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/stepper.d.ts +39 -0
- package/dist/util/stepper.js +38 -0
- package/dist/util/swipeable-list-item.d.ts +35 -0
- package/dist/util/swipeable-list-item.js +32 -0
- package/dist/util/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +132 -0
- package/dist/util/toaster.js +130 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +34 -0
- package/dist/util/top-navbar.js +28 -0
- package/dist/util/touch-target.d.ts +30 -0
- package/dist/util/touch-target.js +30 -0
- package/dist/util/zabi-strings.d.ts +141 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +11 -2
- package/dist/zabi-components-theme-dark-only.css +870 -258
- package/dist/zabi-components-theme-dark.css +870 -258
- package/dist/zabi-components-theme-only.css +2419 -368
- package/dist/zabi-components-theme.css +2419 -368
- package/dist/zabi-components.css +6032 -711
- package/docs/lucide-icons.md +40 -17
- package/docs/theme-imports.md +81 -11
- package/package.json +45 -8
|
@@ -1,15 +1,67 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
-
import type { CardVariant, SizeVariant } from "../types/variants.js";
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { CardVariant, SizeVariant, SurfaceTone } from "../types/variants.js";
|
|
4
4
|
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
|
|
5
5
|
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
6
6
|
size?: SizeVariant;
|
|
7
|
+
/** The look of a `neutral` card. With a `tone` or a `fill` the fill is the edge: no border, and a shadow only for `elevated`. */
|
|
7
8
|
variant?: CardVariant;
|
|
9
|
+
/**
|
|
10
|
+
* What the card is filled with: `neutral` (the default, the card surface),
|
|
11
|
+
* `tint` (the brand tint), `brand` or `accent`. On `brand` and `accent`
|
|
12
|
+
* the text, links, controls and focus rings inside take the fill's label
|
|
13
|
+
* colour; a Card, field or Alert inside is a surface again.
|
|
14
|
+
*/
|
|
15
|
+
tone?: SurfaceTone;
|
|
16
|
+
/**
|
|
17
|
+
* A fill the app chooses, any CSS colour (`var(--group-colour)`). Wins
|
|
18
|
+
* over `tone`. Set `onFill`, the label colour on it. The library cannot
|
|
19
|
+
* check that pair's contrast: the app must hold it to 4.5:1.
|
|
20
|
+
*/
|
|
21
|
+
fill?: string;
|
|
22
|
+
/** The label colour on `fill`, any CSS colour. */
|
|
23
|
+
onFill?: string;
|
|
24
|
+
/**
|
|
25
|
+
* With `href` the card itself is an `<a>`: the whole card is the link,
|
|
26
|
+
* and it works without scripts. A link card must not contain other
|
|
27
|
+
* interactive elements (a link or a button in a link is invalid and
|
|
28
|
+
* unreachable); a card with actions inside takes `onclick` or none.
|
|
29
|
+
* With `onclick` too, it is a link and `onclick` runs on activation.
|
|
30
|
+
*/
|
|
31
|
+
href?: string;
|
|
32
|
+
/** With `href`: where the link opens. */
|
|
33
|
+
target?: HTMLAnchorAttributes["target"];
|
|
34
|
+
/** With `href`: the link's relationship, such as `noopener`. */
|
|
35
|
+
rel?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The card itself is a `<button>`: the whole card is the control, and
|
|
38
|
+
* `submit` posts the form around it without scripts. With `href`
|
|
39
|
+
* the card is a link and this is ignored. Like a link card it must not
|
|
40
|
+
* contain other interactive elements. `onclick`
|
|
41
|
+
* runs on a press, as on any button.
|
|
42
|
+
*/
|
|
43
|
+
type?: "submit" | "button" | "reset";
|
|
44
|
+
/** With `type`: the name the button submits under. */
|
|
45
|
+
name?: string;
|
|
46
|
+
/** With `type`: what the button submits. */
|
|
47
|
+
value?: string;
|
|
48
|
+
/** With `type`: the id of the form it belongs to, when it is not inside one. */
|
|
49
|
+
form?: string;
|
|
50
|
+
/** With `type`: the card cannot be pressed, is dimmed, and is skipped by the keyboard. */
|
|
51
|
+
disabled?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* With `type`: the card's action is on its way, as `Button`'s and
|
|
54
|
+
* `Chip`'s `loading`. `aria-busy` and `aria-disabled` (not `disabled`),
|
|
55
|
+
* the press swallowed (it submits nothing and does not reach
|
|
56
|
+
* `onclick`), the pressed look held, the size and the Tab stop kept.
|
|
57
|
+
* No spinner. Ignored by a link card and a plain card.
|
|
58
|
+
*/
|
|
59
|
+
loading?: boolean;
|
|
8
60
|
fullWidth?: boolean;
|
|
9
61
|
class?: string;
|
|
10
62
|
/** @deprecated use `class` — kept so 7.x call sites keep working. */
|
|
11
63
|
className?: string;
|
|
12
|
-
/**
|
|
64
|
+
/** Names the card when it is interactive and its content does not (the content names a link well enough in most cases). */
|
|
13
65
|
ariaLabel?: string;
|
|
14
66
|
children?: Snippet;
|
|
15
67
|
};
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
* Both are merged here so existing call sites keep working. */
|
|
35
35
|
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
36
36
|
|
|
37
|
-
const headingTag = $derived(`h${level}`);
|
|
38
37
|
const headingClasses = $derived(() => {
|
|
39
38
|
return "text-2xl font-semibold leading-none tracking-tight text-headline";
|
|
40
39
|
});
|
|
@@ -42,9 +41,22 @@
|
|
|
42
41
|
|
|
43
42
|
<header class={cn(`flex flex-col space-y-2 pb-4 ${className}`)} {...restProps}>
|
|
44
43
|
{#if title}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
<!-- One branch per level, not a dynamic element: hydration takes a dynamic
|
|
45
|
+
element out and puts it back, which blurs a control inside it that the user
|
|
46
|
+
had already tabbed to. -->
|
|
47
|
+
{#if level === 1}
|
|
48
|
+
<h1 class={headingClasses()}>{title}</h1>
|
|
49
|
+
{:else if level === 2}
|
|
50
|
+
<h2 class={headingClasses()}>{title}</h2>
|
|
51
|
+
{:else if level === 3}
|
|
52
|
+
<h3 class={headingClasses()}>{title}</h3>
|
|
53
|
+
{:else if level === 4}
|
|
54
|
+
<h4 class={headingClasses()}>{title}</h4>
|
|
55
|
+
{:else if level === 5}
|
|
56
|
+
<h5 class={headingClasses()}>{title}</h5>
|
|
57
|
+
{:else}
|
|
58
|
+
<h6 class={headingClasses()}>{title}</h6>
|
|
59
|
+
{/if}
|
|
48
60
|
{/if}
|
|
49
61
|
{#if children}
|
|
50
62
|
{@render children()}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
2
5
|
import SelectionControl from "./SelectionControl.svelte";
|
|
3
6
|
import type { SelectionControlMarkProps } from "./SelectionControl.svelte";
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
/** Other attributes (`required`, `data-*`, `aria-*`, ...) land on the `<input>`. */
|
|
9
|
+
type Props = Omit<
|
|
10
|
+
HTMLInputAttributes,
|
|
11
|
+
"class" | "id" | "name" | "value" | "disabled" | "checked" | "type" | "onchange"
|
|
12
|
+
> & {
|
|
6
13
|
/** Extra classes for the host element. */
|
|
7
14
|
class?: string;
|
|
8
15
|
id?: string;
|
|
@@ -15,7 +22,32 @@
|
|
|
15
22
|
checked?: boolean;
|
|
16
23
|
onChange?: (event: Event) => void;
|
|
17
24
|
onchange?: (event: Event) => void;
|
|
18
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Drawn inside the one label, between the box and the label text, as
|
|
27
|
+
* decoration for the name `label` already gives. A leading image
|
|
28
|
+
* should have an empty alt: the label names the row.
|
|
29
|
+
*/
|
|
30
|
+
leading?: Snippet;
|
|
31
|
+
/**
|
|
32
|
+
* A line under the label, tied to the box with `aria-describedby`.
|
|
33
|
+
* It is inside the label, so a tap on it ticks the box; it does not
|
|
34
|
+
* join the name.
|
|
35
|
+
*/
|
|
36
|
+
description?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Where the box sits in the row. `end` puts it after the leading, the
|
|
39
|
+
* label and the description, at the far edge; the row is still one
|
|
40
|
+
* label and fills the width it is given.
|
|
41
|
+
*/
|
|
42
|
+
controlPosition?: "start" | "end";
|
|
43
|
+
variant?: SemanticVariant;
|
|
44
|
+
/** Status text under the control. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
45
|
+
message?: string;
|
|
46
|
+
/** Help text under the control, read out with it. */
|
|
47
|
+
hint?: string;
|
|
48
|
+
/** An error under the control. It marks the input invalid and is announced; it wins over `variant` and `message`. */
|
|
49
|
+
error?: string;
|
|
50
|
+
};
|
|
19
51
|
|
|
20
52
|
let {
|
|
21
53
|
class: className = "",
|
|
@@ -26,32 +58,57 @@
|
|
|
26
58
|
loading = false,
|
|
27
59
|
label = "",
|
|
28
60
|
defaultChecked = false,
|
|
29
|
-
checked = $bindable(
|
|
61
|
+
checked = $bindable<Exclude<Props["checked"], undefined>>(),
|
|
30
62
|
onChange,
|
|
31
63
|
onchange,
|
|
64
|
+
leading,
|
|
65
|
+
description,
|
|
66
|
+
controlPosition = "start",
|
|
32
67
|
...restProps
|
|
33
68
|
}: Props = $props();
|
|
69
|
+
|
|
70
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
71
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
72
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
73
|
+
// here instead: at once, for the server and the first render, and again
|
|
74
|
+
// whenever a parent hands back `undefined`.
|
|
75
|
+
const applyDefaults = () => {
|
|
76
|
+
if (checked === undefined) checked = defaultChecked;
|
|
77
|
+
};
|
|
78
|
+
applyDefaults();
|
|
79
|
+
$effect.pre(applyDefaults);
|
|
34
80
|
</script>
|
|
35
81
|
|
|
36
82
|
{#snippet mark(props: SelectionControlMarkProps)}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
83
|
+
<!-- While loading the ring stands in for the tick, so the two never overlap;
|
|
84
|
+
the fill still says whether the box is checked. The ring is only in the
|
|
85
|
+
DOM while busy, which is what lets it take Spinner's reduced-motion
|
|
86
|
+
pulse: a pulse animates opacity and would un-hide a ring that was
|
|
87
|
+
merely transparent. It is drawn in the text colour so the gap in it
|
|
88
|
+
survives: a hand-written border colour class repaints all four sides. -->
|
|
89
|
+
{#if props.loading}
|
|
90
|
+
<span
|
|
91
|
+
class="pointer-events-none absolute z-10 inline-block size-3 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent motion-reduce:animate-pulse {props.checked
|
|
92
|
+
? 'text-action-primary'
|
|
93
|
+
: 'text-brand-500'}"
|
|
94
|
+
aria-hidden="true"
|
|
95
|
+
></span>
|
|
96
|
+
{:else}
|
|
97
|
+
<svg
|
|
98
|
+
class="absolute w-3 h-3 text-action-primary pointer-events-none z-10 opacity-0 group-has-checked/control:opacity-100"
|
|
99
|
+
fill="none"
|
|
100
|
+
stroke="currentColor"
|
|
101
|
+
viewBox="0 0 24 24"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
>
|
|
104
|
+
<path
|
|
105
|
+
stroke-linecap="round"
|
|
106
|
+
stroke-linejoin="round"
|
|
107
|
+
stroke-width="2.5"
|
|
108
|
+
d="M5 13l4 4L19 7"
|
|
109
|
+
/>
|
|
110
|
+
</svg>
|
|
111
|
+
{/if}
|
|
55
112
|
{/snippet}
|
|
56
113
|
|
|
57
114
|
<SelectionControl
|
|
@@ -68,6 +125,9 @@
|
|
|
68
125
|
{onChange}
|
|
69
126
|
{onchange}
|
|
70
127
|
{mark}
|
|
128
|
+
{leading}
|
|
129
|
+
{description}
|
|
130
|
+
{controlPosition}
|
|
71
131
|
class={className}
|
|
72
132
|
{...restProps}
|
|
73
133
|
/>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
4
|
+
/** Other attributes (`required`, `data-*`, `aria-*`, ...) land on the `<input>`. */
|
|
5
|
+
type Props = Omit<HTMLInputAttributes, "class" | "id" | "name" | "value" | "disabled" | "checked" | "type" | "onchange"> & {
|
|
2
6
|
/** Extra classes for the host element. */
|
|
3
7
|
class?: string;
|
|
4
8
|
id?: string;
|
|
@@ -11,7 +15,32 @@ interface Props {
|
|
|
11
15
|
checked?: boolean;
|
|
12
16
|
onChange?: (event: Event) => void;
|
|
13
17
|
onchange?: (event: Event) => void;
|
|
14
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Drawn inside the one label, between the box and the label text, as
|
|
20
|
+
* decoration for the name `label` already gives. A leading image
|
|
21
|
+
* should have an empty alt: the label names the row.
|
|
22
|
+
*/
|
|
23
|
+
leading?: Snippet;
|
|
24
|
+
/**
|
|
25
|
+
* A line under the label, tied to the box with `aria-describedby`.
|
|
26
|
+
* It is inside the label, so a tap on it ticks the box; it does not
|
|
27
|
+
* join the name.
|
|
28
|
+
*/
|
|
29
|
+
description?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Where the box sits in the row. `end` puts it after the leading, the
|
|
32
|
+
* label and the description, at the far edge; the row is still one
|
|
33
|
+
* label and fills the width it is given.
|
|
34
|
+
*/
|
|
35
|
+
controlPosition?: "start" | "end";
|
|
36
|
+
variant?: SemanticVariant;
|
|
37
|
+
/** Status text under the control. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
38
|
+
message?: string;
|
|
39
|
+
/** Help text under the control, read out with it. */
|
|
40
|
+
hint?: string;
|
|
41
|
+
/** An error under the control. It marks the input invalid and is announced; it wins over `variant` and `message`. */
|
|
42
|
+
error?: string;
|
|
43
|
+
};
|
|
15
44
|
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
16
45
|
type Checkbox = ReturnType<typeof Checkbox>;
|
|
17
46
|
export default Checkbox;
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext, onMount, tick, type Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes, HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
import Check from "@lucide/svelte/icons/check";
|
|
5
|
+
import { cn } from "../util/cn.js";
|
|
6
|
+
import { groupBinding } from "../util/hydration.js";
|
|
7
|
+
import { CHIP_GROUP_CONTEXT_KEY, type ChipGroupContext } from "../util/chip.js";
|
|
8
|
+
import { TOUCH_HIT_AREA } from "../util/touch-target.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* One type for the four forms, as Button has one for the button and the
|
|
12
|
+
* link: a union of the elements' attributes is too large to carry through
|
|
13
|
+
* a wrapper. Other attributes land on the element that is the control: the
|
|
14
|
+
* `<a>`, the `<button>` or the `<input>`; `class` is always the chip's.
|
|
15
|
+
*/
|
|
16
|
+
type Props = Omit<HTMLButtonAttributes, "class" | "type" | "value" | "onchange" | "children"> & {
|
|
17
|
+
/** Makes the chip a link to this address, which works without scripts. */
|
|
18
|
+
href?: string;
|
|
19
|
+
target?: HTMLAnchorAttributes["target"];
|
|
20
|
+
rel?: HTMLAnchorAttributes["rel"];
|
|
21
|
+
download?: HTMLAnchorAttributes["download"];
|
|
22
|
+
hreflang?: HTMLAnchorAttributes["hreflang"];
|
|
23
|
+
/** With `href`: what `aria-current` says while `selected`. */
|
|
24
|
+
current?: "page" | "true" | "step" | "location" | "date" | "time";
|
|
25
|
+
/**
|
|
26
|
+
* `checkbox` or `radio`: a real input under the chip, which submits in
|
|
27
|
+
* a form without scripts. Without it (and without `href`) the chip is a
|
|
28
|
+
* toggle button. Inside a ChipGroup with a `type`, the group's.
|
|
29
|
+
* `submit` or `button`: a button that does not toggle. A press posts
|
|
30
|
+
* the form (`submit`, with `name` and `value`) or only calls `onclick`;
|
|
31
|
+
* `selected` is drawn as on a toggle and said with `aria-pressed`, and
|
|
32
|
+
* the app decides it, as a server's answer does.
|
|
33
|
+
*/
|
|
34
|
+
type?: "checkbox" | "radio" | "submit" | "button";
|
|
35
|
+
/** Name the input is submitted under. Inside a ChipGroup with a `name`, the group's. */
|
|
36
|
+
name?: string;
|
|
37
|
+
/** What the input submits, and what a ChipGroup's `value` holds when it is chosen. */
|
|
38
|
+
value?: string;
|
|
39
|
+
/** The chip is chosen. Bindable; the one prop for `checked`, `aria-pressed` and `aria-current`. */
|
|
40
|
+
selected?: boolean;
|
|
41
|
+
required?: boolean;
|
|
42
|
+
form?: string;
|
|
43
|
+
/** 28, 32 or 40px tall. On a coarse pointer the touch target reaches 44px; the box does not grow. */
|
|
44
|
+
size?: "sm" | "md" | "lg";
|
|
45
|
+
/** An icon or an avatar before the label. */
|
|
46
|
+
leading?: Snippet;
|
|
47
|
+
/** A check icon before the label while selected. On for a checkbox and a toggle button, off for a link and a radio. */
|
|
48
|
+
checkmark?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A button chip (`type="submit"`, `"button"` or a toggle) while its
|
|
51
|
+
* action is on its way, as Button's: `aria-busy` and `aria-disabled`,
|
|
52
|
+
* a spinner over the label, the press swallowed. The chip keeps its
|
|
53
|
+
* width and stays a Tab stop, so focus is not lost. Ignored by a link
|
|
54
|
+
* and by `checkbox` / `radio`, which are not actions.
|
|
55
|
+
*/
|
|
56
|
+
loading?: boolean;
|
|
57
|
+
/** Called with the new `selected` when a person changes it. */
|
|
58
|
+
onchange?: (selected: boolean) => void;
|
|
59
|
+
class?: string;
|
|
60
|
+
children?: Snippet;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
let {
|
|
64
|
+
href,
|
|
65
|
+
target,
|
|
66
|
+
rel,
|
|
67
|
+
download,
|
|
68
|
+
hreflang,
|
|
69
|
+
current = "true",
|
|
70
|
+
type: typeProp,
|
|
71
|
+
name: nameProp,
|
|
72
|
+
value,
|
|
73
|
+
selected = $bindable<Exclude<Props["selected"], undefined>>(),
|
|
74
|
+
disabled = false,
|
|
75
|
+
required,
|
|
76
|
+
form,
|
|
77
|
+
size = "md",
|
|
78
|
+
leading,
|
|
79
|
+
checkmark,
|
|
80
|
+
loading = false,
|
|
81
|
+
onchange,
|
|
82
|
+
onclick,
|
|
83
|
+
class: className = "",
|
|
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 (selected === undefined) selected = false;
|
|
95
|
+
};
|
|
96
|
+
applyDefaults();
|
|
97
|
+
$effect.pre(applyDefaults);
|
|
98
|
+
|
|
99
|
+
const group = getContext<ChipGroupContext | undefined>(CHIP_GROUP_CONTEXT_KEY);
|
|
100
|
+
/** Inside a group that has a `type`, the group decides what is chosen. */
|
|
101
|
+
const grouped = $derived(group?.type !== undefined && typeProp === undefined && href === undefined);
|
|
102
|
+
const type = $derived(grouped ? group?.type : typeProp);
|
|
103
|
+
const name = $derived(nameProp ?? (grouped ? group?.name : undefined));
|
|
104
|
+
const isLink = $derived(href !== undefined && href !== null);
|
|
105
|
+
const isInput = $derived(!isLink && (type === "checkbox" || type === "radio"));
|
|
106
|
+
/** A button that does not toggle: the app says what is selected. */
|
|
107
|
+
const isFixedButton = $derived(!isLink && (typeProp === "submit" || typeProp === "button"));
|
|
108
|
+
const isDisabled = $derived(!!disabled || (grouped && !!group?.disabled));
|
|
109
|
+
const chosen = $derived(grouped ? !!group?.isSelected(value ?? "") : !!selected);
|
|
110
|
+
const showCheckmark = $derived(checkmark ?? (!isLink && type !== "radio"));
|
|
111
|
+
|
|
112
|
+
function setChosen(next: boolean, report: boolean) {
|
|
113
|
+
if (grouped) group?.change(value ?? "", next, report);
|
|
114
|
+
else selected = next;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let input: HTMLInputElement | undefined = $state();
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The input is bound, so a choice made before the page hydrated is kept:
|
|
121
|
+
* Svelte's binding finds the input changed and hands its state over here
|
|
122
|
+
* instead of overwriting it (see util/hydration.ts). A choice adopted that
|
|
123
|
+
* way came without a `change` event that anything heard, so it is reported
|
|
124
|
+
* once, with a real `change` on the input, when the component has settled:
|
|
125
|
+
* the callbacks and a form around it hear it as they hear a press.
|
|
126
|
+
*/
|
|
127
|
+
let settling = true;
|
|
128
|
+
let adopted = false;
|
|
129
|
+
|
|
130
|
+
function take(next: boolean) {
|
|
131
|
+
if (next === chosen) return;
|
|
132
|
+
setChosen(next, false);
|
|
133
|
+
if (settling) adopted = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const NOT_CHOSEN = {};
|
|
137
|
+
/** A radio on its own: its own value while it is checked. */
|
|
138
|
+
const aloneBinding = groupBinding<unknown>(
|
|
139
|
+
() => (selected ? value : NOT_CHOSEN),
|
|
140
|
+
(picked) => take(picked === value),
|
|
141
|
+
() => take(false),
|
|
142
|
+
);
|
|
143
|
+
/** A radio in a group: the group's value. */
|
|
144
|
+
const groupedBinding = groupBinding<string>(
|
|
145
|
+
() => group?.radioValue,
|
|
146
|
+
(picked) => {
|
|
147
|
+
if (group?.radioValue === picked) return;
|
|
148
|
+
group?.change(picked, true, false);
|
|
149
|
+
if (settling) adopted = true;
|
|
150
|
+
},
|
|
151
|
+
() => group?.restore(),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
onMount(() => {
|
|
155
|
+
// The bindings adopt in a microtask of their own, queued before this one.
|
|
156
|
+
void tick().then(() => {
|
|
157
|
+
settling = false;
|
|
158
|
+
if (adopted) input?.dispatchEvent(new Event("change", { bubbles: true }));
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
function handleInputChange(event: Event) {
|
|
163
|
+
if (isDisabled) return;
|
|
164
|
+
// A real change is its own report: nothing is left to tell.
|
|
165
|
+
adopted = false;
|
|
166
|
+
const checked = (event.currentTarget as HTMLInputElement).checked;
|
|
167
|
+
setChosen(checked, true);
|
|
168
|
+
onchange?.(checked);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Loading without `disabled`: unavailable, and still where focus is (see Button). */
|
|
172
|
+
const isBusy = $derived(loading && !disabled);
|
|
173
|
+
|
|
174
|
+
function handleButtonClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
|
175
|
+
if (isBusy) {
|
|
176
|
+
// The press ends here: it does not submit, toggle or reach `onclick`.
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
event.stopPropagation();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
onclick?.(event);
|
|
182
|
+
if (event.defaultPrevented || isDisabled) return;
|
|
183
|
+
if (isFixedButton) return;
|
|
184
|
+
selected = !selected;
|
|
185
|
+
onchange?.(selected);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** A disabled link has no `href`, but a pointer can still press it. */
|
|
189
|
+
function handleLinkClick(event: MouseEvent) {
|
|
190
|
+
if (isDisabled) {
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
event.stopPropagation();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
(onclick as ((event: MouseEvent) => void) | null | undefined)?.(event);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 28 / 32 / 40px, a fixed height: a chip never wraps its label, so one
|
|
200
|
+
* line is all it ever holds. The padding is 10 / 12 / 16px.
|
|
201
|
+
*/
|
|
202
|
+
const SIZE = { sm: "h-7 px-[10px]", md: "h-8 px-3", lg: "h-10 px-4" } as const;
|
|
203
|
+
const FORCED = "forced-colors:outline-2 forced-colors:-outline-offset-2 forced-colors:outline-[color:Highlight]";
|
|
204
|
+
|
|
205
|
+
const base = $derived(
|
|
206
|
+
`group/chip relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-[6px] whitespace-nowrap rounded-pill text-sm transition-colors duration-(--duration-base) motion-reduce:transition-none ${SIZE[size] ?? SIZE.md} ${TOUCH_HIT_AREA} ${isDisabled ? "pointer-events-none cursor-not-allowed opacity-50" : ""}`,
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A link and a button read `selected`. The hand-written chip colours are
|
|
211
|
+
* outside every layer, so they cannot be overridden by a variant of
|
|
212
|
+
* another colour: the two states are two class lists, never both.
|
|
213
|
+
*/
|
|
214
|
+
const elementClasses = $derived(
|
|
215
|
+
cn(
|
|
216
|
+
base,
|
|
217
|
+
"focus-ring",
|
|
218
|
+
chosen
|
|
219
|
+
? `bg-chip-selected hover:bg-chip-selected-hover active:bg-chip-selected-active text-chip-selected-text font-semibold ${FORCED}`
|
|
220
|
+
: "bg-chip hover:bg-chip-hover active:bg-chip-active text-chip-text font-medium",
|
|
221
|
+
isBusy && "cursor-progress",
|
|
222
|
+
className,
|
|
223
|
+
),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* An input is chosen by the browser, and the look follows its `:checked`
|
|
228
|
+
* (not `selected`), so a choice made before hydration shows at once.
|
|
229
|
+
* Every colour is a `has-` or `not-has-` variant: with the plain
|
|
230
|
+
* hand-written class on the label, the unlayered rule would win over the
|
|
231
|
+
* selected variant.
|
|
232
|
+
*/
|
|
233
|
+
const labelClasses = $derived(
|
|
234
|
+
cn(
|
|
235
|
+
base,
|
|
236
|
+
"focus-ring font-medium not-has-[:checked]:bg-chip not-has-[:checked]:text-chip-text not-has-[:checked]:hover:bg-chip-hover not-has-[:checked]:active:bg-chip-active has-[:checked]:bg-chip-selected has-[:checked]:text-chip-selected-text has-[:checked]:font-semibold has-[:checked]:hover:bg-chip-selected-hover has-[:checked]:active:bg-chip-selected-active",
|
|
237
|
+
"has-[:checked]:forced-colors:outline-2 has-[:checked]:forced-colors:-outline-offset-2 has-[:checked]:forced-colors:outline-[color:Highlight]",
|
|
238
|
+
"has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus-ring",
|
|
239
|
+
className,
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
/** The real input, stretched over the chip and transparent, so a press lands on it. */
|
|
244
|
+
const INPUT_CLASSES = "absolute inset-0 m-0 size-full cursor-pointer appearance-none rounded-pill opacity-0 outline-none disabled:cursor-not-allowed";
|
|
245
|
+
</script>
|
|
246
|
+
|
|
247
|
+
{#snippet inner(checkClass: string)}
|
|
248
|
+
{#if showCheckmark}
|
|
249
|
+
<Check class="size-3.5 shrink-0 {checkClass}" aria-hidden="true" />
|
|
250
|
+
{/if}
|
|
251
|
+
{#if leading}
|
|
252
|
+
<span class="inline-flex shrink-0 items-center">{@render leading()}</span>
|
|
253
|
+
{/if}
|
|
254
|
+
{#if children}
|
|
255
|
+
<span class="min-w-0 truncate">{@render children()}</span>
|
|
256
|
+
{/if}
|
|
257
|
+
{/snippet}
|
|
258
|
+
|
|
259
|
+
{#if isLink}
|
|
260
|
+
<!-- A link that cannot be followed keeps its role and says it is unavailable, as Button's does. -->
|
|
261
|
+
<a
|
|
262
|
+
href={isDisabled ? undefined : href}
|
|
263
|
+
{target}
|
|
264
|
+
{rel}
|
|
265
|
+
{download}
|
|
266
|
+
{hreflang}
|
|
267
|
+
class={elementClasses}
|
|
268
|
+
role={isDisabled ? "link" : undefined}
|
|
269
|
+
aria-disabled={isDisabled ? "true" : undefined}
|
|
270
|
+
aria-current={chosen ? current : undefined}
|
|
271
|
+
{...restProps as unknown as HTMLAnchorAttributes}
|
|
272
|
+
onclick={handleLinkClick}
|
|
273
|
+
>
|
|
274
|
+
{@render inner(chosen ? "" : "hidden")}
|
|
275
|
+
</a>
|
|
276
|
+
{:else if isInput}
|
|
277
|
+
<label class={labelClasses} data-chip-form={type}>
|
|
278
|
+
<!-- One branch per type: a binding needs a type it can read. -->
|
|
279
|
+
{#if type === "checkbox"}
|
|
280
|
+
<input
|
|
281
|
+
bind:this={input}
|
|
282
|
+
type="checkbox"
|
|
283
|
+
{name}
|
|
284
|
+
{value}
|
|
285
|
+
{required}
|
|
286
|
+
{form}
|
|
287
|
+
bind:checked={() => chosen, take}
|
|
288
|
+
disabled={isDisabled}
|
|
289
|
+
class={INPUT_CLASSES}
|
|
290
|
+
onchange={handleInputChange}
|
|
291
|
+
{...restProps as unknown as HTMLInputAttributes}
|
|
292
|
+
/>
|
|
293
|
+
{:else if grouped}
|
|
294
|
+
<input
|
|
295
|
+
bind:this={input}
|
|
296
|
+
type="radio"
|
|
297
|
+
{name}
|
|
298
|
+
{value}
|
|
299
|
+
{required}
|
|
300
|
+
{form}
|
|
301
|
+
bind:group={groupedBinding.value}
|
|
302
|
+
disabled={isDisabled}
|
|
303
|
+
class={INPUT_CLASSES}
|
|
304
|
+
onchange={handleInputChange}
|
|
305
|
+
{...restProps as unknown as HTMLInputAttributes}
|
|
306
|
+
/>
|
|
307
|
+
{:else}
|
|
308
|
+
<input
|
|
309
|
+
bind:this={input}
|
|
310
|
+
type="radio"
|
|
311
|
+
{name}
|
|
312
|
+
{value}
|
|
313
|
+
{required}
|
|
314
|
+
{form}
|
|
315
|
+
bind:group={aloneBinding.value}
|
|
316
|
+
disabled={isDisabled}
|
|
317
|
+
class={INPUT_CLASSES}
|
|
318
|
+
onchange={handleInputChange}
|
|
319
|
+
{...restProps as unknown as HTMLInputAttributes}
|
|
320
|
+
/>
|
|
321
|
+
{/if}
|
|
322
|
+
{@render inner("hidden group-has-checked/chip:block")}
|
|
323
|
+
</label>
|
|
324
|
+
{:else}
|
|
325
|
+
<button
|
|
326
|
+
type={typeProp === "submit" ? "submit" : "button"}
|
|
327
|
+
{name}
|
|
328
|
+
{value}
|
|
329
|
+
{form}
|
|
330
|
+
class={elementClasses}
|
|
331
|
+
{disabled}
|
|
332
|
+
aria-pressed={chosen}
|
|
333
|
+
aria-busy={isBusy ? "true" : undefined}
|
|
334
|
+
aria-disabled={isBusy ? "true" : undefined}
|
|
335
|
+
{...restProps}
|
|
336
|
+
onclick={handleButtonClick}
|
|
337
|
+
>
|
|
338
|
+
{#if isBusy}
|
|
339
|
+
<!-- The label stays in the chip, only unseen, so the width and the name do not change. -->
|
|
340
|
+
<span class="inline-flex min-w-0 items-center justify-center gap-[6px] opacity-0">
|
|
341
|
+
{@render inner(chosen ? "" : "hidden")}
|
|
342
|
+
</span>
|
|
343
|
+
<span
|
|
344
|
+
class="pointer-events-none absolute inline-block size-3.5 animate-spin rounded-full border-2 border-current border-t-transparent motion-reduce:animate-pulse"
|
|
345
|
+
aria-hidden="true"
|
|
346
|
+
></span>
|
|
347
|
+
{:else}
|
|
348
|
+
{@render inner(chosen ? "" : "hidden")}
|
|
349
|
+
{/if}
|
|
350
|
+
</button>
|
|
351
|
+
{/if}
|