zabi-components 7.0.2 → 8.1.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 +1924 -0
- package/README.md +287 -11
- package/THEME.md +149 -332
- package/THEMING.md +1329 -163
- package/dist/atoms/ActionPanel.svelte +24 -9
- package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
- package/dist/atoms/Avatar.svelte +104 -0
- package/dist/atoms/Avatar.svelte.d.ts +34 -0
- package/dist/atoms/Badge.svelte +124 -58
- package/dist/atoms/Badge.svelte.d.ts +17 -5
- package/dist/atoms/Button.svelte +212 -62
- package/dist/atoms/Button.svelte.d.ts +33 -2
- package/dist/atoms/Card.svelte +31 -28
- package/dist/atoms/Card.svelte.d.ts +5 -2
- package/dist/atoms/CardContent.svelte +10 -2
- package/dist/atoms/CardContent.svelte.d.ts +2 -0
- package/dist/atoms/CardFooter.svelte +10 -2
- package/dist/atoms/CardFooter.svelte.d.ts +2 -0
- package/dist/atoms/CardHeader.svelte +26 -6
- package/dist/atoms/CardHeader.svelte.d.ts +2 -0
- package/dist/atoms/Checkbox.svelte +52 -21
- package/dist/atoms/Checkbox.svelte.d.ts +6 -2
- package/dist/atoms/CodeBlock.svelte +35 -9
- package/dist/atoms/CodeBlock.svelte.d.ts +13 -3
- package/dist/atoms/ColorPicker.svelte +272 -52
- package/dist/atoms/ColorPicker.svelte.d.ts +6 -1
- package/dist/atoms/Container.svelte +17 -10
- package/dist/atoms/DateField.svelte +107 -0
- package/dist/atoms/DateField.svelte.d.ts +53 -0
- package/dist/atoms/Divider.svelte +4 -4
- package/dist/atoms/FeatureCard.svelte +4 -3
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +174 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +93 -32
- package/dist/atoms/Heading.svelte.d.ts +24 -4
- package/dist/atoms/IconButton.svelte +242 -53
- package/dist/atoms/IconButton.svelte.d.ts +49 -4
- package/dist/atoms/Input.svelte +267 -74
- package/dist/atoms/Input.svelte.d.ts +32 -2
- package/dist/atoms/List.svelte +13 -2
- package/dist/atoms/List.svelte.d.ts +2 -0
- package/dist/atoms/ListItem.svelte +44 -11
- package/dist/atoms/ListItem.svelte.d.ts +5 -0
- package/dist/atoms/ListItemLeading.svelte +4 -2
- package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
- package/dist/atoms/OptimizedImage.svelte +11 -2
- package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
- package/dist/atoms/Progress.svelte +31 -9
- package/dist/atoms/Progress.svelte.d.ts +6 -0
- package/dist/atoms/Radio.svelte +23 -3
- package/dist/atoms/Radio.svelte.d.ts +6 -2
- package/dist/atoms/Rating.svelte +494 -0
- package/dist/atoms/Rating.svelte.d.ts +56 -0
- package/dist/atoms/Select.svelte +475 -135
- package/dist/atoms/Select.svelte.d.ts +52 -2
- package/dist/atoms/SelectionControl.svelte +99 -18
- package/dist/atoms/SelectionControl.svelte.d.ts +6 -3
- package/dist/atoms/Skeleton.svelte +5 -3
- package/dist/atoms/Slider.svelte +309 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +152 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +140 -0
- package/dist/atoms/TemporalField.svelte.d.ts +32 -0
- package/dist/atoms/Text.svelte +56 -15
- package/dist/atoms/Text.svelte.d.ts +13 -4
- package/dist/atoms/Textarea.svelte +57 -45
- package/dist/atoms/Textarea.svelte.d.ts +14 -2
- package/dist/atoms/ThemeToggle.svelte +238 -56
- package/dist/atoms/ThemeToggle.svelte.d.ts +28 -3
- package/dist/atoms/TimeField.svelte +108 -0
- package/dist/atoms/TimeField.svelte.d.ts +54 -0
- package/dist/atoms/Toast.svelte +25 -10
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +60 -19
- package/dist/atoms/Toggle.svelte.d.ts +18 -2
- package/dist/atoms/Tooltip.svelte +583 -40
- package/dist/atoms/Tooltip.svelte.d.ts +26 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +17 -0
- package/dist/atoms/index.js +12 -0
- package/dist/atoms/selection-control.styles.d.ts +36 -5
- package/dist/atoms/selection-control.styles.js +40 -9
- package/dist/components/atoms/index.d.ts +17 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +36 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +83 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +46 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +53 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +6 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +15 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +28 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +36 -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 +35 -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/cn.d.ts +8 -0
- package/dist/components/util/cn.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +58 -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/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.d.ts +39 -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 +75 -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/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 +215 -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/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-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 +60 -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/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/ssr-safe.d.ts +4 -4
- package/dist/components/util/ssr-safe.d.ts.map +1 -1
- 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/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +93 -0
- package/dist/components/util/toaster.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 +11 -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 +133 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +169 -0
- package/dist/create-theme/index.d.ts +176 -0
- package/dist/create-theme/index.js +817 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +141 -0
- package/dist/create-theme/theme-data.js +1116 -0
- package/dist/index.d.ts +83 -3
- package/dist/index.js +60 -8
- package/dist/molecules/Alert.svelte +73 -66
- package/dist/molecules/Alert.svelte.d.ts +9 -1
- package/dist/molecules/AppBar.svelte +415 -0
- package/dist/molecules/AppBar.svelte.d.ts +69 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BottomSheet.svelte +540 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
- package/dist/molecules/BottomTabBar.svelte +297 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/Collapsible.svelte +243 -0
- package/dist/molecules/Collapsible.svelte.d.ts +44 -0
- package/dist/molecules/CollapsibleGroup.svelte +126 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +25 -5
- package/dist/molecules/ComponentDemo.svelte.d.ts +5 -0
- package/dist/molecules/ConfirmDialog.svelte +253 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
- package/dist/molecules/ContactForm.svelte +46 -19
- package/dist/molecules/ContactForm.svelte.d.ts +10 -0
- package/dist/molecules/Drawer.svelte +394 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +540 -50
- package/dist/molecules/Dropdown.svelte.d.ts +49 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +56 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/Form.svelte +25 -14
- package/dist/molecules/Form.svelte.d.ts +13 -5
- package/dist/molecules/FormField.svelte +11 -2
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +13 -3
- package/dist/molecules/Header.svelte.d.ts +2 -0
- package/dist/molecules/ImageUpload.svelte +422 -70
- package/dist/molecules/ImageUpload.svelte.d.ts +61 -5
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +340 -45
- package/dist/molecules/Modal.svelte.d.ts +59 -7
- package/dist/molecules/NavigationMenu.svelte +15 -3
- package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
- package/dist/molecules/NavigationMenuContent.svelte +91 -36
- package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuItem.svelte +10 -3
- package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuLink.svelte +10 -2
- package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuList.svelte +19 -4
- package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
- package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
- package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
- package/dist/molecules/Page.svelte +38 -2
- package/dist/molecules/Page.svelte.d.ts +13 -0
- package/dist/molecules/PhotoGrid.svelte +433 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
- package/dist/molecules/PhotoViewer.svelte +1116 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
- package/dist/molecules/PropsTable.svelte +26 -11
- package/dist/molecules/PropsTable.svelte.d.ts +5 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +57 -15
- package/dist/molecules/RadioGroup.svelte.d.ts +16 -3
- package/dist/molecules/Section.svelte +13 -5
- package/dist/molecules/Section.svelte.d.ts +3 -1
- package/dist/molecules/SegmentedControl.svelte +323 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
- package/dist/molecules/SidebarBrandHeader.svelte +24 -5
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +5 -0
- package/dist/molecules/SidebarFooter.svelte +39 -8
- package/dist/molecules/SidebarFooter.svelte.d.ts +5 -0
- package/dist/molecules/SidebarNavSection.svelte +12 -4
- package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
- package/dist/molecules/SlideUp.svelte +253 -30
- package/dist/molecules/SlideUp.svelte.d.ts +31 -4
- package/dist/molecules/SortableList.svelte +632 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +548 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +191 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +254 -24
- package/dist/molecules/Tabs.svelte.d.ts +15 -4
- package/dist/molecules/Toaster.svelte +352 -6
- package/dist/molecules/Toaster.svelte.d.ts +95 -2
- package/dist/molecules/ToasterToast.svelte +279 -62
- package/dist/molecules/ToasterToast.svelte.d.ts +11 -0
- package/dist/molecules/UnsavedChangesBar.svelte +212 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +46 -0
- package/dist/molecules/index.js +27 -0
- package/dist/molecules/toast-store.d.ts +53 -7
- package/dist/molecules/toast-store.js +21 -0
- package/dist/organisms/AppShell.svelte +196 -0
- package/dist/organisms/AppShell.svelte.d.ts +61 -0
- package/dist/organisms/SidebarAccountPanel.svelte +111 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +21 -0
- package/dist/organisms/SidebarNavigation.svelte +193 -69
- package/dist/organisms/SidebarNavigation.svelte.d.ts +31 -1
- package/dist/organisms/SidebarPanel.svelte +45 -16
- package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
- package/dist/organisms/SidebarShell.svelte +263 -0
- package/dist/organisms/SidebarShell.svelte.d.ts +106 -0
- package/dist/organisms/TopNavbar.svelte +329 -33
- package/dist/organisms/TopNavbar.svelte.d.ts +28 -0
- package/dist/organisms/index.d.ts +6 -0
- package/dist/organisms/index.js +4 -0
- package/dist/routes/lib/variant-utils.d.ts +26 -0
- package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
- package/dist/types/components.d.ts +4 -0
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +605 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/variants.ts +17 -1
- package/dist/util/app-shell.d.ts +27 -0
- package/dist/util/app-shell.js +72 -0
- package/dist/util/avatar.d.ts +35 -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 +34 -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/cn.d.ts +7 -0
- package/dist/util/cn.js +48 -0
- package/dist/util/collapsible.d.ts +57 -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/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.d.ts +38 -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 +75 -2
- package/dist/util/focus-utils.js +182 -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/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 +214 -0
- package/dist/util/photo.js +206 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -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-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 +59 -0
- package/dist/util/sheet-drag.js +241 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/ssr-safe.d.ts +4 -4
- 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/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +92 -0
- package/dist/util/toaster.js +75 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +10 -0
- package/dist/util/top-navbar.js +5 -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 +132 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +5 -2
- package/dist/zabi-components-theme-dark-only.css +574 -225
- package/dist/zabi-components-theme-dark.css +574 -225
- package/dist/zabi-components-theme-only.css +1680 -317
- package/dist/zabi-components-theme.css +1680 -317
- package/dist/zabi-components.css +4473 -1158
- package/docs/lucide-icons.md +41 -16
- package/docs/theme-imports.md +81 -11
- package/package.json +51 -29
- package/dist/lib/layout-width-tokens.d.ts +0 -17
- package/dist/lib/layout-width-tokens.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/List.d.ts +0 -3
- package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
- package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
- package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
- package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
- package/dist/lib/showcase/component-docs/index.d.ts +0 -4
- package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
- package/dist/lib/showcase/components-catalog.d.ts +0 -3
- package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
- package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
- package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
- package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
- package/dist/routes/lib/focus-utils.d.ts +0 -12
- package/dist/routes/lib/focus-utils.ts +0 -110
- package/dist/routes/lib/ssr-safe.ts +0 -58
- package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
package/dist/atoms/Input.svelte
CHANGED
|
@@ -1,9 +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";
|
|
8
|
+
import { cn } from "../util/cn.js";
|
|
9
|
+
import { fieldDescribedBy, fieldMessageState } from "../util/field.js";
|
|
4
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";
|
|
5
14
|
|
|
6
|
-
|
|
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
|
+
> & {
|
|
7
35
|
/** Omit to auto-generate; pair with FormField `id` when used inside FormField. */
|
|
8
36
|
id?: string;
|
|
9
37
|
value?: string;
|
|
@@ -19,15 +47,39 @@
|
|
|
19
47
|
loading?: boolean;
|
|
20
48
|
size?: SizeVariant;
|
|
21
49
|
variant?: SemanticVariant;
|
|
50
|
+
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
22
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"`: adds a button at the end of the field that
|
|
65
|
+
* shows the password as text, and hides it again. Ignored for other types.
|
|
66
|
+
*/
|
|
67
|
+
revealable?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Accessible name of that button. It stays the same in both states;
|
|
70
|
+
* the button's pressed state says whether the password is showing.
|
|
71
|
+
*/
|
|
72
|
+
revealLabel?: string;
|
|
23
73
|
oninput?: (event: Event) => void;
|
|
24
74
|
onblur?: (event: Event) => void;
|
|
25
75
|
"aria-label"?: string;
|
|
26
|
-
|
|
76
|
+
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
77
|
+
"aria-describedby"?: string | null;
|
|
78
|
+
};
|
|
27
79
|
|
|
28
80
|
let {
|
|
29
81
|
id: idProp,
|
|
30
|
-
value = $bindable
|
|
82
|
+
value = $bindable<Exclude<Props["value"], undefined>>(),
|
|
31
83
|
type = "text",
|
|
32
84
|
name = "",
|
|
33
85
|
class: className = "",
|
|
@@ -40,84 +92,186 @@
|
|
|
40
92
|
size = "md",
|
|
41
93
|
variant = "default",
|
|
42
94
|
message = "",
|
|
95
|
+
hint = "",
|
|
96
|
+
error = "",
|
|
97
|
+
leading,
|
|
98
|
+
trailing,
|
|
99
|
+
revealable = false,
|
|
100
|
+
revealLabel: revealLabelGiven,
|
|
43
101
|
oninput,
|
|
44
102
|
onblur,
|
|
103
|
+
"aria-describedby": describedBy,
|
|
45
104
|
...restProps
|
|
46
105
|
}: Props = $props();
|
|
47
106
|
|
|
107
|
+
// No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
|
|
108
|
+
// one that has a fallback (`props_invalid_value`), and a page that throws
|
|
109
|
+
// while it hydrates never becomes interactive. The default is applied
|
|
110
|
+
// here instead: at once, for the server and the first render, and again
|
|
111
|
+
// whenever a parent hands back `undefined`.
|
|
112
|
+
const applyDefaults = () => {
|
|
113
|
+
if (value === undefined) value = "";
|
|
114
|
+
};
|
|
115
|
+
applyDefaults();
|
|
116
|
+
$effect.pre(applyDefaults);
|
|
117
|
+
|
|
118
|
+
/** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
|
|
119
|
+
const common = zabiCommonStrings();
|
|
120
|
+
const revealLabel = $derived(revealLabelGiven ?? common().showPassword);
|
|
121
|
+
|
|
48
122
|
const fallbackId = generateId("input");
|
|
49
123
|
const inputId = $derived(idProp ?? fallbackId);
|
|
50
124
|
const isDisabled = $derived(disabled || loading);
|
|
51
125
|
|
|
126
|
+
const status = $derived(fieldMessageState({ variant, message, hint, error }));
|
|
127
|
+
|
|
128
|
+
/** Only a password can be revealed: on any other type the button would have nothing to do. */
|
|
129
|
+
const canReveal = $derived(revealable && type === "password");
|
|
130
|
+
let revealed = $state(false);
|
|
131
|
+
const shownType = $derived(canReveal && revealed ? "text" : type);
|
|
132
|
+
|
|
133
|
+
let input: HTMLInputElement | undefined = $state();
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Changing `type` can drop the caret in some browsers. When the field has
|
|
137
|
+
* focus (a mouse or a finger pressed the button: see `keepFocus`), the
|
|
138
|
+
* selection is put back where it was.
|
|
139
|
+
*/
|
|
140
|
+
async function restoreCaret() {
|
|
141
|
+
const field = input;
|
|
142
|
+
if (!field || document.activeElement !== field) return;
|
|
143
|
+
const { selectionStart, selectionEnd, selectionDirection } = field;
|
|
144
|
+
if (selectionStart === null || selectionEnd === null) return;
|
|
145
|
+
const restore = () => {
|
|
146
|
+
if (document.activeElement !== field) return;
|
|
147
|
+
try {
|
|
148
|
+
field.setSelectionRange(selectionStart, selectionEnd, selectionDirection ?? undefined);
|
|
149
|
+
} catch {
|
|
150
|
+
// A type without a text selection: nothing to put back.
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
await tick();
|
|
154
|
+
restore();
|
|
155
|
+
// Chromium moves the caret to the start once more after the type has
|
|
156
|
+
// changed, so it is put back again when the frame is done.
|
|
157
|
+
requestAnimationFrame(restore);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A press with a pointer does not take focus from the field: on a phone
|
|
162
|
+
* that would close the keyboard in the middle of typing a password. From
|
|
163
|
+
* the keyboard the button has focus and keeps it.
|
|
164
|
+
*/
|
|
165
|
+
function keepFocus(event: Event) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Same fixed height scale as Button, IconButton and Select (32 / 40 / 48),
|
|
171
|
+
* so a field and its submit button are the same height in a row. These
|
|
172
|
+
* used to be 38 / 46 / 50 while Button was 40 / 48 / 64.
|
|
173
|
+
*
|
|
174
|
+
* Below the `sm` breakpoint the text is 16px at every size: iOS Safari
|
|
175
|
+
* zooms the page when a focused field is smaller. Only the font grows;
|
|
176
|
+
* the box keeps its height.
|
|
177
|
+
*/
|
|
52
178
|
const sizeClass = $derived(() => {
|
|
53
179
|
if (size === "sm") {
|
|
54
|
-
return {
|
|
55
|
-
padding: "px-4 py-2",
|
|
56
|
-
text: "text-sm",
|
|
57
|
-
leading: "leading-5",
|
|
58
|
-
spinner: "size-4",
|
|
59
|
-
};
|
|
180
|
+
return { box: "h-8 px-3 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", spinner: "size-3.5" };
|
|
60
181
|
} else if (size === "lg") {
|
|
61
|
-
return {
|
|
62
|
-
padding: "px-4 py-3",
|
|
63
|
-
text: "text-base",
|
|
64
|
-
leading: "leading-6",
|
|
65
|
-
spinner: "size-5",
|
|
66
|
-
};
|
|
182
|
+
return { box: "h-12 px-4", text: "text-base", spinner: "size-5" };
|
|
67
183
|
} else {
|
|
68
|
-
return {
|
|
69
|
-
padding: "px-4 py-2.5",
|
|
70
|
-
text: "text-base",
|
|
71
|
-
leading: "leading-6",
|
|
72
|
-
spinner: "size-5",
|
|
73
|
-
};
|
|
184
|
+
return { box: "h-10 px-3 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", spinner: "size-4" };
|
|
74
185
|
}
|
|
75
186
|
});
|
|
76
187
|
|
|
188
|
+
/**
|
|
189
|
+
* What stands inside the field at either end sits in a slot 4px in from
|
|
190
|
+
* the field's edge and 8px smaller than the field: 24, 32 and 40px. An
|
|
191
|
+
* icon is centred in it, which puts it where the text's own padding would;
|
|
192
|
+
* a button fills it.
|
|
193
|
+
*/
|
|
194
|
+
const SLOT_INSET = 4;
|
|
195
|
+
const SLOTS = {
|
|
196
|
+
sm: { px: 24, min: "min-w-[24px]", reveal: "xs", icon: 14 },
|
|
197
|
+
md: { px: 32, min: "min-w-[32px]", reveal: "sm", icon: 16 },
|
|
198
|
+
lg: { px: 40, min: "min-w-[40px]", reveal: "md", icon: 16 },
|
|
199
|
+
} as const;
|
|
200
|
+
const slot = $derived(SLOTS[size].px);
|
|
201
|
+
const slotClass = $derived(SLOTS[size].min);
|
|
202
|
+
/** The reveal button is one size down from the field, so it fits the slot: 24, 32 and 40px. */
|
|
203
|
+
const revealSize = $derived(SLOTS[size].reveal);
|
|
204
|
+
|
|
205
|
+
const hasLeading = $derived(!!leading);
|
|
206
|
+
/** Whether the end of the field holds more than the spinner. */
|
|
207
|
+
const hasTrailing = $derived(!!trailing || canReveal);
|
|
208
|
+
|
|
209
|
+
/** Measured once mounted; until then, and on the server, the slots are assumed to be their least size. */
|
|
210
|
+
let leadingWidth = $state(0);
|
|
211
|
+
let trailingWidth = $state(0);
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reports the width of what stands at one end of the field, and again
|
|
215
|
+
* when it changes. Where there is no `ResizeObserver` (an old browser, a
|
|
216
|
+
* test without layout) the assumed size stays.
|
|
217
|
+
*/
|
|
218
|
+
function measured(report: (width: number) => void) {
|
|
219
|
+
return (element: HTMLElement) => {
|
|
220
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
221
|
+
const observer = new ResizeObserver(() => report(element.offsetWidth));
|
|
222
|
+
observer.observe(element);
|
|
223
|
+
return () => observer.disconnect();
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const trailingCount = $derived((trailing ? 1 : 0) + (canReveal ? 1 : 0) + (loading ? 1 : 0));
|
|
227
|
+
const leadingPad = $derived(
|
|
228
|
+
hasLeading ? `${(leadingWidth || SLOT_INSET + slot) + SLOT_INSET}px` : undefined,
|
|
229
|
+
);
|
|
230
|
+
const trailingPad = $derived(
|
|
231
|
+
hasTrailing
|
|
232
|
+
? `${(trailingWidth || SLOT_INSET + trailingCount * slot + (trailingCount - 1) * SLOT_INSET) + SLOT_INSET}px`
|
|
233
|
+
: undefined,
|
|
234
|
+
);
|
|
235
|
+
|
|
77
236
|
const variantClass = $derived(() => {
|
|
78
|
-
return variant === "success"
|
|
237
|
+
return status.variant === "success"
|
|
79
238
|
? "border-success focus-visible:border-success"
|
|
80
|
-
: variant === "warning"
|
|
239
|
+
: status.variant === "warning"
|
|
81
240
|
? "border-warning focus-visible:border-warning"
|
|
82
|
-
: variant === "error"
|
|
241
|
+
: status.variant === "error"
|
|
83
242
|
? "border-error focus-visible:border-error"
|
|
84
|
-
: "border-input-border focus-visible:border-input-border";
|
|
243
|
+
: "border-input-border enabled:hover:border-input-border-hover focus-visible:border-input-border";
|
|
85
244
|
});
|
|
86
245
|
|
|
87
246
|
const inputClasses = $derived(() => {
|
|
88
247
|
const sizeStyles = sizeClass();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
248
|
+
// Room for the spinner alone. With more at the end, the measured width is used instead.
|
|
249
|
+
const spinnerPad = loading && !hasTrailing ? SPINNER_PAD[size] : "";
|
|
250
|
+
/**
|
|
251
|
+
* The field is a raised surface (`bg-input` = white in light, an inset
|
|
252
|
+
* step in dark). It used to be a grey fill on a grey page, which read
|
|
253
|
+
* as disabled. `min-w-48` is gone: it forced a 192px floor on every
|
|
254
|
+
* field regardless of the layout around it.
|
|
255
|
+
*/
|
|
94
256
|
const baseClasses =
|
|
95
|
-
"focus-ring w-full
|
|
257
|
+
"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";
|
|
96
258
|
|
|
97
|
-
return `${baseClasses} ${sizeStyles.
|
|
259
|
+
return cn(`${baseClasses} ${sizeStyles.box} ${spinnerPad} ${sizeStyles.text} ${variantClass()} ${className}`);
|
|
98
260
|
});
|
|
99
261
|
|
|
100
262
|
const labelClasses = $derived(
|
|
101
|
-
() => "block text-sm font-medium text-label mb-
|
|
263
|
+
() => "block text-sm font-medium text-label mb-2",
|
|
102
264
|
);
|
|
103
265
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return "text-warning text-sm mt-1 flex items-center gap-1.5";
|
|
111
|
-
}
|
|
112
|
-
return "text-description text-sm mt-1 flex items-center gap-1.5";
|
|
113
|
-
});
|
|
266
|
+
/**
|
|
267
|
+
* Both ends are laid over the field, which stays one box with one focus
|
|
268
|
+
* ring. They let a press through to it, except on something in them that
|
|
269
|
+
* can itself be pressed.
|
|
270
|
+
*/
|
|
271
|
+
const SPINNER_PAD = { sm: "pe-9", md: "pe-10", lg: "pe-10" } as const;
|
|
114
272
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
if (variant === "success") return CheckCircle;
|
|
118
|
-
if (variant === "warning") return AlertTriangle;
|
|
119
|
-
return null;
|
|
120
|
-
});
|
|
273
|
+
const END_BASE =
|
|
274
|
+
"pointer-events-none absolute inset-y-0 flex items-center gap-[4px] text-description [&_:is(a,button,input,select,textarea,[tabindex])]:pointer-events-auto";
|
|
121
275
|
|
|
122
276
|
function handleInput(event: Event) {
|
|
123
277
|
if (oninput) {
|
|
@@ -132,46 +286,85 @@
|
|
|
132
286
|
{/if}
|
|
133
287
|
<div class="relative">
|
|
134
288
|
<input
|
|
289
|
+
bind:this={input}
|
|
135
290
|
id={inputId}
|
|
136
|
-
{
|
|
291
|
+
type={shownType}
|
|
137
292
|
{name}
|
|
138
293
|
bind:value
|
|
139
294
|
{placeholder}
|
|
140
295
|
{required}
|
|
141
296
|
disabled={isDisabled}
|
|
142
297
|
class={inputClasses()}
|
|
298
|
+
style:padding-inline-start={leadingPad}
|
|
299
|
+
style:padding-inline-end={trailingPad}
|
|
143
300
|
oninput={handleInput}
|
|
144
301
|
{onblur}
|
|
145
|
-
aria-invalid={variant === "error" ? "true" : undefined}
|
|
302
|
+
aria-invalid={status.variant === "error" ? "true" : undefined}
|
|
146
303
|
aria-required={required ? "true" : undefined}
|
|
147
304
|
aria-busy={loading ? "true" : undefined}
|
|
148
|
-
aria-describedby={
|
|
305
|
+
aria-describedby={fieldDescribedBy(inputId, status, describedBy)}
|
|
149
306
|
{...restProps}
|
|
150
307
|
/>
|
|
151
|
-
{#if
|
|
308
|
+
{#if leading}
|
|
309
|
+
<span
|
|
310
|
+
class={cn(END_BASE, "start-0 ps-[4px]")}
|
|
311
|
+
{@attach measured((width) => (leadingWidth = width))}
|
|
312
|
+
data-input-leading
|
|
313
|
+
>
|
|
314
|
+
<span class={cn("flex items-center justify-center", slotClass)}>
|
|
315
|
+
{@render leading()}
|
|
316
|
+
</span>
|
|
317
|
+
</span>
|
|
318
|
+
{/if}
|
|
319
|
+
{#if loading || hasTrailing}
|
|
152
320
|
<span
|
|
153
|
-
class="
|
|
154
|
-
|
|
321
|
+
class={cn(END_BASE, "end-0 pe-[4px]")}
|
|
322
|
+
{@attach measured((width) => (trailingWidth = width))}
|
|
323
|
+
data-input-trailing
|
|
155
324
|
>
|
|
156
|
-
|
|
157
|
-
class="
|
|
158
|
-
|
|
159
|
-
|
|
325
|
+
{#if trailing}
|
|
326
|
+
<span class={cn("flex items-center justify-center", slotClass)}>
|
|
327
|
+
{@render trailing()}
|
|
328
|
+
</span>
|
|
329
|
+
{/if}
|
|
330
|
+
{#if canReveal}
|
|
331
|
+
<!-- A toggle with one name: "Show password", pressed or not.
|
|
332
|
+
A name that changed with the state would read as "Hide
|
|
333
|
+
password, pressed". The visible box is 4px inside the field
|
|
334
|
+
at every size, so its corners are the field's less 4px; on a
|
|
335
|
+
touch screen the layer around it is the 44px target. -->
|
|
336
|
+
<IconButton
|
|
337
|
+
variant="ghost"
|
|
338
|
+
size={revealSize}
|
|
339
|
+
bind:pressed={revealed}
|
|
340
|
+
label={revealLabel}
|
|
341
|
+
disabled={isDisabled}
|
|
342
|
+
class={cn(
|
|
343
|
+
"rounded-[calc(var(--radius-control)-4px)] pointer-coarse:relative pointer-coarse:min-h-0 pointer-coarse:min-w-0",
|
|
344
|
+
TOUCH_HIT_AREA,
|
|
345
|
+
)}
|
|
346
|
+
onmousedown={keepFocus}
|
|
347
|
+
onclick={restoreCaret}
|
|
348
|
+
data-input-reveal
|
|
349
|
+
>
|
|
350
|
+
{#if revealed}
|
|
351
|
+
<EyeOff size={SLOTS[size].icon} aria-hidden="true" />
|
|
352
|
+
{:else}
|
|
353
|
+
<Eye size={SLOTS[size].icon} aria-hidden="true" />
|
|
354
|
+
{/if}
|
|
355
|
+
</IconButton>
|
|
356
|
+
{/if}
|
|
357
|
+
{#if loading}
|
|
358
|
+
<!-- 12px from the field's edge, where it has always been. -->
|
|
359
|
+
<span class="flex items-center ps-[4px] pe-[8px]" aria-hidden="true">
|
|
360
|
+
<span
|
|
361
|
+
class="inline-block {sizeClass()
|
|
362
|
+
.spinner} shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-70 motion-reduce:animate-pulse"
|
|
363
|
+
></span>
|
|
364
|
+
</span>
|
|
365
|
+
{/if}
|
|
160
366
|
</span>
|
|
161
367
|
{/if}
|
|
162
368
|
</div>
|
|
163
|
-
{
|
|
164
|
-
<p
|
|
165
|
-
id={`${inputId}-message`}
|
|
166
|
-
class={messageClasses()}
|
|
167
|
-
role={variant === "error" ? "alert" : "status"}
|
|
168
|
-
aria-live={variant === "error" ? "assertive" : "polite"}
|
|
169
|
-
>
|
|
170
|
-
{#if getIcon()}
|
|
171
|
-
{@const Icon = getIcon()}
|
|
172
|
-
<Icon size={14} class="shrink-0" />
|
|
173
|
-
{/if}
|
|
174
|
-
<span>{message}</span>
|
|
175
|
-
</p>
|
|
176
|
-
{/if}
|
|
369
|
+
<FieldMessages fieldId={inputId} {status} {hint} />
|
|
177
370
|
</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,35 @@ 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"`: adds a button at the end of the field that
|
|
39
|
+
* shows the password as text, and hides it again. Ignored for other types.
|
|
40
|
+
*/
|
|
41
|
+
revealable?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Accessible name of that button. It stays the same in both states;
|
|
44
|
+
* the button's pressed state says whether the password is showing.
|
|
45
|
+
*/
|
|
46
|
+
revealLabel?: string;
|
|
19
47
|
oninput?: (event: Event) => void;
|
|
20
48
|
onblur?: (event: Event) => void;
|
|
21
49
|
"aria-label"?: string;
|
|
22
|
-
|
|
50
|
+
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
51
|
+
"aria-describedby"?: string | null;
|
|
52
|
+
};
|
|
23
53
|
declare const Input: import("svelte").Component<Props, {}, "value">;
|
|
24
54
|
type Input = ReturnType<typeof Input>;
|
|
25
55
|
export default Input;
|
package/dist/atoms/List.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import ListItemRow, { type ListItemData } from "./ListItem.svelte";
|
|
3
|
+
import { cn } from "../util/cn.js";
|
|
3
4
|
|
|
4
5
|
export type ListItem = ListItemData;
|
|
5
6
|
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
ariaLabel?: string;
|
|
10
11
|
selectedId?: string;
|
|
11
12
|
showArrow?: boolean;
|
|
13
|
+
class?: string;
|
|
14
|
+
/** @deprecated use `class`. */
|
|
12
15
|
className?: string;
|
|
13
16
|
onclick?: (item: ListItem, event: MouseEvent) => void;
|
|
14
17
|
}
|
|
@@ -18,13 +21,21 @@
|
|
|
18
21
|
ariaLabel = "List items",
|
|
19
22
|
selectedId = "",
|
|
20
23
|
showArrow = true,
|
|
21
|
-
|
|
24
|
+
class: classAttr = "",
|
|
25
|
+
className: legacyClass = "",
|
|
22
26
|
onclick,
|
|
23
27
|
...restProps
|
|
24
28
|
}: Props = $props();
|
|
25
29
|
|
|
30
|
+
/** `class` is the public prop; `className` is a deprecated alias.
|
|
31
|
+
* Both are merged here so existing call sites keep working. */
|
|
32
|
+
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
33
|
+
|
|
26
34
|
const listClasses = $derived(
|
|
27
|
-
`
|
|
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.
|
|
38
|
+
cn(`space-y-1 rounded-[var(--zabi-list-row-radius,var(--radius-container))] ${className}`),
|
|
28
39
|
);
|
|
29
40
|
</script>
|
|
30
41
|
|
|
@@ -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
|
|
|
@@ -21,9 +21,14 @@
|
|
|
21
21
|
interface Props {
|
|
22
22
|
item: ListItemData;
|
|
23
23
|
selected?: boolean;
|
|
24
|
+
/** The arrow at the end of a row that can be pressed. A row with nothing to do never has one. */
|
|
24
25
|
showArrow?: boolean;
|
|
25
26
|
/** Shown before the chevron when `showArrow` is true. */
|
|
26
27
|
trailing?: Snippet;
|
|
28
|
+
/**
|
|
29
|
+
* Called when the row is pressed. A row with neither this nor
|
|
30
|
+
* `item.href` has nothing to do: it is plain content, not a button.
|
|
31
|
+
*/
|
|
27
32
|
onclick?: (item: ListItemData, event: MouseEvent) => void;
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -36,16 +41,37 @@
|
|
|
36
41
|
...restProps
|
|
37
42
|
}: Props = $props();
|
|
38
43
|
|
|
44
|
+
/**
|
|
45
|
+
* A row is a link with `item.href`, a button with `onclick`, and otherwise
|
|
46
|
+
* plain content: it used to be a focusable button with a pointer cursor
|
|
47
|
+
* and an arrow, and nothing happened when it was pressed.
|
|
48
|
+
*/
|
|
49
|
+
const interactive = $derived(!!item.href || !!onclick);
|
|
50
|
+
|
|
39
51
|
const itemClasses = $derived.by(() => {
|
|
40
52
|
const baseClasses =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
? "
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
// The container radius, or the one a `.list-group` shell around the list
|
|
54
|
+
// hands down so the row is concentric with it (see app.css).
|
|
55
|
+
"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-all duration-150";
|
|
56
|
+
if (!interactive) {
|
|
57
|
+
const tone = selected ? "bg-action-primary-subtle border-action-primary" : "border-border";
|
|
58
|
+
return `${baseClasses} ${tone} ${item.disabled ? "opacity-50" : ""}`.trim();
|
|
59
|
+
}
|
|
60
|
+
const cursorClasses = item.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer";
|
|
61
|
+
// A selected row keeps its fill under keyboard focus, which the focus
|
|
62
|
+
// ring already shows; the hover tint is for rows with no fill. The two
|
|
63
|
+
// never share an element, and neither does the selected border with
|
|
64
|
+
// `border-border`: beside each other the second of each pair lost.
|
|
65
|
+
//
|
|
66
|
+
// Hover is for a pointer that can hover (the hand-written rule is
|
|
67
|
+
// gated on it, so the tint does not stay on after a tap); the pressed
|
|
68
|
+
// fill is for every pointer, and is what a finger sees.
|
|
69
|
+
const stateClasses = selected
|
|
70
|
+
? "bg-action-primary-subtle border-action-primary active:bg-action-primary-subtle-active"
|
|
71
|
+
: item.disabled
|
|
72
|
+
? "border-border"
|
|
73
|
+
: "border-border hover:bg-surface-hover focus-visible:bg-surface-hover active:bg-surface-active";
|
|
74
|
+
return `focus-ring ${baseClasses} ${cursorClasses} ${stateClasses}`.trim();
|
|
49
75
|
});
|
|
50
76
|
|
|
51
77
|
const ariaCurrent = $derived(item.href && selected ? "page" : undefined);
|
|
@@ -101,7 +127,7 @@
|
|
|
101
127
|
{@render trailing()}
|
|
102
128
|
</span>
|
|
103
129
|
{/if}
|
|
104
|
-
{#if showArrow}
|
|
130
|
+
{#if showArrow && interactive}
|
|
105
131
|
<ArrowRight
|
|
106
132
|
size={16}
|
|
107
133
|
class="shrink-0 text-description transition-transform duration-150 group-hover:translate-x-1 group-focus-visible:translate-x-1"
|
|
@@ -124,7 +150,7 @@
|
|
|
124
150
|
>
|
|
125
151
|
{@render rowContent()}
|
|
126
152
|
</a>
|
|
127
|
-
{:else}
|
|
153
|
+
{:else if onclick}
|
|
128
154
|
<button
|
|
129
155
|
type="button"
|
|
130
156
|
class={itemClasses}
|
|
@@ -134,4 +160,11 @@
|
|
|
134
160
|
>
|
|
135
161
|
{@render rowContent()}
|
|
136
162
|
</button>
|
|
163
|
+
{:else}
|
|
164
|
+
<!-- A plain row has no role, so `aria-disabled` on it would say nothing
|
|
165
|
+
to anyone: a disabled one is only drawn dimmed. There is nothing in it to
|
|
166
|
+
press, enabled or not. -->
|
|
167
|
+
<div class={itemClasses} {...restProps}>
|
|
168
|
+
{@render rowContent()}
|
|
169
|
+
</div>
|
|
137
170
|
{/if}
|
|
@@ -19,9 +19,14 @@ export interface ListItemData {
|
|
|
19
19
|
interface Props {
|
|
20
20
|
item: ListItemData;
|
|
21
21
|
selected?: boolean;
|
|
22
|
+
/** The arrow at the end of a row that can be pressed. A row with nothing to do never has one. */
|
|
22
23
|
showArrow?: boolean;
|
|
23
24
|
/** Shown before the chevron when `showArrow` is true. */
|
|
24
25
|
trailing?: Snippet;
|
|
26
|
+
/**
|
|
27
|
+
* Called when the row is pressed. A row with neither this nor
|
|
28
|
+
* `item.href` has nothing to do: it is plain content, not a button.
|
|
29
|
+
*/
|
|
25
30
|
onclick?: (item: ListItemData, event: MouseEvent) => void;
|
|
26
31
|
}
|
|
27
32
|
declare const ListItem: Component<Props, {}, "">;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
|
|
4
|
+
import { cn } from "../util/cn.js";
|
|
4
5
|
interface Props {
|
|
5
|
-
/** e.g. `rounded-full` for avatars; default is `rounded-
|
|
6
|
+
/** e.g. `rounded-full` for avatars; default is `rounded-control` for icons. */
|
|
6
7
|
class?: string;
|
|
7
8
|
children?: Snippet;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
let { class: className = "", children }: Props = $props();
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// cn lets a call-site radius win outright, instead of replacing the default wholesale.
|
|
14
|
+
const roundingClass = $derived(cn("rounded-control", className));
|
|
13
15
|
</script>
|
|
14
16
|
|
|
15
17
|
<span
|