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,633 @@
|
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
3
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
4
|
+
import { tick, untrack, type Snippet } from "svelte";
|
|
5
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
6
|
+
import ChevronDown from "@lucide/svelte/icons/chevron-down";
|
|
7
|
+
import ChevronUp from "@lucide/svelte/icons/chevron-up";
|
|
8
|
+
import GripVertical from "@lucide/svelte/icons/grip-vertical";
|
|
9
|
+
import { cn } from "../util/cn.js";
|
|
10
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
11
|
+
import {
|
|
12
|
+
SORTABLE_LIST_STRINGS,
|
|
13
|
+
dropIndex,
|
|
14
|
+
moveItem,
|
|
15
|
+
type SortableListReorderDetail,
|
|
16
|
+
type SortableListRow,
|
|
17
|
+
type SortableListStrings,
|
|
18
|
+
type SortableSlot,
|
|
19
|
+
} from "../util/sortable-list.js";
|
|
20
|
+
|
|
21
|
+
/** Other attributes (`id`, `data-*`, ...) land on the host `<div>`; the two ARIA names go to the `<ul>`. */
|
|
22
|
+
type Props = Omit<
|
|
23
|
+
HTMLAttributes<HTMLDivElement>,
|
|
24
|
+
"class" | "aria-label" | "aria-labelledby"
|
|
25
|
+
> & {
|
|
26
|
+
/** Extra classes for the host element. */
|
|
27
|
+
class?: string;
|
|
28
|
+
/** Extra classes for the `<ul>`, for example a different gap. */
|
|
29
|
+
listClass?: string;
|
|
30
|
+
/** The items, in order; supports `bind:items`. */
|
|
31
|
+
items: T[];
|
|
32
|
+
/** Stable identity of an item. Rows are keyed by it. */
|
|
33
|
+
getKey: (item: T) => string | number;
|
|
34
|
+
/** Name of an item, used in the button names and the announcements. */
|
|
35
|
+
getLabel: (item: T) => string;
|
|
36
|
+
/** Content of one row. The second argument carries the row state and its controls. */
|
|
37
|
+
item: Snippet<[T, SortableListRow]>;
|
|
38
|
+
/** Called once per move (each arrow-key step is a move), after `items` has the new order. */
|
|
39
|
+
onreorder?: (detail: SortableListReorderDetail<T>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* `auto` lays out handle, content and move buttons in a row. `manual`
|
|
42
|
+
* renders only your snippet, which places `row.handle` (and
|
|
43
|
+
* `row.moveButtons` if wanted) itself, for example in a card header.
|
|
44
|
+
* Put them beside a header toggle, never inside another `<button>`
|
|
45
|
+
* or link: a button inside a button is invalid and unreachable. A
|
|
46
|
+
* click on either does not bubble, so a clickable (non-button) header
|
|
47
|
+
* around them is not toggled by it.
|
|
48
|
+
*/
|
|
49
|
+
controls?: "auto" | "manual";
|
|
50
|
+
/**
|
|
51
|
+
* Show the move up / move down buttons when `controls="auto"`. A screen
|
|
52
|
+
* reader in browse mode does not pass arrow keys to the handle, so
|
|
53
|
+
* hiding them removes that way of reordering.
|
|
54
|
+
*/
|
|
55
|
+
showMoveButtons?: boolean;
|
|
56
|
+
/** Disables every handle and move button. */
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Disables one item's own controls. Other items can still move past it,
|
|
60
|
+
* so its index can change; it is not pinned in place.
|
|
61
|
+
*/
|
|
62
|
+
isItemDisabled?: (item: T) => boolean;
|
|
63
|
+
/** Overrides for the built-in strings. */
|
|
64
|
+
strings?: Partial<SortableListStrings>;
|
|
65
|
+
"aria-label"?: string;
|
|
66
|
+
"aria-labelledby"?: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
let {
|
|
70
|
+
class: className = "",
|
|
71
|
+
listClass = "",
|
|
72
|
+
items = $bindable(),
|
|
73
|
+
getKey,
|
|
74
|
+
getLabel,
|
|
75
|
+
item,
|
|
76
|
+
onreorder,
|
|
77
|
+
controls = "auto",
|
|
78
|
+
showMoveButtons = true,
|
|
79
|
+
disabled = false,
|
|
80
|
+
isItemDisabled,
|
|
81
|
+
strings,
|
|
82
|
+
"aria-label": ariaLabel,
|
|
83
|
+
"aria-labelledby": ariaLabelledby,
|
|
84
|
+
...restProps
|
|
85
|
+
}: Props = $props();
|
|
86
|
+
|
|
87
|
+
/** Pointer travel before a press on the handle becomes a drag. */
|
|
88
|
+
const DRAG_THRESHOLD = 4;
|
|
89
|
+
/** Distance from the scroll container's edge at which a drag starts scrolling it. */
|
|
90
|
+
const SCROLL_EDGE = 48;
|
|
91
|
+
const SCROLL_STEP = 12;
|
|
92
|
+
const SETTLE_MS = 160;
|
|
93
|
+
const NBSP = String.fromCharCode(160);
|
|
94
|
+
|
|
95
|
+
const descriptionId = generateId("sortable-list-help");
|
|
96
|
+
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
97
|
+
const provided = zabiStringsFor("sortableList");
|
|
98
|
+
const text = $derived(mergeStrings(SORTABLE_LIST_STRINGS, provided(), strings));
|
|
99
|
+
|
|
100
|
+
let listElement: HTMLUListElement | undefined = $state();
|
|
101
|
+
let announcement = $state("");
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A pointer drag in progress. The DOM order does not change until the
|
|
105
|
+
* drop: rows are only translated. That keeps pointer capture and focus on
|
|
106
|
+
* the handle (moving a node releases both) and makes Escape trivial,
|
|
107
|
+
* because `items` has not been touched yet.
|
|
108
|
+
*/
|
|
109
|
+
let drag = $state<{
|
|
110
|
+
key: string | number;
|
|
111
|
+
from: number;
|
|
112
|
+
to: number;
|
|
113
|
+
/** How far the dragged row is translated, in px. */
|
|
114
|
+
offset: number;
|
|
115
|
+
/** How far a row steps aside: the dragged row's height plus the gap. */
|
|
116
|
+
shift: number;
|
|
117
|
+
/** False until the pointer has travelled past the threshold. */
|
|
118
|
+
active: boolean;
|
|
119
|
+
} | null>(null);
|
|
120
|
+
|
|
121
|
+
/** Bookkeeping for the drag that nothing renders from. */
|
|
122
|
+
let session: {
|
|
123
|
+
pointerId: number;
|
|
124
|
+
handle: HTMLElement;
|
|
125
|
+
/** The dragged item's label, kept in case the item is gone by the time it is announced. */
|
|
126
|
+
label: string;
|
|
127
|
+
/** Key order at drag start. The measured slots only describe this order. */
|
|
128
|
+
keys: (string | number)[];
|
|
129
|
+
slots: SortableSlot[];
|
|
130
|
+
/** Pointer position relative to the list's top edge at drag start. */
|
|
131
|
+
grab: number;
|
|
132
|
+
clientY: number;
|
|
133
|
+
scroller: HTMLElement | null;
|
|
134
|
+
frame: number | undefined;
|
|
135
|
+
} | null = null;
|
|
136
|
+
|
|
137
|
+
function isDisabled(entry: T): boolean {
|
|
138
|
+
return disabled || !!isItemDisabled?.(entry);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function indexOf(entry: T): number {
|
|
142
|
+
const key = getKey(entry);
|
|
143
|
+
return items.findIndex((candidate) => getKey(candidate) === key);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function rowElements(): HTMLElement[] {
|
|
147
|
+
return listElement ? (Array.from(listElement.children) as HTMLElement[]) : [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function announce(message: string) {
|
|
151
|
+
// A live region stays silent when its text does not change, so moving
|
|
152
|
+
// an item there and back would announce only the first move.
|
|
153
|
+
announcement = message === announcement ? message + NBSP : message;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function prefersReducedMotion(): boolean {
|
|
157
|
+
return (
|
|
158
|
+
typeof window.matchMedia === "function" &&
|
|
159
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function rowTops(): Map<HTMLElement, number> {
|
|
164
|
+
return new Map(
|
|
165
|
+
rowElements().map((row) => [row, row.getBoundingClientRect().top]),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Slides each row from where it was painted to where the new order puts it. */
|
|
170
|
+
function settle(before: Map<HTMLElement, number>) {
|
|
171
|
+
if (prefersReducedMotion()) return;
|
|
172
|
+
for (const [row, top] of before) {
|
|
173
|
+
if (!row.isConnected || typeof row.animate !== "function") continue;
|
|
174
|
+
const delta = top - row.getBoundingClientRect().top;
|
|
175
|
+
if (Math.abs(delta) < 1) continue;
|
|
176
|
+
row.animate(
|
|
177
|
+
[
|
|
178
|
+
{ transform: `translateY(${delta}px)` },
|
|
179
|
+
{ transform: "translateY(0)" },
|
|
180
|
+
],
|
|
181
|
+
{ duration: SETTLE_MS, easing: "ease-out" },
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Reordering moves DOM nodes, and a moved node loses focus. */
|
|
187
|
+
function restoreFocus(target: HTMLElement | undefined) {
|
|
188
|
+
if (!target?.isConnected) return;
|
|
189
|
+
if (!(target as HTMLButtonElement).disabled) {
|
|
190
|
+
if (document.activeElement !== target) target.focus();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// A move button that has reached an end is now disabled; its
|
|
194
|
+
// neighbour keeps focus on the same row.
|
|
195
|
+
target.parentElement
|
|
196
|
+
?.querySelector<HTMLElement>("button:not([disabled])")
|
|
197
|
+
?.focus();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function move(from: number, to: number, focusTarget?: HTMLElement) {
|
|
201
|
+
const target = Math.max(0, Math.min(items.length - 1, to));
|
|
202
|
+
const entry = items[from];
|
|
203
|
+
if (entry === undefined || target === from) return;
|
|
204
|
+
|
|
205
|
+
const before = rowTops();
|
|
206
|
+
const next = moveItem(items, from, target);
|
|
207
|
+
items = next;
|
|
208
|
+
announce(
|
|
209
|
+
text.moved({
|
|
210
|
+
label: getLabel(entry),
|
|
211
|
+
position: target + 1,
|
|
212
|
+
total: next.length,
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
215
|
+
onreorder?.({ item: entry, from, to: target, items: next });
|
|
216
|
+
|
|
217
|
+
await tick();
|
|
218
|
+
settle(before);
|
|
219
|
+
restoreFocus(focusTarget);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function handleKeydown(event: KeyboardEvent, entry: T) {
|
|
223
|
+
if (drag || event.altKey || event.ctrlKey || event.metaKey) return;
|
|
224
|
+
const index = indexOf(entry);
|
|
225
|
+
let to: number;
|
|
226
|
+
if (event.key === "ArrowUp") to = index - 1;
|
|
227
|
+
else if (event.key === "ArrowDown") to = index + 1;
|
|
228
|
+
else if (event.key === "Home") to = 0;
|
|
229
|
+
else if (event.key === "End") to = items.length - 1;
|
|
230
|
+
else return;
|
|
231
|
+
|
|
232
|
+
// Also keeps the arrow keys from scrolling the page under the handle.
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
if (isDisabled(entry) || index === -1 || items.length < 2) return;
|
|
235
|
+
|
|
236
|
+
const last = items.length - 1;
|
|
237
|
+
if (Math.max(0, Math.min(last, to)) === index) {
|
|
238
|
+
// A key that does nothing and says nothing reads as a broken control.
|
|
239
|
+
const detail = {
|
|
240
|
+
label: getLabel(entry),
|
|
241
|
+
position: index + 1,
|
|
242
|
+
total: items.length,
|
|
243
|
+
};
|
|
244
|
+
announce(index === 0 ? text.atStart(detail) : text.atEnd(detail));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
void move(index, to, event.currentTarget as HTMLElement);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Neither control lets its click bubble. The handle has no click action,
|
|
252
|
+
* and a move button's action is the move; in a clickable card header
|
|
253
|
+
* (`controls="manual"`) a bubbling click, including the one Enter and
|
|
254
|
+
* Space produce, would also toggle the card. `onreorder` is the way to
|
|
255
|
+
* observe a move.
|
|
256
|
+
*/
|
|
257
|
+
function handleHandleClick(event: MouseEvent) {
|
|
258
|
+
event.stopPropagation();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function handleMoveClick(event: MouseEvent, entry: T, direction: -1 | 1) {
|
|
262
|
+
event.stopPropagation();
|
|
263
|
+
if (isDisabled(entry)) return;
|
|
264
|
+
const index = indexOf(entry);
|
|
265
|
+
void move(index, index + direction, event.currentTarget as HTMLElement);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function scrollParent(node: HTMLElement): HTMLElement | null {
|
|
269
|
+
for (let el = node.parentElement; el; el = el.parentElement) {
|
|
270
|
+
if (el === document.body || el === document.documentElement) break;
|
|
271
|
+
const overflowY = getComputedStyle(el).overflowY;
|
|
272
|
+
if (
|
|
273
|
+
(overflowY === "auto" || overflowY === "scroll") &&
|
|
274
|
+
el.scrollHeight > el.clientHeight
|
|
275
|
+
) {
|
|
276
|
+
return el;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function handlePointerDown(event: PointerEvent, entry: T) {
|
|
283
|
+
if (drag || !listElement || isDisabled(entry)) return;
|
|
284
|
+
// One item has nowhere to go; do not mark it as dragging.
|
|
285
|
+
if (items.length < 2) return;
|
|
286
|
+
if (event.pointerType === "mouse" && event.button !== 0) return;
|
|
287
|
+
|
|
288
|
+
const index = indexOf(entry);
|
|
289
|
+
const listTop = listElement.getBoundingClientRect().top;
|
|
290
|
+
const slots = rowElements().map((row) => {
|
|
291
|
+
const rect = row.getBoundingClientRect();
|
|
292
|
+
return { top: rect.top - listTop, height: rect.height };
|
|
293
|
+
});
|
|
294
|
+
if (index === -1 || !slots[index]) return;
|
|
295
|
+
const gap =
|
|
296
|
+
slots.length > 1
|
|
297
|
+
? Math.max(0, slots[1].top - slots[0].top - slots[0].height)
|
|
298
|
+
: 0;
|
|
299
|
+
|
|
300
|
+
const handle = event.currentTarget as HTMLElement;
|
|
301
|
+
try {
|
|
302
|
+
// Keeps the grabbing cursor and stops hover styles firing on
|
|
303
|
+
// whatever the pointer passes over.
|
|
304
|
+
handle.setPointerCapture(event.pointerId);
|
|
305
|
+
} catch {
|
|
306
|
+
/* not supported (older WebViews, jsdom); the window listeners still work */
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
session = {
|
|
310
|
+
pointerId: event.pointerId,
|
|
311
|
+
handle,
|
|
312
|
+
label: getLabel(entry),
|
|
313
|
+
keys: items.map(getKey),
|
|
314
|
+
slots,
|
|
315
|
+
grab: event.clientY - listTop,
|
|
316
|
+
clientY: event.clientY,
|
|
317
|
+
scroller: scrollParent(listElement),
|
|
318
|
+
frame: undefined,
|
|
319
|
+
};
|
|
320
|
+
drag = {
|
|
321
|
+
key: getKey(entry),
|
|
322
|
+
from: index,
|
|
323
|
+
to: index,
|
|
324
|
+
offset: 0,
|
|
325
|
+
shift: slots[index].height + gap,
|
|
326
|
+
active: false,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
330
|
+
window.addEventListener("pointerup", handlePointerUp);
|
|
331
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
332
|
+
window.addEventListener("keydown", handleDragKeydown, true);
|
|
333
|
+
window.addEventListener("scroll", updateDrag, true);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function stopSession() {
|
|
337
|
+
if (!session) return;
|
|
338
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
339
|
+
window.removeEventListener("pointerup", handlePointerUp);
|
|
340
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
341
|
+
window.removeEventListener("keydown", handleDragKeydown, true);
|
|
342
|
+
window.removeEventListener("scroll", updateDrag, true);
|
|
343
|
+
if (session.frame !== undefined) cancelAnimationFrame(session.frame);
|
|
344
|
+
try {
|
|
345
|
+
session.handle.releasePointerCapture(session.pointerId);
|
|
346
|
+
} catch {
|
|
347
|
+
/* already released */
|
|
348
|
+
}
|
|
349
|
+
session = null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Recomputes the drag from the last pointer position; also runs on scroll. */
|
|
353
|
+
function updateDrag() {
|
|
354
|
+
if (!drag || !session || !listElement) return;
|
|
355
|
+
if (!orderUnchanged()) {
|
|
356
|
+
void cancelDrag();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const { slots } = session;
|
|
361
|
+
const slot = slots[drag.from];
|
|
362
|
+
const last = slots[slots.length - 1];
|
|
363
|
+
const travelled =
|
|
364
|
+
session.clientY -
|
|
365
|
+
listElement.getBoundingClientRect().top -
|
|
366
|
+
session.grab;
|
|
367
|
+
if (!drag.active && Math.abs(travelled) < DRAG_THRESHOLD) return;
|
|
368
|
+
|
|
369
|
+
// The row cannot be dragged out of the list.
|
|
370
|
+
const offset = Math.max(
|
|
371
|
+
slots[0].top - slot.top,
|
|
372
|
+
Math.min(last.top + last.height - slot.top - slot.height, travelled),
|
|
373
|
+
);
|
|
374
|
+
drag.active = true;
|
|
375
|
+
drag.offset = offset;
|
|
376
|
+
drag.to = dropIndex(slots, drag.from, offset);
|
|
377
|
+
|
|
378
|
+
if (session.frame === undefined) {
|
|
379
|
+
session.frame = requestAnimationFrame(autoScroll);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Scrolls the nearest scroll container while the pointer rests near its edge. */
|
|
384
|
+
function autoScroll() {
|
|
385
|
+
if (!session || !drag?.active) return;
|
|
386
|
+
const { scroller, clientY } = session;
|
|
387
|
+
const top = scroller ? scroller.getBoundingClientRect().top : 0;
|
|
388
|
+
const bottom = scroller
|
|
389
|
+
? scroller.getBoundingClientRect().bottom
|
|
390
|
+
: window.innerHeight;
|
|
391
|
+
const step =
|
|
392
|
+
clientY < top + SCROLL_EDGE
|
|
393
|
+
? -SCROLL_STEP
|
|
394
|
+
: clientY > bottom - SCROLL_EDGE
|
|
395
|
+
? SCROLL_STEP
|
|
396
|
+
: 0;
|
|
397
|
+
if (step !== 0) {
|
|
398
|
+
(scroller ?? window).scrollBy(0, step);
|
|
399
|
+
updateDrag();
|
|
400
|
+
}
|
|
401
|
+
session.frame = requestAnimationFrame(autoScroll);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function handlePointerMove(event: PointerEvent) {
|
|
405
|
+
if (!session || event.pointerId !== session.pointerId) return;
|
|
406
|
+
session.clientY = event.clientY;
|
|
407
|
+
updateDrag();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function handlePointerUp(event: PointerEvent) {
|
|
411
|
+
if (!session || !drag || event.pointerId !== session.pointerId) return;
|
|
412
|
+
if (!orderUnchanged()) {
|
|
413
|
+
void cancelDrag();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const { from, to, active } = drag;
|
|
417
|
+
const { handle } = session;
|
|
418
|
+
const before = active ? rowTops() : null;
|
|
419
|
+
stopSession();
|
|
420
|
+
drag = null;
|
|
421
|
+
if (!before) return;
|
|
422
|
+
|
|
423
|
+
if (to !== from) {
|
|
424
|
+
void move(from, to, handle);
|
|
425
|
+
} else {
|
|
426
|
+
void tick().then(() => settle(before));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function handlePointerCancel(event: PointerEvent) {
|
|
431
|
+
if (!session || event.pointerId !== session.pointerId) return;
|
|
432
|
+
void cancelDrag();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function handleDragKeydown(event: KeyboardEvent) {
|
|
436
|
+
if (event.key !== "Escape" || !drag) return;
|
|
437
|
+
if (drag.active) {
|
|
438
|
+
// The drag owns this Escape; a surrounding Modal must not close on it.
|
|
439
|
+
event.preventDefault();
|
|
440
|
+
event.stopPropagation();
|
|
441
|
+
}
|
|
442
|
+
void cancelDrag();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** False once the parent has reordered, added or removed items since the drag started. */
|
|
446
|
+
function orderUnchanged(): boolean {
|
|
447
|
+
if (!session) return true;
|
|
448
|
+
const { keys } = session;
|
|
449
|
+
return (
|
|
450
|
+
keys.length === items.length &&
|
|
451
|
+
items.every((entry, index) => getKey(entry) === keys[index])
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Abandons the drag. `items` was never changed by it, so the order is
|
|
457
|
+
* whatever the parent last set. The dragged item is looked up by key: the
|
|
458
|
+
* parent may have moved or removed it while the pointer was down.
|
|
459
|
+
*/
|
|
460
|
+
async function cancelDrag() {
|
|
461
|
+
if (!drag || !session) return;
|
|
462
|
+
const { key, active } = drag;
|
|
463
|
+
const { label } = session;
|
|
464
|
+
const before = active ? rowTops() : null;
|
|
465
|
+
stopSession();
|
|
466
|
+
drag = null;
|
|
467
|
+
if (!before) return;
|
|
468
|
+
|
|
469
|
+
const index = items.findIndex((entry) => getKey(entry) === key);
|
|
470
|
+
if (index !== -1) {
|
|
471
|
+
announce(
|
|
472
|
+
text.cancelled({
|
|
473
|
+
label,
|
|
474
|
+
position: index + 1,
|
|
475
|
+
total: items.length,
|
|
476
|
+
}),
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
await tick();
|
|
480
|
+
settle(before);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// A parent that replaces `items` mid-drag (a poll, a refetch) invalidates
|
|
484
|
+
// the measured slots and the indexes; releasing would move the wrong item.
|
|
485
|
+
$effect(() => {
|
|
486
|
+
const keys = items.map(getKey);
|
|
487
|
+
untrack(() => {
|
|
488
|
+
if (!session) return;
|
|
489
|
+
const stale =
|
|
490
|
+
keys.length !== session.keys.length ||
|
|
491
|
+
keys.some((key, index) => key !== session?.keys[index]);
|
|
492
|
+
if (stale) void cancelDrag();
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
function rowTransform(index: number): string | undefined {
|
|
497
|
+
if (!drag?.active) return undefined;
|
|
498
|
+
if (index === drag.from) return `translate3d(0, ${drag.offset}px, 0)`;
|
|
499
|
+
if (index > drag.from && index <= drag.to) {
|
|
500
|
+
return `translate3d(0, ${-drag.shift}px, 0)`;
|
|
501
|
+
}
|
|
502
|
+
if (index < drag.from && index >= drag.to) {
|
|
503
|
+
return `translate3d(0, ${drag.shift}px, 0)`;
|
|
504
|
+
}
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Unmounting mid-drag must not leave listeners on `window`.
|
|
509
|
+
$effect(() => stopSession);
|
|
510
|
+
|
|
511
|
+
const controlClasses =
|
|
512
|
+
"focus-ring focus-ring--muted flex size-8 pointer-coarse:min-h-11 pointer-coarse:min-w-11 shrink-0 items-center justify-center rounded-control text-description transition-colors duration-150 hover:bg-surface-hover hover:text-body active:bg-surface-active motion-reduce:transition-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-description disabled:active:bg-transparent";
|
|
513
|
+
</script>
|
|
514
|
+
|
|
515
|
+
<div
|
|
516
|
+
class={cn("relative", className)}
|
|
517
|
+
data-disabled={disabled ? "" : undefined}
|
|
518
|
+
{...restProps}
|
|
519
|
+
>
|
|
520
|
+
<!-- Tailwind's reset removes the markers, and Safari then drops the list
|
|
521
|
+
role from a `<ul>`; the explicit role keeps "list, 5 items" announced. -->
|
|
522
|
+
<!-- svelte-ignore a11y_no_redundant_roles -->
|
|
523
|
+
<ul
|
|
524
|
+
bind:this={listElement}
|
|
525
|
+
role="list"
|
|
526
|
+
class={cn(
|
|
527
|
+
"m-0 flex list-none flex-col gap-2 p-0",
|
|
528
|
+
drag?.active && "select-none",
|
|
529
|
+
listClass,
|
|
530
|
+
)}
|
|
531
|
+
aria-label={ariaLabel}
|
|
532
|
+
aria-labelledby={ariaLabelledby}
|
|
533
|
+
>
|
|
534
|
+
{#each items as entry, index (getKey(entry))}
|
|
535
|
+
{@const label = getLabel(entry)}
|
|
536
|
+
{@const rowDisabled = isDisabled(entry)}
|
|
537
|
+
{@const dragging = !!drag?.active && drag.key === getKey(entry)}
|
|
538
|
+
|
|
539
|
+
{#snippet handle()}
|
|
540
|
+
<!-- `touch-none`: a touch that starts on the handle drags the
|
|
541
|
+
row instead of scrolling the page. -->
|
|
542
|
+
<button
|
|
543
|
+
type="button"
|
|
544
|
+
class={cn(
|
|
545
|
+
controlClasses,
|
|
546
|
+
"touch-none select-none",
|
|
547
|
+
dragging ? "cursor-grabbing" : "cursor-grab",
|
|
548
|
+
)}
|
|
549
|
+
aria-label={text.handleLabel(label)}
|
|
550
|
+
aria-describedby={descriptionId}
|
|
551
|
+
disabled={rowDisabled}
|
|
552
|
+
data-sheet-no-drag
|
|
553
|
+
data-sortable-handle
|
|
554
|
+
onpointerdown={(event) => handlePointerDown(event, entry)}
|
|
555
|
+
onclick={handleHandleClick}
|
|
556
|
+
onkeydown={(event) => handleKeydown(event, entry)}
|
|
557
|
+
>
|
|
558
|
+
<GripVertical size={16} aria-hidden="true" />
|
|
559
|
+
</button>
|
|
560
|
+
{/snippet}
|
|
561
|
+
|
|
562
|
+
{#snippet moveButtons()}
|
|
563
|
+
<!-- 8px between two 44px targets on a touch screen; side by side as they were with a mouse. -->
|
|
564
|
+
<span class="flex shrink-0 items-center pointer-coarse:gap-2">
|
|
565
|
+
<button
|
|
566
|
+
type="button"
|
|
567
|
+
class={cn(controlClasses, "cursor-pointer")}
|
|
568
|
+
aria-label={text.moveUp(label)}
|
|
569
|
+
disabled={rowDisabled || index === 0}
|
|
570
|
+
data-sortable-move="up"
|
|
571
|
+
onclick={(event) => handleMoveClick(event, entry, -1)}
|
|
572
|
+
>
|
|
573
|
+
<ChevronUp size={16} aria-hidden="true" />
|
|
574
|
+
</button>
|
|
575
|
+
<button
|
|
576
|
+
type="button"
|
|
577
|
+
class={cn(controlClasses, "cursor-pointer")}
|
|
578
|
+
aria-label={text.moveDown(label)}
|
|
579
|
+
disabled={rowDisabled || index === items.length - 1}
|
|
580
|
+
data-sortable-move="down"
|
|
581
|
+
onclick={(event) => handleMoveClick(event, entry, 1)}
|
|
582
|
+
>
|
|
583
|
+
<ChevronDown size={16} aria-hidden="true" />
|
|
584
|
+
</button>
|
|
585
|
+
</span>
|
|
586
|
+
{/snippet}
|
|
587
|
+
|
|
588
|
+
<li
|
|
589
|
+
class={cn(
|
|
590
|
+
controls === "auto" && "flex items-start gap-2",
|
|
591
|
+
drag?.active &&
|
|
592
|
+
!dragging &&
|
|
593
|
+
"transition-transform duration-150 ease-out motion-reduce:transition-none",
|
|
594
|
+
dragging && "relative z-10 rounded-container shadow-lg",
|
|
595
|
+
)}
|
|
596
|
+
style:transform={rowTransform(index)}
|
|
597
|
+
data-dragging={dragging ? "" : undefined}
|
|
598
|
+
>
|
|
599
|
+
{#if controls === "manual"}
|
|
600
|
+
{@render item(entry, {
|
|
601
|
+
index,
|
|
602
|
+
dragging,
|
|
603
|
+
disabled: rowDisabled,
|
|
604
|
+
handle,
|
|
605
|
+
moveButtons,
|
|
606
|
+
})}
|
|
607
|
+
{:else}
|
|
608
|
+
{@render handle()}
|
|
609
|
+
<!-- The controls are 44px on a coarse pointer and the row is top-aligned: 6px puts a 32px first line back on their centre. -->
|
|
610
|
+
<div class="min-w-0 flex-1 pointer-coarse:mt-1.5">
|
|
611
|
+
{@render item(entry, {
|
|
612
|
+
index,
|
|
613
|
+
dragging,
|
|
614
|
+
disabled: rowDisabled,
|
|
615
|
+
handle,
|
|
616
|
+
moveButtons,
|
|
617
|
+
})}
|
|
618
|
+
</div>
|
|
619
|
+
{#if showMoveButtons}
|
|
620
|
+
{@render moveButtons()}
|
|
621
|
+
{/if}
|
|
622
|
+
{/if}
|
|
623
|
+
</li>
|
|
624
|
+
{/each}
|
|
625
|
+
</ul>
|
|
626
|
+
|
|
627
|
+
<!-- `hidden` keeps it out of the reading order (it would be read as loose
|
|
628
|
+
text after every list); `aria-describedby` still resolves hidden content. -->
|
|
629
|
+
<span id={descriptionId} hidden>{text.handleDescription}</span>
|
|
630
|
+
<div class="sr-only" role="status" aria-live="polite" aria-atomic="true">
|
|
631
|
+
{announcement}
|
|
632
|
+
</div>
|
|
633
|
+
</div>
|