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
package/dist/atoms/Card.svelte
CHANGED
|
@@ -1,18 +1,71 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
|
-
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
-
import type { CardVariant, SizeVariant } from "../types/variants.js";
|
|
3
|
+
import type { HTMLAnchorAttributes, HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import type { CardVariant, SizeVariant, SurfaceTone } from "../types/variants.js";
|
|
5
5
|
|
|
6
6
|
import { cn } from "../util/cn.js";
|
|
7
|
+
import { toneSurface } from "../util/tone-surface.js";
|
|
7
8
|
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
|
|
8
9
|
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
9
10
|
size?: SizeVariant;
|
|
11
|
+
/** The look of a `neutral` card. With a `tone` or a `fill` the fill is the edge: no border, and a shadow only for `elevated`. */
|
|
10
12
|
variant?: CardVariant;
|
|
13
|
+
/**
|
|
14
|
+
* What the card is filled with: `neutral` (the default, the card surface),
|
|
15
|
+
* `tint` (the brand tint), `brand` or `accent`. On `brand` and `accent`
|
|
16
|
+
* the text, links, controls and focus rings inside take the fill's label
|
|
17
|
+
* colour; a Card, field or Alert inside is a surface again.
|
|
18
|
+
*/
|
|
19
|
+
tone?: SurfaceTone;
|
|
20
|
+
/**
|
|
21
|
+
* A fill the app chooses, any CSS colour (`var(--group-colour)`). Wins
|
|
22
|
+
* over `tone`. Set `onFill`, the label colour on it. The library cannot
|
|
23
|
+
* check that pair's contrast: the app must hold it to 4.5:1.
|
|
24
|
+
*/
|
|
25
|
+
fill?: string;
|
|
26
|
+
/** The label colour on `fill`, any CSS colour. */
|
|
27
|
+
onFill?: string;
|
|
28
|
+
/**
|
|
29
|
+
* With `href` the card itself is an `<a>`: the whole card is the link,
|
|
30
|
+
* and it works without scripts. A link card must not contain other
|
|
31
|
+
* interactive elements (a link or a button in a link is invalid and
|
|
32
|
+
* unreachable); a card with actions inside takes `onclick` or none.
|
|
33
|
+
* With `onclick` too, it is a link and `onclick` runs on activation.
|
|
34
|
+
*/
|
|
35
|
+
href?: string;
|
|
36
|
+
/** With `href`: where the link opens. */
|
|
37
|
+
target?: HTMLAnchorAttributes["target"];
|
|
38
|
+
/** With `href`: the link's relationship, such as `noopener`. */
|
|
39
|
+
rel?: string;
|
|
40
|
+
/**
|
|
41
|
+
* The card itself is a `<button>`: the whole card is the control, and
|
|
42
|
+
* `submit` posts the form around it without scripts. With `href`
|
|
43
|
+
* the card is a link and this is ignored. Like a link card it must not
|
|
44
|
+
* contain other interactive elements. `onclick`
|
|
45
|
+
* runs on a press, as on any button.
|
|
46
|
+
*/
|
|
47
|
+
type?: "submit" | "button" | "reset";
|
|
48
|
+
/** With `type`: the name the button submits under. */
|
|
49
|
+
name?: string;
|
|
50
|
+
/** With `type`: what the button submits. */
|
|
51
|
+
value?: string;
|
|
52
|
+
/** With `type`: the id of the form it belongs to, when it is not inside one. */
|
|
53
|
+
form?: string;
|
|
54
|
+
/** With `type`: the card cannot be pressed, is dimmed, and is skipped by the keyboard. */
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* With `type`: the card's action is on its way, as `Button`'s and
|
|
58
|
+
* `Chip`'s `loading`. `aria-busy` and `aria-disabled` (not `disabled`),
|
|
59
|
+
* the press swallowed (it submits nothing and does not reach
|
|
60
|
+
* `onclick`), the pressed look held, the size and the Tab stop kept.
|
|
61
|
+
* No spinner. Ignored by a link card and a plain card.
|
|
62
|
+
*/
|
|
63
|
+
loading?: boolean;
|
|
11
64
|
fullWidth?: boolean;
|
|
12
65
|
class?: string;
|
|
13
66
|
/** @deprecated use `class` — kept so 7.x call sites keep working. */
|
|
14
67
|
className?: string;
|
|
15
|
-
/**
|
|
68
|
+
/** Names the card when it is interactive and its content does not (the content names a link well enough in most cases). */
|
|
16
69
|
ariaLabel?: string;
|
|
17
70
|
children?: Snippet;
|
|
18
71
|
};
|
|
@@ -21,10 +74,23 @@
|
|
|
21
74
|
onclick,
|
|
22
75
|
size = "md",
|
|
23
76
|
variant = "default",
|
|
77
|
+
tone = "neutral",
|
|
78
|
+
fill,
|
|
79
|
+
onFill,
|
|
80
|
+
href,
|
|
81
|
+
target,
|
|
82
|
+
rel,
|
|
83
|
+
type: buttonType,
|
|
84
|
+
name,
|
|
85
|
+
value,
|
|
86
|
+
form,
|
|
87
|
+
disabled = false,
|
|
88
|
+
loading = false,
|
|
24
89
|
fullWidth = true,
|
|
25
90
|
class: classProp = "",
|
|
26
91
|
className = "",
|
|
27
92
|
ariaLabel,
|
|
93
|
+
style: styleProp,
|
|
28
94
|
children,
|
|
29
95
|
...restProps
|
|
30
96
|
}: Props = $props();
|
|
@@ -35,36 +101,79 @@
|
|
|
35
101
|
size === "sm" ? "p-4" : size === "lg" ? "p-8" : "p-6",
|
|
36
102
|
);
|
|
37
103
|
|
|
104
|
+
const surface = $derived(toneSurface({ tone, fill, onFill }));
|
|
105
|
+
|
|
106
|
+
const isLink = $derived(href !== undefined && href !== null);
|
|
107
|
+
const isButton = $derived(!isLink && buttonType !== undefined);
|
|
108
|
+
|
|
38
109
|
const variantClasses = $derived(
|
|
39
|
-
|
|
40
|
-
?
|
|
41
|
-
|
|
42
|
-
|
|
110
|
+
surface.filled
|
|
111
|
+
? // The fill is the edge: no border, flat unless the variant lifts it.
|
|
112
|
+
`${surface.classes} ${variant === "elevated" ? "shadow-lg" : "shadow-none"}`
|
|
113
|
+
: variant === "elevated"
|
|
114
|
+
? "bg-card-elevated shadow-lg"
|
|
43
115
|
: variant === "flat"
|
|
44
|
-
? "bg-card shadow-none border-none"
|
|
45
|
-
: "bg-card shadow-
|
|
116
|
+
? "bg-card-flat shadow-none border-none"
|
|
117
|
+
: "bg-card border border-border shadow-none",
|
|
46
118
|
);
|
|
47
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Hover and pressed on a fill the card does not know: a veil of the card's
|
|
122
|
+
* own text colour (`bg-current`), so it reads on a tint, the brand, the
|
|
123
|
+
* accent and any colour an app passes. The card is `relative` for it.
|
|
124
|
+
*/
|
|
125
|
+
const STATE_LAYER =
|
|
126
|
+
"relative after:absolute after:inset-0 after:rounded-[inherit] after:bg-current after:opacity-0 after:pointer-events-none after:transition-opacity after:duration-(--duration-base) motion-reduce:after:transition-none hover:after:opacity-8 active:after:opacity-12";
|
|
127
|
+
|
|
128
|
+
/** Loading without `disabled`: unavailable, and still where focus is (see Button). */
|
|
129
|
+
const isBusy = $derived(isButton && loading && !disabled);
|
|
130
|
+
|
|
131
|
+
const interactive = $derived(!!onclick || isLink || isButton);
|
|
132
|
+
|
|
48
133
|
const interactiveClasses = $derived.by(() => {
|
|
49
|
-
if (!
|
|
134
|
+
if (!interactive) return "";
|
|
135
|
+
if (isBusy) {
|
|
136
|
+
// The pressed look, held: no hover or pressed rule is left to change it.
|
|
137
|
+
// Forced colours drop the fills the held look is made of: GrayText text and a dashed inset edge say it there.
|
|
138
|
+
const held =
|
|
139
|
+
"cursor-progress focus-ring forced-colors:text-[color:GrayText] forced-colors:outline-1 forced-colors:outline-dashed forced-colors:-outline-offset-1 forced-colors:outline-[color:GrayText]";
|
|
140
|
+
if (surface.filled) return `${held} relative after:absolute after:inset-0 after:rounded-[inherit] after:bg-current after:opacity-12 after:pointer-events-none`;
|
|
141
|
+
switch (variant) {
|
|
142
|
+
case "elevated":
|
|
143
|
+
return `${held} bg-card-active`;
|
|
144
|
+
case "flat":
|
|
145
|
+
return `${held} bg-card-flat-active`;
|
|
146
|
+
default:
|
|
147
|
+
return `${held} border-border-medium bg-card-active`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
50
150
|
const shared = "cursor-pointer focus-ring";
|
|
151
|
+
if (surface.filled) return `${shared} ${STATE_LAYER}`;
|
|
51
152
|
switch (variant) {
|
|
52
153
|
case "elevated":
|
|
53
154
|
return `${shared} hover:bg-card-hover active:bg-card-active`;
|
|
54
|
-
case "outlined":
|
|
55
|
-
return `${shared} hover:border-border-medium hover:bg-card-hover active:bg-card-active`;
|
|
56
155
|
case "flat":
|
|
57
|
-
return `${shared} hover:bg-card-hover active:bg-card-active`;
|
|
156
|
+
return `${shared} hover:bg-card-flat-hover active:bg-card-flat-active`;
|
|
58
157
|
default:
|
|
59
|
-
return `${shared} hover:
|
|
158
|
+
return `${shared} hover:border-border-medium hover:bg-card-hover active:bg-card-active`;
|
|
60
159
|
}
|
|
61
160
|
});
|
|
62
161
|
|
|
162
|
+
/** A link card keeps the body colour (its whole content is not link-coloured); a scoped fill sets its own. */
|
|
163
|
+
const linkClasses = $derived(
|
|
164
|
+
isLink
|
|
165
|
+
? `block no-underline min-h-11 ${surface.scoped ? "" : "text-body"}`
|
|
166
|
+
: isButton
|
|
167
|
+
? `block text-start min-h-11 ${surface.scoped ? "" : "text-body"} ${disabled ? "cursor-not-allowed opacity-50 pointer-events-none" : ""}`
|
|
168
|
+
: "",
|
|
169
|
+
);
|
|
170
|
+
|
|
63
171
|
const cardClasses = $derived(
|
|
64
172
|
cn(
|
|
65
173
|
"rounded-container transition-all duration-150",
|
|
66
174
|
variantClasses,
|
|
67
175
|
interactiveClasses,
|
|
176
|
+
linkClasses,
|
|
68
177
|
fullWidth ? "w-full" : "",
|
|
69
178
|
sizeClass,
|
|
70
179
|
classProp,
|
|
@@ -72,6 +181,20 @@
|
|
|
72
181
|
),
|
|
73
182
|
);
|
|
74
183
|
|
|
184
|
+
const cardStyle = $derived(
|
|
185
|
+
[surface.style, typeof styleProp === "string" ? styleProp : undefined].filter(Boolean).join("; ") || undefined,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
/** A busy card is not `disabled`, so the press reaches it: it ends here, and submits nothing. */
|
|
189
|
+
function handleButtonClick(event: MouseEvent) {
|
|
190
|
+
if (isBusy) {
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
event.stopPropagation();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
onclick?.(event);
|
|
196
|
+
}
|
|
197
|
+
|
|
75
198
|
function handleKeydown(event: KeyboardEvent) {
|
|
76
199
|
if (onclick && (event.key === "Enter" || event.key === " ")) {
|
|
77
200
|
event.preventDefault();
|
|
@@ -82,14 +205,47 @@
|
|
|
82
205
|
const cardRole = $derived(onclick ? "button" : undefined);
|
|
83
206
|
</script>
|
|
84
207
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
208
|
+
{#if isLink}
|
|
209
|
+
<a
|
|
210
|
+
class={cardClasses}
|
|
211
|
+
style={cardStyle}
|
|
212
|
+
{href}
|
|
213
|
+
{target}
|
|
214
|
+
{rel}
|
|
215
|
+
{onclick}
|
|
216
|
+
aria-label={ariaLabel}
|
|
217
|
+
{...restProps as HTMLAnchorAttributes}
|
|
218
|
+
>
|
|
219
|
+
{@render children?.()}
|
|
220
|
+
</a>
|
|
221
|
+
{:else if isButton}
|
|
222
|
+
<button
|
|
223
|
+
type={buttonType}
|
|
224
|
+
{name}
|
|
225
|
+
{value}
|
|
226
|
+
{form}
|
|
227
|
+
{disabled}
|
|
228
|
+
class={cardClasses}
|
|
229
|
+
style={cardStyle}
|
|
230
|
+
aria-label={ariaLabel}
|
|
231
|
+
aria-busy={isBusy ? "true" : undefined}
|
|
232
|
+
aria-disabled={isBusy ? "true" : undefined}
|
|
233
|
+
{...restProps as HTMLAttributes<HTMLButtonElement>}
|
|
234
|
+
onclick={handleButtonClick}
|
|
235
|
+
>
|
|
236
|
+
{@render children?.()}
|
|
237
|
+
</button>
|
|
238
|
+
{:else}
|
|
239
|
+
<div
|
|
240
|
+
class={cardClasses}
|
|
241
|
+
style={cardStyle}
|
|
242
|
+
{onclick}
|
|
243
|
+
role={cardRole}
|
|
244
|
+
{...onclick ? { tabindex: 0 } : {}}
|
|
245
|
+
aria-label={ariaLabel}
|
|
246
|
+
onkeydown={onclick ? handleKeydown : undefined}
|
|
247
|
+
{...restProps}
|
|
248
|
+
>
|
|
249
|
+
{@render children?.()}
|
|
250
|
+
</div>
|
|
251
|
+
{/if}
|
|
@@ -1,15 +1,67 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
-
import type { CardVariant, SizeVariant } from "../types/variants.js";
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { CardVariant, SizeVariant, SurfaceTone } from "../types/variants.js";
|
|
4
4
|
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
|
|
5
5
|
onclick?: (event: MouseEvent) => void | Promise<void>;
|
|
6
6
|
size?: SizeVariant;
|
|
7
|
+
/** The look of a `neutral` card. With a `tone` or a `fill` the fill is the edge: no border, and a shadow only for `elevated`. */
|
|
7
8
|
variant?: CardVariant;
|
|
9
|
+
/**
|
|
10
|
+
* What the card is filled with: `neutral` (the default, the card surface),
|
|
11
|
+
* `tint` (the brand tint), `brand` or `accent`. On `brand` and `accent`
|
|
12
|
+
* the text, links, controls and focus rings inside take the fill's label
|
|
13
|
+
* colour; a Card, field or Alert inside is a surface again.
|
|
14
|
+
*/
|
|
15
|
+
tone?: SurfaceTone;
|
|
16
|
+
/**
|
|
17
|
+
* A fill the app chooses, any CSS colour (`var(--group-colour)`). Wins
|
|
18
|
+
* over `tone`. Set `onFill`, the label colour on it. The library cannot
|
|
19
|
+
* check that pair's contrast: the app must hold it to 4.5:1.
|
|
20
|
+
*/
|
|
21
|
+
fill?: string;
|
|
22
|
+
/** The label colour on `fill`, any CSS colour. */
|
|
23
|
+
onFill?: string;
|
|
24
|
+
/**
|
|
25
|
+
* With `href` the card itself is an `<a>`: the whole card is the link,
|
|
26
|
+
* and it works without scripts. A link card must not contain other
|
|
27
|
+
* interactive elements (a link or a button in a link is invalid and
|
|
28
|
+
* unreachable); a card with actions inside takes `onclick` or none.
|
|
29
|
+
* With `onclick` too, it is a link and `onclick` runs on activation.
|
|
30
|
+
*/
|
|
31
|
+
href?: string;
|
|
32
|
+
/** With `href`: where the link opens. */
|
|
33
|
+
target?: HTMLAnchorAttributes["target"];
|
|
34
|
+
/** With `href`: the link's relationship, such as `noopener`. */
|
|
35
|
+
rel?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The card itself is a `<button>`: the whole card is the control, and
|
|
38
|
+
* `submit` posts the form around it without scripts. With `href`
|
|
39
|
+
* the card is a link and this is ignored. Like a link card it must not
|
|
40
|
+
* contain other interactive elements. `onclick`
|
|
41
|
+
* runs on a press, as on any button.
|
|
42
|
+
*/
|
|
43
|
+
type?: "submit" | "button" | "reset";
|
|
44
|
+
/** With `type`: the name the button submits under. */
|
|
45
|
+
name?: string;
|
|
46
|
+
/** With `type`: what the button submits. */
|
|
47
|
+
value?: string;
|
|
48
|
+
/** With `type`: the id of the form it belongs to, when it is not inside one. */
|
|
49
|
+
form?: string;
|
|
50
|
+
/** With `type`: the card cannot be pressed, is dimmed, and is skipped by the keyboard. */
|
|
51
|
+
disabled?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* With `type`: the card's action is on its way, as `Button`'s and
|
|
54
|
+
* `Chip`'s `loading`. `aria-busy` and `aria-disabled` (not `disabled`),
|
|
55
|
+
* the press swallowed (it submits nothing and does not reach
|
|
56
|
+
* `onclick`), the pressed look held, the size and the Tab stop kept.
|
|
57
|
+
* No spinner. Ignored by a link card and a plain card.
|
|
58
|
+
*/
|
|
59
|
+
loading?: boolean;
|
|
8
60
|
fullWidth?: boolean;
|
|
9
61
|
class?: string;
|
|
10
62
|
/** @deprecated use `class` — kept so 7.x call sites keep working. */
|
|
11
63
|
className?: string;
|
|
12
|
-
/**
|
|
64
|
+
/** Names the card when it is interactive and its content does not (the content names a link well enough in most cases). */
|
|
13
65
|
ariaLabel?: string;
|
|
14
66
|
children?: Snippet;
|
|
15
67
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
2
3
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
3
5
|
import SelectionControl from "./SelectionControl.svelte";
|
|
4
6
|
import type { SelectionControlMarkProps } from "./SelectionControl.svelte";
|
|
5
7
|
|
|
@@ -20,6 +22,31 @@
|
|
|
20
22
|
checked?: boolean;
|
|
21
23
|
onChange?: (event: Event) => void;
|
|
22
24
|
onchange?: (event: Event) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Drawn inside the one label, between the box and the label text, as
|
|
27
|
+
* decoration for the name `label` already gives. A leading image
|
|
28
|
+
* should have an empty alt: the label names the row.
|
|
29
|
+
*/
|
|
30
|
+
leading?: Snippet;
|
|
31
|
+
/**
|
|
32
|
+
* A line under the label, tied to the box with `aria-describedby`.
|
|
33
|
+
* It is inside the label, so a tap on it ticks the box; it does not
|
|
34
|
+
* join the name.
|
|
35
|
+
*/
|
|
36
|
+
description?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Where the box sits in the row. `end` puts it after the leading, the
|
|
39
|
+
* label and the description, at the far edge; the row is still one
|
|
40
|
+
* label and fills the width it is given.
|
|
41
|
+
*/
|
|
42
|
+
controlPosition?: "start" | "end";
|
|
43
|
+
variant?: SemanticVariant;
|
|
44
|
+
/** Status text under the control. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
45
|
+
message?: string;
|
|
46
|
+
/** Help text under the control, read out with it. */
|
|
47
|
+
hint?: string;
|
|
48
|
+
/** An error under the control. It marks the input invalid and is announced; it wins over `variant` and `message`. */
|
|
49
|
+
error?: string;
|
|
23
50
|
};
|
|
24
51
|
|
|
25
52
|
let {
|
|
@@ -34,6 +61,9 @@
|
|
|
34
61
|
checked = $bindable<Exclude<Props["checked"], undefined>>(),
|
|
35
62
|
onChange,
|
|
36
63
|
onchange,
|
|
64
|
+
leading,
|
|
65
|
+
description,
|
|
66
|
+
controlPosition = "start",
|
|
37
67
|
...restProps
|
|
38
68
|
}: Props = $props();
|
|
39
69
|
|
|
@@ -95,6 +125,9 @@
|
|
|
95
125
|
{onChange}
|
|
96
126
|
{onchange}
|
|
97
127
|
{mark}
|
|
128
|
+
{leading}
|
|
129
|
+
{description}
|
|
130
|
+
{controlPosition}
|
|
98
131
|
class={className}
|
|
99
132
|
{...restProps}
|
|
100
133
|
/>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
1
2
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
|
+
import type { SemanticVariant } from "../types/variants.js";
|
|
2
4
|
/** Other attributes (`required`, `data-*`, `aria-*`, ...) land on the `<input>`. */
|
|
3
5
|
type Props = Omit<HTMLInputAttributes, "class" | "id" | "name" | "value" | "disabled" | "checked" | "type" | "onchange"> & {
|
|
4
6
|
/** Extra classes for the host element. */
|
|
@@ -13,6 +15,31 @@ type Props = Omit<HTMLInputAttributes, "class" | "id" | "name" | "value" | "disa
|
|
|
13
15
|
checked?: boolean;
|
|
14
16
|
onChange?: (event: Event) => void;
|
|
15
17
|
onchange?: (event: Event) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Drawn inside the one label, between the box and the label text, as
|
|
20
|
+
* decoration for the name `label` already gives. A leading image
|
|
21
|
+
* should have an empty alt: the label names the row.
|
|
22
|
+
*/
|
|
23
|
+
leading?: Snippet;
|
|
24
|
+
/**
|
|
25
|
+
* A line under the label, tied to the box with `aria-describedby`.
|
|
26
|
+
* It is inside the label, so a tap on it ticks the box; it does not
|
|
27
|
+
* join the name.
|
|
28
|
+
*/
|
|
29
|
+
description?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Where the box sits in the row. `end` puts it after the leading, the
|
|
32
|
+
* label and the description, at the far edge; the row is still one
|
|
33
|
+
* label and fills the width it is given.
|
|
34
|
+
*/
|
|
35
|
+
controlPosition?: "start" | "end";
|
|
36
|
+
variant?: SemanticVariant;
|
|
37
|
+
/** Status text under the control. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
38
|
+
message?: string;
|
|
39
|
+
/** Help text under the control, read out with it. */
|
|
40
|
+
hint?: string;
|
|
41
|
+
/** An error under the control. It marks the input invalid and is announced; it wins over `variant` and `message`. */
|
|
42
|
+
error?: string;
|
|
16
43
|
};
|
|
17
44
|
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
18
45
|
type Checkbox = ReturnType<typeof Checkbox>;
|