zabi-components 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2935 -0
- package/README.md +243 -10
- package/THEME.md +162 -332
- package/THEMING.md +1419 -259
- package/dist/atoms/ActionPanel.svelte +15 -5
- package/dist/atoms/Avatar.svelte +120 -0
- package/dist/atoms/Avatar.svelte.d.ts +46 -0
- package/dist/atoms/Badge.svelte +71 -13
- package/dist/atoms/Badge.svelte.d.ts +8 -2
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +217 -30
- package/dist/atoms/Button.svelte.d.ts +33 -4
- package/dist/atoms/Card.svelte +182 -26
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/CardHeader.svelte +16 -4
- package/dist/atoms/Checkbox.svelte +81 -21
- package/dist/atoms/Checkbox.svelte.d.ts +31 -2
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeBlock.svelte +20 -2
- package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +260 -49
- package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
- package/dist/atoms/Container.svelte +16 -8
- package/dist/atoms/DateField.svelte +145 -0
- package/dist/atoms/DateField.svelte.d.ts +81 -0
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +176 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +63 -5
- package/dist/atoms/Heading.svelte.d.ts +14 -0
- package/dist/atoms/IconButton.svelte +294 -26
- package/dist/atoms/IconButton.svelte.d.ts +62 -4
- package/dist/atoms/Input.svelte +259 -53
- package/dist/atoms/Input.svelte.d.ts +33 -2
- package/dist/atoms/List.svelte +5 -1
- package/dist/atoms/ListItem.svelte +70 -13
- package/dist/atoms/ListItem.svelte.d.ts +11 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +99 -19
- package/dist/atoms/Progress.svelte.d.ts +15 -1
- package/dist/atoms/Radio.svelte +51 -3
- package/dist/atoms/Radio.svelte.d.ts +30 -2
- package/dist/atoms/Rating.svelte +620 -0
- package/dist/atoms/Rating.svelte.d.ts +77 -0
- package/dist/atoms/Select.svelte +438 -98
- package/dist/atoms/Select.svelte.d.ts +61 -3
- package/dist/atoms/SelectionControl.svelte +193 -23
- package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
- package/dist/atoms/Skeleton.svelte +114 -18
- package/dist/atoms/Skeleton.svelte.d.ts +30 -3
- package/dist/atoms/SkeletonGroup.svelte +43 -0
- package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
- package/dist/atoms/Slider.svelte +344 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Sparkline.svelte +109 -0
- package/dist/atoms/Sparkline.svelte.d.ts +27 -0
- package/dist/atoms/SpeechBubble.svelte +187 -0
- package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +151 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +472 -0
- package/dist/atoms/TemporalField.svelte.d.ts +48 -0
- package/dist/atoms/Text.svelte +23 -11
- package/dist/atoms/Text.svelte.d.ts +6 -3
- package/dist/atoms/Textarea.svelte +150 -44
- package/dist/atoms/Textarea.svelte.d.ts +35 -2
- package/dist/atoms/ThemeToggle.svelte +223 -44
- package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +151 -0
- package/dist/atoms/TimeField.svelte.d.ts +85 -0
- package/dist/atoms/Toast.svelte +22 -8
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +98 -14
- package/dist/atoms/Toggle.svelte.d.ts +26 -2
- package/dist/atoms/Tooltip.svelte +528 -24
- package/dist/atoms/Tooltip.svelte.d.ts +18 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +38 -0
- package/dist/atoms/index.js +29 -0
- package/dist/atoms/selection-control.styles.d.ts +47 -5
- package/dist/atoms/selection-control.styles.js +52 -10
- package/dist/components/atoms/index.d.ts +38 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +47 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +104 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map.d.ts +11 -0
- package/dist/components/map.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +51 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +83 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +10 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +24 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +79 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +39 -0
- package/dist/components/util/avatar.d.ts.map +1 -0
- package/dist/components/util/bottom-sheet.d.ts +30 -0
- package/dist/components/util/bottom-sheet.d.ts.map +1 -0
- package/dist/components/util/bottom-tab-bar.d.ts +41 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
- package/dist/components/util/calendar.d.ts +56 -0
- package/dist/components/util/calendar.d.ts.map +1 -0
- package/dist/components/util/chart.d.ts +140 -0
- package/dist/components/util/chart.d.ts.map +1 -0
- package/dist/components/util/chip.d.ts +66 -0
- package/dist/components/util/chip.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts.map +1 -1
- package/dist/components/util/code-field.d.ts +63 -0
- package/dist/components/util/code-field.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +64 -0
- package/dist/components/util/collapsible.d.ts.map +1 -0
- package/dist/components/util/confirm-dialog.d.ts +12 -0
- package/dist/components/util/confirm-dialog.d.ts.map +1 -0
- package/dist/components/util/date.d.ts +96 -0
- package/dist/components/util/date.d.ts.map +1 -0
- package/dist/components/util/display-voice.d.ts +10 -0
- package/dist/components/util/display-voice.d.ts.map +1 -0
- package/dist/components/util/drawer.d.ts +13 -0
- package/dist/components/util/drawer.d.ts.map +1 -0
- package/dist/components/util/dropdown.d.ts +47 -0
- package/dist/components/util/dropdown.d.ts.map +1 -0
- package/dist/components/util/field-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +45 -0
- package/dist/components/util/field.d.ts.map +1 -0
- package/dist/components/util/fit-in-viewport.d.ts +201 -0
- package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +77 -2
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/hydration.d.ts +45 -0
- package/dist/components/util/hydration.d.ts.map +1 -0
- package/dist/components/util/keyboard-inset.d.ts +18 -0
- package/dist/components/util/keyboard-inset.d.ts.map +1 -0
- package/dist/components/util/map-view.d.ts +144 -0
- package/dist/components/util/map-view.d.ts.map +1 -0
- package/dist/components/util/media-grid.d.ts +51 -0
- package/dist/components/util/media-grid.d.ts.map +1 -0
- package/dist/components/util/media.d.ts +6 -0
- package/dist/components/util/media.d.ts.map +1 -0
- package/dist/components/util/overlay-chrome.d.ts +53 -0
- package/dist/components/util/overlay-chrome.d.ts.map +1 -0
- package/dist/components/util/overlay.d.ts +72 -0
- package/dist/components/util/overlay.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +234 -0
- package/dist/components/util/photo.d.ts.map +1 -0
- package/dist/components/util/portal.d.ts +23 -0
- package/dist/components/util/portal.d.ts.map +1 -0
- package/dist/components/util/progress.d.ts +19 -0
- package/dist/components/util/progress.d.ts.map +1 -0
- package/dist/components/util/pull-to-refresh.d.ts +27 -0
- package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
- package/dist/components/util/radio-keys.d.ts +25 -0
- package/dist/components/util/radio-keys.d.ts.map +1 -0
- package/dist/components/util/rating.d.ts +23 -0
- package/dist/components/util/rating.d.ts.map +1 -0
- package/dist/components/util/ready-made-strings.d.ts +77 -0
- package/dist/components/util/ready-made-strings.d.ts.map +1 -0
- package/dist/components/util/scroll-edge.d.ts +33 -0
- package/dist/components/util/scroll-edge.d.ts.map +1 -0
- package/dist/components/util/scroll-padding.d.ts +20 -0
- package/dist/components/util/scroll-padding.d.ts.map +1 -0
- package/dist/components/util/segmented-control.d.ts +16 -0
- package/dist/components/util/segmented-control.d.ts.map +1 -0
- package/dist/components/util/select.d.ts +41 -0
- package/dist/components/util/select.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +84 -0
- package/dist/components/util/sheet-drag.d.ts.map +1 -0
- package/dist/components/util/sidebar.d.ts +63 -0
- package/dist/components/util/sidebar.d.ts.map +1 -0
- package/dist/components/util/skeleton.d.ts +15 -0
- package/dist/components/util/skeleton.d.ts.map +1 -0
- package/dist/components/util/sliding-indicator.d.ts +25 -0
- package/dist/components/util/sliding-indicator.d.ts.map +1 -0
- package/dist/components/util/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/stepper.d.ts +40 -0
- package/dist/components/util/stepper.d.ts.map +1 -0
- package/dist/components/util/swipeable-list-item.d.ts +36 -0
- package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
- package/dist/components/util/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.d.ts.map +1 -0
- package/dist/components/util/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +133 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tone-surface.d.ts +34 -0
- package/dist/components/util/tone-surface.d.ts.map +1 -0
- package/dist/components/util/tooltip.d.ts +10 -0
- package/dist/components/util/tooltip.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +35 -0
- package/dist/components/util/top-navbar.d.ts.map +1 -0
- package/dist/components/util/touch-target.d.ts +31 -0
- package/dist/components/util/touch-target.d.ts.map +1 -0
- package/dist/components/util/zabi-strings.d.ts +142 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +173 -0
- package/dist/create-theme/index.d.ts +190 -0
- package/dist/create-theme/index.js +1034 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +238 -0
- package/dist/create-theme/theme-data.js +2644 -0
- package/dist/index.d.ts +104 -2
- package/dist/index.js +75 -2
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +63 -16
- package/dist/molecules/Alert.svelte.d.ts +14 -1
- package/dist/molecules/AppBar.svelte +674 -0
- package/dist/molecules/AppBar.svelte.d.ts +131 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +608 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
- package/dist/molecules/BottomTabBar.svelte +403 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +347 -0
- package/dist/molecules/Collapsible.svelte.d.ts +61 -0
- package/dist/molecules/CollapsibleGroup.svelte +136 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +16 -4
- package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
- package/dist/molecules/ConfirmDialog.svelte +257 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
- package/dist/molecules/ContactForm.svelte +34 -17
- package/dist/molecules/ContactForm.svelte.d.ts +8 -0
- package/dist/molecules/Drawer.svelte +415 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +517 -48
- package/dist/molecules/Dropdown.svelte.d.ts +47 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +55 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/FormField.svelte +9 -1
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +492 -69
- package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
- package/dist/molecules/LineChart.svelte +459 -0
- package/dist/molecules/LineChart.svelte.d.ts +53 -0
- package/dist/molecules/MapView.svelte +565 -0
- package/dist/molecules/MapView.svelte.d.ts +88 -0
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +293 -72
- package/dist/molecules/Modal.svelte.d.ts +51 -2
- package/dist/molecules/NavigationMenuContent.svelte +58 -1
- package/dist/molecules/NavigationMenuItem.svelte +0 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuList.svelte +10 -3
- package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
- package/dist/molecules/Page.svelte +32 -2
- package/dist/molecules/Page.svelte.d.ts +11 -0
- package/dist/molecules/PhotoGrid.svelte +523 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
- package/dist/molecules/PhotoViewer.svelte +1213 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/PropsTable.svelte +22 -10
- package/dist/molecules/PropsTable.svelte.d.ts +3 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +72 -18
- package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
- package/dist/molecules/Section.svelte +1 -1
- package/dist/molecules/SegmentedControl.svelte +456 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
- package/dist/molecules/SidebarBrandHeader.svelte +11 -1
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
- package/dist/molecules/SidebarFooter.svelte +30 -7
- package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SlideUp.svelte +220 -61
- package/dist/molecules/SlideUp.svelte.d.ts +26 -0
- package/dist/molecules/SortableList.svelte +633 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +562 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +250 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +310 -9
- package/dist/molecules/Tabs.svelte.d.ts +7 -0
- package/dist/molecules/Toaster.svelte +514 -5
- package/dist/molecules/Toaster.svelte.d.ts +153 -2
- package/dist/molecules/ToasterToast.svelte +495 -77
- package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
- package/dist/molecules/UnsavedChangesBar.svelte +223 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +51 -0
- package/dist/molecules/index.js +31 -0
- package/dist/molecules/toast-store.d.ts +83 -7
- package/dist/molecules/toast-store.js +25 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +696 -0
- package/dist/organisms/AppShell.svelte.d.ts +157 -0
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarAccountPanel.svelte +107 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
- package/dist/organisms/SidebarNavigation.svelte +111 -43
- package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
- package/dist/organisms/SidebarPanel.svelte +34 -17
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +164 -14
- package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
- package/dist/organisms/TopNavbar.svelte +353 -39
- package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +10 -0
- package/dist/organisms/index.js +7 -0
- package/dist/types/components.d.ts +21 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +1093 -26
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +28 -1
- package/dist/util/app-shell.d.ts +78 -0
- package/dist/util/app-shell.js +97 -0
- package/dist/util/avatar.d.ts +38 -0
- package/dist/util/avatar.js +52 -0
- package/dist/util/bottom-sheet.d.ts +29 -0
- package/dist/util/bottom-sheet.js +43 -0
- package/dist/util/bottom-tab-bar.d.ts +40 -0
- package/dist/util/bottom-tab-bar.js +54 -0
- package/dist/util/calendar.d.ts +55 -0
- package/dist/util/calendar.js +74 -0
- package/dist/util/chart.d.ts +139 -0
- package/dist/util/chart.js +305 -0
- package/dist/util/chip.d.ts +65 -0
- package/dist/util/chip.js +58 -0
- package/dist/util/cn.js +28 -2
- package/dist/util/code-field.d.ts +62 -0
- package/dist/util/code-field.js +112 -0
- package/dist/util/collapsible.d.ts +63 -0
- package/dist/util/collapsible.js +23 -0
- package/dist/util/confirm-dialog.d.ts +11 -0
- package/dist/util/confirm-dialog.js +2 -0
- package/dist/util/date.d.ts +95 -0
- package/dist/util/date.js +237 -0
- package/dist/util/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -0
- package/dist/util/drawer.d.ts +12 -0
- package/dist/util/drawer.js +7 -0
- package/dist/util/dropdown.d.ts +46 -0
- package/dist/util/dropdown.js +21 -0
- package/dist/util/field-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +44 -0
- package/dist/util/field.js +38 -0
- package/dist/util/fit-in-viewport.d.ts +200 -0
- package/dist/util/fit-in-viewport.js +399 -0
- package/dist/util/focus-utils.d.ts +77 -2
- package/dist/util/focus-utils.js +186 -9
- package/dist/util/hydration.d.ts +44 -0
- package/dist/util/hydration.js +58 -0
- package/dist/util/keyboard-inset.d.ts +17 -0
- package/dist/util/keyboard-inset.js +53 -0
- package/dist/util/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -0
- package/dist/util/media-grid.d.ts +50 -0
- package/dist/util/media-grid.js +81 -0
- package/dist/util/media.d.ts +5 -0
- package/dist/util/media.js +10 -0
- package/dist/util/overlay-chrome.d.ts +52 -0
- package/dist/util/overlay-chrome.js +56 -0
- package/dist/util/overlay.d.ts +71 -0
- package/dist/util/overlay.js +240 -0
- package/dist/util/photo.d.ts +233 -0
- package/dist/util/photo.js +209 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -0
- package/dist/util/pull-to-refresh.d.ts +26 -0
- package/dist/util/pull-to-refresh.js +30 -0
- package/dist/util/radio-keys.d.ts +24 -0
- package/dist/util/radio-keys.js +49 -0
- package/dist/util/rating.d.ts +22 -0
- package/dist/util/rating.js +26 -0
- package/dist/util/ready-made-strings.d.ts +76 -0
- package/dist/util/ready-made-strings.js +67 -0
- package/dist/util/scroll-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -0
- package/dist/util/scroll-padding.d.ts +19 -0
- package/dist/util/scroll-padding.js +44 -0
- package/dist/util/segmented-control.d.ts +15 -0
- package/dist/util/segmented-control.js +1 -0
- package/dist/util/select.d.ts +40 -0
- package/dist/util/select.js +63 -0
- package/dist/util/sheet-drag.d.ts +83 -0
- package/dist/util/sheet-drag.js +311 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/skeleton.d.ts +14 -0
- package/dist/util/skeleton.js +22 -0
- package/dist/util/sliding-indicator.d.ts +24 -0
- package/dist/util/sliding-indicator.js +44 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/stepper.d.ts +39 -0
- package/dist/util/stepper.js +38 -0
- package/dist/util/swipeable-list-item.d.ts +35 -0
- package/dist/util/swipeable-list-item.js +32 -0
- package/dist/util/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +132 -0
- package/dist/util/toaster.js +130 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +34 -0
- package/dist/util/top-navbar.js +28 -0
- package/dist/util/touch-target.d.ts +30 -0
- package/dist/util/touch-target.js +30 -0
- package/dist/util/zabi-strings.d.ts +141 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +11 -2
- package/dist/zabi-components-theme-dark-only.css +870 -258
- package/dist/zabi-components-theme-dark.css +870 -258
- package/dist/zabi-components-theme-only.css +2419 -368
- package/dist/zabi-components-theme.css +2419 -368
- package/dist/zabi-components.css +6032 -711
- package/docs/lucide-icons.md +40 -17
- package/docs/theme-imports.md +81 -11
- package/package.json +45 -8
package/dist/util/focus-utils.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/** From `app.css` `@layer components` — import the library/app stylesheet. */
|
|
3
3
|
export const FOCUS_BRAND_CLASS = "focus-brand";
|
|
4
4
|
export const FOCUS_NAV_CLASS = "focus-nav";
|
|
5
|
+
/** Every dialog panel: `dialog` and `alertdialog` are both dialogs to a focus trap. */
|
|
6
|
+
export const DIALOG_SELECTOR = '[role="dialog"], [role="alertdialog"]';
|
|
5
7
|
export function getFocusableElements(container) {
|
|
6
8
|
const selector = [
|
|
7
9
|
'button:not([disabled])',
|
|
@@ -12,10 +14,81 @@ export function getFocusableElements(container) {
|
|
|
12
14
|
'[tabindex]:not([tabindex="-1"])',
|
|
13
15
|
].join(', ');
|
|
14
16
|
return Array.from(container.querySelectorAll(selector)).filter((el) => {
|
|
17
|
+
// `tabindex="-1"` takes a control out of the Tab order whatever it
|
|
18
|
+
// is: the items of a list with one roving Tab stop are buttons,
|
|
19
|
+
// and counting them made a focus trap think the last of them was
|
|
20
|
+
// its last stop, so Tab from the real one left the dialog.
|
|
21
|
+
if (el.getAttribute('tabindex') === '-1')
|
|
22
|
+
return false;
|
|
15
23
|
const style = window.getComputedStyle(el);
|
|
16
24
|
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
17
25
|
});
|
|
18
26
|
}
|
|
27
|
+
/** The region a `Toaster` renders its toasts in. */
|
|
28
|
+
export const TOAST_REGION_SELECTOR = "[data-zabi-toaster]";
|
|
29
|
+
/**
|
|
30
|
+
* Whether an event's target is in a toast. A toast is drawn over whatever is
|
|
31
|
+
* open, and a press on it is for the toast (to hold it, read it, dismiss it):
|
|
32
|
+
* a menu or a panel that closes on a press outside itself asks this first,
|
|
33
|
+
* and leaves a press on a toast alone.
|
|
34
|
+
*/
|
|
35
|
+
export function isInsideToastRegion(target) {
|
|
36
|
+
if (typeof Node === "undefined" || !(target instanceof Node))
|
|
37
|
+
return false;
|
|
38
|
+
// A press on text can report the text node.
|
|
39
|
+
const element = target instanceof Element ? target : target.parentElement;
|
|
40
|
+
return !!element?.closest(TOAST_REGION_SELECTOR);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The controls of the toasts on screen. A toast is drawn over a modal
|
|
44
|
+
* overlay and can cover its buttons, so a focus trap that kept the keyboard
|
|
45
|
+
* out of it would leave no way to dismiss it: the trap takes these in, after
|
|
46
|
+
* the overlay's own controls.
|
|
47
|
+
*/
|
|
48
|
+
export function getToastFocusables() {
|
|
49
|
+
if (typeof document === "undefined")
|
|
50
|
+
return [];
|
|
51
|
+
return Array.from(document.querySelectorAll(TOAST_REGION_SELECTOR)).flatMap((region) => getFocusableElements(region));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One step of the Tab cycle of a modal overlay: through the controls of
|
|
55
|
+
* `container`, then through the toasts on screen, and round again. Focusables
|
|
56
|
+
* are looked up on every Tab, so what is added while the overlay is open is
|
|
57
|
+
* in the cycle. Focus that is nowhere in it goes to the first control.
|
|
58
|
+
*/
|
|
59
|
+
export function cycleTrappedFocus(container, event) {
|
|
60
|
+
const focusable = [...getFocusableElements(container), ...getToastFocusables()];
|
|
61
|
+
if (focusable.length === 0) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
container.focus();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const first = focusable[0];
|
|
67
|
+
const last = focusable[focusable.length - 1];
|
|
68
|
+
const active = document.activeElement;
|
|
69
|
+
if (!active || !focusable.includes(active)) {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
first.focus();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (event.shiftKey && active === first) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
last.focus();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!event.shiftKey && active === last) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
first.focus();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Between the overlay and the toasts the browser's own order would walk
|
|
85
|
+
// the page that lies between them in the document: step across by hand.
|
|
86
|
+
const next = focusable[focusable.indexOf(active) + (event.shiftKey ? -1 : 1)];
|
|
87
|
+
if (active.closest(TOAST_REGION_SELECTOR) !== next.closest(TOAST_REGION_SELECTOR)) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
next.focus();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
19
92
|
/** Tab wraps first↔last inside `container`. Caller should pair with `saveFocus` / `returnFocus`. */
|
|
20
93
|
export function trapFocus(container) {
|
|
21
94
|
const focusableElements = getFocusableElements(container);
|
|
@@ -53,7 +126,11 @@ export function trapFocus(container) {
|
|
|
53
126
|
container.removeEventListener('keydown', handleKeydown);
|
|
54
127
|
};
|
|
55
128
|
}
|
|
56
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* One entry per overlay open; `returnFocus` pops LIFO (nested modals). The id
|
|
131
|
+
* is kept beside the element: the element may be gone by the time the overlay
|
|
132
|
+
* closes, replaced by another that carries the same id.
|
|
133
|
+
*/
|
|
57
134
|
const focusReturnStack = [];
|
|
58
135
|
export function saveFocus() {
|
|
59
136
|
if (typeof document === 'undefined') {
|
|
@@ -61,22 +138,31 @@ export function saveFocus() {
|
|
|
61
138
|
}
|
|
62
139
|
const active = document.activeElement;
|
|
63
140
|
if (active instanceof HTMLElement && active !== document.body) {
|
|
64
|
-
focusReturnStack.push(active);
|
|
141
|
+
focusReturnStack.push({ element: active, id: active.id });
|
|
65
142
|
}
|
|
66
143
|
else {
|
|
67
144
|
focusReturnStack.push(null);
|
|
68
145
|
}
|
|
69
146
|
}
|
|
70
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* Restores focus from the last `saveFocus`. If that element has left the
|
|
149
|
+
* document, the element that now has its id takes the focus instead (a
|
|
150
|
+
* dropzone swapped for its Change button, say); with neither, nothing happens.
|
|
151
|
+
*/
|
|
71
152
|
export function returnFocus() {
|
|
72
153
|
if (typeof document === 'undefined') {
|
|
73
154
|
return;
|
|
74
155
|
}
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
156
|
+
const saved = focusReturnStack.pop();
|
|
157
|
+
if (!saved) {
|
|
77
158
|
return;
|
|
78
159
|
}
|
|
79
|
-
|
|
160
|
+
const el = saved.element.isConnected
|
|
161
|
+
? saved.element
|
|
162
|
+
: saved.id
|
|
163
|
+
? document.getElementById(saved.id)
|
|
164
|
+
: null;
|
|
165
|
+
if (!el || typeof el.focus !== 'function') {
|
|
80
166
|
return;
|
|
81
167
|
}
|
|
82
168
|
try {
|
|
@@ -86,9 +172,100 @@ export function returnFocus() {
|
|
|
86
172
|
/* embeds / shadow roots may reject programmatic focus */
|
|
87
173
|
}
|
|
88
174
|
}
|
|
89
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Moves focus into an overlay: to its first focusable element, or to the
|
|
177
|
+
* container itself when it has none (give the container `tabindex="-1"`), so
|
|
178
|
+
* focus never stays on the opener behind the backdrop.
|
|
179
|
+
*
|
|
180
|
+
* `preferred` is a CSS selector, looked up inside the container: the matching
|
|
181
|
+
* control takes focus instead of the first one, if it can take focus.
|
|
182
|
+
*/
|
|
183
|
+
export function focusFirstElement(container, preferred) {
|
|
90
184
|
const focusableElements = getFocusableElements(container);
|
|
91
|
-
|
|
92
|
-
focusableElements
|
|
185
|
+
const wanted = preferred
|
|
186
|
+
? focusableElements.find((element) => element.matches(preferred))
|
|
187
|
+
: undefined;
|
|
188
|
+
(wanted ?? focusableElements[0] ?? container).focus();
|
|
189
|
+
}
|
|
190
|
+
/** Open modal overlays, oldest first. The last one is the one on top. */
|
|
191
|
+
const overlayStack = [];
|
|
192
|
+
/**
|
|
193
|
+
* Joins the stack of open modal overlays (Modal, SlideUp, a drawer).
|
|
194
|
+
*
|
|
195
|
+
* Every overlay shares one z-index, so without this the paint order is the
|
|
196
|
+
* DOM order, and an overlay opened later but placed earlier in the document
|
|
197
|
+
* opens behind the first. `depth` is 0 for the first overlay and one more than
|
|
198
|
+
* the highest open one after that: put it on the overlay root as
|
|
199
|
+
* `z-index: calc(var(--z-modal) + depth)`.
|
|
200
|
+
*
|
|
201
|
+
* `panel` is the element with the dialog role. Call this when the overlay
|
|
202
|
+
* opens, in the browser, and `leave()` when it closes.
|
|
203
|
+
*/
|
|
204
|
+
export function joinOverlayStack(panel) {
|
|
205
|
+
const depth = overlayStack.reduce((max, entry) => Math.max(max, entry.depth + 1), 0);
|
|
206
|
+
const entry = { panel, depth };
|
|
207
|
+
overlayStack.push(entry);
|
|
208
|
+
return {
|
|
209
|
+
depth,
|
|
210
|
+
leave() {
|
|
211
|
+
const index = overlayStack.indexOf(entry);
|
|
212
|
+
if (index !== -1)
|
|
213
|
+
overlayStack.splice(index, 1);
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Whether `panel` is the overlay on top of the stack: the one opened last. */
|
|
218
|
+
export function isTopOverlay(panel) {
|
|
219
|
+
return overlayStack[overlayStack.length - 1]?.panel === panel;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Keeps a modal overlay in charge of the keyboard when focus has left it
|
|
223
|
+
* without a Tab: the focused control was disabled or removed and the browser
|
|
224
|
+
* dropped focus on `<body>`, where the overlay's own key handlers never hear
|
|
225
|
+
* the key, and Tab would walk the page behind it.
|
|
226
|
+
*
|
|
227
|
+
* While installed, a Tab pressed with focus outside every dialog moves focus
|
|
228
|
+
* to the first focusable element of `container` (or to `container` itself),
|
|
229
|
+
* and Escape calls `onEscape`. Only the overlay on top of the stack acts (the
|
|
230
|
+
* one opened last), so every open overlay can install its own. An overlay that
|
|
231
|
+
* has not called `joinOverlayStack` is added to the stack for as long as the
|
|
232
|
+
* recovery is installed.
|
|
233
|
+
*
|
|
234
|
+
* `container` is the panel with `role="dialog"` or `"alertdialog"` and
|
|
235
|
+
* `aria-modal="true"`. Call it when the overlay opens, in the browser, and call
|
|
236
|
+
* the returned function when it closes.
|
|
237
|
+
*/
|
|
238
|
+
export function recoverStrayFocus(container, options = {}) {
|
|
239
|
+
const joined = overlayStack.some((entry) => entry.panel === container)
|
|
240
|
+
? null
|
|
241
|
+
: joinOverlayStack(container);
|
|
242
|
+
function handleKeydown(event) {
|
|
243
|
+
if (event.defaultPrevented)
|
|
244
|
+
return;
|
|
245
|
+
if (event.key !== 'Tab' && event.key !== 'Escape')
|
|
246
|
+
return;
|
|
247
|
+
// Inside a dialog, that dialog's own handlers deal with the key.
|
|
248
|
+
if (document.activeElement?.closest(DIALOG_SELECTOR))
|
|
249
|
+
return;
|
|
250
|
+
if (overlayStack[overlayStack.length - 1]?.panel !== container)
|
|
251
|
+
return;
|
|
252
|
+
// In a toast: part of the trap. Tab goes on through the toasts and
|
|
253
|
+
// back into the overlay; Escape is the toast region's own business.
|
|
254
|
+
if (document.activeElement?.closest(TOAST_REGION_SELECTOR)) {
|
|
255
|
+
if (event.key === 'Tab')
|
|
256
|
+
cycleTrappedFocus(container, event);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
if (event.key === 'Escape') {
|
|
261
|
+
options.onEscape?.(event);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
(getFocusableElements(container)[0] ?? container).focus();
|
|
93
265
|
}
|
|
266
|
+
document.addEventListener('keydown', handleKeydown);
|
|
267
|
+
return () => {
|
|
268
|
+
document.removeEventListener('keydown', handleKeydown);
|
|
269
|
+
joined?.leave();
|
|
270
|
+
};
|
|
94
271
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls that keep what a visitor chose before the page hydrated.
|
|
3
|
+
*
|
|
4
|
+
* A server-rendered page can be used before its scripts arrive: a checkbox
|
|
5
|
+
* can be ticked, a radio chosen. A component that writes `checked={…}` from
|
|
6
|
+
* its own state and listens for `change` loses that on hydration: Svelte sets
|
|
7
|
+
* `checked` from the state, which still says nothing was chosen.
|
|
8
|
+
*
|
|
9
|
+
* Svelte's own bindings do not. `bind:checked` and `bind:group` know when
|
|
10
|
+
* they run during hydration, compare the input with the markup the server
|
|
11
|
+
* sent (`defaultChecked`), and where they differ hand the input's state to
|
|
12
|
+
* the binding instead of overwriting it. They also put the binding back on a
|
|
13
|
+
* form reset. So the controls bind, and what they bind to is an accessor of
|
|
14
|
+
* their own: the place where a choice is taken in, by whatever road it came.
|
|
15
|
+
*
|
|
16
|
+
* `bind:group` takes only an identifier or a member expression, which is why
|
|
17
|
+
* this is an object with a `value` and not a pair of functions.
|
|
18
|
+
*/
|
|
19
|
+
/** What Svelte hands a group binding when no radio is checked: after a reset, or when hydration finds none. */
|
|
20
|
+
export type NoChoice = null | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The target of a radio's `bind:group={binding.value}`.
|
|
23
|
+
*
|
|
24
|
+
* `read` gives the chosen value; a radio is checked while its own value is
|
|
25
|
+
* that. `choose` is called when a radio was picked: by the user, or before
|
|
26
|
+
* hydration. `restore` is called with nothing chosen: a form is being reset
|
|
27
|
+
* (see `resetValueOf` for what to go back to).
|
|
28
|
+
*/
|
|
29
|
+
export declare function groupBinding<T>(read: () => T | NoChoice, choose: (value: T) => void, restore: () => void): {
|
|
30
|
+
value: T | NoChoice;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The value a group of radios goes back to on a form reset: that of the one
|
|
34
|
+
* whose `checked` attribute is set, in the type the component's options have
|
|
35
|
+
* (Svelte keeps it on the input as `__value`), or `undefined` when none is.
|
|
36
|
+
*
|
|
37
|
+
* It reads the attribute (`defaultChecked`), not `checked`: Svelte calls a
|
|
38
|
+
* binding's reset handler in a microtask after the `reset` event, and when a
|
|
39
|
+
* user pressed the reset button that is before the browser has reset
|
|
40
|
+
* anything. Svelte takes the `checked` attribute off a bound input once it
|
|
41
|
+
* has hydrated, so in practice a reset empties the group, as it does a bound
|
|
42
|
+
* native input.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resetValueOf<T>(inputs: Iterable<HTMLInputElement | undefined>): T | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls that keep what a visitor chose before the page hydrated.
|
|
3
|
+
*
|
|
4
|
+
* A server-rendered page can be used before its scripts arrive: a checkbox
|
|
5
|
+
* can be ticked, a radio chosen. A component that writes `checked={…}` from
|
|
6
|
+
* its own state and listens for `change` loses that on hydration: Svelte sets
|
|
7
|
+
* `checked` from the state, which still says nothing was chosen.
|
|
8
|
+
*
|
|
9
|
+
* Svelte's own bindings do not. `bind:checked` and `bind:group` know when
|
|
10
|
+
* they run during hydration, compare the input with the markup the server
|
|
11
|
+
* sent (`defaultChecked`), and where they differ hand the input's state to
|
|
12
|
+
* the binding instead of overwriting it. They also put the binding back on a
|
|
13
|
+
* form reset. So the controls bind, and what they bind to is an accessor of
|
|
14
|
+
* their own: the place where a choice is taken in, by whatever road it came.
|
|
15
|
+
*
|
|
16
|
+
* `bind:group` takes only an identifier or a member expression, which is why
|
|
17
|
+
* this is an object with a `value` and not a pair of functions.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The target of a radio's `bind:group={binding.value}`.
|
|
21
|
+
*
|
|
22
|
+
* `read` gives the chosen value; a radio is checked while its own value is
|
|
23
|
+
* that. `choose` is called when a radio was picked: by the user, or before
|
|
24
|
+
* hydration. `restore` is called with nothing chosen: a form is being reset
|
|
25
|
+
* (see `resetValueOf` for what to go back to).
|
|
26
|
+
*/
|
|
27
|
+
export function groupBinding(read, choose, restore) {
|
|
28
|
+
return {
|
|
29
|
+
get value() {
|
|
30
|
+
return read();
|
|
31
|
+
},
|
|
32
|
+
set value(next) {
|
|
33
|
+
if (next === null || next === undefined)
|
|
34
|
+
restore();
|
|
35
|
+
else
|
|
36
|
+
choose(next);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The value a group of radios goes back to on a form reset: that of the one
|
|
42
|
+
* whose `checked` attribute is set, in the type the component's options have
|
|
43
|
+
* (Svelte keeps it on the input as `__value`), or `undefined` when none is.
|
|
44
|
+
*
|
|
45
|
+
* It reads the attribute (`defaultChecked`), not `checked`: Svelte calls a
|
|
46
|
+
* binding's reset handler in a microtask after the `reset` event, and when a
|
|
47
|
+
* user pressed the reset button that is before the browser has reset
|
|
48
|
+
* anything. Svelte takes the `checked` attribute off a bound input once it
|
|
49
|
+
* has hydrated, so in practice a reset empties the group, as it does a bound
|
|
50
|
+
* native input.
|
|
51
|
+
*/
|
|
52
|
+
export function resetValueOf(inputs) {
|
|
53
|
+
for (const input of inputs) {
|
|
54
|
+
if (input?.defaultChecked)
|
|
55
|
+
return input.__value;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much of the bottom of the page the on-screen keyboard covers.
|
|
3
|
+
*
|
|
4
|
+
* Browsers disagree on what a keyboard does. Some shrink the layout viewport,
|
|
5
|
+
* and everything pinned to the bottom moves up by itself. Others (iOS Safari,
|
|
6
|
+
* and Chrome on Android by default) leave the layout alone and only shrink
|
|
7
|
+
* the *visual* viewport, so a bar pinned to the bottom ends up behind the
|
|
8
|
+
* keyboard. The difference between the two viewports is the part to lift a
|
|
9
|
+
* bar by; where the layout has already shrunk, it is 0.
|
|
10
|
+
*/
|
|
11
|
+
/** The covered height in px, or 0 where it cannot be told (the server, an old browser). */
|
|
12
|
+
export declare function readKeyboardInset(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Calls `onChange` with the inset now, and again whenever it changes. Call it
|
|
15
|
+
* in the browser; the returned function stops it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function watchKeyboardInset(onChange: (inset: number) => void): () => void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much of the bottom of the page the on-screen keyboard covers.
|
|
3
|
+
*
|
|
4
|
+
* Browsers disagree on what a keyboard does. Some shrink the layout viewport,
|
|
5
|
+
* and everything pinned to the bottom moves up by itself. Others (iOS Safari,
|
|
6
|
+
* and Chrome on Android by default) leave the layout alone and only shrink
|
|
7
|
+
* the *visual* viewport, so a bar pinned to the bottom ends up behind the
|
|
8
|
+
* keyboard. The difference between the two viewports is the part to lift a
|
|
9
|
+
* bar by; where the layout has already shrunk, it is 0.
|
|
10
|
+
*/
|
|
11
|
+
/** The covered height in px, or 0 where it cannot be told (the server, an old browser). */
|
|
12
|
+
export function readKeyboardInset() {
|
|
13
|
+
if (typeof window === "undefined")
|
|
14
|
+
return 0;
|
|
15
|
+
const viewport = window.visualViewport;
|
|
16
|
+
if (!viewport)
|
|
17
|
+
return 0;
|
|
18
|
+
// Pinch zoom shrinks the visual viewport too, and is not a keyboard.
|
|
19
|
+
if (Math.abs(viewport.scale - 1) > 0.01)
|
|
20
|
+
return 0;
|
|
21
|
+
const layoutHeight = document.documentElement.clientHeight;
|
|
22
|
+
const inset = layoutHeight - viewport.height - viewport.offsetTop;
|
|
23
|
+
// A pixel of rounding is not a keyboard either.
|
|
24
|
+
return inset > 1 ? Math.round(inset) : 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Calls `onChange` with the inset now, and again whenever it changes. Call it
|
|
28
|
+
* in the browser; the returned function stops it.
|
|
29
|
+
*/
|
|
30
|
+
export function watchKeyboardInset(onChange) {
|
|
31
|
+
if (typeof window === "undefined")
|
|
32
|
+
return () => { };
|
|
33
|
+
const viewport = window.visualViewport;
|
|
34
|
+
let last = -1;
|
|
35
|
+
const update = () => {
|
|
36
|
+
const next = readKeyboardInset();
|
|
37
|
+
if (next === last)
|
|
38
|
+
return;
|
|
39
|
+
last = next;
|
|
40
|
+
onChange(next);
|
|
41
|
+
};
|
|
42
|
+
update();
|
|
43
|
+
if (!viewport)
|
|
44
|
+
return () => { };
|
|
45
|
+
viewport.addEventListener("resize", update);
|
|
46
|
+
viewport.addEventListener("scroll", update);
|
|
47
|
+
window.addEventListener("resize", update);
|
|
48
|
+
return () => {
|
|
49
|
+
viewport.removeEventListener("resize", update);
|
|
50
|
+
viewport.removeEventListener("scroll", update);
|
|
51
|
+
window.removeEventListener("resize", update);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types and pure helpers for `MapView`.
|
|
3
|
+
*
|
|
4
|
+
* Which markers become one count marker is worked out here, on pixels, so it
|
|
5
|
+
* can be tested without a browser or the map library. Nothing in this file
|
|
6
|
+
* imports MapLibre: `mapViewSupported()` can be asked before the library is
|
|
7
|
+
* loaded, and the library's own types stay out of the package's.
|
|
8
|
+
*/
|
|
9
|
+
export type MapPointKey = string | number;
|
|
10
|
+
/** A place on the map. */
|
|
11
|
+
export interface MapLngLat {
|
|
12
|
+
lng: number;
|
|
13
|
+
lat: number;
|
|
14
|
+
}
|
|
15
|
+
/** One marker. */
|
|
16
|
+
export interface MapPoint extends MapLngLat {
|
|
17
|
+
/** Stable identity. */
|
|
18
|
+
id: MapPointKey;
|
|
19
|
+
/** The marker's accessible name, written by the app. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** Drawn filled, with a check mark; left out or false, round and outlined. */
|
|
22
|
+
checked?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Room kept clear at the edges of the map, in px: a sheet over its lower part is a `bottom`. */
|
|
25
|
+
export interface MapInset {
|
|
26
|
+
top?: number;
|
|
27
|
+
right?: number;
|
|
28
|
+
bottom?: number;
|
|
29
|
+
left?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where the map stands:
|
|
33
|
+
* - `loading`: the library or the first style is on its way;
|
|
34
|
+
* - `ready`: the map has drawn;
|
|
35
|
+
* - `library-error`: the map library could not be loaded or started (no WebGL, a failed chunk);
|
|
36
|
+
* - `tiles-error`: the style could not be fetched, or tiles failed and none has arrived (offline).
|
|
37
|
+
*/
|
|
38
|
+
export type MapViewStatus = "loading" | "ready" | "library-error" | "tiles-error";
|
|
39
|
+
/** Payload of MapView's `onmove`: where the map is after a move has ended. */
|
|
40
|
+
export interface MapViewPosition {
|
|
41
|
+
center: MapLngLat;
|
|
42
|
+
zoom: number;
|
|
43
|
+
}
|
|
44
|
+
/** Every built-in string of MapView. Pass a partial object to `strings` to translate. */
|
|
45
|
+
export interface MapViewStrings {
|
|
46
|
+
/** Accessible name of the map. */
|
|
47
|
+
label: string;
|
|
48
|
+
/** Accessible name of a marker that stands for several points. */
|
|
49
|
+
cluster: (count: number) => string;
|
|
50
|
+
/** Accessible name of the dot at the viewer's own position. */
|
|
51
|
+
viewer: string;
|
|
52
|
+
}
|
|
53
|
+
export declare const MAP_VIEW_STRINGS: MapViewStrings;
|
|
54
|
+
/** A marker's tap area, in px. It does not grow with the text size. */
|
|
55
|
+
export declare const MAP_MARKER_TAP = 44;
|
|
56
|
+
/** The closest the opening fit zooms in, so one marker does not fill the screen: street level. */
|
|
57
|
+
export declare const MAP_FIT_MAX_ZOOM = 16;
|
|
58
|
+
/**
|
|
59
|
+
* Whether this browser can draw the map: it has WebGL. Safe on the server
|
|
60
|
+
* (false) and cheap: it does not load the map library.
|
|
61
|
+
*/
|
|
62
|
+
export declare function mapViewSupported(): boolean;
|
|
63
|
+
export interface MapPixel {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
}
|
|
67
|
+
/** What is drawn: one point, or several under one count marker. */
|
|
68
|
+
export interface MapMarker<T> {
|
|
69
|
+
/** The key of its first point in the list: the marker keeps its element, and the focus, while others join or leave it. */
|
|
70
|
+
key: MapPointKey;
|
|
71
|
+
/** Its points, in the order of the list. */
|
|
72
|
+
points: T[];
|
|
73
|
+
/** Place in the list of its first point: the marker's place in the tab order. */
|
|
74
|
+
order: number;
|
|
75
|
+
/** Where it is drawn: the middle of its points. */
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Groups points whose tap areas overlap into one marker, and goes on until no
|
|
81
|
+
* two markers overlap: a count marker sits in the middle of its points, which
|
|
82
|
+
* may bring it over a neighbour. Markers come back in the order of the list,
|
|
83
|
+
* each at the place of its first point.
|
|
84
|
+
*
|
|
85
|
+
* `pixels[i]` is where `points[i]` is on the screen. The result depends only
|
|
86
|
+
* on how far apart the points are, so a pan changes nothing and a pinch splits
|
|
87
|
+
* markers as they stop overlapping.
|
|
88
|
+
*/
|
|
89
|
+
export declare function clusterMarkers<T extends {
|
|
90
|
+
id: MapPointKey;
|
|
91
|
+
}>(points: readonly T[], pixels: readonly MapPixel[], tap?: number): MapMarker<T>[];
|
|
92
|
+
/** The corners of the box around `points`, or null with none. */
|
|
93
|
+
export declare function boundsOf(points: readonly MapLngLat[]): [[number, number], [number, number]] | null;
|
|
94
|
+
/** Changes when a point is added, taken away or moved, and not when only its name or its check does. */
|
|
95
|
+
export declare function pointsSignature(points: readonly MapPoint[]): string;
|
|
96
|
+
/**
|
|
97
|
+
* How far from the middle of the map the middle of the area inside `inset`
|
|
98
|
+
* is, in px: where a point is put so that it is in view above a sheet.
|
|
99
|
+
*/
|
|
100
|
+
export declare function insetOffset(inset: MapInset | undefined): MapPixel;
|
|
101
|
+
/** The words of an attribution the style carries as HTML, as plain text. */
|
|
102
|
+
export declare function attributionText(html: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* The part of MapLibre GL JS that MapView uses, written out here so that the
|
|
105
|
+
* package's types do not need the library's.
|
|
106
|
+
*/
|
|
107
|
+
export interface MapLibreMapLike {
|
|
108
|
+
on(type: string, listener: (event: any) => void): unknown;
|
|
109
|
+
off(type: string, listener: (event: any) => void): unknown;
|
|
110
|
+
remove(): void;
|
|
111
|
+
resize(): unknown;
|
|
112
|
+
jumpTo(options: {
|
|
113
|
+
center?: [number, number] | MapLngLat;
|
|
114
|
+
zoom?: number;
|
|
115
|
+
}): unknown;
|
|
116
|
+
cameraForBounds(bounds: [[number, number], [number, number]], options?: {
|
|
117
|
+
padding?: number;
|
|
118
|
+
maxZoom?: number;
|
|
119
|
+
}): {
|
|
120
|
+
center?: unknown;
|
|
121
|
+
zoom?: number;
|
|
122
|
+
} | undefined;
|
|
123
|
+
project(lngLat: [number, number]): MapPixel;
|
|
124
|
+
unproject(point: [number, number]): MapLngLat;
|
|
125
|
+
getCenter(): MapLngLat;
|
|
126
|
+
getZoom(): number;
|
|
127
|
+
setStyle(style: string | object): unknown;
|
|
128
|
+
getStyle(): {
|
|
129
|
+
sources?: Record<string, {
|
|
130
|
+
attribution?: string;
|
|
131
|
+
}>;
|
|
132
|
+
} | undefined;
|
|
133
|
+
getSource?(id: string): {
|
|
134
|
+
attribution?: string;
|
|
135
|
+
} | undefined;
|
|
136
|
+
getCanvas(): HTMLCanvasElement;
|
|
137
|
+
touchZoomRotate?: {
|
|
138
|
+
disableRotation(): void;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export interface MapLibreLike {
|
|
142
|
+
Map: new (options: Record<string, unknown>) => MapLibreMapLike;
|
|
143
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types and pure helpers for `MapView`.
|
|
3
|
+
*
|
|
4
|
+
* Which markers become one count marker is worked out here, on pixels, so it
|
|
5
|
+
* can be tested without a browser or the map library. Nothing in this file
|
|
6
|
+
* imports MapLibre: `mapViewSupported()` can be asked before the library is
|
|
7
|
+
* loaded, and the library's own types stay out of the package's.
|
|
8
|
+
*/
|
|
9
|
+
export const MAP_VIEW_STRINGS = {
|
|
10
|
+
label: "Map",
|
|
11
|
+
cluster: (count) => `${count} places`,
|
|
12
|
+
viewer: "Your position",
|
|
13
|
+
};
|
|
14
|
+
/** A marker's tap area, in px. It does not grow with the text size. */
|
|
15
|
+
export const MAP_MARKER_TAP = 44;
|
|
16
|
+
/** The closest the opening fit zooms in, so one marker does not fill the screen: street level. */
|
|
17
|
+
export const MAP_FIT_MAX_ZOOM = 16;
|
|
18
|
+
/**
|
|
19
|
+
* Whether this browser can draw the map: it has WebGL. Safe on the server
|
|
20
|
+
* (false) and cheap: it does not load the map library.
|
|
21
|
+
*/
|
|
22
|
+
export function mapViewSupported() {
|
|
23
|
+
if (typeof document === "undefined")
|
|
24
|
+
return false;
|
|
25
|
+
try {
|
|
26
|
+
const canvas = document.createElement("canvas");
|
|
27
|
+
return !!(canvas.getContext("webgl2") ?? canvas.getContext("webgl"));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Groups points whose tap areas overlap into one marker, and goes on until no
|
|
35
|
+
* two markers overlap: a count marker sits in the middle of its points, which
|
|
36
|
+
* may bring it over a neighbour. Markers come back in the order of the list,
|
|
37
|
+
* each at the place of its first point.
|
|
38
|
+
*
|
|
39
|
+
* `pixels[i]` is where `points[i]` is on the screen. The result depends only
|
|
40
|
+
* on how far apart the points are, so a pan changes nothing and a pinch splits
|
|
41
|
+
* markers as they stop overlapping.
|
|
42
|
+
*/
|
|
43
|
+
export function clusterMarkers(points, pixels, tap = MAP_MARKER_TAP) {
|
|
44
|
+
let groups = points.map((_, index) => ({
|
|
45
|
+
members: [index],
|
|
46
|
+
x: pixels[index].x,
|
|
47
|
+
y: pixels[index].y,
|
|
48
|
+
}));
|
|
49
|
+
let merged = true;
|
|
50
|
+
while (merged) {
|
|
51
|
+
merged = false;
|
|
52
|
+
const next = [];
|
|
53
|
+
for (const group of groups) {
|
|
54
|
+
const into = next.find((other) => Math.abs(other.x - group.x) < tap && Math.abs(other.y - group.y) < tap);
|
|
55
|
+
if (!into) {
|
|
56
|
+
next.push(group);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
into.members = [...into.members, ...group.members];
|
|
60
|
+
into.x = into.members.reduce((sum, index) => sum + pixels[index].x, 0) / into.members.length;
|
|
61
|
+
into.y = into.members.reduce((sum, index) => sum + pixels[index].y, 0) / into.members.length;
|
|
62
|
+
merged = true;
|
|
63
|
+
}
|
|
64
|
+
groups = next;
|
|
65
|
+
}
|
|
66
|
+
return groups
|
|
67
|
+
.map((group) => {
|
|
68
|
+
const members = [...group.members].sort((a, b) => a - b);
|
|
69
|
+
return {
|
|
70
|
+
key: points[members[0]].id,
|
|
71
|
+
points: members.map((index) => points[index]),
|
|
72
|
+
order: members[0],
|
|
73
|
+
x: group.x,
|
|
74
|
+
y: group.y,
|
|
75
|
+
};
|
|
76
|
+
})
|
|
77
|
+
.sort((a, b) => a.order - b.order);
|
|
78
|
+
}
|
|
79
|
+
/** The corners of the box around `points`, or null with none. */
|
|
80
|
+
export function boundsOf(points) {
|
|
81
|
+
if (points.length === 0)
|
|
82
|
+
return null;
|
|
83
|
+
let west = Infinity;
|
|
84
|
+
let south = Infinity;
|
|
85
|
+
let east = -Infinity;
|
|
86
|
+
let north = -Infinity;
|
|
87
|
+
for (const point of points) {
|
|
88
|
+
west = Math.min(west, point.lng);
|
|
89
|
+
east = Math.max(east, point.lng);
|
|
90
|
+
south = Math.min(south, point.lat);
|
|
91
|
+
north = Math.max(north, point.lat);
|
|
92
|
+
}
|
|
93
|
+
return [
|
|
94
|
+
[west, south],
|
|
95
|
+
[east, north],
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
/** Changes when a point is added, taken away or moved, and not when only its name or its check does. */
|
|
99
|
+
export function pointsSignature(points) {
|
|
100
|
+
return points.map((point) => `${point.id}:${point.lng}:${point.lat}`).join("|");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* How far from the middle of the map the middle of the area inside `inset`
|
|
104
|
+
* is, in px: where a point is put so that it is in view above a sheet.
|
|
105
|
+
*/
|
|
106
|
+
export function insetOffset(inset) {
|
|
107
|
+
return {
|
|
108
|
+
x: ((inset?.left ?? 0) - (inset?.right ?? 0)) / 2,
|
|
109
|
+
y: ((inset?.top ?? 0) - (inset?.bottom ?? 0)) / 2,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** The words of an attribution the style carries as HTML, as plain text. */
|
|
113
|
+
export function attributionText(html) {
|
|
114
|
+
return html
|
|
115
|
+
.replace(/<[^>]*>/g, " ")
|
|
116
|
+
.replace(/©/g, "©")
|
|
117
|
+
.replace(/&/g, "&")
|
|
118
|
+
.replace(/ /g, " ")
|
|
119
|
+
.replace(/\s+/g, " ")
|
|
120
|
+
.trim();
|
|
121
|
+
}
|