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,131 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { type ScrollEdgeMode } from "../util/scroll-edge.js";
|
|
4
|
+
/**
|
|
5
|
+
* The bar at the top of a phone screen: where you are, the way back, and
|
|
6
|
+
* at most two things to do here.
|
|
7
|
+
*
|
|
8
|
+
* It is one row, 56px tall, whenever the title has 72px or more beside
|
|
9
|
+
* the controls, at every text size. The bar is chrome: when the reader
|
|
10
|
+
* enlarges text, the room belongs to the page under it. So the controls
|
|
11
|
+
* are sized in px (48px targets), the title grows with the text only up
|
|
12
|
+
* to 1.3 times its size, and a title too long for the room it has is cut
|
|
13
|
+
* with an ellipsis (after two lines with `titleLines={2}`). The heading
|
|
14
|
+
* keeps its full text for a screen reader; keep the visible start of it
|
|
15
|
+
* enough to name the screen.
|
|
16
|
+
*
|
|
17
|
+
* The title never has less than 72px. Where the back control, `leading`
|
|
18
|
+
* and the actions leave it less (a wide chip in `leading`, a very narrow
|
|
19
|
+
* bar), it takes a second row of its own, as wide as the bar, and they
|
|
20
|
+
* stay together on the first.
|
|
21
|
+
*
|
|
22
|
+
* The bar is flush with the page and page-coloured at rest. Once content
|
|
23
|
+
* has scrolled under it, it turns to frosted glass with a hairline below
|
|
24
|
+
* it. Inside an `AppShell` the shell says when; on a page of its own the
|
|
25
|
+
* bar watches the window or its nearest scrolling ancestor.
|
|
26
|
+
*
|
|
27
|
+
* With `largeTitle` the title is also drawn large on a second row under
|
|
28
|
+
* the bar. That row scrolls away with the content (nothing is animated),
|
|
29
|
+
* the 56px row stays, and its own small title fades in once the large
|
|
30
|
+
* one has gone under it. The heading is the large one.
|
|
31
|
+
*/
|
|
32
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "title"> & {
|
|
33
|
+
/** The name of the screen, as a heading. */
|
|
34
|
+
title?: string;
|
|
35
|
+
/** Heading level of the title. 1 when the bar names the page. */
|
|
36
|
+
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
37
|
+
/** Shows the back control as a link to this address. */
|
|
38
|
+
backHref?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Called when the back control is activated. On its own it makes the
|
|
41
|
+
* control a button; with `backHref` it runs before the link is
|
|
42
|
+
* followed, and `event.preventDefault()` stops that.
|
|
43
|
+
*/
|
|
44
|
+
onback?: (event: MouseEvent) => void;
|
|
45
|
+
/** Accessible name of the back control. */
|
|
46
|
+
backLabel?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Hides the bar while the page scrolls down and brings it back as
|
|
49
|
+
* soon as it scrolls up. It follows the nearest scrolling ancestor
|
|
50
|
+
* (the one in `AppShell`), or the window. The bar never hides while
|
|
51
|
+
* keyboard focus is inside it, and focus arriving from the keyboard
|
|
52
|
+
* brings it back. Focus left behind by a tap or a click does not hold
|
|
53
|
+
* it: it hides as usual, and the next key press brings it back.
|
|
54
|
+
*/
|
|
55
|
+
collapseOnScroll?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* How many lines the title may take before it is cut with an
|
|
58
|
+
* ellipsis. The bar is taller only while the second line is in use.
|
|
59
|
+
*/
|
|
60
|
+
titleLines?: 1 | 2;
|
|
61
|
+
/**
|
|
62
|
+
* Before the title, after the back control: a logo, an avatar, a
|
|
63
|
+
* chip. Rendered as it is, a child of the bar's row. It keeps its
|
|
64
|
+
* width; when that leaves the title under 72px the title moves to a
|
|
65
|
+
* row of its own, and a `leading` wider than the row is held to the
|
|
66
|
+
* row's width.
|
|
67
|
+
* The bar's row is on a fixed pixel scale (`--spacing: 4px` on the
|
|
68
|
+
* row), so the bar's own controls do not grow with the text size.
|
|
69
|
+
* Size what you place here on the same scale (Tailwind numeric
|
|
70
|
+
* classes such as `size-9`, or `calc(var(--spacing) * 9)`), not in
|
|
71
|
+
* rem, or it grows at 200% text while the bar's controls do not.
|
|
72
|
+
*/
|
|
73
|
+
leading?: Snippet;
|
|
74
|
+
/**
|
|
75
|
+
* After the title. At most two icon buttons, at `size="lg"` so each is
|
|
76
|
+
* a 48px target; put anything more in a menu. Their size does not
|
|
77
|
+
* grow with the text size here: inside the bar the spacing scale is
|
|
78
|
+
* in px.
|
|
79
|
+
* The bar's row is on a fixed pixel scale (`--spacing: 4px` on the
|
|
80
|
+
* row), so the bar's own controls do not grow with the text size.
|
|
81
|
+
* Size what you place here on the same scale (Tailwind numeric
|
|
82
|
+
* classes such as `size-9`, or `calc(var(--spacing) * 9)`), not in
|
|
83
|
+
* rem, or it grows at 200% text while the bar's controls do not.
|
|
84
|
+
*/
|
|
85
|
+
actions?: Snippet;
|
|
86
|
+
/**
|
|
87
|
+
* When the bar shows the glass and the hairline. `auto` follows the
|
|
88
|
+
* scroll position: flush at the top, glass once content is under it.
|
|
89
|
+
* `always` is glass all the time, `never` flush all the time.
|
|
90
|
+
*/
|
|
91
|
+
scrollEdge?: ScrollEdgeMode;
|
|
92
|
+
/**
|
|
93
|
+
* Draws the title large (30px bold, two lines at most) on a second row
|
|
94
|
+
* under the bar. It scrolls away with the content; the bar keeps its
|
|
95
|
+
* 56px row, which shows the title small once the large one is gone.
|
|
96
|
+
* The heading is the large title; the small one is hidden from
|
|
97
|
+
* assistive technology. Needs `title`. `collapseOnScroll` is ignored
|
|
98
|
+
* with it. Before the bar has been measured (the server, no scripts)
|
|
99
|
+
* the whole header simply sticks and the large title stays.
|
|
100
|
+
*/
|
|
101
|
+
largeTitle?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* The bar's fill. `default` is the page colour at rest. `transparent`
|
|
104
|
+
* has none at rest, for a canvas, an image or a colour block laid
|
|
105
|
+
* behind the bar, and takes the glass once content is under it.
|
|
106
|
+
* `brand` is the brand colour with on-brand text and controls, at rest
|
|
107
|
+
* and scrolled, with no glass and no hairline, so a brand block
|
|
108
|
+
* directly under the bar joins it. With `largeTitle` the large row is
|
|
109
|
+
* part of the brand block.
|
|
110
|
+
* `inherit` is for a bar inside a block of another colour: no fill of
|
|
111
|
+
* its own, no glass and no hairline at any scroll position, and the
|
|
112
|
+
* title, back control, actions, links, borders and focus rings take
|
|
113
|
+
* the colour the block gives the text. A sticky bar that has to cover
|
|
114
|
+
* scrolling content takes its fill from the app:
|
|
115
|
+
* `class="[--color-bar:var(--my-block-colour)]"`.
|
|
116
|
+
*/
|
|
117
|
+
tone?: "default" | "transparent" | "brand" | "inherit";
|
|
118
|
+
/**
|
|
119
|
+
* `sticky` (default) keeps the bar at the top of what scrolls.
|
|
120
|
+
* `static` leaves it where it is in the page, for a bar that sits
|
|
121
|
+
* inside a block of the content: it scrolls away with the block, is
|
|
122
|
+
* never glass, and `collapseOnScroll` and the condensing of
|
|
123
|
+
* `largeTitle` do nothing. `class="static"`, the 8.1 way to say this,
|
|
124
|
+
* is read as `position="static"`.
|
|
125
|
+
*/
|
|
126
|
+
position?: "sticky" | "static";
|
|
127
|
+
class?: string;
|
|
128
|
+
};
|
|
129
|
+
declare const AppBar: import("svelte").Component<Props, {}, "">;
|
|
130
|
+
type AppBar = ReturnType<typeof AppBar>;
|
|
131
|
+
export default AppBar;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
3
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
4
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
5
|
+
import Avatar from "../atoms/Avatar.svelte";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_AVATAR_GROUP_STRINGS,
|
|
8
|
+
splitGroup,
|
|
9
|
+
type AvatarGroupStrings,
|
|
10
|
+
type AvatarPerson,
|
|
11
|
+
type AvatarSize,
|
|
12
|
+
} from "../util/avatar.js";
|
|
13
|
+
import { cn } from "../util/cn.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A row of overlapping avatars: who is going, the members, who rated.
|
|
17
|
+
*
|
|
18
|
+
* It is a list, each avatar named by its person. Past `max` the rest
|
|
19
|
+
* stand behind a "+3" of the same size and shape. Not interactive.
|
|
20
|
+
*
|
|
21
|
+
* Between two avatars is a ring in the colour of the surface under the
|
|
22
|
+
* group. It cannot read that colour, so it is the raised surface (a
|
|
23
|
+
* card) unless the group is told otherwise:
|
|
24
|
+
*
|
|
25
|
+
* ```svelte
|
|
26
|
+
* <AvatarGroup {people} style="--zabi-avatar-ring: var(--color-surface-base)" />
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* Other attributes (`id`, `data-*`, `style`, ...) land on the list.
|
|
30
|
+
*/
|
|
31
|
+
type Props = Omit<HTMLAttributes<HTMLUListElement>, "class" | "aria-label"> & {
|
|
32
|
+
/** The people, in the order they are shown. */
|
|
33
|
+
people: AvatarPerson[];
|
|
34
|
+
/**
|
|
35
|
+
* How many are shown before the rest become "+N". Below 1 is 1. One
|
|
36
|
+
* person too many is shown and not counted: there is no "+1".
|
|
37
|
+
*/
|
|
38
|
+
max?: number;
|
|
39
|
+
/** Diameter of each avatar: 24, 32 and 48px. */
|
|
40
|
+
size?: "sm" | "md" | "lg";
|
|
41
|
+
/** Accessible name of the list ("Who's going"). */
|
|
42
|
+
label?: string;
|
|
43
|
+
/** Overrides for the built-in strings. */
|
|
44
|
+
strings?: Partial<AvatarGroupStrings>;
|
|
45
|
+
/** The language the initials are worked out in; passed to each Avatar. Without it, the page's. */
|
|
46
|
+
locale?: string;
|
|
47
|
+
/** Extra classes for the list. */
|
|
48
|
+
class?: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let {
|
|
52
|
+
people,
|
|
53
|
+
max = 4,
|
|
54
|
+
size = "md",
|
|
55
|
+
label,
|
|
56
|
+
strings,
|
|
57
|
+
locale,
|
|
58
|
+
class: className = "",
|
|
59
|
+
...restProps
|
|
60
|
+
}: Props = $props();
|
|
61
|
+
|
|
62
|
+
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
63
|
+
const provided = zabiStringsFor("avatarGroup");
|
|
64
|
+
const text = $derived(mergeStrings(DEFAULT_AVATAR_GROUP_STRINGS, provided(), strings));
|
|
65
|
+
const split = $derived(splitGroup(people.length, max));
|
|
66
|
+
const shown = $derived(people.slice(0, split.shown));
|
|
67
|
+
const hiddenNames = $derived(people.slice(split.shown).map((person) => person.name));
|
|
68
|
+
|
|
69
|
+
// A quarter of the circle lies under the next one.
|
|
70
|
+
const OVERLAP: Record<AvatarSize, string> = {
|
|
71
|
+
sm: "-ms-[6px]",
|
|
72
|
+
md: "-ms-[8px]",
|
|
73
|
+
lg: "-ms-[12px]",
|
|
74
|
+
};
|
|
75
|
+
const CHIP: Record<AvatarSize, string> = {
|
|
76
|
+
sm: "size-[24px] text-[10px]",
|
|
77
|
+
md: "size-[32px] text-[12px]",
|
|
78
|
+
lg: "size-[48px] text-[16px]",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** The ring that parts one circle from the next. A shadow, so it takes no room; forced colours draw the avatars' own borders instead. */
|
|
82
|
+
const ring = "rounded-pill shadow-[0_0_0_2px_var(--zabi-avatar-ring,var(--color-surface-raised))]";
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<!-- `role`, because a list without bullets loses its role in Safari. -->
|
|
86
|
+
<ul
|
|
87
|
+
class={cn("m-0 flex list-none items-center p-0", className)}
|
|
88
|
+
role="list"
|
|
89
|
+
aria-label={label || undefined}
|
|
90
|
+
data-avatar-group
|
|
91
|
+
data-size={size}
|
|
92
|
+
{...restProps}
|
|
93
|
+
>
|
|
94
|
+
{#each shown as person, index (index)}
|
|
95
|
+
<li class={cn("flex", index > 0 && (OVERLAP[size] ?? OVERLAP.md))}>
|
|
96
|
+
<Avatar name={person.name} src={person.src} children={person.picture} {size} {locale} class={ring} />
|
|
97
|
+
</li>
|
|
98
|
+
{/each}
|
|
99
|
+
{#if split.hidden > 0}
|
|
100
|
+
<li class={cn("flex", OVERLAP[size] ?? OVERLAP.md)}>
|
|
101
|
+
<!-- The same circle as an avatar, in the neutral pair: it is a count, not a person. -->
|
|
102
|
+
<span
|
|
103
|
+
class={cn(
|
|
104
|
+
"inline-flex shrink-0 items-center justify-center border border-transparent bg-neutral-subtle leading-none font-semibold text-neutral-text tabular-nums select-none",
|
|
105
|
+
CHIP[size] ?? CHIP.md,
|
|
106
|
+
ring,
|
|
107
|
+
)}
|
|
108
|
+
role="img"
|
|
109
|
+
aria-label={text.more(split.hidden, hiddenNames)}
|
|
110
|
+
data-avatar-more
|
|
111
|
+
>
|
|
112
|
+
<span aria-hidden="true">+{split.hidden}</span>
|
|
113
|
+
</span>
|
|
114
|
+
</li>
|
|
115
|
+
{/if}
|
|
116
|
+
</ul>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
import { type AvatarGroupStrings, type AvatarPerson } from "../util/avatar.js";
|
|
3
|
+
/**
|
|
4
|
+
* A row of overlapping avatars: who is going, the members, who rated.
|
|
5
|
+
*
|
|
6
|
+
* It is a list, each avatar named by its person. Past `max` the rest
|
|
7
|
+
* stand behind a "+3" of the same size and shape. Not interactive.
|
|
8
|
+
*
|
|
9
|
+
* Between two avatars is a ring in the colour of the surface under the
|
|
10
|
+
* group. It cannot read that colour, so it is the raised surface (a
|
|
11
|
+
* card) unless the group is told otherwise:
|
|
12
|
+
*
|
|
13
|
+
* ```svelte
|
|
14
|
+
* <AvatarGroup {people} style="--zabi-avatar-ring: var(--color-surface-base)" />
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Other attributes (`id`, `data-*`, `style`, ...) land on the list.
|
|
18
|
+
*/
|
|
19
|
+
type Props = Omit<HTMLAttributes<HTMLUListElement>, "class" | "aria-label"> & {
|
|
20
|
+
/** The people, in the order they are shown. */
|
|
21
|
+
people: AvatarPerson[];
|
|
22
|
+
/**
|
|
23
|
+
* How many are shown before the rest become "+N". Below 1 is 1. One
|
|
24
|
+
* person too many is shown and not counted: there is no "+1".
|
|
25
|
+
*/
|
|
26
|
+
max?: number;
|
|
27
|
+
/** Diameter of each avatar: 24, 32 and 48px. */
|
|
28
|
+
size?: "sm" | "md" | "lg";
|
|
29
|
+
/** Accessible name of the list ("Who's going"). */
|
|
30
|
+
label?: string;
|
|
31
|
+
/** Overrides for the built-in strings. */
|
|
32
|
+
strings?: Partial<AvatarGroupStrings>;
|
|
33
|
+
/** The language the initials are worked out in; passed to each Avatar. Without it, the page's. */
|
|
34
|
+
locale?: string;
|
|
35
|
+
/** Extra classes for the list. */
|
|
36
|
+
class?: string;
|
|
37
|
+
};
|
|
38
|
+
declare const AvatarGroup: import("svelte").Component<Props, {}, "">;
|
|
39
|
+
type AvatarGroup = ReturnType<typeof AvatarGroup>;
|
|
40
|
+
export default AvatarGroup;
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import Table from "../atoms/Table.svelte";
|
|
4
|
+
import { cn } from "../util/cn.js";
|
|
5
|
+
import {
|
|
6
|
+
CHART_GLASS,
|
|
7
|
+
chartColor,
|
|
8
|
+
chartFormatter,
|
|
9
|
+
barPath,
|
|
10
|
+
chartScale,
|
|
11
|
+
chartTextScale,
|
|
12
|
+
labelEvery,
|
|
13
|
+
measureWidth,
|
|
14
|
+
widestText,
|
|
15
|
+
type ChartColor,
|
|
16
|
+
type ChartDatum,
|
|
17
|
+
type ChartFormat,
|
|
18
|
+
} from "../util/chart.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A bar chart of one set of values, drawn as plain SVG: upright bars from
|
|
22
|
+
* a zero line, a label under each and its value over it. A bar is drawn
|
|
23
|
+
* the way `Progress` draws its fill, and with a `yMax` each bar stands in
|
|
24
|
+
* a track up to it, as a `Progress` does: so many of so many.
|
|
25
|
+
*
|
|
26
|
+
* It is a `figure`: the `label` is its caption, the drawing is hidden
|
|
27
|
+
* from assistive technology, and the same numbers are in a real table
|
|
28
|
+
* (off screen unless `showTable`). The bars are one colour, since the
|
|
29
|
+
* labels tell them apart; a datum may carry its own.
|
|
30
|
+
*/
|
|
31
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
|
|
32
|
+
/** The bars, in order: a `label`, a `value`, and a `color` of its own if it needs one. */
|
|
33
|
+
data: ChartDatum[];
|
|
34
|
+
/** The colour of the bars: a theme colour by name or any CSS colour. Without it, the first series colour. */
|
|
35
|
+
color?: ChartColor;
|
|
36
|
+
/** The top of the value axis, and the ceiling each bar is measured against: with it every bar stands in a track that reaches it. Without it, the highest value rounded up to a tick, and no tracks. */
|
|
37
|
+
yMax?: number;
|
|
38
|
+
/** How a value is written: `number` (default), `percent` (72 is "72 %"), or a function. */
|
|
39
|
+
yFormat?: ChartFormat;
|
|
40
|
+
/** Height of the drawing in px. The width is the container's. */
|
|
41
|
+
height?: number;
|
|
42
|
+
/** The caption, and what the chart is called. */
|
|
43
|
+
label: string;
|
|
44
|
+
/** Keeps the caption for assistive technology and takes it off the screen. */
|
|
45
|
+
labelHidden?: boolean;
|
|
46
|
+
/** Writes each value over its bar. */
|
|
47
|
+
showValues?: boolean;
|
|
48
|
+
/** Shows the data table under the chart; otherwise it is there for assistive technology only. */
|
|
49
|
+
showTable?: boolean;
|
|
50
|
+
/** The heading of the table's first column. */
|
|
51
|
+
categoryTitle?: string;
|
|
52
|
+
/** The heading of the table's value column. */
|
|
53
|
+
valueTitle?: string;
|
|
54
|
+
/** The language of the built-in number formats. Without it, the runtime's. */
|
|
55
|
+
locale?: string;
|
|
56
|
+
/** Stands the chart on a panel of the thick material (frosted glass), for a chart that floats over a wash, a tint or a picture. On a plain card it shows nothing: leave it off there. */
|
|
57
|
+
glass?: boolean;
|
|
58
|
+
class?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let {
|
|
62
|
+
data,
|
|
63
|
+
color,
|
|
64
|
+
yMax,
|
|
65
|
+
yFormat = "number",
|
|
66
|
+
height = 240,
|
|
67
|
+
label,
|
|
68
|
+
labelHidden = false,
|
|
69
|
+
showValues = true,
|
|
70
|
+
showTable = false,
|
|
71
|
+
categoryTitle = "Category",
|
|
72
|
+
valueTitle = "Value",
|
|
73
|
+
locale,
|
|
74
|
+
glass = false,
|
|
75
|
+
class: className = "",
|
|
76
|
+
...restProps
|
|
77
|
+
}: Props = $props();
|
|
78
|
+
|
|
79
|
+
/** Before it is measured (on the server) the drawing is laid out this wide and scaled to fit. */
|
|
80
|
+
const FALLBACK_WIDTH = 600;
|
|
81
|
+
/** The widest a bar gets: a bar is a `Progress` stood on end, a rod and not a block. */
|
|
82
|
+
const WIDEST = 24;
|
|
83
|
+
|
|
84
|
+
const uid = $props.id();
|
|
85
|
+
const clipId = `${uid}-bars`;
|
|
86
|
+
const veilId = `${uid}-veil`;
|
|
87
|
+
const shineId = `${uid}-shine`;
|
|
88
|
+
const streakId = `${uid}-streak`;
|
|
89
|
+
const softId = `${uid}-soft`;
|
|
90
|
+
|
|
91
|
+
let measured = $state(0);
|
|
92
|
+
let tickWidth = $state(0);
|
|
93
|
+
|
|
94
|
+
const width = $derived(measured > 0 ? measured : FALLBACK_WIDTH);
|
|
95
|
+
/** Chart text is set in rem; the room for it grows with the reader's text size. */
|
|
96
|
+
const rem = $derived(measured > 0 ? chartTextScale() : 1);
|
|
97
|
+
const plotHeight = $derived(Math.max(height, 80));
|
|
98
|
+
const top = $derived(showValues ? Math.round(24 * rem) : 8);
|
|
99
|
+
const bottom = $derived(Math.round(28 * rem));
|
|
100
|
+
const format = $derived(chartFormatter(yFormat, locale));
|
|
101
|
+
const isValue = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value);
|
|
102
|
+
const scale = $derived(chartScale(data.map((datum) => datum.value), { max: yMax, zero: true }));
|
|
103
|
+
const tickLabels = $derived(scale.ticks.map((tick) => format(tick)));
|
|
104
|
+
/** The value axis takes the width of its widest label as drawn; until that is known, an estimate. */
|
|
105
|
+
const left = $derived((tickWidth || Math.max(...tickLabels.map((text) => text.length), 1) * 7 * rem) + 12);
|
|
106
|
+
const innerWidth = $derived(Math.max(width - left, 10));
|
|
107
|
+
const innerHeight = $derived(Math.max(plotHeight - top - bottom, 10));
|
|
108
|
+
const band = $derived(innerWidth / Math.max(data.length, 1));
|
|
109
|
+
const barWidth = $derived(Math.max(1, Math.min(band * 0.5, WIDEST)));
|
|
110
|
+
/** A ceiling the app gave, and nothing below zero: each bar has a track up to it. */
|
|
111
|
+
const tracks = $derived(isValue(yMax) && scale.min >= 0 && scale.max > 0);
|
|
112
|
+
const every = $derived(labelEvery(data.map((datum) => datum.label), innerWidth, 7 * rem));
|
|
113
|
+
/** A value over every bar only while each has room for it. */
|
|
114
|
+
const valuesFit = $derived(showValues && band >= Math.max(...tickLabels.map((text) => text.length), 1) * 7 * rem);
|
|
115
|
+
/** Where the bars stand: zero, or the end of the axis nearest to it. */
|
|
116
|
+
const zero = $derived(Math.min(Math.max(0, scale.min), scale.max));
|
|
117
|
+
|
|
118
|
+
const y = (value: number) => top + innerHeight - ((value - scale.min) / (scale.max - scale.min)) * innerHeight;
|
|
119
|
+
|
|
120
|
+
const bars = $derived(
|
|
121
|
+
data.map((datum, index) => {
|
|
122
|
+
const value = isValue(datum.value) ? Math.min(Math.max(datum.value, scale.min), scale.max) : 0;
|
|
123
|
+
const from = y(zero);
|
|
124
|
+
const to = y(value);
|
|
125
|
+
return {
|
|
126
|
+
label: datum.label,
|
|
127
|
+
text: isValue(datum.value) ? format(datum.value) : "",
|
|
128
|
+
color: datum.color ? chartColor(datum.color, index) : chartColor(color, 0),
|
|
129
|
+
centre: left + band * (index + 0.5),
|
|
130
|
+
top: Math.min(from, to),
|
|
131
|
+
height: Math.abs(from - to),
|
|
132
|
+
path: barPath(left + band * (index + 0.5) - barWidth / 2, barWidth, from, to, barWidth / 2),
|
|
133
|
+
track: barPath(left + band * (index + 0.5) - barWidth / 2, barWidth, from, y(scale.max), barWidth / 2),
|
|
134
|
+
below: value < zero,
|
|
135
|
+
};
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<figure class={cn("zabi-chart relative m-0 flex flex-col gap-3", glass && CHART_GLASS, className)} {...restProps}>
|
|
141
|
+
<figcaption class={cn("text-label text-sm font-medium", labelHidden && "sr-only")}>{label}</figcaption>
|
|
142
|
+
|
|
143
|
+
<div class="w-full" use:measureWidth={(value) => (measured = value)}>
|
|
144
|
+
<svg class="plot block w-full" viewBox="0 0 {width} {plotHeight}" height={plotHeight} aria-hidden="true" focusable="false">
|
|
145
|
+
<g use:widestText={{ onwidth: (value) => (tickWidth = value), key: [tickLabels, measured] }}>
|
|
146
|
+
{#each scale.ticks as tick, index (tick)}
|
|
147
|
+
{#if tick !== zero}
|
|
148
|
+
<line class="grid" x1={left} x2={left + innerWidth} y1={y(tick)} y2={y(tick)} />
|
|
149
|
+
{/if}
|
|
150
|
+
<text class="tick" x={left - 12} y={y(tick)} text-anchor="end" dominant-baseline="central">
|
|
151
|
+
{tickLabels[index]}
|
|
152
|
+
</text>
|
|
153
|
+
{/each}
|
|
154
|
+
</g>
|
|
155
|
+
|
|
156
|
+
{#each bars as bar, index (index)}
|
|
157
|
+
{#if tracks}<path class="track" d={bar.track} />{/if}
|
|
158
|
+
<path class="bar" style:--_c={bar.color} d={bar.path}>
|
|
159
|
+
<title>{bar.label}: {bar.text}</title>
|
|
160
|
+
</path>
|
|
161
|
+
{#if valuesFit && bar.text}
|
|
162
|
+
<text
|
|
163
|
+
class="value"
|
|
164
|
+
x={bar.centre}
|
|
165
|
+
y={bar.below ? bar.top + bar.height + 16 * rem : (tracks ? top : bar.top) - 8}
|
|
166
|
+
text-anchor="middle"
|
|
167
|
+
>
|
|
168
|
+
{bar.text}
|
|
169
|
+
</text>
|
|
170
|
+
{/if}
|
|
171
|
+
{#if index % every === 0}
|
|
172
|
+
<text class="label" x={bar.centre} y={plotHeight - bottom + 20 * rem} text-anchor="middle">{bar.label}</text>
|
|
173
|
+
{/if}
|
|
174
|
+
{/each}
|
|
175
|
+
|
|
176
|
+
<!--
|
|
177
|
+
The glass, as on a PieChart: the bars keep their colour, opaque, and over them
|
|
178
|
+
lie the veil of a solid control, a hairline of light on the round end of each
|
|
179
|
+
bar and a soft sheen down its near side. Decoration only; not drawn where the materials are opaque.
|
|
180
|
+
-->
|
|
181
|
+
<defs>
|
|
182
|
+
<clipPath id={clipId}>
|
|
183
|
+
{#each bars as bar, index (index)}<path d={bar.path} />{/each}
|
|
184
|
+
</clipPath>
|
|
185
|
+
<linearGradient id={veilId} x1="0" y1="0" x2="0" y2="1">
|
|
186
|
+
<stop class="veil-top" offset="0" />
|
|
187
|
+
<stop class="veil-bottom" offset="1" />
|
|
188
|
+
</linearGradient>
|
|
189
|
+
<linearGradient id={shineId} x1="0" y1="0" x2="0" y2="1">
|
|
190
|
+
<stop class="light" offset="0" />
|
|
191
|
+
<stop class="light off" offset="0.5" />
|
|
192
|
+
</linearGradient>
|
|
193
|
+
<linearGradient id={streakId} gradientUnits="userSpaceOnUse" x1="0" y1={top} x2="0" y2={top + innerHeight}>
|
|
194
|
+
<stop class="light" offset="0" />
|
|
195
|
+
<stop class="light off" offset="1" />
|
|
196
|
+
</linearGradient>
|
|
197
|
+
<filter id={softId} filterUnits="userSpaceOnUse" x="0" y="0" {width} height={plotHeight}>
|
|
198
|
+
<feGaussianBlur stdDeviation="1.2" />
|
|
199
|
+
</filter>
|
|
200
|
+
</defs>
|
|
201
|
+
<g class="glass" clip-path="url(#{clipId})">
|
|
202
|
+
{#each bars as bar, index (index)}
|
|
203
|
+
{#if bar.height > 0}
|
|
204
|
+
<path class="veil" d={bar.path} fill="url(#{veilId})" />
|
|
205
|
+
<path class="rim" d={bar.path} stroke="url(#{shineId})" />
|
|
206
|
+
{#if barWidth >= 12 && bar.height > barWidth}
|
|
207
|
+
<line
|
|
208
|
+
class="sheen"
|
|
209
|
+
filter="url(#{softId})"
|
|
210
|
+
x1={bar.centre - barWidth / 2 + 4.5}
|
|
211
|
+
x2={bar.centre - barWidth / 2 + 4.5}
|
|
212
|
+
y1={bar.top + barWidth / 2}
|
|
213
|
+
y2={bar.top + bar.height - barWidth / 2}
|
|
214
|
+
stroke="url(#{streakId})"
|
|
215
|
+
/>
|
|
216
|
+
{/if}
|
|
217
|
+
{/if}
|
|
218
|
+
{/each}
|
|
219
|
+
</g>
|
|
220
|
+
|
|
221
|
+
<!-- The zero line is drawn last, so every bar stands on it. -->
|
|
222
|
+
<line class="grid axis" x1={left} x2={left + innerWidth} y1={y(zero)} y2={y(zero)} />
|
|
223
|
+
</svg>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- The library's own Table, so the numbers look like every other table in the app. -->
|
|
227
|
+
<Table class={showTable ? "" : "sr-only"}>
|
|
228
|
+
<thead class="border-border bg-surface-elevated border-b">
|
|
229
|
+
<tr>
|
|
230
|
+
<th class="text-headline px-4 py-3 text-start font-medium" scope="col">{categoryTitle}</th>
|
|
231
|
+
<th class="text-headline px-4 py-3 text-end font-medium" scope="col">{valueTitle}</th>
|
|
232
|
+
</tr>
|
|
233
|
+
</thead>
|
|
234
|
+
<tbody class="divide-border divide-y">
|
|
235
|
+
{#each bars as bar, index (index)}
|
|
236
|
+
<tr>
|
|
237
|
+
<th class="text-body px-4 py-3 text-start font-normal" scope="row">{bar.label}</th>
|
|
238
|
+
<td class="text-body px-4 py-3 text-end tabular-nums">{bar.text}</td>
|
|
239
|
+
</tr>
|
|
240
|
+
{/each}
|
|
241
|
+
</tbody>
|
|
242
|
+
</Table>
|
|
243
|
+
</figure>
|
|
244
|
+
|
|
245
|
+
<style>
|
|
246
|
+
/*
|
|
247
|
+
* The series colours an app may set, on the chart or on anything around
|
|
248
|
+
* it; read with a fallback (in util/chart.ts) and never declared here:
|
|
249
|
+
* --zabi-chart-1 … --zabi-chart-6
|
|
250
|
+
*/
|
|
251
|
+
.plot {
|
|
252
|
+
overflow: visible;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* The library's hairline, as between the rows of a list; the line the bars
|
|
256
|
+
stand on is the edge a Progress track has. */
|
|
257
|
+
.grid {
|
|
258
|
+
stroke: var(--color-border);
|
|
259
|
+
stroke-width: 1;
|
|
260
|
+
shape-rendering: crispEdges;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.grid.axis {
|
|
264
|
+
stroke: var(--color-border-strong);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* The track of a Progress, stood on end: same fill, same edge. */
|
|
268
|
+
.track {
|
|
269
|
+
fill: var(--color-progress-track);
|
|
270
|
+
stroke: var(--color-progress-track-border);
|
|
271
|
+
stroke-width: 1;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.tick,
|
|
275
|
+
.label {
|
|
276
|
+
fill: var(--color-caption);
|
|
277
|
+
font-size: 0.75rem;
|
|
278
|
+
font-variant-numeric: tabular-nums;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* What a bar is called reads a step firmer than the scale beside it. */
|
|
282
|
+
.label {
|
|
283
|
+
fill: var(--color-description);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.value {
|
|
287
|
+
fill: var(--color-headline);
|
|
288
|
+
font-size: 0.75rem;
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
font-variant-numeric: tabular-nums;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.bar {
|
|
294
|
+
fill: var(--_c);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/*
|
|
298
|
+
* The bars are glass, drawn, in the same terms as the slices of a PieChart (which has the
|
|
299
|
+
* list): the veil of a solid control, one pixel of light on the round end fading down the
|
|
300
|
+
* sides, a soft sheen down the near side, and the contact shadow.
|
|
301
|
+
*/
|
|
302
|
+
.bar {
|
|
303
|
+
/* The contact shadow only: a wider one is a smudge under a rod this thin. */
|
|
304
|
+
filter: drop-shadow(0 1px 2px rgb(var(--shadow-color) / var(--shadow-opacity-contact)));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.glass {
|
|
308
|
+
pointer-events: none;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.veil-top {
|
|
312
|
+
stop-color: var(--color-control-gradient-accent-start);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.veil-bottom {
|
|
316
|
+
stop-color: var(--color-control-gradient-accent-end);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.light {
|
|
320
|
+
stop-color: color-mix(in srgb, var(--zabi-base-50) 30%, transparent);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.light.off {
|
|
324
|
+
stop-opacity: 0;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.rim {
|
|
328
|
+
fill: none;
|
|
329
|
+
stroke-width: 2px;
|
|
330
|
+
vector-effect: non-scaling-stroke;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.sheen {
|
|
334
|
+
stroke-width: 3;
|
|
335
|
+
stroke-linecap: round;
|
|
336
|
+
opacity: 0.45;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Flat where the library's materials are opaque: the same switches, the same reasons. */
|
|
340
|
+
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) {
|
|
341
|
+
.glass {
|
|
342
|
+
display: none;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.bar {
|
|
346
|
+
filter: none;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
:global([data-materials="opaque"]) .glass {
|
|
351
|
+
display: none;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
:global([data-materials="opaque"]) .bar {
|
|
355
|
+
filter: none;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* Forced colours: bars in the system's text colour; the labels tell them apart. */
|
|
359
|
+
@media (forced-colors: active) {
|
|
360
|
+
.bar,
|
|
361
|
+
.tick,
|
|
362
|
+
.label,
|
|
363
|
+
.value {
|
|
364
|
+
fill: CanvasText;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.grid,
|
|
368
|
+
.grid.axis {
|
|
369
|
+
stroke: GrayText;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.track {
|
|
373
|
+
fill: Canvas;
|
|
374
|
+
stroke: CanvasText;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.bar {
|
|
378
|
+
fill: Highlight;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
</style>
|