zabi-components 8.1.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 +1011 -0
- package/README.md +1 -1
- package/THEME.md +18 -5
- package/THEMING.md +308 -74
- package/dist/atoms/ActionPanel.svelte +1 -1
- package/dist/atoms/Avatar.svelte +19 -3
- package/dist/atoms/Avatar.svelte.d.ts +12 -0
- package/dist/atoms/Badge.svelte +37 -8
- package/dist/atoms/Badge.svelte.d.ts +6 -0
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +52 -15
- package/dist/atoms/Button.svelte.d.ts +3 -2
- package/dist/atoms/Card.svelte +181 -25
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/Checkbox.svelte +33 -0
- package/dist/atoms/Checkbox.svelte.d.ts +27 -0
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +1 -0
- package/dist/atoms/DateField.svelte +41 -3
- package/dist/atoms/DateField.svelte.d.ts +31 -3
- package/dist/atoms/FloatingActionButton.svelte +5 -3
- package/dist/atoms/FloatingActionButton.svelte.d.ts +1 -1
- package/dist/atoms/Heading.svelte +27 -1
- package/dist/atoms/Heading.svelte.d.ts +7 -0
- package/dist/atoms/IconButton.svelte +81 -9
- package/dist/atoms/IconButton.svelte.d.ts +13 -0
- package/dist/atoms/Input.svelte +10 -5
- package/dist/atoms/Input.svelte.d.ts +3 -2
- package/dist/atoms/List.svelte +3 -2
- package/dist/atoms/ListItem.svelte +31 -7
- package/dist/atoms/ListItem.svelte.d.ts +6 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +78 -17
- package/dist/atoms/Progress.svelte.d.ts +11 -1
- package/dist/atoms/Radio.svelte +32 -0
- package/dist/atoms/Radio.svelte.d.ts +26 -0
- package/dist/atoms/Rating.svelte +132 -6
- package/dist/atoms/Rating.svelte.d.ts +24 -3
- package/dist/atoms/Select.svelte +24 -26
- package/dist/atoms/Select.svelte.d.ts +12 -0
- package/dist/atoms/SelectionControl.svelte +99 -6
- package/dist/atoms/SelectionControl.svelte.d.ts +25 -0
- 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 +35 -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/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/TemporalField.svelte +342 -10
- package/dist/atoms/TemporalField.svelte.d.ts +20 -4
- package/dist/atoms/Textarea.svelte +100 -5
- package/dist/atoms/Textarea.svelte.d.ts +21 -0
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +46 -3
- package/dist/atoms/TimeField.svelte.d.ts +34 -3
- package/dist/atoms/Toast.svelte +6 -5
- package/dist/atoms/Toggle.svelte +61 -10
- package/dist/atoms/Toggle.svelte.d.ts +15 -0
- package/dist/atoms/Tooltip.svelte +20 -1
- package/dist/atoms/Tooltip.svelte.d.ts +7 -0
- package/dist/atoms/index.d.ts +21 -0
- package/dist/atoms/index.js +17 -0
- package/dist/atoms/selection-control.styles.d.ts +15 -4
- package/dist/atoms/selection-control.styles.js +16 -5
- package/dist/components/atoms/index.d.ts +21 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +15 -4
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +27 -3
- 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 +7 -2
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +30 -0
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +6 -1
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +12 -3
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +55 -4
- package/dist/components/util/app-shell.d.ts.map +1 -1
- package/dist/components/util/avatar.d.ts +3 -0
- package/dist/components/util/avatar.d.ts.map +1 -1
- package/dist/components/util/bottom-tab-bar.d.ts +6 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -1
- 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 +6 -0
- package/dist/components/util/collapsible.d.ts.map +1 -1
- 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/field-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +6 -0
- package/dist/components/util/field.d.ts.map +1 -1
- package/dist/components/util/focus-utils.d.ts +2 -0
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- 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/photo.d.ts +19 -0
- package/dist/components/util/photo.d.ts.map +1 -1
- package/dist/components/util/progress.d.ts +19 -0
- package/dist/components/util/progress.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/sheet-drag.d.ts +25 -1
- package/dist/components/util/sheet-drag.d.ts.map +1 -1
- 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/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +40 -0
- package/dist/components/util/toaster.d.ts.map +1 -1
- 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/top-navbar.d.ts +26 -2
- package/dist/components/util/top-navbar.d.ts.map +1 -1
- package/dist/components/util/zabi-strings.d.ts +9 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -1
- package/dist/create-theme/cli.js +6 -2
- package/dist/create-theme/index.d.ts +17 -3
- package/dist/create-theme/index.js +252 -35
- package/dist/create-theme/lib/resolve.js +97 -0
- package/dist/create-theme/theme-data.js +1618 -90
- package/dist/index.d.ts +27 -3
- package/dist/index.js +18 -0
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +31 -12
- package/dist/molecules/Alert.svelte.d.ts +12 -1
- package/dist/molecules/AppBar.svelte +287 -28
- package/dist/molecules/AppBar.svelte.d.ts +62 -0
- package/dist/molecules/AvatarGroup.svelte +1 -1
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +88 -20
- package/dist/molecules/BottomSheet.svelte.d.ts +17 -1
- package/dist/molecules/BottomTabBar.svelte +134 -28
- package/dist/molecules/BottomTabBar.svelte.d.ts +36 -2
- package/dist/molecules/Calendar.svelte.d.ts +1 -1
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +158 -54
- package/dist/molecules/Collapsible.svelte.d.ts +23 -6
- package/dist/molecules/CollapsibleGroup.svelte +16 -6
- package/dist/molecules/ConfirmDialog.svelte +5 -1
- package/dist/molecules/ConfirmDialog.svelte.d.ts +3 -0
- package/dist/molecules/Drawer.svelte +33 -12
- package/dist/molecules/Dropdown.svelte +9 -1
- package/dist/molecules/Dropdown.svelte.d.ts +2 -0
- package/dist/molecules/DropdownItem.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +230 -120
- package/dist/molecules/ImageUpload.svelte.d.ts +38 -3
- 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/Modal.svelte +11 -6
- package/dist/molecules/PhotoGrid.svelte +100 -10
- package/dist/molecules/PhotoGrid.svelte.d.ts +11 -0
- package/dist/molecules/PhotoViewer.svelte +121 -24
- package/dist/molecules/PhotoViewer.svelte.d.ts +18 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/RadioGroup.svelte +20 -4
- package/dist/molecules/RadioGroup.svelte.d.ts +5 -0
- package/dist/molecules/SegmentedControl.svelte +136 -3
- package/dist/molecules/SegmentedControl.svelte.d.ts +6 -0
- package/dist/molecules/SidebarFooter.svelte +1 -1
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SortableList.svelte +1 -0
- package/dist/molecules/Stepper.svelte +21 -7
- package/dist/molecules/StickyActionBar.svelte +67 -8
- package/dist/molecules/StickyActionBar.svelte.d.ts +14 -1
- package/dist/molecules/Tabs.svelte +79 -2
- package/dist/molecules/Toaster.svelte +171 -7
- package/dist/molecules/Toaster.svelte.d.ts +60 -2
- package/dist/molecules/ToasterToast.svelte +292 -60
- package/dist/molecules/ToasterToast.svelte.d.ts +6 -0
- package/dist/molecules/UnsavedChangesBar.svelte +13 -2
- package/dist/molecules/index.d.ts +7 -2
- package/dist/molecules/index.js +4 -0
- package/dist/molecules/toast-store.d.ts +30 -0
- package/dist/molecules/toast-store.js +4 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +539 -39
- package/dist/organisms/AppShell.svelte.d.ts +102 -6
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarNavigation.svelte +23 -36
- package/dist/organisms/SidebarPanel.svelte +10 -14
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +2 -3
- package/dist/organisms/TopNavbar.svelte +117 -28
- package/dist/organisms/TopNavbar.svelte.d.ts +22 -1
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +6 -1
- package/dist/organisms/index.js +4 -0
- package/dist/types/components.d.ts +17 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +497 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +14 -3
- package/dist/util/app-shell.d.ts +55 -4
- package/dist/util/app-shell.js +32 -7
- package/dist/util/avatar.d.ts +3 -0
- package/dist/util/bottom-tab-bar.d.ts +6 -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 +6 -0
- package/dist/util/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -0
- package/dist/util/field-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +6 -0
- package/dist/util/focus-utils.d.ts +2 -0
- package/dist/util/focus-utils.js +4 -0
- package/dist/util/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -0
- package/dist/util/photo.d.ts +19 -0
- package/dist/util/photo.js +3 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -0
- package/dist/util/scroll-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -0
- package/dist/util/sheet-drag.d.ts +25 -1
- package/dist/util/sheet-drag.js +71 -1
- 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/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/toaster.d.ts +40 -0
- package/dist/util/toaster.js +55 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/top-navbar.d.ts +26 -2
- package/dist/util/top-navbar.js +23 -0
- package/dist/util/zabi-strings.d.ts +9 -0
- package/dist/zabi-components-colors.css +9 -3
- package/dist/zabi-components-theme-dark-only.css +474 -164
- package/dist/zabi-components-theme-dark.css +474 -164
- package/dist/zabi-components-theme-only.css +933 -99
- package/dist/zabi-components-theme.css +933 -99
- package/dist/zabi-components.css +2275 -321
- package/package.json +11 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The display voice: the weight and tracking of a figure or a display line.
|
|
3
|
+
* Both are read from custom properties that are declared nowhere, so any
|
|
4
|
+
* ancestor can set them (`--zabi-display-weight: 400` for a light serif) and
|
|
5
|
+
* the defaults are what the display face looked like before. The `number:`
|
|
6
|
+
* hint is what makes Tailwind read the weight as a weight and not a family.
|
|
7
|
+
* Heading `variant="display"` and Stat share this string.
|
|
8
|
+
*/
|
|
9
|
+
export const displayVoice = "font-[number:var(--zabi-display-weight,700)] tracking-[var(--zabi-display-tracking,-0.02em)]";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The look of a field-shaped control's box: Select's trigger and native select
|
|
3
|
+
* and PickerField draw it from here, so a picker beside a Select is the same
|
|
4
|
+
* box. Kept in one place so they cannot drift.
|
|
5
|
+
*/
|
|
6
|
+
export type FieldControlSize = "sm" | "md" | "lg";
|
|
7
|
+
export interface FieldControlSizeClasses {
|
|
8
|
+
/** The trigger's box: a minimum height, so a label that wraps makes it taller. */
|
|
9
|
+
box: string;
|
|
10
|
+
/** The text size: 16px below `sm`, as Input. */
|
|
11
|
+
text: string;
|
|
12
|
+
/** The same box for a native `<select>`, which is always one line. */
|
|
13
|
+
native: string;
|
|
14
|
+
/** Where the trailing chevron sits. */
|
|
15
|
+
chevron: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Same fixed height scale as Button, IconButton and Input (32 / 40 / 48), and
|
|
19
|
+
* the same 16px text below `sm`. One line is exactly 32, 40 or 48px: the
|
|
20
|
+
* padding leaves room for the line and the border inside it, and is a step
|
|
21
|
+
* smaller below `sm`, where the text is 16px on a 24px line.
|
|
22
|
+
*/
|
|
23
|
+
export declare function fieldControlSize(size: FieldControlSize): FieldControlSizeClasses;
|
|
24
|
+
/** The edge colour for a status: the field's own, or the status colour. */
|
|
25
|
+
export declare function fieldControlEdge(variant: string): string;
|
|
26
|
+
/** The field's surface, edge and states. */
|
|
27
|
+
export declare const FIELD_CONTROL_SURFACE = "focus-ring w-full min-w-0 cursor-pointer rounded-control border bg-input text-body transition-colors duration-(--duration-base) hover:bg-input-hover active:bg-input-active focus-visible:bg-input-focus focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-input-disabled disabled:text-action-disabled-text";
|
|
28
|
+
/** What the trigger adds to the surface: one row, text and chevron apart. */
|
|
29
|
+
export declare const FIELD_CONTROL_TRIGGER = "focus-ring w-full min-w-0 cursor-pointer rounded-control border bg-input text-body transition-colors duration-(--duration-base) hover:bg-input-hover active:bg-input-active focus-visible:bg-input-focus focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-input-disabled disabled:text-action-disabled-text flex items-center justify-between gap-2";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The look of a field-shaped control's box: Select's trigger and native select
|
|
3
|
+
* and PickerField draw it from here, so a picker beside a Select is the same
|
|
4
|
+
* box. Kept in one place so they cannot drift.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Same fixed height scale as Button, IconButton and Input (32 / 40 / 48), and
|
|
8
|
+
* the same 16px text below `sm`. One line is exactly 32, 40 or 48px: the
|
|
9
|
+
* padding leaves room for the line and the border inside it, and is a step
|
|
10
|
+
* smaller below `sm`, where the text is 16px on a 24px line.
|
|
11
|
+
*/
|
|
12
|
+
export function fieldControlSize(size) {
|
|
13
|
+
if (size === "sm")
|
|
14
|
+
return { box: "min-h-8 px-3 py-1 max-sm:py-0 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", native: "h-8 ps-3 pe-9 pointer-coarse:h-11", chevron: "end-3" };
|
|
15
|
+
if (size === "lg")
|
|
16
|
+
return { box: "min-h-12 px-4 py-2", text: "text-base", native: "h-12 ps-4 pe-10", chevron: "end-4" };
|
|
17
|
+
return { box: "min-h-10 px-3 py-2 max-sm:py-1 pointer-coarse:min-h-11", text: "text-sm max-sm:text-base", native: "h-10 ps-3 pe-9 pointer-coarse:h-11", chevron: "end-3" };
|
|
18
|
+
}
|
|
19
|
+
/** The edge colour for a status: the field's own, or the status colour. */
|
|
20
|
+
export function fieldControlEdge(variant) {
|
|
21
|
+
return variant === "success"
|
|
22
|
+
? "border-success focus-visible:border-success"
|
|
23
|
+
: variant === "warning"
|
|
24
|
+
? "border-warning focus-visible:border-warning"
|
|
25
|
+
: variant === "error"
|
|
26
|
+
? "border-error focus-visible:border-error"
|
|
27
|
+
: "border-input-border enabled:hover:border-input-border-hover";
|
|
28
|
+
}
|
|
29
|
+
/** The field's surface, edge and states. */
|
|
30
|
+
export const FIELD_CONTROL_SURFACE = "focus-ring w-full min-w-0 cursor-pointer rounded-control border bg-input text-body transition-colors duration-(--duration-base) hover:bg-input-hover active:bg-input-active focus-visible:bg-input-focus focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-input-disabled disabled:text-action-disabled-text";
|
|
31
|
+
/** What the trigger adds to the surface: one row, text and chevron apart. */
|
|
32
|
+
export const FIELD_CONTROL_TRIGGER = `${FIELD_CONTROL_SURFACE} flex items-center justify-between gap-2`;
|
package/dist/util/field.d.ts
CHANGED
|
@@ -36,3 +36,9 @@ export declare const fieldMessageId: (fieldId: string) => string;
|
|
|
36
36
|
* that are rendered; `undefined` when there are none, so no empty attribute.
|
|
37
37
|
*/
|
|
38
38
|
export declare function fieldDescribedBy(fieldId: string, state: Pick<FieldMessageState, "showMessage" | "showHint">, own?: string | null): string | undefined;
|
|
39
|
+
/** What Textarea's `countText` is given: the characters typed, the limit, and how many are left. */
|
|
40
|
+
export interface TextareaCount {
|
|
41
|
+
length: number;
|
|
42
|
+
max: number;
|
|
43
|
+
remaining: number;
|
|
44
|
+
}
|
|
@@ -62,6 +62,8 @@ export declare function joinOverlayStack(panel: HTMLElement): {
|
|
|
62
62
|
depth: number;
|
|
63
63
|
leave: () => void;
|
|
64
64
|
};
|
|
65
|
+
/** Whether `panel` is the overlay on top of the stack: the one opened last. */
|
|
66
|
+
export declare function isTopOverlay(panel: HTMLElement): boolean;
|
|
65
67
|
/**
|
|
66
68
|
* Keeps a modal overlay in charge of the keyboard when focus has left it
|
|
67
69
|
* without a Tab: the focused control was disabled or removed and the browser
|
package/dist/util/focus-utils.js
CHANGED
|
@@ -214,6 +214,10 @@ export function joinOverlayStack(panel) {
|
|
|
214
214
|
},
|
|
215
215
|
};
|
|
216
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
|
+
}
|
|
217
221
|
/**
|
|
218
222
|
* Keeps a modal overlay in charge of the keyboard when focus has left it
|
|
219
223
|
* without a Tab: the focused control was disabled or removed and the browser
|
|
@@ -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
|
+
}
|
package/dist/util/photo.d.ts
CHANGED
|
@@ -24,7 +24,20 @@ export interface Photo {
|
|
|
24
24
|
height: number;
|
|
25
25
|
/** Shown under the photo in the viewer. */
|
|
26
26
|
caption?: string;
|
|
27
|
+
/**
|
|
28
|
+
* A second, quieter line under the caption in the viewer: who took the
|
|
29
|
+
* photo, when. It is never folded away with a long caption.
|
|
30
|
+
*/
|
|
31
|
+
credit?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Where a photo that is being added stands, drawn on its tile in the grid:
|
|
34
|
+
* `uploading` (on its way) or `error` (it did not arrive). Left out: saved.
|
|
35
|
+
*/
|
|
36
|
+
status?: PhotoStatus;
|
|
37
|
+
/** How far an `uploading` photo has come, from 0 to 1. Without it the tile shows a spinner. */
|
|
38
|
+
progress?: number;
|
|
27
39
|
}
|
|
40
|
+
export type PhotoStatus = "uploading" | "error";
|
|
28
41
|
export declare function photoKey(photo: Photo): PhotoKey;
|
|
29
42
|
/** Payload of PhotoGrid's `onselect`. */
|
|
30
43
|
export interface PhotoGridSelectDetail {
|
|
@@ -47,6 +60,12 @@ export interface PhotoGridStrings {
|
|
|
47
60
|
openPhoto: (name: string) => string;
|
|
48
61
|
/** How to get around by keyboard. Read once when focus enters the grid, and shown while it has keyboard focus. */
|
|
49
62
|
keyboardHint: string;
|
|
63
|
+
/** Added to the name of a photo whose `status` is `uploading`. */
|
|
64
|
+
uploading: string;
|
|
65
|
+
/** Added to the name of a photo whose `status` is `error`. */
|
|
66
|
+
uploadFailed: string;
|
|
67
|
+
/** Name of the button on a failed photo that tries again. */
|
|
68
|
+
retryUpload: (name: string) => string;
|
|
50
69
|
}
|
|
51
70
|
export declare const PHOTO_GRID_STRINGS: PhotoGridStrings;
|
|
52
71
|
/** What the user did to close the viewer. `swipe` is a swipe down on the photo. */
|
package/dist/util/photo.js
CHANGED
|
@@ -15,6 +15,9 @@ export const PHOTO_GRID_STRINGS = {
|
|
|
15
15
|
morePhotos: (count) => `${count} more ${count === 1 ? "photo" : "photos"}`,
|
|
16
16
|
openPhoto: (name) => `Open ${name}`,
|
|
17
17
|
keyboardHint: "Use the arrow keys to move between photos.",
|
|
18
|
+
uploading: "Uploading",
|
|
19
|
+
uploadFailed: "Not uploaded",
|
|
20
|
+
retryUpload: (name) => `Try again: ${name}`,
|
|
18
21
|
};
|
|
19
22
|
export const PHOTO_VIEWER_STRINGS = {
|
|
20
23
|
close: "Close",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Types and pure helpers for `Progress`. Kept out of the component so they can be tested without a DOM. */
|
|
2
|
+
/** Every built-in string. Pass a partial object to `strings` to translate. */
|
|
3
|
+
export interface ProgressStrings {
|
|
4
|
+
/**
|
|
5
|
+
* The count of a segmented bar, as its accessible value and as the
|
|
6
|
+
* read-out beside the label: "4 of 19". `value` is the number of filled
|
|
7
|
+
* segments.
|
|
8
|
+
*/
|
|
9
|
+
valueText: (value: number, max: number) => string;
|
|
10
|
+
}
|
|
11
|
+
export declare const PROGRESS_STRINGS: ProgressStrings;
|
|
12
|
+
/** The fewest and the most segments a segmented bar is drawn with. */
|
|
13
|
+
export declare const SEGMENTS_MIN = 2;
|
|
14
|
+
export declare const SEGMENTS_MAX = 24;
|
|
15
|
+
/** Whether `max` can be drawn as separate segments: a whole number from 2 to 24. */
|
|
16
|
+
export declare function canSegment(max: number): boolean;
|
|
17
|
+
/** Filled segments: `value` rounded down, held within 0..max; 0 for something that is not a number. */
|
|
18
|
+
export declare function filledSegments(value: number, max: number): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Types and pure helpers for `Progress`. Kept out of the component so they can be tested without a DOM. */
|
|
2
|
+
export const PROGRESS_STRINGS = {
|
|
3
|
+
valueText: (value, max) => `${value} of ${max}`,
|
|
4
|
+
};
|
|
5
|
+
/** The fewest and the most segments a segmented bar is drawn with. */
|
|
6
|
+
export const SEGMENTS_MIN = 2;
|
|
7
|
+
export const SEGMENTS_MAX = 24;
|
|
8
|
+
/** Whether `max` can be drawn as separate segments: a whole number from 2 to 24. */
|
|
9
|
+
export function canSegment(max) {
|
|
10
|
+
return Number.isInteger(max) && max >= SEGMENTS_MIN && max <= SEGMENTS_MAX;
|
|
11
|
+
}
|
|
12
|
+
/** Filled segments: `value` rounded down, held within 0..max; 0 for something that is not a number. */
|
|
13
|
+
export function filledSegments(value, max) {
|
|
14
|
+
if (!Number.isFinite(value))
|
|
15
|
+
return 0;
|
|
16
|
+
return Math.max(0, Math.min(max, Math.floor(value)));
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether content is under a bar (D123): the one thing a bar needs to know to
|
|
3
|
+
* choose between flush with the page and glass.
|
|
4
|
+
*
|
|
5
|
+
* Pure DOM, no Svelte. `AppShell` uses it on its scroller and shares the
|
|
6
|
+
* result with the bars inside it; a bar on its own uses it on its nearest
|
|
7
|
+
* scroll parent, or on the window.
|
|
8
|
+
*/
|
|
9
|
+
/** `scrollEdge` on a bar: follow the scroll position, or force the glass on or off. */
|
|
10
|
+
export type ScrollEdgeMode = "auto" | "always" | "never";
|
|
11
|
+
export interface ScrollEdge {
|
|
12
|
+
/** Scrolled away from the start: content is under a bar at the top. */
|
|
13
|
+
top: boolean;
|
|
14
|
+
/** Not at the end: content is under a bar at the bottom. */
|
|
15
|
+
bottom: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The edges for a scroll position, clamped against rubber-banding past either end. */
|
|
18
|
+
export declare function measureScrollEdge(position: number, scrollable: number, topThreshold?: number): ScrollEdge;
|
|
19
|
+
/** The value a bar writes as `data-scrolled-under`: the mode, or what the scroll position says. */
|
|
20
|
+
export declare function resolveScrolledUnder(mode: ScrollEdgeMode | undefined, auto: boolean): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Reports `{ top, bottom }` for `container` (null for the window): once on
|
|
23
|
+
* start, then whenever either changes. It listens to `scroll` (passive) and,
|
|
24
|
+
* where `ResizeObserver` exists, to the size of the container and of what is
|
|
25
|
+
* in it, since content that grows or shrinks moves the end. Returns the
|
|
26
|
+
* cleanup. Call it in the browser.
|
|
27
|
+
*
|
|
28
|
+
* `topThreshold` (px, default 0) is how far it has to scroll before `top` is
|
|
29
|
+
* true: a bar with a large title is only over content once the large row has
|
|
30
|
+
* gone under it. Call it again to change it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function watchScrollEdge(container: HTMLElement | null, onChange: (edge: ScrollEdge) => void, topThreshold?: number): () => void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether content is under a bar (D123): the one thing a bar needs to know to
|
|
3
|
+
* choose between flush with the page and glass.
|
|
4
|
+
*
|
|
5
|
+
* Pure DOM, no Svelte. `AppShell` uses it on its scroller and shares the
|
|
6
|
+
* result with the bars inside it; a bar on its own uses it on its nearest
|
|
7
|
+
* scroll parent, or on the window.
|
|
8
|
+
*/
|
|
9
|
+
/** Less than this from the end counts as being at the end (fractional scroll positions). */
|
|
10
|
+
const END_TOLERANCE = 1;
|
|
11
|
+
/** The edges for a scroll position, clamped against rubber-banding past either end. */
|
|
12
|
+
export function measureScrollEdge(position, scrollable, topThreshold = 0) {
|
|
13
|
+
const end = Math.max(0, scrollable);
|
|
14
|
+
const at = Math.min(Math.max(0, position), end);
|
|
15
|
+
return { top: at > topThreshold, bottom: end - at > END_TOLERANCE };
|
|
16
|
+
}
|
|
17
|
+
/** The value a bar writes as `data-scrolled-under`: the mode, or what the scroll position says. */
|
|
18
|
+
export function resolveScrolledUnder(mode, auto) {
|
|
19
|
+
if (mode === "always")
|
|
20
|
+
return true;
|
|
21
|
+
if (mode === "never")
|
|
22
|
+
return false;
|
|
23
|
+
return auto;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reports `{ top, bottom }` for `container` (null for the window): once on
|
|
27
|
+
* start, then whenever either changes. It listens to `scroll` (passive) and,
|
|
28
|
+
* where `ResizeObserver` exists, to the size of the container and of what is
|
|
29
|
+
* in it, since content that grows or shrinks moves the end. Returns the
|
|
30
|
+
* cleanup. Call it in the browser.
|
|
31
|
+
*
|
|
32
|
+
* `topThreshold` (px, default 0) is how far it has to scroll before `top` is
|
|
33
|
+
* true: a bar with a large title is only over content once the large row has
|
|
34
|
+
* gone under it. Call it again to change it.
|
|
35
|
+
*/
|
|
36
|
+
export function watchScrollEdge(container, onChange, topThreshold = 0) {
|
|
37
|
+
const target = container ?? window;
|
|
38
|
+
let last;
|
|
39
|
+
const measure = () => {
|
|
40
|
+
const root = document.documentElement;
|
|
41
|
+
const edge = container
|
|
42
|
+
? measureScrollEdge(container.scrollTop, container.scrollHeight - container.clientHeight, topThreshold)
|
|
43
|
+
: measureScrollEdge(window.scrollY, root.scrollHeight - window.innerHeight, topThreshold);
|
|
44
|
+
if (last && last.top === edge.top && last.bottom === edge.bottom)
|
|
45
|
+
return;
|
|
46
|
+
last = edge;
|
|
47
|
+
onChange(edge);
|
|
48
|
+
};
|
|
49
|
+
target.addEventListener("scroll", measure, { passive: true });
|
|
50
|
+
measure();
|
|
51
|
+
let sizes;
|
|
52
|
+
let nodes;
|
|
53
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
54
|
+
const sized = new ResizeObserver(measure);
|
|
55
|
+
sizes = sized;
|
|
56
|
+
const watch = () => {
|
|
57
|
+
sized.disconnect();
|
|
58
|
+
if (container) {
|
|
59
|
+
sized.observe(container);
|
|
60
|
+
for (const child of container.children)
|
|
61
|
+
sized.observe(child);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
sized.observe(document.documentElement);
|
|
65
|
+
sized.observe(document.body);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
watch();
|
|
69
|
+
// What is inside a scroller comes and goes: follow it.
|
|
70
|
+
if (container && typeof MutationObserver !== "undefined") {
|
|
71
|
+
nodes = new MutationObserver(() => {
|
|
72
|
+
watch();
|
|
73
|
+
measure();
|
|
74
|
+
});
|
|
75
|
+
nodes.observe(container, { childList: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!container)
|
|
79
|
+
window.addEventListener("resize", measure);
|
|
80
|
+
return () => {
|
|
81
|
+
target.removeEventListener("scroll", measure);
|
|
82
|
+
if (!container)
|
|
83
|
+
window.removeEventListener("resize", measure);
|
|
84
|
+
sizes?.disconnect();
|
|
85
|
+
nodes?.disconnect();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -25,12 +25,36 @@ export interface SheetDragTargets {
|
|
|
25
25
|
handleZone: HTMLElement;
|
|
26
26
|
/**
|
|
27
27
|
* The scrolling content. A touch that starts here scrolls it; only a
|
|
28
|
-
* downward swipe that begins with the content at its top moves the sheet
|
|
28
|
+
* downward swipe that begins with the content at its top moves the sheet,
|
|
29
|
+
* and not one that begins on a box inside it that scrolls itself or on
|
|
30
|
+
* something that handles its own gesture: marked `data-sheet-no-drag`,
|
|
31
|
+
* a range input, or with a `touch-action` that gives up vertical
|
|
32
|
+
* panning (`contentTakesTouch`). A swipe that begins sideways stays
|
|
33
|
+
* sideways and never moves the sheet.
|
|
29
34
|
*/
|
|
30
35
|
scroller?: HTMLElement | null;
|
|
31
36
|
}
|
|
32
37
|
/** Movement under this many px is a press, not a drag. */
|
|
33
38
|
export declare const DRAG_SLOP = 6;
|
|
39
|
+
/**
|
|
40
|
+
* Movement, on either axis, after which a touch in the content has an axis:
|
|
41
|
+
* more across than down is sideways (a carousel, a row that scrolls, a
|
|
42
|
+
* swipeable item), and stays so until the finger lifts.
|
|
43
|
+
*/
|
|
44
|
+
export declare const AXIS_SLOP = 8;
|
|
45
|
+
/**
|
|
46
|
+
* Whether a touch that begins on `target` belongs to the content and not to the
|
|
47
|
+
* sheet: it started on something that is dragged itself (`OWN_DRAG`), or it
|
|
48
|
+
* has, between it and the sheet's `scroller`, a box that scrolls on the y axis
|
|
49
|
+
* or one whose `touch-action` leaves vertical panning to the page's own code
|
|
50
|
+
* (`ownsVerticalGesture`: a map, a canvas, a drawing pad, a slider).
|
|
51
|
+
* A box that can scroll keeps the gesture whether it is at its top or not: a
|
|
52
|
+
* drag that sometimes scrolls a list and sometimes closes the sheet cannot be
|
|
53
|
+
* predicted, and the list does not hand over what it cannot use
|
|
54
|
+
* (`overscroll-contain`). The scroller itself is not asked here; it is the
|
|
55
|
+
* sheet's own content, and hands a pull down at its top to the sheet.
|
|
56
|
+
*/
|
|
57
|
+
export declare function contentTakesTouch(target: EventTarget | null, scroller: HTMLElement): boolean;
|
|
34
58
|
/**
|
|
35
59
|
* When a pointer or touch event happened: its own `timeStamp`, which is when
|
|
36
60
|
* the finger moved. The time the handler runs is later by however busy the
|