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,263 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
3
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
4
|
+
import type { Snippet } from "svelte";
|
|
5
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
6
|
+
import ArrowDown from "@lucide/svelte/icons/arrow-down";
|
|
7
|
+
import Spinner from "../atoms/Spinner.svelte";
|
|
8
|
+
import { cn } from "../util/cn.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_PULL_TO_REFRESH_STRINGS,
|
|
11
|
+
pullDistance,
|
|
12
|
+
scrollContainerOf,
|
|
13
|
+
type PullToRefreshStrings,
|
|
14
|
+
} from "../util/pull-to-refresh.js";
|
|
15
|
+
import { DRAG_SLOP, prefersReducedMotion } from "../util/sheet-drag.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A list that reloads when it is pulled down from its top, on a touch
|
|
19
|
+
* screen.
|
|
20
|
+
*
|
|
21
|
+
* The pull is a shortcut. The same refresh is a button at the top of the
|
|
22
|
+
* region, out of sight until keyboard focus reaches it, and named for a
|
|
23
|
+
* screen reader; "Refreshing" and "Updated" are announced, and the
|
|
24
|
+
* region is `aria-busy` meanwhile.
|
|
25
|
+
*
|
|
26
|
+
* It only acts while what scrolls the list (the page, or the nearest
|
|
27
|
+
* scrolling ancestor, such as the content of an `AppShell`) is at its
|
|
28
|
+
* top, and never takes an ordinary scroll. The indicator is in the flow
|
|
29
|
+
* of the region, above the list, so under an app bar it comes out below
|
|
30
|
+
* the bar.
|
|
31
|
+
*
|
|
32
|
+
* ```svelte
|
|
33
|
+
* <PullToRefresh onrefresh={() => loadRounds()}>
|
|
34
|
+
* <List items={rounds} />
|
|
35
|
+
* </PullToRefresh>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "children" | "aria-busy"> & {
|
|
39
|
+
/**
|
|
40
|
+
* Reloads the list. Return the promise: the indicator stays until it
|
|
41
|
+
* settles. A promise that rejects ends the refresh too, and nothing
|
|
42
|
+
* is announced as updated; say what went wrong yourself.
|
|
43
|
+
*/
|
|
44
|
+
onrefresh?: () => Promise<void> | void;
|
|
45
|
+
/** Whether a refresh is running. Bindable: set it to show the indicator for a refresh of your own. */
|
|
46
|
+
refreshing?: boolean;
|
|
47
|
+
/** No pull and no button. */
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
/** How far the indicator has to come out for letting go to refresh, in px. */
|
|
50
|
+
threshold?: number;
|
|
51
|
+
/**
|
|
52
|
+
* The button that refreshes without the gesture. Without it the pull
|
|
53
|
+
* is the only way this component offers, and the app has to give
|
|
54
|
+
* another (WCAG 2.5.1).
|
|
55
|
+
*/
|
|
56
|
+
showButton?: boolean;
|
|
57
|
+
/** The words the component says by itself, for another language. */
|
|
58
|
+
strings?: Partial<PullToRefreshStrings>;
|
|
59
|
+
class?: string;
|
|
60
|
+
/** The list. */
|
|
61
|
+
children?: Snippet;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
let {
|
|
65
|
+
onrefresh,
|
|
66
|
+
refreshing = $bindable<Exclude<Props["refreshing"], undefined>>(),
|
|
67
|
+
disabled = false,
|
|
68
|
+
threshold = 64,
|
|
69
|
+
showButton = true,
|
|
70
|
+
strings,
|
|
71
|
+
class: className = "",
|
|
72
|
+
children,
|
|
73
|
+
...restProps
|
|
74
|
+
}: Props = $props();
|
|
75
|
+
|
|
76
|
+
// No fallback on a bindable prop (see Collapsible): the default is applied here.
|
|
77
|
+
const applyDefaults = () => {
|
|
78
|
+
if (refreshing === undefined) refreshing = false;
|
|
79
|
+
};
|
|
80
|
+
applyDefaults();
|
|
81
|
+
$effect.pre(applyDefaults);
|
|
82
|
+
|
|
83
|
+
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
84
|
+
const provided = zabiStringsFor("pullToRefresh");
|
|
85
|
+
const text = $derived(mergeStrings(DEFAULT_PULL_TO_REFRESH_STRINGS, provided(), strings));
|
|
86
|
+
|
|
87
|
+
let root = $state<HTMLDivElement>();
|
|
88
|
+
/** How far the indicator is out under a finger, in px; null when no pull is going on. */
|
|
89
|
+
let pulled = $state<number | null>(null);
|
|
90
|
+
/** What the status region says. It is in the page, empty, before anything is said. */
|
|
91
|
+
let status = $state("");
|
|
92
|
+
/** Set when a refresh of this component's own failed: nothing was updated. */
|
|
93
|
+
let failed = false;
|
|
94
|
+
|
|
95
|
+
const armed = $derived(pulled !== null && pulled >= threshold);
|
|
96
|
+
|
|
97
|
+
async function refresh() {
|
|
98
|
+
if (disabled || refreshing) return;
|
|
99
|
+
failed = false;
|
|
100
|
+
refreshing = true;
|
|
101
|
+
try {
|
|
102
|
+
await onrefresh?.();
|
|
103
|
+
} catch {
|
|
104
|
+
// The app's to report (see `onrefresh`). The refresh is over all the same.
|
|
105
|
+
failed = true;
|
|
106
|
+
} finally {
|
|
107
|
+
refreshing = false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The announcements follow `refreshing`, whoever set it.
|
|
112
|
+
let wasRefreshing = false;
|
|
113
|
+
$effect(() => {
|
|
114
|
+
const now = !!refreshing;
|
|
115
|
+
if (now === wasRefreshing) return;
|
|
116
|
+
wasRefreshing = now;
|
|
117
|
+
status = now ? text.refreshing : failed ? "" : text.done;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// The browser's own pull-to-refresh must not fire as well: that is a
|
|
121
|
+
// property of what scrolls, which is not this element.
|
|
122
|
+
$effect(() => {
|
|
123
|
+
const element = root;
|
|
124
|
+
if (!element || disabled) return;
|
|
125
|
+
const scroller = scrollContainerOf(element);
|
|
126
|
+
const before = scroller.style.overscrollBehaviorY;
|
|
127
|
+
scroller.style.overscrollBehaviorY = "contain";
|
|
128
|
+
return () => {
|
|
129
|
+
scroller.style.overscrollBehaviorY = before;
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// --- The pull. Touch events, because the browser owns the gesture (it
|
|
134
|
+
// scrolls) until it is taken, and only a touch listener that is not
|
|
135
|
+
// passive can take it. A mouse does not pull.
|
|
136
|
+
$effect(() => {
|
|
137
|
+
const element = root;
|
|
138
|
+
if (!element || disabled) return;
|
|
139
|
+
const scroller = scrollContainerOf(element);
|
|
140
|
+
let touchId: number | null = null;
|
|
141
|
+
let startX = 0;
|
|
142
|
+
let startY = 0;
|
|
143
|
+
let pulling = false;
|
|
144
|
+
|
|
145
|
+
const find = (list: TouchList) => Array.from(list).find((touch) => touch.identifier === touchId);
|
|
146
|
+
|
|
147
|
+
function onTouchStart(event: TouchEvent) {
|
|
148
|
+
if (touchId !== null || event.touches.length !== 1 || refreshing) return;
|
|
149
|
+
if (scroller.scrollTop > 0) return;
|
|
150
|
+
const touch = event.touches[0];
|
|
151
|
+
touchId = touch.identifier;
|
|
152
|
+
startX = touch.clientX;
|
|
153
|
+
startY = touch.clientY;
|
|
154
|
+
pulling = false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function onTouchMove(event: TouchEvent) {
|
|
158
|
+
const touch = find(event.touches);
|
|
159
|
+
if (!touch) return;
|
|
160
|
+
const dx = touch.clientX - startX;
|
|
161
|
+
const dy = touch.clientY - startY;
|
|
162
|
+
if (!pulling) {
|
|
163
|
+
// Scrolled since, upwards, or sideways: not a pull.
|
|
164
|
+
if (scroller.scrollTop > 0 || dy < DRAG_SLOP) return;
|
|
165
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
166
|
+
touchId = null;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
pulling = true;
|
|
170
|
+
// Measured from where the pull was recognised.
|
|
171
|
+
startY = touch.clientY;
|
|
172
|
+
}
|
|
173
|
+
if (event.cancelable) event.preventDefault();
|
|
174
|
+
pulled = pullDistance(touch.clientY - startY, threshold);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function finish(event: TouchEvent, cancelled: boolean) {
|
|
178
|
+
if (!find(event.changedTouches)) return;
|
|
179
|
+
touchId = null;
|
|
180
|
+
const far = pulling && !cancelled && pulled !== null && pulled >= threshold;
|
|
181
|
+
pulling = false;
|
|
182
|
+
pulled = null;
|
|
183
|
+
if (far) void refresh();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const onTouchEnd = (event: TouchEvent) => finish(event, false);
|
|
187
|
+
const onTouchCancel = (event: TouchEvent) => finish(event, true);
|
|
188
|
+
|
|
189
|
+
element.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
190
|
+
element.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
191
|
+
element.addEventListener("touchend", onTouchEnd);
|
|
192
|
+
element.addEventListener("touchcancel", onTouchCancel);
|
|
193
|
+
return () => {
|
|
194
|
+
element.removeEventListener("touchstart", onTouchStart);
|
|
195
|
+
element.removeEventListener("touchmove", onTouchMove);
|
|
196
|
+
element.removeEventListener("touchend", onTouchEnd);
|
|
197
|
+
element.removeEventListener("touchcancel", onTouchCancel);
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The height of the indicator: under the finger while pulling, at the
|
|
203
|
+
* threshold while the refresh runs. Where motion is unwanted it does not
|
|
204
|
+
* follow the finger: it is there, at full height, or it is not.
|
|
205
|
+
*/
|
|
206
|
+
const indicatorHeight = $derived.by(() => {
|
|
207
|
+
if (refreshing) return threshold;
|
|
208
|
+
if (pulled === null) return 0;
|
|
209
|
+
return prefersReducedMotion() ? (pulled > 0 ? threshold : 0) : pulled;
|
|
210
|
+
});
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<div
|
|
214
|
+
{...restProps}
|
|
215
|
+
bind:this={root}
|
|
216
|
+
class={cn("relative", className)}
|
|
217
|
+
aria-busy={refreshing ? "true" : undefined}
|
|
218
|
+
data-pull-to-refresh
|
|
219
|
+
data-refreshing={refreshing ? "true" : "false"}
|
|
220
|
+
>
|
|
221
|
+
<!-- In the page before it says anything, or the first announcement is lost. -->
|
|
222
|
+
<div class="sr-only" role="status" aria-live="polite" data-pull-status>{status}</div>
|
|
223
|
+
|
|
224
|
+
{#if showButton && !disabled}
|
|
225
|
+
<!-- Out of sight until keyboard focus reaches it: the list is what the region is for. -->
|
|
226
|
+
<button
|
|
227
|
+
type="button"
|
|
228
|
+
class="focus-ring sr-only cursor-pointer rounded-control bg-action-secondary text-sm font-medium text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active focus-visible:not-sr-only focus-visible:absolute focus-visible:start-[8px] focus-visible:top-[8px] focus-visible:z-10 focus-visible:min-h-11 focus-visible:px-[16px]"
|
|
229
|
+
aria-disabled={refreshing ? "true" : undefined}
|
|
230
|
+
data-pull-button
|
|
231
|
+
onclick={() => void refresh()}
|
|
232
|
+
>
|
|
233
|
+
{text.refresh}
|
|
234
|
+
</button>
|
|
235
|
+
{/if}
|
|
236
|
+
|
|
237
|
+
<!-- Decorative: the status region and the button carry it for assistive technology. -->
|
|
238
|
+
<div
|
|
239
|
+
class={cn(
|
|
240
|
+
"flex items-end justify-center overflow-hidden text-sm text-description",
|
|
241
|
+
pulled === null && "transition-[height] duration-200 ease-out motion-reduce:transition-none",
|
|
242
|
+
)}
|
|
243
|
+
style:height="{indicatorHeight}px"
|
|
244
|
+
aria-hidden="true"
|
|
245
|
+
data-pull-indicator
|
|
246
|
+
data-armed={armed ? "true" : "false"}
|
|
247
|
+
>
|
|
248
|
+
<div class="flex h-[64px] shrink-0 items-center gap-[8px]">
|
|
249
|
+
{#if refreshing}
|
|
250
|
+
<Spinner size="md" />
|
|
251
|
+
<span>{text.refreshing}</span>
|
|
252
|
+
{:else}
|
|
253
|
+
<ArrowDown
|
|
254
|
+
size={16}
|
|
255
|
+
class={cn("transition-transform duration-150 motion-reduce:transition-none", armed && "rotate-180")}
|
|
256
|
+
/>
|
|
257
|
+
<span>{armed ? text.release : text.pull}</span>
|
|
258
|
+
{/if}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
{@render children?.()}
|
|
263
|
+
</div>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { type PullToRefreshStrings } from "../util/pull-to-refresh.js";
|
|
4
|
+
/**
|
|
5
|
+
* A list that reloads when it is pulled down from its top, on a touch
|
|
6
|
+
* screen.
|
|
7
|
+
*
|
|
8
|
+
* The pull is a shortcut. The same refresh is a button at the top of the
|
|
9
|
+
* region, out of sight until keyboard focus reaches it, and named for a
|
|
10
|
+
* screen reader; "Refreshing" and "Updated" are announced, and the
|
|
11
|
+
* region is `aria-busy` meanwhile.
|
|
12
|
+
*
|
|
13
|
+
* It only acts while what scrolls the list (the page, or the nearest
|
|
14
|
+
* scrolling ancestor, such as the content of an `AppShell`) is at its
|
|
15
|
+
* top, and never takes an ordinary scroll. The indicator is in the flow
|
|
16
|
+
* of the region, above the list, so under an app bar it comes out below
|
|
17
|
+
* the bar.
|
|
18
|
+
*
|
|
19
|
+
* ```svelte
|
|
20
|
+
* <PullToRefresh onrefresh={() => loadRounds()}>
|
|
21
|
+
* <List items={rounds} />
|
|
22
|
+
* </PullToRefresh>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "children" | "aria-busy"> & {
|
|
26
|
+
/**
|
|
27
|
+
* Reloads the list. Return the promise: the indicator stays until it
|
|
28
|
+
* settles. A promise that rejects ends the refresh too, and nothing
|
|
29
|
+
* is announced as updated; say what went wrong yourself.
|
|
30
|
+
*/
|
|
31
|
+
onrefresh?: () => Promise<void> | void;
|
|
32
|
+
/** Whether a refresh is running. Bindable: set it to show the indicator for a refresh of your own. */
|
|
33
|
+
refreshing?: boolean;
|
|
34
|
+
/** No pull and no button. */
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
/** How far the indicator has to come out for letting go to refresh, in px. */
|
|
37
|
+
threshold?: number;
|
|
38
|
+
/**
|
|
39
|
+
* The button that refreshes without the gesture. Without it the pull
|
|
40
|
+
* is the only way this component offers, and the app has to give
|
|
41
|
+
* another (WCAG 2.5.1).
|
|
42
|
+
*/
|
|
43
|
+
showButton?: boolean;
|
|
44
|
+
/** The words the component says by itself, for another language. */
|
|
45
|
+
strings?: Partial<PullToRefreshStrings>;
|
|
46
|
+
class?: string;
|
|
47
|
+
/** The list. */
|
|
48
|
+
children?: Snippet;
|
|
49
|
+
};
|
|
50
|
+
declare const PullToRefresh: import("svelte").Component<Props, {}, "refreshing">;
|
|
51
|
+
type PullToRefresh = ReturnType<typeof PullToRefresh>;
|
|
52
|
+
export default PullToRefresh;
|
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
RADIO_GROUP_CONTROL_SHELL,
|
|
5
5
|
RADIO_GROUP_OPTION_LABEL_ROW,
|
|
6
6
|
RADIO_GROUP_RING_OVERLAY,
|
|
7
|
+
SELECTION_CONTROL_INPUT,
|
|
7
8
|
} from "../atoms/selection-control.styles";
|
|
9
|
+
import type { HTMLFieldsetAttributes } from "svelte/elements";
|
|
8
10
|
import { generateId } from "../util/ssr-safe.js";
|
|
9
11
|
import { SvelteMap } from "svelte/reactivity";
|
|
10
12
|
import { cn } from "../util/cn.js";
|
|
13
|
+
import { resetValueOf, groupBinding } from "../util/hydration.js";
|
|
11
14
|
|
|
12
15
|
export type RadioGroupOption = {
|
|
13
16
|
value: string;
|
|
@@ -16,32 +19,63 @@
|
|
|
16
19
|
description?: string;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
/** Other attributes (`id`, `data-*`, `aria-*`, ...) land on the `<fieldset>`. */
|
|
23
|
+
type Props = Omit<HTMLFieldsetAttributes, "class" | "name" | "disabled"> & {
|
|
20
24
|
/** Extra classes for the host element. */
|
|
21
25
|
class?: string;
|
|
22
26
|
options: RadioGroupOption[];
|
|
23
27
|
/** Selected value; `bind:value`. Use `undefined` for no selection. */
|
|
24
28
|
value?: string | undefined;
|
|
25
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The selection to start with when `value` is `undefined` as the
|
|
31
|
+
* group is created. Applied once: it does not come back when the
|
|
32
|
+
* value is set to `undefined` later, and it never replaces a choice.
|
|
33
|
+
*/
|
|
26
34
|
defaultValue?: string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Name the value is submitted under in a form. Without a name the
|
|
37
|
+
* group is not part of the form around it: it submits nothing and a
|
|
38
|
+
* form reset leaves it alone.
|
|
39
|
+
*/
|
|
27
40
|
name?: string;
|
|
28
41
|
disabled?: boolean;
|
|
29
42
|
legend?: string;
|
|
30
43
|
label?: string;
|
|
31
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Draw a ring round the chosen row. Off, the filled radio alone shows
|
|
46
|
+
* the choice; keyboard focus keeps its ring either way.
|
|
47
|
+
*/
|
|
48
|
+
chosenRing?: boolean;
|
|
49
|
+
};
|
|
32
50
|
|
|
33
51
|
let {
|
|
34
52
|
class: className = "",
|
|
35
53
|
options,
|
|
36
54
|
defaultValue,
|
|
37
|
-
value = $bindable(
|
|
55
|
+
value = $bindable(),
|
|
38
56
|
name: nameProp,
|
|
39
57
|
disabled = false,
|
|
40
58
|
legend = "",
|
|
41
59
|
label = "",
|
|
60
|
+
chosenRing = true,
|
|
42
61
|
...restProps
|
|
43
62
|
}: Props = $props();
|
|
44
63
|
|
|
64
|
+
// No fallback on the bindable prop: with one, `bind:value={undefined}`
|
|
65
|
+
// (which the prop's own documentation names as "no selection") throws
|
|
66
|
+
// `props_invalid_value` and the page never hydrates. The default is
|
|
67
|
+
// applied once, here, so the server renders it too. A choice made in the
|
|
68
|
+
// server markup before hydration is taken in after this and wins.
|
|
69
|
+
(() => {
|
|
70
|
+
if (value === undefined && defaultValue !== undefined) value = defaultValue;
|
|
71
|
+
})();
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The radios always share a name, the caller's or one made here: without
|
|
75
|
+
* one they are not a group to the browser. With a name made here they are
|
|
76
|
+
* kept out of any form around them (`form` names a form that does not
|
|
77
|
+
* exist): it used to be submitted as `radiogroup-…`, a field nobody named.
|
|
78
|
+
*/
|
|
45
79
|
const fallbackName = generateId("radiogroup");
|
|
46
80
|
const groupName = $derived(nameProp || fallbackName);
|
|
47
81
|
const legendText = $derived(legend || label);
|
|
@@ -75,13 +109,24 @@
|
|
|
75
109
|
return option.value === fallbackValue ? 0 : -1;
|
|
76
110
|
}
|
|
77
111
|
|
|
78
|
-
function handleChange(event: Event) {
|
|
79
|
-
if (disabled) return;
|
|
80
|
-
const target = event.target as HTMLInputElement;
|
|
81
|
-
value = target.value;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
112
|
const elementsByValue = new SvelteMap<string, HTMLInputElement>();
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The radios are bound, so an option picked before the page hydrated is
|
|
116
|
+
* kept: Svelte's binding hands it over here instead of overwriting it
|
|
117
|
+
* (see util/hydration.ts). A form reset empties the selection, as it empties
|
|
118
|
+
* a bound native input.
|
|
119
|
+
*/
|
|
120
|
+
const chosen = groupBinding<string>(
|
|
121
|
+
() => value,
|
|
122
|
+
(next) => {
|
|
123
|
+
if (!disabled) value = next;
|
|
124
|
+
},
|
|
125
|
+
() => {
|
|
126
|
+
if (!disabled) value = resetValueOf<string>(elementsByValue.values());
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
|
|
85
130
|
function registerRadio(valueKey: string) {
|
|
86
131
|
return (node: HTMLInputElement) => {
|
|
87
132
|
elementsByValue.set(valueKey, node);
|
|
@@ -166,20 +211,28 @@
|
|
|
166
211
|
{/if}
|
|
167
212
|
|
|
168
213
|
<div class="space-y-3">
|
|
169
|
-
{#each options as option (option.value)}
|
|
214
|
+
{#each options as option, index (option.value)}
|
|
170
215
|
{@const optionDisabled = isOptionDisabled(option)}
|
|
171
|
-
{@const
|
|
172
|
-
|
|
173
|
-
|
|
216
|
+
{@const descriptionId = option.description ? `${fallbackName}-${index}-description` : undefined}
|
|
217
|
+
<!-- A one-line row is centred in its height; with a description the radio stays at the top, by the label. -->
|
|
218
|
+
<label
|
|
219
|
+
class={cn(
|
|
220
|
+
optionLabelWrapperClasses,
|
|
221
|
+
!option.description && "items-center",
|
|
222
|
+
!chosenRing && "selection-control-option-row-quiet",
|
|
223
|
+
)}
|
|
224
|
+
>
|
|
225
|
+
<span class={cn(radioControlShellClasses, !option.description && "mt-0")}>
|
|
174
226
|
<input
|
|
175
227
|
type="radio"
|
|
176
228
|
name={groupName}
|
|
229
|
+
form={nameProp ? undefined : groupName}
|
|
177
230
|
value={option.value}
|
|
178
|
-
|
|
231
|
+
bind:group={chosen.value}
|
|
179
232
|
disabled={optionDisabled}
|
|
180
233
|
tabindex={tabIndexFor(option)}
|
|
181
|
-
|
|
182
|
-
|
|
234
|
+
aria-describedby={descriptionId}
|
|
235
|
+
class={SELECTION_CONTROL_INPUT}
|
|
183
236
|
{@attach registerRadio(option.value)}
|
|
184
237
|
/>
|
|
185
238
|
<span class={radioRingOverlayClasses}></span>
|
|
@@ -190,7 +243,8 @@
|
|
|
190
243
|
{option.label}
|
|
191
244
|
</span>
|
|
192
245
|
{#if option.description}
|
|
193
|
-
|
|
246
|
+
<!-- aria-hidden keeps it out of the name; aria-describedby still reads it. -->
|
|
247
|
+
<span id={descriptionId} class="text-sm text-description" aria-hidden="true">
|
|
194
248
|
{option.description}
|
|
195
249
|
</span>
|
|
196
250
|
{/if}
|
|
@@ -1,22 +1,38 @@
|
|
|
1
|
+
import type { HTMLFieldsetAttributes } from "svelte/elements";
|
|
1
2
|
export type RadioGroupOption = {
|
|
2
3
|
value: string;
|
|
3
4
|
label: string;
|
|
4
5
|
disabled?: boolean;
|
|
5
6
|
description?: string;
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
+
/** Other attributes (`id`, `data-*`, `aria-*`, ...) land on the `<fieldset>`. */
|
|
9
|
+
type Props = Omit<HTMLFieldsetAttributes, "class" | "name" | "disabled"> & {
|
|
8
10
|
/** Extra classes for the host element. */
|
|
9
11
|
class?: string;
|
|
10
12
|
options: RadioGroupOption[];
|
|
11
13
|
/** Selected value; `bind:value`. Use `undefined` for no selection. */
|
|
12
14
|
value?: string | undefined;
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The selection to start with when `value` is `undefined` as the
|
|
17
|
+
* group is created. Applied once: it does not come back when the
|
|
18
|
+
* value is set to `undefined` later, and it never replaces a choice.
|
|
19
|
+
*/
|
|
14
20
|
defaultValue?: string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Name the value is submitted under in a form. Without a name the
|
|
23
|
+
* group is not part of the form around it: it submits nothing and a
|
|
24
|
+
* form reset leaves it alone.
|
|
25
|
+
*/
|
|
15
26
|
name?: string;
|
|
16
27
|
disabled?: boolean;
|
|
17
28
|
legend?: string;
|
|
18
29
|
label?: string;
|
|
19
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Draw a ring round the chosen row. Off, the filled radio alone shows
|
|
32
|
+
* the choice; keyboard focus keeps its ring either way.
|
|
33
|
+
*/
|
|
34
|
+
chosenRing?: boolean;
|
|
35
|
+
};
|
|
20
36
|
declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
|
|
21
37
|
type RadioGroup = ReturnType<typeof RadioGroup>;
|
|
22
38
|
export default RadioGroup;
|