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
package/dist/atoms/Input.svelte
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { zabiCommonStrings } from "../util/zabi-strings.js";
|
|
3
|
+
import Eye from "@lucide/svelte/icons/eye";
|
|
4
|
+
import EyeOff from "@lucide/svelte/icons/eye-off";
|
|
5
|
+
import { tick, type Snippet } from "svelte";
|
|
6
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
7
|
import type { SemanticVariant, SizeVariant } from "../types/variants.js";
|
|
4
|
-
import { generateId } from "../util/ssr-safe.js";
|
|
5
8
|
import { cn } from "../util/cn.js";
|
|
9
|
+
import { fieldDescribedBy, fieldMessageState } from "../util/field.js";
|
|
10
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
11
|
+
import { TOUCH_HIT_AREA } from "../util/touch-target.js";
|
|
12
|
+
import FieldMessages from "./FieldMessages.svelte";
|
|
13
|
+
import IconButton from "./IconButton.svelte";
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Other attributes (`autocomplete`, `inputmode`, `maxlength`,
|
|
17
|
+
* `enterkeyhint`, `data-*`, `onchange`, ...) land on the `<input>`.
|
|
18
|
+
*/
|
|
19
|
+
type Props = Omit<
|
|
20
|
+
HTMLInputAttributes,
|
|
21
|
+
| "class"
|
|
22
|
+
| "size"
|
|
23
|
+
| "value"
|
|
24
|
+
| "type"
|
|
25
|
+
| "id"
|
|
26
|
+
| "name"
|
|
27
|
+
| "placeholder"
|
|
28
|
+
| "required"
|
|
29
|
+
| "disabled"
|
|
30
|
+
| "oninput"
|
|
31
|
+
| "onblur"
|
|
32
|
+
| "aria-label"
|
|
33
|
+
| "aria-describedby"
|
|
34
|
+
> & {
|
|
8
35
|
/** Omit to auto-generate; pair with FormField `id` when used inside FormField. */
|
|
9
36
|
id?: string;
|
|
10
37
|
value?: string;
|
|
@@ -20,15 +47,40 @@
|
|
|
20
47
|
loading?: boolean;
|
|
21
48
|
size?: SizeVariant;
|
|
22
49
|
variant?: SemanticVariant;
|
|
50
|
+
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
23
51
|
message?: string;
|
|
52
|
+
/** Help text under the field, read out with it. */
|
|
53
|
+
hint?: string;
|
|
54
|
+
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
55
|
+
error?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Drawn inside the field, before the text: an icon, a unit. Decoration
|
|
58
|
+
* lets a press through to the field; a button or link in it is pressable.
|
|
59
|
+
*/
|
|
60
|
+
leading?: Snippet;
|
|
61
|
+
/** Drawn inside the field, after the text and before the loading spinner's place. */
|
|
62
|
+
trailing?: Snippet;
|
|
63
|
+
/**
|
|
64
|
+
* With `type="password"`: a button at the end of the field that shows
|
|
65
|
+
* the password as text, and hides it again. On by default; `false`
|
|
66
|
+
* leaves it out. Ignored for other types.
|
|
67
|
+
*/
|
|
68
|
+
revealable?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Accessible name of that button. It stays the same in both states;
|
|
71
|
+
* the button's pressed state says whether the password is showing.
|
|
72
|
+
*/
|
|
73
|
+
revealLabel?: string;
|
|
24
74
|
oninput?: (event: Event) => void;
|
|
25
75
|
onblur?: (event: Event) => void;
|
|
26
76
|
"aria-label"?: string;
|
|
27
|
-
|
|
77
|
+
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
78
|
+
"aria-describedby"?: string | null;
|
|
79
|
+
};
|
|
28
80
|
|
|
29
81
|
let {
|
|
30
82
|
id: idProp,
|
|
31
|
-
value = $bindable
|
|
83
|
+
value = $bindable<Exclude<Props["value"], undefined>>(),
|
|
32
84
|
type = "text",
|
|
33
85
|
name = "",
|
|
34
86
|
class: className = "",
|
|
@@ -41,43 +93,161 @@
|
|
|
41
93
|
size = "md",
|
|
42
94
|
variant = "default",
|
|
43
95
|
message = "",
|
|
96
|
+
hint = "",
|
|
97
|
+
error = "",
|
|
98
|
+
leading,
|
|
99
|
+
trailing,
|
|
100
|
+
revealable = true,
|
|
101
|
+
revealLabel: revealLabelGiven,
|
|
44
102
|
oninput,
|
|
45
103
|
onblur,
|
|
104
|
+
"aria-describedby": describedBy,
|
|
46
105
|
...restProps
|
|
47
106
|
}: Props = $props();
|
|
48
107
|
|
|
108
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
109
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
110
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
111
|
+
// here instead: at once, for the server and the first render, and again
|
|
112
|
+
// whenever a parent hands back `undefined`.
|
|
113
|
+
const applyDefaults = () => {
|
|
114
|
+
if (value === undefined) value = "";
|
|
115
|
+
};
|
|
116
|
+
applyDefaults();
|
|
117
|
+
$effect.pre(applyDefaults);
|
|
118
|
+
|
|
119
|
+
/** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
|
|
120
|
+
const common = zabiCommonStrings();
|
|
121
|
+
const revealLabel = $derived(revealLabelGiven ?? common().showPassword);
|
|
122
|
+
|
|
49
123
|
const fallbackId = generateId("input");
|
|
50
124
|
const inputId = $derived(idProp ?? fallbackId);
|
|
51
125
|
const isDisabled = $derived(disabled || loading);
|
|
52
126
|
|
|
127
|
+
const status = $derived(fieldMessageState({ variant, message, hint, error }));
|
|
128
|
+
|
|
129
|
+
/** Only a password can be revealed: on any other type the button would have nothing to do. */
|
|
130
|
+
const canReveal = $derived(revealable && type === "password");
|
|
131
|
+
let revealed = $state(false);
|
|
132
|
+
const shownType = $derived(canReveal && revealed ? "text" : type);
|
|
133
|
+
|
|
134
|
+
let input: HTMLInputElement | undefined = $state();
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Changing `type` can drop the caret in some browsers. When the field has
|
|
138
|
+
* focus (a mouse or a finger pressed the button: see `keepFocus`), the
|
|
139
|
+
* selection is put back where it was.
|
|
140
|
+
*/
|
|
141
|
+
async function restoreCaret() {
|
|
142
|
+
const field = input;
|
|
143
|
+
if (!field || document.activeElement !== field) return;
|
|
144
|
+
const { selectionStart, selectionEnd, selectionDirection } = field;
|
|
145
|
+
if (selectionStart === null || selectionEnd === null) return;
|
|
146
|
+
const restore = () => {
|
|
147
|
+
if (document.activeElement !== field) return;
|
|
148
|
+
try {
|
|
149
|
+
field.setSelectionRange(selectionStart, selectionEnd, selectionDirection ?? undefined);
|
|
150
|
+
} catch {
|
|
151
|
+
// A type without a text selection: nothing to put back.
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
await tick();
|
|
155
|
+
restore();
|
|
156
|
+
// Chromium moves the caret to the start once more after the type has
|
|
157
|
+
// changed, so it is put back again when the frame is done.
|
|
158
|
+
requestAnimationFrame(restore);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A press with a pointer does not take focus from the field: on a phone
|
|
163
|
+
* that would close the keyboard in the middle of typing a password. From
|
|
164
|
+
* the keyboard the button has focus and keeps it.
|
|
165
|
+
*/
|
|
166
|
+
function keepFocus(event: Event) {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
}
|
|
169
|
+
|
|
53
170
|
/**
|
|
54
171
|
* Same fixed height scale as Button, IconButton and Select (32 / 40 / 48),
|
|
55
172
|
* so a field and its submit button are the same height in a row. These
|
|
56
173
|
* used to be 38 / 46 / 50 while Button was 40 / 48 / 64.
|
|
174
|
+
*
|
|
175
|
+
* Below the `sm` breakpoint the text is 16px at every size: iOS Safari
|
|
176
|
+
* zooms the page when a focused field is smaller. Only the font grows;
|
|
177
|
+
* the box keeps its height.
|
|
57
178
|
*/
|
|
58
179
|
const sizeClass = $derived(() => {
|
|
59
180
|
if (size === "sm") {
|
|
60
|
-
return { box: "h-8 px-3", text: "text-sm", spinner: "size-3.5" };
|
|
181
|
+
return { box: "h-8 px-3 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", spinner: "size-3.5" };
|
|
61
182
|
} else if (size === "lg") {
|
|
62
183
|
return { box: "h-12 px-4", text: "text-base", spinner: "size-5" };
|
|
63
184
|
} else {
|
|
64
|
-
return { box: "h-10 px-3", text: "text-sm", spinner: "size-4" };
|
|
185
|
+
return { box: "h-10 px-3 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", spinner: "size-4" };
|
|
65
186
|
}
|
|
66
187
|
});
|
|
67
188
|
|
|
189
|
+
/**
|
|
190
|
+
* What stands inside the field at either end sits in a slot 4px in from
|
|
191
|
+
* the field's edge and 8px smaller than the field: 24, 32 and 40px. An
|
|
192
|
+
* icon is centred in it, which puts it where the text's own padding would;
|
|
193
|
+
* a button fills it.
|
|
194
|
+
*/
|
|
195
|
+
const SLOT_INSET = 4;
|
|
196
|
+
const SLOTS = {
|
|
197
|
+
sm: { px: 24, min: "min-w-[24px]", reveal: "xs", icon: 14 },
|
|
198
|
+
md: { px: 32, min: "min-w-[32px]", reveal: "sm", icon: 16 },
|
|
199
|
+
lg: { px: 40, min: "min-w-[40px]", reveal: "md", icon: 16 },
|
|
200
|
+
} as const;
|
|
201
|
+
const slot = $derived(SLOTS[size].px);
|
|
202
|
+
const slotClass = $derived(SLOTS[size].min);
|
|
203
|
+
/** The reveal button is one size down from the field, so it fits the slot: 24, 32 and 40px. */
|
|
204
|
+
const revealSize = $derived(SLOTS[size].reveal);
|
|
205
|
+
|
|
206
|
+
const hasLeading = $derived(!!leading);
|
|
207
|
+
/** Whether the end of the field holds more than the spinner. */
|
|
208
|
+
const hasTrailing = $derived(!!trailing || canReveal);
|
|
209
|
+
|
|
210
|
+
/** Measured once mounted; until then, and on the server, the slots are assumed to be their least size. */
|
|
211
|
+
let leadingWidth = $state(0);
|
|
212
|
+
let trailingWidth = $state(0);
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Reports the width of what stands at one end of the field, and again
|
|
216
|
+
* when it changes. Where there is no `ResizeObserver` (an old browser, a
|
|
217
|
+
* test without layout) the assumed size stays.
|
|
218
|
+
*/
|
|
219
|
+
function measured(report: (width: number) => void) {
|
|
220
|
+
return (element: HTMLElement) => {
|
|
221
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
222
|
+
const observer = new ResizeObserver(() => report(element.offsetWidth));
|
|
223
|
+
observer.observe(element);
|
|
224
|
+
return () => observer.disconnect();
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const trailingCount = $derived((trailing ? 1 : 0) + (canReveal ? 1 : 0) + (loading ? 1 : 0));
|
|
228
|
+
const leadingPad = $derived(
|
|
229
|
+
hasLeading ? `${(leadingWidth || SLOT_INSET + slot) + SLOT_INSET}px` : undefined,
|
|
230
|
+
);
|
|
231
|
+
const trailingPad = $derived(
|
|
232
|
+
hasTrailing
|
|
233
|
+
? `${(trailingWidth || SLOT_INSET + trailingCount * slot + (trailingCount - 1) * SLOT_INSET) + SLOT_INSET}px`
|
|
234
|
+
: undefined,
|
|
235
|
+
);
|
|
236
|
+
|
|
68
237
|
const variantClass = $derived(() => {
|
|
69
|
-
return variant === "success"
|
|
238
|
+
return status.variant === "success"
|
|
70
239
|
? "border-success focus-visible:border-success"
|
|
71
|
-
: variant === "warning"
|
|
240
|
+
: status.variant === "warning"
|
|
72
241
|
? "border-warning focus-visible:border-warning"
|
|
73
|
-
: variant === "error"
|
|
242
|
+
: status.variant === "error"
|
|
74
243
|
? "border-error focus-visible:border-error"
|
|
75
|
-
: "border-input-border focus-visible:border-input-border";
|
|
244
|
+
: "border-input-border enabled:hover:border-input-border-hover focus-visible:border-input-border";
|
|
76
245
|
});
|
|
77
246
|
|
|
78
247
|
const inputClasses = $derived(() => {
|
|
79
248
|
const sizeStyles = sizeClass();
|
|
80
|
-
|
|
249
|
+
// Room for the spinner alone. With more at the end, the measured width is used instead.
|
|
250
|
+
const spinnerPad = loading && !hasTrailing ? SPINNER_PAD[size] : "";
|
|
81
251
|
/**
|
|
82
252
|
* The field is a raised surface (`bg-input` = white in light, an inset
|
|
83
253
|
* step in dark). It used to be a grey fill on a grey page, which read
|
|
@@ -85,31 +255,28 @@
|
|
|
85
255
|
* field regardless of the layout around it.
|
|
86
256
|
*/
|
|
87
257
|
const baseClasses =
|
|
88
|
-
"focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-control transition-colors duration-150 placeholder:text-input-placeholder text-body focus:outline-none focus-visible:outline-none disabled:text-action-disabled-text disabled:cursor-not-allowed";
|
|
258
|
+
"focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-control transition-colors duration-150 placeholder:text-input-placeholder placeholder-shown:text-ellipsis text-body focus:outline-none focus-visible:outline-none disabled:text-action-disabled-text disabled:cursor-not-allowed";
|
|
259
|
+
|
|
260
|
+
// A search field that brings its own clear button in `trailing` would
|
|
261
|
+
// show two crosses: the browser's is taken away.
|
|
262
|
+
const nativeClear = trailing ? "[&::-webkit-search-cancel-button]:appearance-none" : "";
|
|
89
263
|
|
|
90
|
-
return cn(`${baseClasses} ${sizeStyles.box} ${
|
|
264
|
+
return cn(`${baseClasses} ${sizeStyles.box} ${spinnerPad} ${sizeStyles.text} ${variantClass()} ${nativeClear} ${className}`);
|
|
91
265
|
});
|
|
92
266
|
|
|
93
267
|
const labelClasses = $derived(
|
|
94
268
|
() => "block text-sm font-medium text-label mb-2",
|
|
95
269
|
);
|
|
96
270
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (variant === "warning") return `text-warning-text ${base}`;
|
|
104
|
-
return `text-description ${base}`;
|
|
105
|
-
});
|
|
271
|
+
/**
|
|
272
|
+
* Both ends are laid over the field, which stays one box with one focus
|
|
273
|
+
* ring. They let a press through to it, except on something in them that
|
|
274
|
+
* can itself be pressed.
|
|
275
|
+
*/
|
|
276
|
+
const SPINNER_PAD = { sm: "pe-9", md: "pe-10", lg: "pe-10" } as const;
|
|
106
277
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
if (variant === "success") return CheckCircle;
|
|
110
|
-
if (variant === "warning") return AlertTriangle;
|
|
111
|
-
return null;
|
|
112
|
-
});
|
|
278
|
+
const END_BASE =
|
|
279
|
+
"pointer-events-none absolute inset-y-0 flex items-center gap-[4px] text-description [&_:is(a,button,input,select,textarea,[tabindex])]:pointer-events-auto";
|
|
113
280
|
|
|
114
281
|
function handleInput(event: Event) {
|
|
115
282
|
if (oninput) {
|
|
@@ -124,46 +291,85 @@
|
|
|
124
291
|
{/if}
|
|
125
292
|
<div class="relative">
|
|
126
293
|
<input
|
|
294
|
+
bind:this={input}
|
|
127
295
|
id={inputId}
|
|
128
|
-
{
|
|
296
|
+
type={shownType}
|
|
129
297
|
{name}
|
|
130
298
|
bind:value
|
|
131
299
|
{placeholder}
|
|
132
300
|
{required}
|
|
133
301
|
disabled={isDisabled}
|
|
134
302
|
class={inputClasses()}
|
|
303
|
+
style:padding-inline-start={leadingPad}
|
|
304
|
+
style:padding-inline-end={trailingPad}
|
|
135
305
|
oninput={handleInput}
|
|
136
306
|
{onblur}
|
|
137
|
-
aria-invalid={variant === "error" ? "true" : undefined}
|
|
307
|
+
aria-invalid={status.variant === "error" ? "true" : undefined}
|
|
138
308
|
aria-required={required ? "true" : undefined}
|
|
139
309
|
aria-busy={loading ? "true" : undefined}
|
|
140
|
-
aria-describedby={
|
|
310
|
+
aria-describedby={fieldDescribedBy(inputId, status, describedBy)}
|
|
141
311
|
{...restProps}
|
|
142
312
|
/>
|
|
143
|
-
{#if
|
|
313
|
+
{#if leading}
|
|
144
314
|
<span
|
|
145
|
-
class="
|
|
146
|
-
|
|
315
|
+
class={cn(END_BASE, "start-0 ps-[4px]")}
|
|
316
|
+
{@attach measured((width) => (leadingWidth = width))}
|
|
317
|
+
data-input-leading
|
|
147
318
|
>
|
|
148
|
-
<span
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
319
|
+
<span class={cn("flex items-center justify-center", slotClass)}>
|
|
320
|
+
{@render leading()}
|
|
321
|
+
</span>
|
|
322
|
+
</span>
|
|
323
|
+
{/if}
|
|
324
|
+
{#if loading || hasTrailing}
|
|
325
|
+
<span
|
|
326
|
+
class={cn(END_BASE, "end-0 pe-[4px]")}
|
|
327
|
+
{@attach measured((width) => (trailingWidth = width))}
|
|
328
|
+
data-input-trailing
|
|
329
|
+
>
|
|
330
|
+
{#if trailing}
|
|
331
|
+
<span class={cn("flex items-center justify-center", slotClass)}>
|
|
332
|
+
{@render trailing()}
|
|
333
|
+
</span>
|
|
334
|
+
{/if}
|
|
335
|
+
{#if canReveal}
|
|
336
|
+
<!-- A toggle with one name: "Show password", pressed or not.
|
|
337
|
+
A name that changed with the state would read as "Hide
|
|
338
|
+
password, pressed". The visible box is 4px inside the field
|
|
339
|
+
at every size, so its corners are the field's less 4px; on a
|
|
340
|
+
touch screen the layer around it is the 44px target. -->
|
|
341
|
+
<IconButton
|
|
342
|
+
variant="ghost"
|
|
343
|
+
size={revealSize}
|
|
344
|
+
bind:pressed={revealed}
|
|
345
|
+
label={revealLabel}
|
|
346
|
+
disabled={isDisabled}
|
|
347
|
+
class={cn(
|
|
348
|
+
"rounded-[calc(var(--radius-control)-4px)] pointer-coarse:relative pointer-coarse:min-h-0 pointer-coarse:min-w-0",
|
|
349
|
+
TOUCH_HIT_AREA,
|
|
350
|
+
)}
|
|
351
|
+
onmousedown={keepFocus}
|
|
352
|
+
onclick={restoreCaret}
|
|
353
|
+
data-input-reveal
|
|
354
|
+
>
|
|
355
|
+
{#if revealed}
|
|
356
|
+
<EyeOff size={SLOTS[size].icon} aria-hidden="true" />
|
|
357
|
+
{:else}
|
|
358
|
+
<Eye size={SLOTS[size].icon} aria-hidden="true" />
|
|
359
|
+
{/if}
|
|
360
|
+
</IconButton>
|
|
361
|
+
{/if}
|
|
362
|
+
{#if loading}
|
|
363
|
+
<!-- 12px from the field's edge, where it has always been. -->
|
|
364
|
+
<span class="flex items-center ps-[4px] pe-[8px]" aria-hidden="true">
|
|
365
|
+
<span
|
|
366
|
+
class="inline-block {sizeClass()
|
|
367
|
+
.spinner} shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-70 motion-reduce:animate-pulse"
|
|
368
|
+
></span>
|
|
369
|
+
</span>
|
|
370
|
+
{/if}
|
|
152
371
|
</span>
|
|
153
372
|
{/if}
|
|
154
373
|
</div>
|
|
155
|
-
{
|
|
156
|
-
<p
|
|
157
|
-
id={`${inputId}-message`}
|
|
158
|
-
class={messageClasses()}
|
|
159
|
-
role={variant === "error" ? "alert" : "status"}
|
|
160
|
-
aria-live={variant === "error" ? "assertive" : "polite"}
|
|
161
|
-
>
|
|
162
|
-
{#if getIcon()}
|
|
163
|
-
{@const Icon = getIcon()}
|
|
164
|
-
<Icon size={14} class="shrink-0" />
|
|
165
|
-
{/if}
|
|
166
|
-
<span>{message}</span>
|
|
167
|
-
</p>
|
|
168
|
-
{/if}
|
|
374
|
+
<FieldMessages fieldId={inputId} {status} {hint} />
|
|
169
375
|
</div>
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
1
3
|
import type { SemanticVariant, SizeVariant } from "../types/variants.js";
|
|
2
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Other attributes (`autocomplete`, `inputmode`, `maxlength`,
|
|
6
|
+
* `enterkeyhint`, `data-*`, `onchange`, ...) land on the `<input>`.
|
|
7
|
+
*/
|
|
8
|
+
type Props = Omit<HTMLInputAttributes, "class" | "size" | "value" | "type" | "id" | "name" | "placeholder" | "required" | "disabled" | "oninput" | "onblur" | "aria-label" | "aria-describedby"> & {
|
|
3
9
|
/** Omit to auto-generate; pair with FormField `id` when used inside FormField. */
|
|
4
10
|
id?: string;
|
|
5
11
|
value?: string;
|
|
@@ -15,11 +21,36 @@ interface Props {
|
|
|
15
21
|
loading?: boolean;
|
|
16
22
|
size?: SizeVariant;
|
|
17
23
|
variant?: SemanticVariant;
|
|
24
|
+
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
18
25
|
message?: string;
|
|
26
|
+
/** Help text under the field, read out with it. */
|
|
27
|
+
hint?: string;
|
|
28
|
+
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
29
|
+
error?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Drawn inside the field, before the text: an icon, a unit. Decoration
|
|
32
|
+
* lets a press through to the field; a button or link in it is pressable.
|
|
33
|
+
*/
|
|
34
|
+
leading?: Snippet;
|
|
35
|
+
/** Drawn inside the field, after the text and before the loading spinner's place. */
|
|
36
|
+
trailing?: Snippet;
|
|
37
|
+
/**
|
|
38
|
+
* With `type="password"`: a button at the end of the field that shows
|
|
39
|
+
* the password as text, and hides it again. On by default; `false`
|
|
40
|
+
* leaves it out. Ignored for other types.
|
|
41
|
+
*/
|
|
42
|
+
revealable?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Accessible name of that button. It stays the same in both states;
|
|
45
|
+
* the button's pressed state says whether the password is showing.
|
|
46
|
+
*/
|
|
47
|
+
revealLabel?: string;
|
|
19
48
|
oninput?: (event: Event) => void;
|
|
20
49
|
onblur?: (event: Event) => void;
|
|
21
50
|
"aria-label"?: string;
|
|
22
|
-
|
|
51
|
+
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
52
|
+
"aria-describedby"?: string | null;
|
|
53
|
+
};
|
|
23
54
|
declare const Input: import("svelte").Component<Props, {}, "value">;
|
|
24
55
|
type Input = ReturnType<typeof Input>;
|
|
25
56
|
export default Input;
|
package/dist/atoms/List.svelte
CHANGED
|
@@ -32,7 +32,11 @@
|
|
|
32
32
|
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
33
33
|
|
|
34
34
|
const listClasses = $derived(
|
|
35
|
-
|
|
35
|
+
// The same radius as its rows. Not `overflow-hidden`: the rows fill
|
|
36
|
+
// the list edge to edge, so clipping here cut a row's focus ring off
|
|
37
|
+
// on every side but the gap to the next row. `list-dividers` draws the
|
|
38
|
+
// hairline in the gap between rows (app.css).
|
|
39
|
+
cn(`list-dividers space-y-1 rounded-[var(--zabi-list-row-radius,var(--radius-container))] ${className}`),
|
|
36
40
|
);
|
|
37
41
|
</script>
|
|
38
42
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import ArrowRight from "@lucide/svelte/icons/arrow-right";
|
|
3
3
|
import type { Component, Snippet } from "svelte";
|
|
4
4
|
import ListItemLeading from "./ListItemLeading.svelte";
|
|
5
5
|
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
avatar?: string;
|
|
14
14
|
/** `avatar` alt text; `""` if decorative. */
|
|
15
15
|
avatarAlt?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Leading picture drawn in the round, in place of an address: an
|
|
18
|
+
* inline SVG. Wins over `avatar` and `icon`. Decorative unless
|
|
19
|
+
* `avatarAlt` is set, which then names it.
|
|
20
|
+
*/
|
|
21
|
+
avatarPicture?: Snippet;
|
|
16
22
|
target?: "_self" | "_blank" | "_parent" | "_top";
|
|
17
23
|
rel?: string;
|
|
18
24
|
disabled?: boolean;
|
|
@@ -21,9 +27,14 @@
|
|
|
21
27
|
interface Props {
|
|
22
28
|
item: ListItemData;
|
|
23
29
|
selected?: boolean;
|
|
30
|
+
/** The arrow at the end of a row that can be pressed. A row with nothing to do never has one. */
|
|
24
31
|
showArrow?: boolean;
|
|
25
32
|
/** Shown before the chevron when `showArrow` is true. */
|
|
26
33
|
trailing?: Snippet;
|
|
34
|
+
/**
|
|
35
|
+
* Called when the row is pressed. A row with neither this nor
|
|
36
|
+
* `item.href` has nothing to do: it is plain content, not a button.
|
|
37
|
+
*/
|
|
27
38
|
onclick?: (item: ListItemData, event: MouseEvent) => void;
|
|
28
39
|
}
|
|
29
40
|
|
|
@@ -36,16 +47,42 @@
|
|
|
36
47
|
...restProps
|
|
37
48
|
}: Props = $props();
|
|
38
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A row is a link with `item.href`, a button with `onclick`, and otherwise
|
|
52
|
+
* plain content: it used to be a focusable button with a pointer cursor
|
|
53
|
+
* and an arrow, and nothing happened when it was pressed.
|
|
54
|
+
*/
|
|
55
|
+
const interactive = $derived(!!item.href || !!onclick);
|
|
56
|
+
|
|
39
57
|
const itemClasses = $derived.by(() => {
|
|
40
58
|
const baseClasses =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
// The container radius, or the one a `.list-group` shell around the list
|
|
60
|
+
// hands down so the row is concentric with it (see app.css).
|
|
61
|
+
// The 1px border is kept so the row's height and text do not move, but
|
|
62
|
+
// it is transparent: rows are told apart by the divider between them
|
|
63
|
+
// (app.css), and a bordered row inside a bordered shell was two
|
|
64
|
+
// outlines. `--zabi-list-row-border-color` puts the 8.1 edge back.
|
|
65
|
+
"group flex w-full items-center gap-3 rounded-[var(--zabi-list-row-radius,var(--radius-container))] border px-4 py-3 pr-5 text-left transition-colors duration-(--duration-base)";
|
|
66
|
+
const edge = "border-[color:var(--zabi-list-row-border-color,transparent)]";
|
|
67
|
+
if (!interactive) {
|
|
68
|
+
const tone = selected ? "bg-action-primary-subtle border-action-primary" : edge;
|
|
69
|
+
return `${baseClasses} ${tone} ${item.disabled ? "opacity-50" : ""}`.trim();
|
|
70
|
+
}
|
|
71
|
+
const cursorClasses = item.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer";
|
|
72
|
+
// A selected row keeps its fill under keyboard focus, which the focus
|
|
73
|
+
// ring already shows; the hover tint is for rows with no fill. The two
|
|
74
|
+
// never share an element, and neither does the selected border with
|
|
75
|
+
// `border-border`: beside each other the second of each pair lost.
|
|
76
|
+
//
|
|
77
|
+
// Hover is for a pointer that can hover (the hand-written rule is
|
|
78
|
+
// gated on it, so the tint does not stay on after a tap); the pressed
|
|
79
|
+
// fill is for every pointer, and is what a finger sees.
|
|
80
|
+
const stateClasses = selected
|
|
81
|
+
? "bg-action-primary-subtle border-action-primary active:bg-action-primary-subtle-active"
|
|
82
|
+
: item.disabled
|
|
83
|
+
? edge
|
|
84
|
+
: `${edge} hover:bg-surface-hover focus-visible:bg-surface-hover active:bg-surface-active`;
|
|
85
|
+
return `focus-ring ${baseClasses} ${cursorClasses} ${stateClasses}`.trim();
|
|
49
86
|
});
|
|
50
87
|
|
|
51
88
|
const ariaCurrent = $derived(item.href && selected ? "page" : undefined);
|
|
@@ -70,7 +107,18 @@
|
|
|
70
107
|
</script>
|
|
71
108
|
|
|
72
109
|
{#snippet rowContent()}
|
|
73
|
-
{#if item.
|
|
110
|
+
{#if item.avatarPicture}
|
|
111
|
+
<ListItemLeading class="rounded-full">
|
|
112
|
+
<span
|
|
113
|
+
class="flex size-full items-center justify-center"
|
|
114
|
+
role={item.avatarAlt ? "img" : undefined}
|
|
115
|
+
aria-label={item.avatarAlt || undefined}
|
|
116
|
+
aria-hidden={item.avatarAlt ? undefined : "true"}
|
|
117
|
+
>
|
|
118
|
+
{@render item.avatarPicture()}
|
|
119
|
+
</span>
|
|
120
|
+
</ListItemLeading>
|
|
121
|
+
{:else if item.avatar}
|
|
74
122
|
<ListItemLeading class="rounded-full">
|
|
75
123
|
<img
|
|
76
124
|
src={item.avatar}
|
|
@@ -101,10 +149,10 @@
|
|
|
101
149
|
{@render trailing()}
|
|
102
150
|
</span>
|
|
103
151
|
{/if}
|
|
104
|
-
{#if showArrow}
|
|
152
|
+
{#if showArrow && interactive}
|
|
105
153
|
<ArrowRight
|
|
106
154
|
size={16}
|
|
107
|
-
class="shrink-0 text-description transition-transform duration-
|
|
155
|
+
class="shrink-0 text-description transition-transform duration-(--duration-base) group-hover:translate-x-1 group-focus-visible:translate-x-1"
|
|
108
156
|
aria-hidden="true"
|
|
109
157
|
/>
|
|
110
158
|
{/if}
|
|
@@ -119,19 +167,28 @@
|
|
|
119
167
|
aria-disabled={item.disabled ? "true" : undefined}
|
|
120
168
|
tabindex={item.disabled ? -1 : undefined}
|
|
121
169
|
class={itemClasses}
|
|
170
|
+
data-selected={selected ? "true" : undefined}
|
|
122
171
|
onclick={handleItemClick}
|
|
123
172
|
{...restProps}
|
|
124
173
|
>
|
|
125
174
|
{@render rowContent()}
|
|
126
175
|
</a>
|
|
127
|
-
{:else}
|
|
176
|
+
{:else if onclick}
|
|
128
177
|
<button
|
|
129
178
|
type="button"
|
|
130
179
|
class={itemClasses}
|
|
180
|
+
data-selected={selected ? "true" : undefined}
|
|
131
181
|
disabled={item.disabled}
|
|
132
182
|
onclick={handleItemClick}
|
|
133
183
|
{...restProps}
|
|
134
184
|
>
|
|
135
185
|
{@render rowContent()}
|
|
136
186
|
</button>
|
|
187
|
+
{:else}
|
|
188
|
+
<!-- A plain row has no role, so `aria-disabled` on it would say nothing
|
|
189
|
+
to anyone: a disabled one is only drawn dimmed. There is nothing in it to
|
|
190
|
+
press, enabled or not. -->
|
|
191
|
+
<div class={itemClasses} data-selected={selected ? "true" : undefined} {...restProps}>
|
|
192
|
+
{@render rowContent()}
|
|
193
|
+
</div>
|
|
137
194
|
{/if}
|
|
@@ -12,6 +12,12 @@ export interface ListItemData {
|
|
|
12
12
|
avatar?: string;
|
|
13
13
|
/** `avatar` alt text; `""` if decorative. */
|
|
14
14
|
avatarAlt?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Leading picture drawn in the round, in place of an address: an
|
|
17
|
+
* inline SVG. Wins over `avatar` and `icon`. Decorative unless
|
|
18
|
+
* `avatarAlt` is set, which then names it.
|
|
19
|
+
*/
|
|
20
|
+
avatarPicture?: Snippet;
|
|
15
21
|
target?: "_self" | "_blank" | "_parent" | "_top";
|
|
16
22
|
rel?: string;
|
|
17
23
|
disabled?: boolean;
|
|
@@ -19,9 +25,14 @@ export interface ListItemData {
|
|
|
19
25
|
interface Props {
|
|
20
26
|
item: ListItemData;
|
|
21
27
|
selected?: boolean;
|
|
28
|
+
/** The arrow at the end of a row that can be pressed. A row with nothing to do never has one. */
|
|
22
29
|
showArrow?: boolean;
|
|
23
30
|
/** Shown before the chevron when `showArrow` is true. */
|
|
24
31
|
trailing?: Snippet;
|
|
32
|
+
/**
|
|
33
|
+
* Called when the row is pressed. A row with neither this nor
|
|
34
|
+
* `item.href` has nothing to do: it is plain content, not a button.
|
|
35
|
+
*/
|
|
25
36
|
onclick?: (item: ListItemData, event: MouseEvent) => void;
|
|
26
37
|
}
|
|
27
38
|
declare const ListItem: Component<Props, {}, "">;
|