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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { HTMLAttributes } from "svelte/elements";
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
5
|
import type { OnFillTone } from "../types/variants.js";
|
|
6
|
+
import { displayVoice } from "../util/display-voice.js";
|
|
6
7
|
|
|
7
8
|
/** Semantic heading level — also the default visual size. */
|
|
8
9
|
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
@@ -23,6 +24,13 @@
|
|
|
23
24
|
* `on-accent`.
|
|
24
25
|
*/
|
|
25
26
|
tone?: "headline" | OnFillTone;
|
|
27
|
+
/**
|
|
28
|
+
* `heading` is the document heading voice. `display` is the second
|
|
29
|
+
* face (`--font-family-display`) with larger steps, for a hero, a
|
|
30
|
+
* score or a big number; its weight and tracking are the custom
|
|
31
|
+
* properties `--zabi-display-weight` and `--zabi-display-tracking`.
|
|
32
|
+
*/
|
|
33
|
+
variant?: "heading" | "display";
|
|
26
34
|
class?: string;
|
|
27
35
|
children?: Snippet;
|
|
28
36
|
};
|
|
@@ -32,6 +40,7 @@
|
|
|
32
40
|
size,
|
|
33
41
|
text = "",
|
|
34
42
|
tone = "headline",
|
|
43
|
+
variant = "heading",
|
|
35
44
|
class: className = "",
|
|
36
45
|
children,
|
|
37
46
|
...restProps
|
|
@@ -61,8 +70,25 @@
|
|
|
61
70
|
6: "text-base leading-6 font-semibold",
|
|
62
71
|
};
|
|
63
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Display steps: 60px (48 on a phone), 48 (36), 36 (30), 30, 24, 20. Weight and tracking come from
|
|
75
|
+
* `displayVoice`, so no size carries its own.
|
|
76
|
+
*/
|
|
77
|
+
const displaySizeClasses: Record<HeadingLevel, string> = {
|
|
78
|
+
1: "text-5xl sm:text-6xl leading-[1.0]",
|
|
79
|
+
2: "text-4xl sm:text-5xl leading-[1.05]",
|
|
80
|
+
3: "text-3xl sm:text-4xl leading-[1.1]",
|
|
81
|
+
4: "text-3xl leading-[1.2]",
|
|
82
|
+
5: "text-2xl leading-[1.2]",
|
|
83
|
+
6: "text-xl leading-[1.2]",
|
|
84
|
+
};
|
|
85
|
+
|
|
64
86
|
const headingClasses = $derived(
|
|
65
|
-
|
|
87
|
+
variant === "display"
|
|
88
|
+
? cn(
|
|
89
|
+
`${toneClasses[tone] ?? toneClasses.headline} font-display text-balance ${displayVoice} ${displaySizeClasses[visualSize] ?? displaySizeClasses[6]} ${className}`,
|
|
90
|
+
)
|
|
91
|
+
: cn(`${toneClasses[tone] ?? toneClasses.headline} ${sizeClasses[visualSize] ?? sizeClasses[6]} ${className}`),
|
|
66
92
|
);
|
|
67
93
|
</script>
|
|
68
94
|
|
|
@@ -19,6 +19,13 @@ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, "class"> & {
|
|
|
19
19
|
* `on-accent`.
|
|
20
20
|
*/
|
|
21
21
|
tone?: "headline" | OnFillTone;
|
|
22
|
+
/**
|
|
23
|
+
* `heading` is the document heading voice. `display` is the second
|
|
24
|
+
* face (`--font-family-display`) with larger steps, for a hero, a
|
|
25
|
+
* score or a big number; its weight and tracking are the custom
|
|
26
|
+
* properties `--zabi-display-weight` and `--zabi-display-tracking`.
|
|
27
|
+
*/
|
|
28
|
+
variant?: "heading" | "display";
|
|
22
29
|
class?: string;
|
|
23
30
|
children?: Snippet;
|
|
24
31
|
};
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
|
4
4
|
import type { ButtonVariant, SizeVariant } from "../types/variants.js";
|
|
5
5
|
import { cn } from "../util/cn.js";
|
|
6
|
+
import { isDevBuild } from "../util/app-shell.js";
|
|
7
|
+
import { badgeText, defaultBadgeLabel } from "../util/bottom-tab-bar.js";
|
|
8
|
+
import Badge from "./Badge.svelte";
|
|
6
9
|
|
|
7
10
|
/** The shared scale plus `xs`, which only the icon button has. */
|
|
8
11
|
type IconButtonSize = SizeVariant | "xs";
|
|
@@ -56,6 +59,19 @@
|
|
|
56
59
|
loading?: boolean;
|
|
57
60
|
/** Required for icon-only usage (no visible text). */
|
|
58
61
|
label?: string;
|
|
62
|
+
/**
|
|
63
|
+
* A count drawn on the button, in the attention colour, inside the
|
|
64
|
+
* button's own box (so a bar or an `overflow` ancestor cannot cut it).
|
|
65
|
+
* Nothing is drawn for 0, a negative number or when it is left out;
|
|
66
|
+
* while `loading` it is hidden. At `xs` and `sm` there is no room for
|
|
67
|
+
* a number: a dot is drawn instead. Needs `label`: the accessible name
|
|
68
|
+
* becomes the label, a comma and `countLabel(count)`.
|
|
69
|
+
*/
|
|
70
|
+
count?: number;
|
|
71
|
+
/** What the count adds to the accessible name, after the label. Default: "3 new". */
|
|
72
|
+
countLabel?: (count: number) => string;
|
|
73
|
+
/** Above this the badge reads `99+`. The accessible name keeps the real count. */
|
|
74
|
+
countMax?: number;
|
|
59
75
|
class?: string;
|
|
60
76
|
children?: Snippet;
|
|
61
77
|
};
|
|
@@ -70,6 +86,9 @@
|
|
|
70
86
|
type,
|
|
71
87
|
href,
|
|
72
88
|
label = "",
|
|
89
|
+
count,
|
|
90
|
+
countLabel = defaultBadgeLabel,
|
|
91
|
+
countMax = 99,
|
|
73
92
|
class: className = "",
|
|
74
93
|
onclick,
|
|
75
94
|
children,
|
|
@@ -77,6 +96,22 @@
|
|
|
77
96
|
}: Props = $props();
|
|
78
97
|
|
|
79
98
|
const isDisabled = $derived(!!disabled || loading);
|
|
99
|
+
|
|
100
|
+
/** A count is drawn only for a positive number, and not while loading. */
|
|
101
|
+
const hasCount = $derived(typeof count === "number" && count > 0);
|
|
102
|
+
const showCount = $derived(hasCount && !loading);
|
|
103
|
+
/** `xs` and `sm` have no room for a number beside the icon: a dot. */
|
|
104
|
+
const countAsDot = $derived(size === "xs" || size === "sm");
|
|
105
|
+
const accessibleName = $derived(
|
|
106
|
+
showCount && label ? `${label}, ${countLabel(count as number)}` : label || undefined,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
$effect(() => {
|
|
110
|
+
if (!isDevBuild() || !hasCount || label) return;
|
|
111
|
+
console.warn(
|
|
112
|
+
"[zabi-components] IconButton: `count` needs a `label`; without it the count has no accessible name.",
|
|
113
|
+
);
|
|
114
|
+
});
|
|
80
115
|
/**
|
|
81
116
|
* Loading without `disabled`: unavailable, and still where focus is.
|
|
82
117
|
*
|
|
@@ -157,13 +192,18 @@
|
|
|
157
192
|
});
|
|
158
193
|
|
|
159
194
|
const disabledClass =
|
|
160
|
-
"disabled:bg-action-disabled disabled:text-action-disabled-text disabled:border-transparent disabled:shadow-none disabled:cursor-not-allowed disabled:active:scale-100";
|
|
195
|
+
"disabled:bg-action-disabled disabled:text-action-disabled-text disabled:border-transparent disabled:bg-none disabled:inset-shadow-none disabled:shadow-none disabled:cursor-not-allowed disabled:active:scale-100";
|
|
161
196
|
|
|
162
197
|
const isDangerTone = $derived(
|
|
163
198
|
tone === "danger" && (variant === "ghost" || variant === "outline"),
|
|
164
199
|
);
|
|
165
200
|
|
|
166
201
|
const variantClass = $derived.by(() => {
|
|
202
|
+
// The veil of the solid fills, as on Button. Not when disabled, and
|
|
203
|
+
// not on a toggled-on button (`pressedClass`): that holds the pressed
|
|
204
|
+
// fill flat, with its outline as the shape.
|
|
205
|
+
const veil = disabled || (isToggle && pressed) ? "" : "bg-control-gradient ";
|
|
206
|
+
const veilAccent = disabled || (isToggle && pressed) ? "" : "bg-control-gradient-accent ";
|
|
167
207
|
if (isDangerTone) {
|
|
168
208
|
const fill =
|
|
169
209
|
"text-error hover:bg-action-danger-subtle active:bg-action-danger-subtle-hover active:scale-[0.98] motion-reduce:active:scale-100 focus-ring--danger";
|
|
@@ -174,19 +214,22 @@
|
|
|
174
214
|
switch (variant) {
|
|
175
215
|
case "secondary":
|
|
176
216
|
return "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98] motion-reduce:active:scale-100";
|
|
217
|
+
case "tonal":
|
|
218
|
+
return "bg-action-tonal text-action-tonal active:scale-[0.98] motion-reduce:active:scale-100";
|
|
177
219
|
case "danger":
|
|
178
|
-
return
|
|
220
|
+
return `${veil}bg-action-danger text-action-danger-text hover:bg-action-danger-hover active:bg-action-danger-active active:scale-[0.98] motion-reduce:active:scale-100 focus-ring--danger`;
|
|
179
221
|
case "ghost":
|
|
180
222
|
return "bg-transparent text-headline hover:bg-surface-hover active:bg-surface-active active:scale-[0.98] motion-reduce:active:scale-100 focus-ring--muted";
|
|
181
223
|
case "outline":
|
|
182
|
-
return "bg-transparent border border-
|
|
224
|
+
return "bg-transparent border border-action-outline text-headline hover:bg-surface-hover hover:border-action-outline-hover active:bg-surface-active active:scale-[0.98] motion-reduce:active:scale-100";
|
|
183
225
|
case "link":
|
|
226
|
+
case "text":
|
|
184
227
|
return "bg-transparent text-link hover:text-link-hover focus-ring--muted";
|
|
185
228
|
case "accent":
|
|
186
|
-
return
|
|
229
|
+
return `${veilAccent}bg-accent text-on-accent hover:bg-accent-hover active:bg-accent-active active:scale-[0.98] motion-reduce:active:scale-100`;
|
|
187
230
|
case "primary":
|
|
188
231
|
default:
|
|
189
|
-
return
|
|
232
|
+
return `${veil}bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98] motion-reduce:active:scale-100`;
|
|
190
233
|
}
|
|
191
234
|
});
|
|
192
235
|
|
|
@@ -215,7 +258,12 @@
|
|
|
215
258
|
case "ghost":
|
|
216
259
|
case "outline":
|
|
217
260
|
case "link":
|
|
261
|
+
case "text":
|
|
218
262
|
return `bg-action-primary-subtle text-link hover:bg-action-primary-subtle-hover active:bg-action-primary-subtle-active ${outline}`;
|
|
263
|
+
case "tonal":
|
|
264
|
+
// The tonal fill keeps its own hover and pressed steps; the
|
|
265
|
+
// outline is what shows it is on.
|
|
266
|
+
return outline;
|
|
219
267
|
case "accent":
|
|
220
268
|
return `bg-accent-active hover:bg-accent-active ${outline}`;
|
|
221
269
|
case "primary":
|
|
@@ -226,7 +274,7 @@
|
|
|
226
274
|
|
|
227
275
|
const buttonClasses = $derived.by(() => {
|
|
228
276
|
const base =
|
|
229
|
-
"inline-flex focus-ring items-center justify-center rounded-control shrink-0 transition-colors duration-
|
|
277
|
+
"inline-flex focus-ring items-center justify-center rounded-control shrink-0 transition-colors duration-(--duration-base) cursor-pointer select-none";
|
|
230
278
|
// `disabled:` does not match an `<a>`: a disabled link wears the
|
|
231
279
|
// disabled pair itself, in place of its variant. So does a loading
|
|
232
280
|
// button, which is not `:disabled` (it keeps focus): with the
|
|
@@ -236,7 +284,8 @@
|
|
|
236
284
|
isDisabled && (isLink || isBusy)
|
|
237
285
|
? "bg-(--color-action-disabled) text-action-disabled-text border border-transparent cursor-not-allowed"
|
|
238
286
|
: `${variantClass} ${isLink ? "" : pressedClass} ${disabledClass}`;
|
|
239
|
-
|
|
287
|
+
// Relative only for the badge: it is placed against the button's own box.
|
|
288
|
+
return cn(`${base} ${showCount ? "relative " : ""}${sizeClass.box} ${state} ${className}`);
|
|
240
289
|
});
|
|
241
290
|
</script>
|
|
242
291
|
|
|
@@ -249,6 +298,29 @@
|
|
|
249
298
|
{:else if children}
|
|
250
299
|
{@render children()}
|
|
251
300
|
{/if}
|
|
301
|
+
{#if showCount}
|
|
302
|
+
<!-- Inside the box, in its upper end corner, whatever the size: the box
|
|
303
|
+
edge is the outer edge, so nothing outside it can cut it. Decorative; the
|
|
304
|
+
name carries the count. -->
|
|
305
|
+
{#if countAsDot}
|
|
306
|
+
<span
|
|
307
|
+
class="pointer-events-none absolute end-0.5 top-0.5 size-2 rounded-full bg-error"
|
|
308
|
+
aria-hidden="true"
|
|
309
|
+
data-icon-button-count="dot"
|
|
310
|
+
></span>
|
|
311
|
+
{:else}
|
|
312
|
+
<Badge
|
|
313
|
+
size="sm"
|
|
314
|
+
emphasis="solid"
|
|
315
|
+
variant="error"
|
|
316
|
+
aria-hidden="true"
|
|
317
|
+
data-icon-button-count="badge"
|
|
318
|
+
class="pointer-events-none absolute end-0.5 top-0.5 h-[18px] min-h-[18px] min-w-[18px] px-[5px] py-0 text-[11px] leading-[18px] whitespace-nowrap"
|
|
319
|
+
>
|
|
320
|
+
{badgeText(count as number, countMax)}
|
|
321
|
+
</Badge>
|
|
322
|
+
{/if}
|
|
323
|
+
{/if}
|
|
252
324
|
{/snippet}
|
|
253
325
|
|
|
254
326
|
{#if isLink}
|
|
@@ -261,7 +333,7 @@
|
|
|
261
333
|
role={isDisabled ? "link" : undefined}
|
|
262
334
|
aria-disabled={isDisabled ? "true" : undefined}
|
|
263
335
|
aria-busy={loading ? "true" : undefined}
|
|
264
|
-
aria-label={
|
|
336
|
+
aria-label={accessibleName}
|
|
265
337
|
{...(restProps as unknown as HTMLAnchorAttributes)}
|
|
266
338
|
onclick={handleLinkClick}
|
|
267
339
|
>
|
|
@@ -276,7 +348,7 @@
|
|
|
276
348
|
aria-busy={loading ? "true" : undefined}
|
|
277
349
|
aria-pressed={pressed}
|
|
278
350
|
onclick={handleClick}
|
|
279
|
-
aria-label={
|
|
351
|
+
aria-label={accessibleName}
|
|
280
352
|
{...restProps}
|
|
281
353
|
>
|
|
282
354
|
{@render content()}
|
|
@@ -52,6 +52,19 @@ type Props = Omit<HTMLButtonAttributes, "class"> & {
|
|
|
52
52
|
loading?: boolean;
|
|
53
53
|
/** Required for icon-only usage (no visible text). */
|
|
54
54
|
label?: string;
|
|
55
|
+
/**
|
|
56
|
+
* A count drawn on the button, in the attention colour, inside the
|
|
57
|
+
* button's own box (so a bar or an `overflow` ancestor cannot cut it).
|
|
58
|
+
* Nothing is drawn for 0, a negative number or when it is left out;
|
|
59
|
+
* while `loading` it is hidden. At `xs` and `sm` there is no room for
|
|
60
|
+
* a number: a dot is drawn instead. Needs `label`: the accessible name
|
|
61
|
+
* becomes the label, a comma and `countLabel(count)`.
|
|
62
|
+
*/
|
|
63
|
+
count?: number;
|
|
64
|
+
/** What the count adds to the accessible name, after the label. Default: "3 new". */
|
|
65
|
+
countLabel?: (count: number) => string;
|
|
66
|
+
/** Above this the badge reads `99+`. The accessible name keeps the real count. */
|
|
67
|
+
countMax?: number;
|
|
55
68
|
class?: string;
|
|
56
69
|
children?: Snippet;
|
|
57
70
|
};
|
package/dist/atoms/Input.svelte
CHANGED
|
@@ -61,8 +61,9 @@
|
|
|
61
61
|
/** Drawn inside the field, after the text and before the loading spinner's place. */
|
|
62
62
|
trailing?: Snippet;
|
|
63
63
|
/**
|
|
64
|
-
* With `type="password"`:
|
|
65
|
-
*
|
|
64
|
+
* With `type="password"`: a button at the end of the field that shows
|
|
65
|
+
* the password as text, and hides it again. On by default; `false`
|
|
66
|
+
* leaves it out. Ignored for other types.
|
|
66
67
|
*/
|
|
67
68
|
revealable?: boolean;
|
|
68
69
|
/**
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
error = "",
|
|
97
98
|
leading,
|
|
98
99
|
trailing,
|
|
99
|
-
revealable =
|
|
100
|
+
revealable = true,
|
|
100
101
|
revealLabel: revealLabelGiven,
|
|
101
102
|
oninput,
|
|
102
103
|
onblur,
|
|
@@ -254,9 +255,13 @@
|
|
|
254
255
|
* field regardless of the layout around it.
|
|
255
256
|
*/
|
|
256
257
|
const baseClasses =
|
|
257
|
-
"focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-control transition-colors duration-150 placeholder:text-input-placeholder text-body focus:outline-none focus-visible:outline-none disabled:text-action-disabled-text disabled:cursor-not-allowed";
|
|
258
|
+
"focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-control transition-colors duration-150 placeholder:text-input-placeholder placeholder-shown:text-ellipsis text-body focus:outline-none focus-visible:outline-none disabled:text-action-disabled-text disabled:cursor-not-allowed";
|
|
258
259
|
|
|
259
|
-
|
|
260
|
+
// A search field that brings its own clear button in `trailing` would
|
|
261
|
+
// show two crosses: the browser's is taken away.
|
|
262
|
+
const nativeClear = trailing ? "[&::-webkit-search-cancel-button]:appearance-none" : "";
|
|
263
|
+
|
|
264
|
+
return cn(`${baseClasses} ${sizeStyles.box} ${spinnerPad} ${sizeStyles.text} ${variantClass()} ${nativeClear} ${className}`);
|
|
260
265
|
});
|
|
261
266
|
|
|
262
267
|
const labelClasses = $derived(
|
|
@@ -35,8 +35,9 @@ type Props = Omit<HTMLInputAttributes, "class" | "size" | "value" | "type" | "id
|
|
|
35
35
|
/** Drawn inside the field, after the text and before the loading spinner's place. */
|
|
36
36
|
trailing?: Snippet;
|
|
37
37
|
/**
|
|
38
|
-
* With `type="password"`:
|
|
39
|
-
*
|
|
38
|
+
* With `type="password"`: a button at the end of the field that shows
|
|
39
|
+
* the password as text, and hides it again. On by default; `false`
|
|
40
|
+
* leaves it out. Ignored for other types.
|
|
40
41
|
*/
|
|
41
42
|
revealable?: boolean;
|
|
42
43
|
/**
|
package/dist/atoms/List.svelte
CHANGED
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
const listClasses = $derived(
|
|
35
35
|
// The same radius as its rows. Not `overflow-hidden`: the rows fill
|
|
36
36
|
// the list edge to edge, so clipping here cut a row's focus ring off
|
|
37
|
-
// on every side but the gap to the next row.
|
|
38
|
-
|
|
37
|
+
// on every side but the gap to the next row. `list-dividers` draws the
|
|
38
|
+
// hairline in the gap between rows (app.css).
|
|
39
|
+
cn(`list-dividers space-y-1 rounded-[var(--zabi-list-row-radius,var(--radius-container))] ${className}`),
|
|
39
40
|
);
|
|
40
41
|
</script>
|
|
41
42
|
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
avatar?: string;
|
|
14
14
|
/** `avatar` alt text; `""` if decorative. */
|
|
15
15
|
avatarAlt?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Leading picture drawn in the round, in place of an address: an
|
|
18
|
+
* inline SVG. Wins over `avatar` and `icon`. Decorative unless
|
|
19
|
+
* `avatarAlt` is set, which then names it.
|
|
20
|
+
*/
|
|
21
|
+
avatarPicture?: Snippet;
|
|
16
22
|
target?: "_self" | "_blank" | "_parent" | "_top";
|
|
17
23
|
rel?: string;
|
|
18
24
|
disabled?: boolean;
|
|
@@ -52,9 +58,14 @@
|
|
|
52
58
|
const baseClasses =
|
|
53
59
|
// The container radius, or the one a `.list-group` shell around the list
|
|
54
60
|
// hands down so the row is concentric with it (see app.css).
|
|
55
|
-
|
|
61
|
+
// The 1px border is kept so the row's height and text do not move, but
|
|
62
|
+
// it is transparent: rows are told apart by the divider between them
|
|
63
|
+
// (app.css), and a bordered row inside a bordered shell was two
|
|
64
|
+
// outlines. `--zabi-list-row-border-color` puts the 8.1 edge back.
|
|
65
|
+
"group flex w-full items-center gap-3 rounded-[var(--zabi-list-row-radius,var(--radius-container))] border px-4 py-3 pr-5 text-left transition-colors duration-(--duration-base)";
|
|
66
|
+
const edge = "border-[color:var(--zabi-list-row-border-color,transparent)]";
|
|
56
67
|
if (!interactive) {
|
|
57
|
-
const tone = selected ? "bg-action-primary-subtle border-action-primary" :
|
|
68
|
+
const tone = selected ? "bg-action-primary-subtle border-action-primary" : edge;
|
|
58
69
|
return `${baseClasses} ${tone} ${item.disabled ? "opacity-50" : ""}`.trim();
|
|
59
70
|
}
|
|
60
71
|
const cursorClasses = item.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer";
|
|
@@ -69,8 +80,8 @@
|
|
|
69
80
|
const stateClasses = selected
|
|
70
81
|
? "bg-action-primary-subtle border-action-primary active:bg-action-primary-subtle-active"
|
|
71
82
|
: item.disabled
|
|
72
|
-
?
|
|
73
|
-
:
|
|
83
|
+
? edge
|
|
84
|
+
: `${edge} hover:bg-surface-hover focus-visible:bg-surface-hover active:bg-surface-active`;
|
|
74
85
|
return `focus-ring ${baseClasses} ${cursorClasses} ${stateClasses}`.trim();
|
|
75
86
|
});
|
|
76
87
|
|
|
@@ -96,7 +107,18 @@
|
|
|
96
107
|
</script>
|
|
97
108
|
|
|
98
109
|
{#snippet rowContent()}
|
|
99
|
-
{#if item.
|
|
110
|
+
{#if item.avatarPicture}
|
|
111
|
+
<ListItemLeading class="rounded-full">
|
|
112
|
+
<span
|
|
113
|
+
class="flex size-full items-center justify-center"
|
|
114
|
+
role={item.avatarAlt ? "img" : undefined}
|
|
115
|
+
aria-label={item.avatarAlt || undefined}
|
|
116
|
+
aria-hidden={item.avatarAlt ? undefined : "true"}
|
|
117
|
+
>
|
|
118
|
+
{@render item.avatarPicture()}
|
|
119
|
+
</span>
|
|
120
|
+
</ListItemLeading>
|
|
121
|
+
{:else if item.avatar}
|
|
100
122
|
<ListItemLeading class="rounded-full">
|
|
101
123
|
<img
|
|
102
124
|
src={item.avatar}
|
|
@@ -130,7 +152,7 @@
|
|
|
130
152
|
{#if showArrow && interactive}
|
|
131
153
|
<ArrowRight
|
|
132
154
|
size={16}
|
|
133
|
-
class="shrink-0 text-description transition-transform duration-
|
|
155
|
+
class="shrink-0 text-description transition-transform duration-(--duration-base) group-hover:translate-x-1 group-focus-visible:translate-x-1"
|
|
134
156
|
aria-hidden="true"
|
|
135
157
|
/>
|
|
136
158
|
{/if}
|
|
@@ -145,6 +167,7 @@
|
|
|
145
167
|
aria-disabled={item.disabled ? "true" : undefined}
|
|
146
168
|
tabindex={item.disabled ? -1 : undefined}
|
|
147
169
|
class={itemClasses}
|
|
170
|
+
data-selected={selected ? "true" : undefined}
|
|
148
171
|
onclick={handleItemClick}
|
|
149
172
|
{...restProps}
|
|
150
173
|
>
|
|
@@ -154,6 +177,7 @@
|
|
|
154
177
|
<button
|
|
155
178
|
type="button"
|
|
156
179
|
class={itemClasses}
|
|
180
|
+
data-selected={selected ? "true" : undefined}
|
|
157
181
|
disabled={item.disabled}
|
|
158
182
|
onclick={handleItemClick}
|
|
159
183
|
{...restProps}
|
|
@@ -164,7 +188,7 @@
|
|
|
164
188
|
<!-- A plain row has no role, so `aria-disabled` on it would say nothing
|
|
165
189
|
to anyone: a disabled one is only drawn dimmed. There is nothing in it to
|
|
166
190
|
press, enabled or not. -->
|
|
167
|
-
<div class={itemClasses} {...restProps}>
|
|
191
|
+
<div class={itemClasses} data-selected={selected ? "true" : undefined} {...restProps}>
|
|
168
192
|
{@render rowContent()}
|
|
169
193
|
</div>
|
|
170
194
|
{/if}
|
|
@@ -12,6 +12,12 @@ export interface ListItemData {
|
|
|
12
12
|
avatar?: string;
|
|
13
13
|
/** `avatar` alt text; `""` if decorative. */
|
|
14
14
|
avatarAlt?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Leading picture drawn in the round, in place of an address: an
|
|
17
|
+
* inline SVG. Wins over `avatar` and `icon`. Decorative unless
|
|
18
|
+
* `avatarAlt` is set, which then names it.
|
|
19
|
+
*/
|
|
20
|
+
avatarPicture?: Snippet;
|
|
15
21
|
target?: "_self" | "_blank" | "_parent" | "_top";
|
|
16
22
|
rel?: string;
|
|
17
23
|
disabled?: boolean;
|