zabi-components 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2935 -0
- package/README.md +243 -10
- package/THEME.md +162 -332
- package/THEMING.md +1419 -259
- package/dist/atoms/ActionPanel.svelte +15 -5
- package/dist/atoms/Avatar.svelte +120 -0
- package/dist/atoms/Avatar.svelte.d.ts +46 -0
- package/dist/atoms/Badge.svelte +71 -13
- package/dist/atoms/Badge.svelte.d.ts +8 -2
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +217 -30
- package/dist/atoms/Button.svelte.d.ts +33 -4
- package/dist/atoms/Card.svelte +182 -26
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/CardHeader.svelte +16 -4
- package/dist/atoms/Checkbox.svelte +81 -21
- package/dist/atoms/Checkbox.svelte.d.ts +31 -2
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeBlock.svelte +20 -2
- package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +260 -49
- package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
- package/dist/atoms/Container.svelte +16 -8
- package/dist/atoms/DateField.svelte +145 -0
- package/dist/atoms/DateField.svelte.d.ts +81 -0
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +176 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +63 -5
- package/dist/atoms/Heading.svelte.d.ts +14 -0
- package/dist/atoms/IconButton.svelte +294 -26
- package/dist/atoms/IconButton.svelte.d.ts +62 -4
- package/dist/atoms/Input.svelte +259 -53
- package/dist/atoms/Input.svelte.d.ts +33 -2
- package/dist/atoms/List.svelte +5 -1
- package/dist/atoms/ListItem.svelte +70 -13
- package/dist/atoms/ListItem.svelte.d.ts +11 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +99 -19
- package/dist/atoms/Progress.svelte.d.ts +15 -1
- package/dist/atoms/Radio.svelte +51 -3
- package/dist/atoms/Radio.svelte.d.ts +30 -2
- package/dist/atoms/Rating.svelte +620 -0
- package/dist/atoms/Rating.svelte.d.ts +77 -0
- package/dist/atoms/Select.svelte +438 -98
- package/dist/atoms/Select.svelte.d.ts +61 -3
- package/dist/atoms/SelectionControl.svelte +193 -23
- package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
- package/dist/atoms/Skeleton.svelte +114 -18
- package/dist/atoms/Skeleton.svelte.d.ts +30 -3
- package/dist/atoms/SkeletonGroup.svelte +43 -0
- package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
- package/dist/atoms/Slider.svelte +344 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Sparkline.svelte +109 -0
- package/dist/atoms/Sparkline.svelte.d.ts +27 -0
- package/dist/atoms/SpeechBubble.svelte +187 -0
- package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +151 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +472 -0
- package/dist/atoms/TemporalField.svelte.d.ts +48 -0
- package/dist/atoms/Text.svelte +23 -11
- package/dist/atoms/Text.svelte.d.ts +6 -3
- package/dist/atoms/Textarea.svelte +150 -44
- package/dist/atoms/Textarea.svelte.d.ts +35 -2
- package/dist/atoms/ThemeToggle.svelte +223 -44
- package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +151 -0
- package/dist/atoms/TimeField.svelte.d.ts +85 -0
- package/dist/atoms/Toast.svelte +22 -8
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +98 -14
- package/dist/atoms/Toggle.svelte.d.ts +26 -2
- package/dist/atoms/Tooltip.svelte +528 -24
- package/dist/atoms/Tooltip.svelte.d.ts +18 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +38 -0
- package/dist/atoms/index.js +29 -0
- package/dist/atoms/selection-control.styles.d.ts +47 -5
- package/dist/atoms/selection-control.styles.js +52 -10
- package/dist/components/atoms/index.d.ts +38 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +47 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +104 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map.d.ts +11 -0
- package/dist/components/map.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +51 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +83 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +10 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +24 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +79 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +39 -0
- package/dist/components/util/avatar.d.ts.map +1 -0
- package/dist/components/util/bottom-sheet.d.ts +30 -0
- package/dist/components/util/bottom-sheet.d.ts.map +1 -0
- package/dist/components/util/bottom-tab-bar.d.ts +41 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
- package/dist/components/util/calendar.d.ts +56 -0
- package/dist/components/util/calendar.d.ts.map +1 -0
- package/dist/components/util/chart.d.ts +140 -0
- package/dist/components/util/chart.d.ts.map +1 -0
- package/dist/components/util/chip.d.ts +66 -0
- package/dist/components/util/chip.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts.map +1 -1
- package/dist/components/util/code-field.d.ts +63 -0
- package/dist/components/util/code-field.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +64 -0
- package/dist/components/util/collapsible.d.ts.map +1 -0
- package/dist/components/util/confirm-dialog.d.ts +12 -0
- package/dist/components/util/confirm-dialog.d.ts.map +1 -0
- package/dist/components/util/date.d.ts +96 -0
- package/dist/components/util/date.d.ts.map +1 -0
- package/dist/components/util/display-voice.d.ts +10 -0
- package/dist/components/util/display-voice.d.ts.map +1 -0
- package/dist/components/util/drawer.d.ts +13 -0
- package/dist/components/util/drawer.d.ts.map +1 -0
- package/dist/components/util/dropdown.d.ts +47 -0
- package/dist/components/util/dropdown.d.ts.map +1 -0
- package/dist/components/util/field-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +45 -0
- package/dist/components/util/field.d.ts.map +1 -0
- package/dist/components/util/fit-in-viewport.d.ts +201 -0
- package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +77 -2
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/hydration.d.ts +45 -0
- package/dist/components/util/hydration.d.ts.map +1 -0
- package/dist/components/util/keyboard-inset.d.ts +18 -0
- package/dist/components/util/keyboard-inset.d.ts.map +1 -0
- package/dist/components/util/map-view.d.ts +144 -0
- package/dist/components/util/map-view.d.ts.map +1 -0
- package/dist/components/util/media-grid.d.ts +51 -0
- package/dist/components/util/media-grid.d.ts.map +1 -0
- package/dist/components/util/media.d.ts +6 -0
- package/dist/components/util/media.d.ts.map +1 -0
- package/dist/components/util/overlay-chrome.d.ts +53 -0
- package/dist/components/util/overlay-chrome.d.ts.map +1 -0
- package/dist/components/util/overlay.d.ts +72 -0
- package/dist/components/util/overlay.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +234 -0
- package/dist/components/util/photo.d.ts.map +1 -0
- package/dist/components/util/portal.d.ts +23 -0
- package/dist/components/util/portal.d.ts.map +1 -0
- package/dist/components/util/progress.d.ts +19 -0
- package/dist/components/util/progress.d.ts.map +1 -0
- package/dist/components/util/pull-to-refresh.d.ts +27 -0
- package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
- package/dist/components/util/radio-keys.d.ts +25 -0
- package/dist/components/util/radio-keys.d.ts.map +1 -0
- package/dist/components/util/rating.d.ts +23 -0
- package/dist/components/util/rating.d.ts.map +1 -0
- package/dist/components/util/ready-made-strings.d.ts +77 -0
- package/dist/components/util/ready-made-strings.d.ts.map +1 -0
- package/dist/components/util/scroll-edge.d.ts +33 -0
- package/dist/components/util/scroll-edge.d.ts.map +1 -0
- package/dist/components/util/scroll-padding.d.ts +20 -0
- package/dist/components/util/scroll-padding.d.ts.map +1 -0
- package/dist/components/util/segmented-control.d.ts +16 -0
- package/dist/components/util/segmented-control.d.ts.map +1 -0
- package/dist/components/util/select.d.ts +41 -0
- package/dist/components/util/select.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +84 -0
- package/dist/components/util/sheet-drag.d.ts.map +1 -0
- package/dist/components/util/sidebar.d.ts +63 -0
- package/dist/components/util/sidebar.d.ts.map +1 -0
- package/dist/components/util/skeleton.d.ts +15 -0
- package/dist/components/util/skeleton.d.ts.map +1 -0
- package/dist/components/util/sliding-indicator.d.ts +25 -0
- package/dist/components/util/sliding-indicator.d.ts.map +1 -0
- package/dist/components/util/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/stepper.d.ts +40 -0
- package/dist/components/util/stepper.d.ts.map +1 -0
- package/dist/components/util/swipeable-list-item.d.ts +36 -0
- package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
- package/dist/components/util/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.d.ts.map +1 -0
- package/dist/components/util/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +133 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tone-surface.d.ts +34 -0
- package/dist/components/util/tone-surface.d.ts.map +1 -0
- package/dist/components/util/tooltip.d.ts +10 -0
- package/dist/components/util/tooltip.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +35 -0
- package/dist/components/util/top-navbar.d.ts.map +1 -0
- package/dist/components/util/touch-target.d.ts +31 -0
- package/dist/components/util/touch-target.d.ts.map +1 -0
- package/dist/components/util/zabi-strings.d.ts +142 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +173 -0
- package/dist/create-theme/index.d.ts +190 -0
- package/dist/create-theme/index.js +1034 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +238 -0
- package/dist/create-theme/theme-data.js +2644 -0
- package/dist/index.d.ts +104 -2
- package/dist/index.js +75 -2
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +63 -16
- package/dist/molecules/Alert.svelte.d.ts +14 -1
- package/dist/molecules/AppBar.svelte +674 -0
- package/dist/molecules/AppBar.svelte.d.ts +131 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +608 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
- package/dist/molecules/BottomTabBar.svelte +403 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +347 -0
- package/dist/molecules/Collapsible.svelte.d.ts +61 -0
- package/dist/molecules/CollapsibleGroup.svelte +136 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +16 -4
- package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
- package/dist/molecules/ConfirmDialog.svelte +257 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
- package/dist/molecules/ContactForm.svelte +34 -17
- package/dist/molecules/ContactForm.svelte.d.ts +8 -0
- package/dist/molecules/Drawer.svelte +415 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +517 -48
- package/dist/molecules/Dropdown.svelte.d.ts +47 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +55 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/FormField.svelte +9 -1
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +492 -69
- package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
- package/dist/molecules/LineChart.svelte +459 -0
- package/dist/molecules/LineChart.svelte.d.ts +53 -0
- package/dist/molecules/MapView.svelte +565 -0
- package/dist/molecules/MapView.svelte.d.ts +88 -0
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +293 -72
- package/dist/molecules/Modal.svelte.d.ts +51 -2
- package/dist/molecules/NavigationMenuContent.svelte +58 -1
- package/dist/molecules/NavigationMenuItem.svelte +0 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuList.svelte +10 -3
- package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
- package/dist/molecules/Page.svelte +32 -2
- package/dist/molecules/Page.svelte.d.ts +11 -0
- package/dist/molecules/PhotoGrid.svelte +523 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
- package/dist/molecules/PhotoViewer.svelte +1213 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/PropsTable.svelte +22 -10
- package/dist/molecules/PropsTable.svelte.d.ts +3 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +72 -18
- package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
- package/dist/molecules/Section.svelte +1 -1
- package/dist/molecules/SegmentedControl.svelte +456 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
- package/dist/molecules/SidebarBrandHeader.svelte +11 -1
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
- package/dist/molecules/SidebarFooter.svelte +30 -7
- package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SlideUp.svelte +220 -61
- package/dist/molecules/SlideUp.svelte.d.ts +26 -0
- package/dist/molecules/SortableList.svelte +633 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +562 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +250 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +310 -9
- package/dist/molecules/Tabs.svelte.d.ts +7 -0
- package/dist/molecules/Toaster.svelte +514 -5
- package/dist/molecules/Toaster.svelte.d.ts +153 -2
- package/dist/molecules/ToasterToast.svelte +495 -77
- package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
- package/dist/molecules/UnsavedChangesBar.svelte +223 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +51 -0
- package/dist/molecules/index.js +31 -0
- package/dist/molecules/toast-store.d.ts +83 -7
- package/dist/molecules/toast-store.js +25 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +696 -0
- package/dist/organisms/AppShell.svelte.d.ts +157 -0
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarAccountPanel.svelte +107 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
- package/dist/organisms/SidebarNavigation.svelte +111 -43
- package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
- package/dist/organisms/SidebarPanel.svelte +34 -17
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +164 -14
- package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
- package/dist/organisms/TopNavbar.svelte +353 -39
- package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +10 -0
- package/dist/organisms/index.js +7 -0
- package/dist/types/components.d.ts +21 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +1093 -26
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +28 -1
- package/dist/util/app-shell.d.ts +78 -0
- package/dist/util/app-shell.js +97 -0
- package/dist/util/avatar.d.ts +38 -0
- package/dist/util/avatar.js +52 -0
- package/dist/util/bottom-sheet.d.ts +29 -0
- package/dist/util/bottom-sheet.js +43 -0
- package/dist/util/bottom-tab-bar.d.ts +40 -0
- package/dist/util/bottom-tab-bar.js +54 -0
- package/dist/util/calendar.d.ts +55 -0
- package/dist/util/calendar.js +74 -0
- package/dist/util/chart.d.ts +139 -0
- package/dist/util/chart.js +305 -0
- package/dist/util/chip.d.ts +65 -0
- package/dist/util/chip.js +58 -0
- package/dist/util/cn.js +28 -2
- package/dist/util/code-field.d.ts +62 -0
- package/dist/util/code-field.js +112 -0
- package/dist/util/collapsible.d.ts +63 -0
- package/dist/util/collapsible.js +23 -0
- package/dist/util/confirm-dialog.d.ts +11 -0
- package/dist/util/confirm-dialog.js +2 -0
- package/dist/util/date.d.ts +95 -0
- package/dist/util/date.js +237 -0
- package/dist/util/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -0
- package/dist/util/drawer.d.ts +12 -0
- package/dist/util/drawer.js +7 -0
- package/dist/util/dropdown.d.ts +46 -0
- package/dist/util/dropdown.js +21 -0
- package/dist/util/field-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +44 -0
- package/dist/util/field.js +38 -0
- package/dist/util/fit-in-viewport.d.ts +200 -0
- package/dist/util/fit-in-viewport.js +399 -0
- package/dist/util/focus-utils.d.ts +77 -2
- package/dist/util/focus-utils.js +186 -9
- package/dist/util/hydration.d.ts +44 -0
- package/dist/util/hydration.js +58 -0
- package/dist/util/keyboard-inset.d.ts +17 -0
- package/dist/util/keyboard-inset.js +53 -0
- package/dist/util/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -0
- package/dist/util/media-grid.d.ts +50 -0
- package/dist/util/media-grid.js +81 -0
- package/dist/util/media.d.ts +5 -0
- package/dist/util/media.js +10 -0
- package/dist/util/overlay-chrome.d.ts +52 -0
- package/dist/util/overlay-chrome.js +56 -0
- package/dist/util/overlay.d.ts +71 -0
- package/dist/util/overlay.js +240 -0
- package/dist/util/photo.d.ts +233 -0
- package/dist/util/photo.js +209 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -0
- package/dist/util/pull-to-refresh.d.ts +26 -0
- package/dist/util/pull-to-refresh.js +30 -0
- package/dist/util/radio-keys.d.ts +24 -0
- package/dist/util/radio-keys.js +49 -0
- package/dist/util/rating.d.ts +22 -0
- package/dist/util/rating.js +26 -0
- package/dist/util/ready-made-strings.d.ts +76 -0
- package/dist/util/ready-made-strings.js +67 -0
- package/dist/util/scroll-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -0
- package/dist/util/scroll-padding.d.ts +19 -0
- package/dist/util/scroll-padding.js +44 -0
- package/dist/util/segmented-control.d.ts +15 -0
- package/dist/util/segmented-control.js +1 -0
- package/dist/util/select.d.ts +40 -0
- package/dist/util/select.js +63 -0
- package/dist/util/sheet-drag.d.ts +83 -0
- package/dist/util/sheet-drag.js +311 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/skeleton.d.ts +14 -0
- package/dist/util/skeleton.js +22 -0
- package/dist/util/sliding-indicator.d.ts +24 -0
- package/dist/util/sliding-indicator.js +44 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/stepper.d.ts +39 -0
- package/dist/util/stepper.js +38 -0
- package/dist/util/swipeable-list-item.d.ts +35 -0
- package/dist/util/swipeable-list-item.js +32 -0
- package/dist/util/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +132 -0
- package/dist/util/toaster.js +130 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +34 -0
- package/dist/util/top-navbar.js +28 -0
- package/dist/util/touch-target.d.ts +30 -0
- package/dist/util/touch-target.js +30 -0
- package/dist/util/zabi-strings.d.ts +141 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +11 -2
- package/dist/zabi-components-theme-dark-only.css +870 -258
- package/dist/zabi-components-theme-dark.css +870 -258
- package/dist/zabi-components-theme-only.css +2419 -368
- package/dist/zabi-components-theme.css +2419 -368
- package/dist/zabi-components.css +6032 -711
- package/docs/lucide-icons.md +40 -17
- package/docs/theme-imports.md +81 -11
- package/package.json +45 -8
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
/**
|
|
4
|
+
* One type for the four forms, as Button has one for the button and the
|
|
5
|
+
* link: a union of the elements' attributes is too large to carry through
|
|
6
|
+
* a wrapper. Other attributes land on the element that is the control: the
|
|
7
|
+
* `<a>`, the `<button>` or the `<input>`; `class` is always the chip's.
|
|
8
|
+
*/
|
|
9
|
+
type Props = Omit<HTMLButtonAttributes, "class" | "type" | "value" | "onchange" | "children"> & {
|
|
10
|
+
/** Makes the chip a link to this address, which works without scripts. */
|
|
11
|
+
href?: string;
|
|
12
|
+
target?: HTMLAnchorAttributes["target"];
|
|
13
|
+
rel?: HTMLAnchorAttributes["rel"];
|
|
14
|
+
download?: HTMLAnchorAttributes["download"];
|
|
15
|
+
hreflang?: HTMLAnchorAttributes["hreflang"];
|
|
16
|
+
/** With `href`: what `aria-current` says while `selected`. */
|
|
17
|
+
current?: "page" | "true" | "step" | "location" | "date" | "time";
|
|
18
|
+
/**
|
|
19
|
+
* `checkbox` or `radio`: a real input under the chip, which submits in
|
|
20
|
+
* a form without scripts. Without it (and without `href`) the chip is a
|
|
21
|
+
* toggle button. Inside a ChipGroup with a `type`, the group's.
|
|
22
|
+
* `submit` or `button`: a button that does not toggle. A press posts
|
|
23
|
+
* the form (`submit`, with `name` and `value`) or only calls `onclick`;
|
|
24
|
+
* `selected` is drawn as on a toggle and said with `aria-pressed`, and
|
|
25
|
+
* the app decides it, as a server's answer does.
|
|
26
|
+
*/
|
|
27
|
+
type?: "checkbox" | "radio" | "submit" | "button";
|
|
28
|
+
/** Name the input is submitted under. Inside a ChipGroup with a `name`, the group's. */
|
|
29
|
+
name?: string;
|
|
30
|
+
/** What the input submits, and what a ChipGroup's `value` holds when it is chosen. */
|
|
31
|
+
value?: string;
|
|
32
|
+
/** The chip is chosen. Bindable; the one prop for `checked`, `aria-pressed` and `aria-current`. */
|
|
33
|
+
selected?: boolean;
|
|
34
|
+
required?: boolean;
|
|
35
|
+
form?: string;
|
|
36
|
+
/** 28, 32 or 40px tall. On a coarse pointer the touch target reaches 44px; the box does not grow. */
|
|
37
|
+
size?: "sm" | "md" | "lg";
|
|
38
|
+
/** An icon or an avatar before the label. */
|
|
39
|
+
leading?: Snippet;
|
|
40
|
+
/** A check icon before the label while selected. On for a checkbox and a toggle button, off for a link and a radio. */
|
|
41
|
+
checkmark?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* A button chip (`type="submit"`, `"button"` or a toggle) while its
|
|
44
|
+
* action is on its way, as Button's: `aria-busy` and `aria-disabled`,
|
|
45
|
+
* a spinner over the label, the press swallowed. The chip keeps its
|
|
46
|
+
* width and stays a Tab stop, so focus is not lost. Ignored by a link
|
|
47
|
+
* and by `checkbox` / `radio`, which are not actions.
|
|
48
|
+
*/
|
|
49
|
+
loading?: boolean;
|
|
50
|
+
/** Called with the new `selected` when a person changes it. */
|
|
51
|
+
onchange?: (selected: boolean) => void;
|
|
52
|
+
class?: string;
|
|
53
|
+
children?: Snippet;
|
|
54
|
+
};
|
|
55
|
+
declare const Chip: import("svelte").Component<Props, {}, "selected">;
|
|
56
|
+
type Chip = ReturnType<typeof Chip>;
|
|
57
|
+
export default Chip;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { DEFAULT_CODE_BLOCK_TEXTS, zabiStringsFor } from "../util/zabi-strings.js";
|
|
2
3
|
import Check from "@lucide/svelte/icons/check";
|
|
3
4
|
import Copy from "@lucide/svelte/icons/copy";
|
|
4
5
|
import IconButton from "./IconButton.svelte";
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
/** @deprecated use `class`. */
|
|
13
14
|
className?: string;
|
|
14
15
|
showCopyButton?: boolean;
|
|
16
|
+
/** Accessible name of the copy button. */
|
|
17
|
+
copyLabel?: string;
|
|
18
|
+
/** Its name for the two seconds after the code was copied. */
|
|
19
|
+
copiedLabel?: string;
|
|
15
20
|
/** If true, `{@html code}` — only trusted, sanitized input. */
|
|
16
21
|
trustHtml?: boolean;
|
|
17
22
|
}
|
|
@@ -22,10 +27,17 @@
|
|
|
22
27
|
class: classAttr = "",
|
|
23
28
|
className: legacyClass = "",
|
|
24
29
|
showCopyButton = true,
|
|
30
|
+
copyLabel: copyLabelGiven,
|
|
31
|
+
copiedLabel: copiedLabelGiven,
|
|
25
32
|
trustHtml = false,
|
|
26
33
|
...restProps
|
|
27
34
|
}: Props & Record<string, unknown> = $props();
|
|
28
35
|
|
|
36
|
+
/** Each text: the prop, else the app-wide word from a `ZabiStringsProvider` above this one, else English. */
|
|
37
|
+
const provided = zabiStringsFor("codeBlock");
|
|
38
|
+
const copyLabel = $derived(copyLabelGiven ?? provided()?.copyLabel ?? DEFAULT_CODE_BLOCK_TEXTS.copyLabel);
|
|
39
|
+
const copiedLabel = $derived(copiedLabelGiven ?? provided()?.copiedLabel ?? DEFAULT_CODE_BLOCK_TEXTS.copiedLabel);
|
|
40
|
+
|
|
29
41
|
/** `class` is the public prop; `className` is a deprecated alias.
|
|
30
42
|
* Both are merged here so existing call sites keep working. */
|
|
31
43
|
const className = $derived(cn(`${classAttr} ${legacyClass}`));
|
|
@@ -61,7 +73,7 @@
|
|
|
61
73
|
<IconButton
|
|
62
74
|
variant="ghost"
|
|
63
75
|
size="sm"
|
|
64
|
-
label={copied ?
|
|
76
|
+
label={copied ? copiedLabel : copyLabel}
|
|
65
77
|
onclick={copyToClipboard}
|
|
66
78
|
>
|
|
67
79
|
{#if copied}
|
|
@@ -70,6 +82,9 @@
|
|
|
70
82
|
<Copy size={16} class="shrink-0" aria-hidden="true" />
|
|
71
83
|
{/if}
|
|
72
84
|
</IconButton>
|
|
85
|
+
<!-- A button's new name is not read out while it keeps focus.
|
|
86
|
+
This says it: always in the page, filled when the code is copied. -->
|
|
87
|
+
<span class="sr-only" role="status" data-code-block-status>{copied ? copiedLabel : ""}</span>
|
|
73
88
|
{/if}
|
|
74
89
|
</div>
|
|
75
90
|
|
|
@@ -80,8 +95,11 @@
|
|
|
80
95
|
</div>
|
|
81
96
|
|
|
82
97
|
<style>
|
|
98
|
+
/* The token's default is this same stack. It is repeated as the fallback
|
|
99
|
+
because Tailwind only emits theme variables it sees used, and it does
|
|
100
|
+
not read a component's scoped styles. */
|
|
83
101
|
.code-block {
|
|
84
|
-
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
|
|
102
|
+
font-family: var(--font-family-mono, "Monaco", "Menlo", "Ubuntu Mono", monospace);
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
:global(.language-svelte .token.tag),
|
|
@@ -6,6 +6,10 @@ interface Props {
|
|
|
6
6
|
/** @deprecated use `class`. */
|
|
7
7
|
className?: string;
|
|
8
8
|
showCopyButton?: boolean;
|
|
9
|
+
/** Accessible name of the copy button. */
|
|
10
|
+
copyLabel?: string;
|
|
11
|
+
/** Its name for the two seconds after the code was copied. */
|
|
12
|
+
copiedLabel?: string;
|
|
9
13
|
/** If true, `{@html code}` — only trusted, sanitized input. */
|
|
10
14
|
trustHtml?: boolean;
|
|
11
15
|
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
4
|
+
import { cn } from "../util/cn.js";
|
|
5
|
+
import type { SizeVariant } from "../types/variants.js";
|
|
6
|
+
import {
|
|
7
|
+
activePlace,
|
|
8
|
+
caretAfterSanitise,
|
|
9
|
+
CODE_FIELD_GAP_REM,
|
|
10
|
+
CODE_FIELD_GROUP_GAP_REM,
|
|
11
|
+
CODE_FIELD_SIZES,
|
|
12
|
+
codeGroups,
|
|
13
|
+
codeLength,
|
|
14
|
+
DEFAULT_CODE_FIELD_STRINGS,
|
|
15
|
+
isCodeChar,
|
|
16
|
+
nearestBox,
|
|
17
|
+
pasteInto,
|
|
18
|
+
replaceAt,
|
|
19
|
+
sanitiseCode,
|
|
20
|
+
type CodeFieldMode,
|
|
21
|
+
type CodeFieldStrings,
|
|
22
|
+
} from "../util/code-field.js";
|
|
23
|
+
import { fieldDescribedBy, fieldMessageState } from "../util/field.js";
|
|
24
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
25
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
26
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
27
|
+
import FieldMessages from "./FieldMessages.svelte";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A field for a one-time or verification code. One real `<input>` holds
|
|
31
|
+
* the code, so autofill from a message, paste, the numeric keyboard, form
|
|
32
|
+
* posting by `name` and a single accessible name all work as on `Input`;
|
|
33
|
+
* the boxes are drawn under it and hidden from assistive technology.
|
|
34
|
+
* Other attributes (`autocomplete`, `inputmode`, `data-*`, ...) land on
|
|
35
|
+
* the `<input>`.
|
|
36
|
+
*/
|
|
37
|
+
type Props = Omit<
|
|
38
|
+
HTMLInputAttributes,
|
|
39
|
+
| "class"
|
|
40
|
+
| "size"
|
|
41
|
+
| "value"
|
|
42
|
+
| "type"
|
|
43
|
+
| "id"
|
|
44
|
+
| "name"
|
|
45
|
+
| "required"
|
|
46
|
+
| "disabled"
|
|
47
|
+
| "readonly"
|
|
48
|
+
| "maxlength"
|
|
49
|
+
| "oninput"
|
|
50
|
+
| "onblur"
|
|
51
|
+
| "aria-label"
|
|
52
|
+
| "aria-describedby"
|
|
53
|
+
> & {
|
|
54
|
+
/** Omit to auto-generate. */
|
|
55
|
+
id?: string;
|
|
56
|
+
/** The code typed so far. Bindable. */
|
|
57
|
+
value?: string;
|
|
58
|
+
/** The name the code is posted under, with or without scripts. */
|
|
59
|
+
name?: string;
|
|
60
|
+
class?: string;
|
|
61
|
+
/** Places in the code. A whole number from 1 to 12. */
|
|
62
|
+
length?: number;
|
|
63
|
+
/** `numeric` (default): digits only. `alphanumeric` also takes letters. */
|
|
64
|
+
mode?: CodeFieldMode;
|
|
65
|
+
/** Box size: `sm` (44px tall, a compact row), `md` (default), `lg`. */
|
|
66
|
+
size?: SizeVariant;
|
|
67
|
+
/**
|
|
68
|
+
* Draws the boxes in groups with a wider gap between them: a number is
|
|
69
|
+
* the size of each group (`4` makes 4 and 4 of 8), a list gives each
|
|
70
|
+
* group's size. Only the drawing changes: one input, one value, and a
|
|
71
|
+
* paste with spaces or dashes still fills it. A row that cannot fit at
|
|
72
|
+
* large text or a narrow width wraps between groups.
|
|
73
|
+
*/
|
|
74
|
+
groups?: number | number[];
|
|
75
|
+
/** Names the field. Defaults to the provider's `codeField.label`, then "Verification code". */
|
|
76
|
+
label?: string;
|
|
77
|
+
/** Keeps the label for assistive technology and takes it off the screen. */
|
|
78
|
+
labelHidden?: boolean;
|
|
79
|
+
required?: boolean;
|
|
80
|
+
disabled?: boolean;
|
|
81
|
+
readonly?: boolean;
|
|
82
|
+
variant?: SemanticVariant;
|
|
83
|
+
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
84
|
+
message?: string;
|
|
85
|
+
/** Help text under the field, read out with it. */
|
|
86
|
+
hint?: string;
|
|
87
|
+
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
88
|
+
error?: string;
|
|
89
|
+
/** Called with the code when the last place is filled by typing, pasting or autofill. It does not submit anything. */
|
|
90
|
+
oncomplete?: (value: string) => void;
|
|
91
|
+
/** Overrides for the built-in strings: label, description. */
|
|
92
|
+
strings?: Partial<CodeFieldStrings>;
|
|
93
|
+
oninput?: (event: Event) => void;
|
|
94
|
+
onblur?: (event: Event) => void;
|
|
95
|
+
"aria-label"?: string;
|
|
96
|
+
/** Ids of other elements that describe the field. The length, the hint and the message are added after them. */
|
|
97
|
+
"aria-describedby"?: string | null;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** An event on the input, as Svelte types the handlers of an element. */
|
|
101
|
+
type OnInput<E extends Event> = E & { currentTarget: EventTarget & HTMLInputElement };
|
|
102
|
+
|
|
103
|
+
let {
|
|
104
|
+
id: idProp,
|
|
105
|
+
value = $bindable<Exclude<Props["value"], undefined>>(),
|
|
106
|
+
name = "",
|
|
107
|
+
class: className = "",
|
|
108
|
+
length: lengthProp = 6,
|
|
109
|
+
mode = "numeric",
|
|
110
|
+
size = "md",
|
|
111
|
+
groups,
|
|
112
|
+
label,
|
|
113
|
+
labelHidden = false,
|
|
114
|
+
required = false,
|
|
115
|
+
disabled = false,
|
|
116
|
+
readonly = false,
|
|
117
|
+
variant = "default",
|
|
118
|
+
message = "",
|
|
119
|
+
hint = "",
|
|
120
|
+
error = "",
|
|
121
|
+
oncomplete,
|
|
122
|
+
strings,
|
|
123
|
+
oninput,
|
|
124
|
+
onblur,
|
|
125
|
+
"aria-label": ariaLabel,
|
|
126
|
+
"aria-describedby": describedBy,
|
|
127
|
+
...restProps
|
|
128
|
+
}: Props = $props();
|
|
129
|
+
|
|
130
|
+
// No fallback on a bindable prop (see Input): the default is applied here,
|
|
131
|
+
// at once and whenever a parent hands back `undefined`.
|
|
132
|
+
const applyDefaults = () => {
|
|
133
|
+
if (value === undefined) value = "";
|
|
134
|
+
};
|
|
135
|
+
applyDefaults();
|
|
136
|
+
$effect.pre(applyDefaults);
|
|
137
|
+
|
|
138
|
+
const length = $derived(codeLength(lengthProp));
|
|
139
|
+
const sizing = $derived(CODE_FIELD_SIZES[size] ?? CODE_FIELD_SIZES.md);
|
|
140
|
+
const groupSizes = $derived(codeGroups(groups, length));
|
|
141
|
+
const grouped = $derived(groupSizes.length > 1);
|
|
142
|
+
/** Which group each box is in, for the drawing, and the first box of each. */
|
|
143
|
+
const groupStarts = $derived(groupSizes.map((_, index) => groupSizes.slice(0, index).reduce((sum, n) => sum + n, 0)));
|
|
144
|
+
const provided = zabiStringsFor("codeField");
|
|
145
|
+
const text = $derived(mergeStrings(DEFAULT_CODE_FIELD_STRINGS, provided(), strings));
|
|
146
|
+
const shownLabel = $derived(label || text.label);
|
|
147
|
+
|
|
148
|
+
const fallbackId = generateId("code");
|
|
149
|
+
const inputId = $derived(idProp ?? fallbackId);
|
|
150
|
+
const lengthId = $derived(`${inputId}-length`);
|
|
151
|
+
const status = $derived(fieldMessageState({ variant, message, hint, error }));
|
|
152
|
+
|
|
153
|
+
let input: HTMLInputElement | undefined = $state();
|
|
154
|
+
let cells: HTMLElement | undefined = $state();
|
|
155
|
+
let focused = $state(false);
|
|
156
|
+
let caret = $state(0);
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Without scripts the input is a plain bordered field showing what is
|
|
160
|
+
* typed, which posts like any other. Once the page is interactive it
|
|
161
|
+
* becomes the invisible layer over the boxes.
|
|
162
|
+
*/
|
|
163
|
+
let enhanced = $state(false);
|
|
164
|
+
$effect(() => {
|
|
165
|
+
enhanced = true;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const places = $derived(Array.from({ length }, (_, index) => value?.[index] ?? ""));
|
|
169
|
+
const active = $derived(enhanced && focused && !disabled && !readonly ? activePlace(caret, length) : -1);
|
|
170
|
+
|
|
171
|
+
const variantClass = $derived(
|
|
172
|
+
status.variant === "success"
|
|
173
|
+
? "border-success"
|
|
174
|
+
: status.variant === "warning"
|
|
175
|
+
? "border-warning"
|
|
176
|
+
: status.variant === "error"
|
|
177
|
+
? "border-error"
|
|
178
|
+
: "border-input-border",
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
/** The focus ring of `.focus-ring`, drawn on the box the caret is in. */
|
|
182
|
+
const RING = "shadow-[0_0_0_2px_var(--color-focus-ring-offset),0_0_0_4px_var(--color-focus-ring)]";
|
|
183
|
+
|
|
184
|
+
let complete = "";
|
|
185
|
+
|
|
186
|
+
/** Sets the code, and calls `oncomplete` when this makes it whole. */
|
|
187
|
+
function commit(next: string, caretAt: number) {
|
|
188
|
+
const previous = value ?? "";
|
|
189
|
+
if (next !== previous) value = next;
|
|
190
|
+
if (input) {
|
|
191
|
+
if (input.value !== next) input.value = next;
|
|
192
|
+
try {
|
|
193
|
+
input.setSelectionRange(caretAt, caretAt);
|
|
194
|
+
} catch {
|
|
195
|
+
// A browser that cannot place the caret: typing still works.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
caret = caretAt;
|
|
199
|
+
if (next.length === length) {
|
|
200
|
+
if (next !== complete) {
|
|
201
|
+
complete = next;
|
|
202
|
+
oncomplete?.(next);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
complete = "";
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function syncCaret() {
|
|
210
|
+
if (input) caret = input.selectionStart ?? input.value.length;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function handleInput(event: OnInput<Event>) {
|
|
214
|
+
oninput?.(event);
|
|
215
|
+
const field = event.currentTarget;
|
|
216
|
+
const raw = field.value;
|
|
217
|
+
const clean = sanitiseCode(raw, mode, length);
|
|
218
|
+
const at = caretAfterSanitise(raw, field.selectionStart ?? raw.length, mode, length);
|
|
219
|
+
commit(clean, Math.min(at, clean.length));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* A character typed over a filled place replaces it; one typed at the end
|
|
224
|
+
* is added. A character that does not belong never gets in.
|
|
225
|
+
*/
|
|
226
|
+
function handleKeydown(event: OnInput<KeyboardEvent>) {
|
|
227
|
+
restProps.onkeydown?.(event);
|
|
228
|
+
if (event.defaultPrevented || readonly || disabled) return;
|
|
229
|
+
if (event.ctrlKey || event.metaKey || event.altKey || event.key.length !== 1) return;
|
|
230
|
+
const field = event.currentTarget;
|
|
231
|
+
if (!isCodeChar(event.key, mode)) {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const start = field.selectionStart ?? 0;
|
|
236
|
+
const end = field.selectionEnd ?? start;
|
|
237
|
+
if (start === end && start < field.value.length) {
|
|
238
|
+
event.preventDefault();
|
|
239
|
+
commit(replaceAt(field.value, start, event.key), start + 1);
|
|
240
|
+
} else if (start === end && field.value.length >= length) {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Takes the whole code from the clipboard, wherever the caret is; "123 456" and "123-456" are 123456. */
|
|
246
|
+
function handlePaste(event: OnInput<ClipboardEvent>) {
|
|
247
|
+
restProps.onpaste?.(event);
|
|
248
|
+
if (event.defaultPrevented || readonly || disabled) return;
|
|
249
|
+
const field = event.currentTarget;
|
|
250
|
+
event.preventDefault();
|
|
251
|
+
const pasted = event.clipboardData?.getData("text") ?? "";
|
|
252
|
+
const start = field.selectionStart ?? field.value.length;
|
|
253
|
+
const end = field.selectionEnd ?? start;
|
|
254
|
+
const next = pasteInto(field.value, start, end, pasted, mode, length);
|
|
255
|
+
if (next === field.value && sanitiseCode(pasted, mode, length) === "") return;
|
|
256
|
+
commit(next, next.length === length ? length : Math.min(next.length, start + sanitiseCode(pasted, mode, length).length));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** A tap on a box puts the caret there, or at the end of the code when that box is still empty. */
|
|
260
|
+
function handleClick(event: OnInput<MouseEvent>) {
|
|
261
|
+
restProps.onclick?.(event);
|
|
262
|
+
if (!input || !cells) return;
|
|
263
|
+
const boxes = Array.from(cells.querySelectorAll<HTMLElement>("[data-code-cell]"));
|
|
264
|
+
let index = nearestBox(
|
|
265
|
+
boxes.map((box) => box.getBoundingClientRect()),
|
|
266
|
+
event.clientX,
|
|
267
|
+
event.clientY,
|
|
268
|
+
);
|
|
269
|
+
if (index < 0) return syncCaret();
|
|
270
|
+
index = Math.min(index, input.value.length);
|
|
271
|
+
try {
|
|
272
|
+
input.setSelectionRange(index, index);
|
|
273
|
+
} catch {
|
|
274
|
+
// Nothing to place.
|
|
275
|
+
}
|
|
276
|
+
syncCaret();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const fieldClass = $derived(
|
|
280
|
+
enhanced
|
|
281
|
+
? "absolute inset-0 size-full min-w-0 cursor-text border-0 bg-transparent p-0 text-base text-transparent caret-transparent outline-none selection:bg-transparent disabled:cursor-not-allowed"
|
|
282
|
+
: cn(
|
|
283
|
+
sizing.box,
|
|
284
|
+
"w-full rounded-control border bg-input px-3 text-center text-xl font-medium tracking-[0.35em] text-body tabular-nums hover:bg-input-hover disabled:cursor-not-allowed disabled:bg-input-disabled disabled:text-action-disabled-text focus-ring",
|
|
285
|
+
variantClass,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
</script>
|
|
289
|
+
|
|
290
|
+
{#snippet cell(place: string, index: number)}
|
|
291
|
+
<span
|
|
292
|
+
data-code-cell
|
|
293
|
+
data-active={index === active ? "" : undefined}
|
|
294
|
+
style:max-inline-size="{sizing.maxRem}rem"
|
|
295
|
+
style:min-inline-size="{sizing.minEm}em"
|
|
296
|
+
class={cn(
|
|
297
|
+
"flex flex-1 items-center justify-center rounded-control border bg-input font-medium text-body tabular-nums transition-colors duration-150",
|
|
298
|
+
sizing.box,
|
|
299
|
+
sizing.text,
|
|
300
|
+
variantClass,
|
|
301
|
+
disabled && "cursor-not-allowed bg-input-disabled text-action-disabled-text",
|
|
302
|
+
index === active && RING,
|
|
303
|
+
)}
|
|
304
|
+
>
|
|
305
|
+
{place}
|
|
306
|
+
</span>
|
|
307
|
+
{/snippet}
|
|
308
|
+
|
|
309
|
+
<div class={cn("w-full", className)}>
|
|
310
|
+
<label for={inputId} class={cn("mb-2 block text-sm font-medium text-label", labelHidden && "sr-only")}>
|
|
311
|
+
{shownLabel}
|
|
312
|
+
</label>
|
|
313
|
+
<div
|
|
314
|
+
class="relative"
|
|
315
|
+
style:max-inline-size="calc({length} * {sizing.maxRem}rem + {length - 1 - Math.max(groupSizes.length - 1, 0)} * {CODE_FIELD_GAP_REM}rem + {Math.max(groupSizes.length - 1, 0)} * {CODE_FIELD_GROUP_GAP_REM}rem)"
|
|
316
|
+
bind:this={cells}
|
|
317
|
+
>
|
|
318
|
+
{#if enhanced}
|
|
319
|
+
<div
|
|
320
|
+
class={cn("flex flex-wrap", grouped ? "gap-x-4 gap-y-2" : "gap-2")}
|
|
321
|
+
aria-hidden="true"
|
|
322
|
+
data-code-cells
|
|
323
|
+
data-grouped={grouped ? "" : undefined}
|
|
324
|
+
>
|
|
325
|
+
{#if grouped}
|
|
326
|
+
{#each groupSizes as groupSize, group (group)}
|
|
327
|
+
<div
|
|
328
|
+
data-code-group
|
|
329
|
+
class="flex gap-2"
|
|
330
|
+
style:flex="{groupSize} 1 0"
|
|
331
|
+
style:min-inline-size="calc({groupSize} * {sizing.minEm * sizing.digitRem}rem + {groupSize - 1} * {CODE_FIELD_GAP_REM}rem)"
|
|
332
|
+
style:max-inline-size="calc({groupSize} * {sizing.maxRem}rem + {groupSize - 1} * {CODE_FIELD_GAP_REM}rem)"
|
|
333
|
+
>
|
|
334
|
+
{#each places.slice(groupStarts[group], groupStarts[group] + groupSize) as place, offset (offset)}
|
|
335
|
+
{@render cell(place, groupStarts[group] + offset)}
|
|
336
|
+
{/each}
|
|
337
|
+
</div>
|
|
338
|
+
{/each}
|
|
339
|
+
{:else}
|
|
340
|
+
{#each places as place, index (index)}
|
|
341
|
+
{@render cell(place, index)}
|
|
342
|
+
{/each}
|
|
343
|
+
{/if}
|
|
344
|
+
</div>
|
|
345
|
+
{/if}
|
|
346
|
+
<input
|
|
347
|
+
bind:this={input}
|
|
348
|
+
id={inputId}
|
|
349
|
+
type="text"
|
|
350
|
+
{name}
|
|
351
|
+
bind:value
|
|
352
|
+
{required}
|
|
353
|
+
{disabled}
|
|
354
|
+
{readonly}
|
|
355
|
+
inputmode={mode === "numeric" ? "numeric" : "text"}
|
|
356
|
+
autocomplete="one-time-code"
|
|
357
|
+
autocapitalize="off"
|
|
358
|
+
autocorrect="off"
|
|
359
|
+
spellcheck={false}
|
|
360
|
+
class={fieldClass}
|
|
361
|
+
aria-label={ariaLabel}
|
|
362
|
+
aria-invalid={status.variant === "error" ? "true" : undefined}
|
|
363
|
+
aria-required={required ? "true" : undefined}
|
|
364
|
+
aria-describedby={fieldDescribedBy(inputId, status, [describedBy, lengthId].filter(Boolean).join(" "))}
|
|
365
|
+
{...restProps}
|
|
366
|
+
oninput={handleInput}
|
|
367
|
+
onkeydown={handleKeydown}
|
|
368
|
+
onpaste={handlePaste}
|
|
369
|
+
onclick={handleClick}
|
|
370
|
+
onkeyup={syncCaret}
|
|
371
|
+
onselect={syncCaret}
|
|
372
|
+
onfocus={(event) => {
|
|
373
|
+
focused = true;
|
|
374
|
+
syncCaret();
|
|
375
|
+
restProps.onfocus?.(event);
|
|
376
|
+
}}
|
|
377
|
+
onblur={(event) => {
|
|
378
|
+
focused = false;
|
|
379
|
+
onblur?.(event);
|
|
380
|
+
}}
|
|
381
|
+
/>
|
|
382
|
+
</div>
|
|
383
|
+
<span id={lengthId} class="sr-only">{text.description(length, mode)}</span>
|
|
384
|
+
<FieldMessages fieldId={inputId} {status} {hint} />
|
|
385
|
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
2
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
3
|
+
import type { SizeVariant } from "../types/variants.js";
|
|
4
|
+
import { type CodeFieldMode, type CodeFieldStrings } from "../util/code-field.js";
|
|
5
|
+
/**
|
|
6
|
+
* A field for a one-time or verification code. One real `<input>` holds
|
|
7
|
+
* the code, so autofill from a message, paste, the numeric keyboard, form
|
|
8
|
+
* posting by `name` and a single accessible name all work as on `Input`;
|
|
9
|
+
* the boxes are drawn under it and hidden from assistive technology.
|
|
10
|
+
* Other attributes (`autocomplete`, `inputmode`, `data-*`, ...) land on
|
|
11
|
+
* the `<input>`.
|
|
12
|
+
*/
|
|
13
|
+
type Props = Omit<HTMLInputAttributes, "class" | "size" | "value" | "type" | "id" | "name" | "required" | "disabled" | "readonly" | "maxlength" | "oninput" | "onblur" | "aria-label" | "aria-describedby"> & {
|
|
14
|
+
/** Omit to auto-generate. */
|
|
15
|
+
id?: string;
|
|
16
|
+
/** The code typed so far. Bindable. */
|
|
17
|
+
value?: string;
|
|
18
|
+
/** The name the code is posted under, with or without scripts. */
|
|
19
|
+
name?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
/** Places in the code. A whole number from 1 to 12. */
|
|
22
|
+
length?: number;
|
|
23
|
+
/** `numeric` (default): digits only. `alphanumeric` also takes letters. */
|
|
24
|
+
mode?: CodeFieldMode;
|
|
25
|
+
/** Box size: `sm` (44px tall, a compact row), `md` (default), `lg`. */
|
|
26
|
+
size?: SizeVariant;
|
|
27
|
+
/**
|
|
28
|
+
* Draws the boxes in groups with a wider gap between them: a number is
|
|
29
|
+
* the size of each group (`4` makes 4 and 4 of 8), a list gives each
|
|
30
|
+
* group's size. Only the drawing changes: one input, one value, and a
|
|
31
|
+
* paste with spaces or dashes still fills it. A row that cannot fit at
|
|
32
|
+
* large text or a narrow width wraps between groups.
|
|
33
|
+
*/
|
|
34
|
+
groups?: number | number[];
|
|
35
|
+
/** Names the field. Defaults to the provider's `codeField.label`, then "Verification code". */
|
|
36
|
+
label?: string;
|
|
37
|
+
/** Keeps the label for assistive technology and takes it off the screen. */
|
|
38
|
+
labelHidden?: boolean;
|
|
39
|
+
required?: boolean;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
readonly?: boolean;
|
|
42
|
+
variant?: SemanticVariant;
|
|
43
|
+
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
44
|
+
message?: string;
|
|
45
|
+
/** Help text under the field, read out with it. */
|
|
46
|
+
hint?: string;
|
|
47
|
+
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
48
|
+
error?: string;
|
|
49
|
+
/** Called with the code when the last place is filled by typing, pasting or autofill. It does not submit anything. */
|
|
50
|
+
oncomplete?: (value: string) => void;
|
|
51
|
+
/** Overrides for the built-in strings: label, description. */
|
|
52
|
+
strings?: Partial<CodeFieldStrings>;
|
|
53
|
+
oninput?: (event: Event) => void;
|
|
54
|
+
onblur?: (event: Event) => void;
|
|
55
|
+
"aria-label"?: string;
|
|
56
|
+
/** Ids of other elements that describe the field. The length, the hint and the message are added after them. */
|
|
57
|
+
"aria-describedby"?: string | null;
|
|
58
|
+
};
|
|
59
|
+
declare const CodeField: import("svelte").Component<Props, {}, "value">;
|
|
60
|
+
type CodeField = ReturnType<typeof CodeField>;
|
|
61
|
+
export default CodeField;
|