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,7 +3,7 @@
|
|
|
3
3
|
import type { SemanticVariant, SizeVariant } from '../types/variants.js';
|
|
4
4
|
import { generateId } from "../util/ssr-safe.js";
|
|
5
5
|
import { cn } from "../util/cn.js";
|
|
6
|
-
import { fieldDescribedBy, fieldMessageState } from "../util/field.js";
|
|
6
|
+
import { fieldDescribedBy, fieldMessageState, type TextareaCount } from "../util/field.js";
|
|
7
7
|
import FieldMessages from "./FieldMessages.svelte";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
disabled?: boolean;
|
|
35
35
|
loading?: boolean;
|
|
36
36
|
rows?: number;
|
|
37
|
+
/**
|
|
38
|
+
* The resize grip. `auto` (default): the browser's vertical grip with a mouse or
|
|
39
|
+
* trackpad, none on a touch screen, where dragging it scrolls the page. `vertical`
|
|
40
|
+
* shows it everywhere, `none` never. With `autogrow` there is no grip.
|
|
41
|
+
*/
|
|
42
|
+
resize?: 'auto' | 'none' | 'vertical';
|
|
43
|
+
/** The field grows with its content, from `rows` up to `maxRows`, and scrolls after that. */
|
|
44
|
+
autogrow?: boolean;
|
|
45
|
+
/** The tallest an `autogrow` field gets, in rows. Without it the field has no limit. */
|
|
46
|
+
maxRows?: number;
|
|
37
47
|
size?: SizeVariant;
|
|
38
48
|
variant?: SemanticVariant;
|
|
39
49
|
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
@@ -42,6 +52,16 @@
|
|
|
42
52
|
hint?: string;
|
|
43
53
|
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
44
54
|
error?: string;
|
|
55
|
+
/**
|
|
56
|
+
* A character count under the field, at the end of the line the hint is on. It needs
|
|
57
|
+
* `maxlength`; without one nothing is shown. It is part of the field's description
|
|
58
|
+
* (`aria-describedby`) and is not a live region, so it is not read out at every key.
|
|
59
|
+
*/
|
|
60
|
+
count?: boolean;
|
|
61
|
+
/** The count is shown from this many characters on. `0` (default): always. */
|
|
62
|
+
countFrom?: number;
|
|
63
|
+
/** The words of the count, for the page's language. Default: "12 / 2000". */
|
|
64
|
+
countText?: (count: TextareaCount) => string;
|
|
45
65
|
oninput?: (event: Event) => void;
|
|
46
66
|
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
47
67
|
"aria-describedby"?: string | null;
|
|
@@ -59,11 +79,17 @@
|
|
|
59
79
|
disabled = false,
|
|
60
80
|
loading = false,
|
|
61
81
|
rows = 4,
|
|
82
|
+
resize = 'auto',
|
|
83
|
+
autogrow = false,
|
|
84
|
+
maxRows,
|
|
62
85
|
size = 'md',
|
|
63
86
|
variant = 'default',
|
|
64
87
|
message = '',
|
|
65
88
|
hint = '',
|
|
66
89
|
error = '',
|
|
90
|
+
count = false,
|
|
91
|
+
countFrom = 0,
|
|
92
|
+
countText,
|
|
67
93
|
oninput,
|
|
68
94
|
'aria-describedby': describedBy,
|
|
69
95
|
...restProps
|
|
@@ -86,6 +112,26 @@
|
|
|
86
112
|
|
|
87
113
|
const status = $derived(fieldMessageState({ variant, message, hint, error }));
|
|
88
114
|
|
|
115
|
+
// The count: only with a `maxlength`, and only once `countFrom` is reached.
|
|
116
|
+
// The length is the one `maxlength` counts (UTF-16 units).
|
|
117
|
+
const countMax = $derived(Number(restProps.maxlength));
|
|
118
|
+
const countLength = $derived((value ?? '').length);
|
|
119
|
+
const showCount = $derived(
|
|
120
|
+
count && Number.isFinite(countMax) && countMax > 0 && countLength >= countFrom,
|
|
121
|
+
);
|
|
122
|
+
const countLabel = $derived.by(() => {
|
|
123
|
+
if (!showCount) return '';
|
|
124
|
+
const state = { length: countLength, max: countMax, remaining: Math.max(0, countMax - countLength) };
|
|
125
|
+
return countText ? countText(state) : `${state.length} / ${state.max}`;
|
|
126
|
+
});
|
|
127
|
+
const countId = $derived(`${textareaId}-count`);
|
|
128
|
+
// The caller's ids, the hint and the message, then the count.
|
|
129
|
+
const fieldDescription = $derived(
|
|
130
|
+
[fieldDescribedBy(textareaId, status, describedBy), showCount ? countId : '']
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.join(' ') || undefined,
|
|
133
|
+
);
|
|
134
|
+
|
|
89
135
|
const variantClass = $derived(() => {
|
|
90
136
|
return status.variant === 'success'
|
|
91
137
|
? 'border-success focus-visible:border-success'
|
|
@@ -96,12 +142,44 @@
|
|
|
96
142
|
: 'border-input-border enabled:hover:border-input-border-hover';
|
|
97
143
|
});
|
|
98
144
|
|
|
145
|
+
const resizeClass = $derived(
|
|
146
|
+
autogrow || resize === 'none'
|
|
147
|
+
? 'resize-none'
|
|
148
|
+
: resize === 'vertical'
|
|
149
|
+
? 'resize-y'
|
|
150
|
+
: 'resize-y pointer-coarse:resize-none',
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Autogrow: `field-sizing: content` where the browser has it, held between
|
|
154
|
+
// `rows` and `maxRows` lines (`leading-6`, plus `py-2` and the two borders,
|
|
155
|
+
// since the box is border-box). Elsewhere the height is measured from the
|
|
156
|
+
// content after each change; the same two limits clamp it.
|
|
157
|
+
const linesHeight = (lines: number) => `calc(${lines * 1.5 + 1}rem + 2px)`;
|
|
158
|
+
const minHeight = $derived(autogrow ? linesHeight(Math.max(1, rows)) : undefined);
|
|
159
|
+
const maxHeight = $derived(
|
|
160
|
+
autogrow && maxRows !== undefined ? linesHeight(Math.max(rows, maxRows)) : undefined,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
let element: HTMLTextAreaElement | undefined = $state();
|
|
164
|
+
|
|
165
|
+
$effect(() => {
|
|
166
|
+
void value;
|
|
167
|
+
if (!autogrow || !element) return;
|
|
168
|
+
if (typeof CSS !== 'undefined' && CSS.supports?.('field-sizing', 'content')) return;
|
|
169
|
+
const field = element;
|
|
170
|
+
field.style.height = 'auto';
|
|
171
|
+
field.style.height = `${field.scrollHeight + field.offsetHeight - field.clientHeight}px`;
|
|
172
|
+
return () => {
|
|
173
|
+
field.style.height = '';
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
|
|
99
177
|
// 16px below `sm`: iOS Safari zooms the page when a focused field is smaller.
|
|
100
178
|
const textareaClasses = $derived(() => {
|
|
101
179
|
const baseClasses =
|
|
102
|
-
'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
|
|
180
|
+
'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 px-3 py-2 text-sm max-sm:text-base leading-6';
|
|
103
181
|
|
|
104
|
-
return cn(`${baseClasses} ${variantClass()} ${className}`);
|
|
182
|
+
return cn(`${baseClasses} ${resizeClass} ${variantClass()} ${className}`);
|
|
105
183
|
});
|
|
106
184
|
|
|
107
185
|
const labelClasses = $derived(
|
|
@@ -121,6 +199,7 @@
|
|
|
121
199
|
{/if}
|
|
122
200
|
<div class="relative">
|
|
123
201
|
<textarea
|
|
202
|
+
bind:this={element}
|
|
124
203
|
id={textareaId}
|
|
125
204
|
{name}
|
|
126
205
|
bind:value
|
|
@@ -129,11 +208,15 @@
|
|
|
129
208
|
disabled={isDisabled}
|
|
130
209
|
{rows}
|
|
131
210
|
class={textareaClasses()}
|
|
211
|
+
style:field-sizing={autogrow ? 'content' : undefined}
|
|
212
|
+
style:min-height={minHeight}
|
|
213
|
+
style:max-height={maxHeight}
|
|
214
|
+
style:overflow-y={maxHeight ? 'auto' : undefined}
|
|
132
215
|
oninput={handleInput}
|
|
133
216
|
aria-invalid={status.variant === 'error' ? 'true' : undefined}
|
|
134
217
|
aria-required={required ? 'true' : undefined}
|
|
135
218
|
aria-busy={loading ? 'true' : undefined}
|
|
136
|
-
aria-describedby={
|
|
219
|
+
aria-describedby={fieldDescription}
|
|
137
220
|
{...restProps}
|
|
138
221
|
></textarea>
|
|
139
222
|
{#if loading}
|
|
@@ -147,5 +230,17 @@
|
|
|
147
230
|
</span>
|
|
148
231
|
{/if}
|
|
149
232
|
</div>
|
|
150
|
-
|
|
233
|
+
{#if showCount}
|
|
234
|
+
<!-- The count ends the line the hint starts. Not a live region: it is read with the field. -->
|
|
235
|
+
<div class="flex items-start gap-3">
|
|
236
|
+
<div class="min-w-0 flex-1">
|
|
237
|
+
<FieldMessages fieldId={textareaId} {status} {hint} gap="mt-1" />
|
|
238
|
+
</div>
|
|
239
|
+
<p id={countId} class="mt-1 shrink-0 text-sm text-description tabular-nums" data-textarea-count>
|
|
240
|
+
{countLabel}
|
|
241
|
+
</p>
|
|
242
|
+
</div>
|
|
243
|
+
{:else}
|
|
244
|
+
<FieldMessages fieldId={textareaId} {status} {hint} gap="mt-1" />
|
|
245
|
+
{/if}
|
|
151
246
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
2
2
|
import type { SemanticVariant, SizeVariant } from '../types/variants.js';
|
|
3
|
+
import { type TextareaCount } from "../util/field.js";
|
|
3
4
|
/**
|
|
4
5
|
* Other attributes (`autocomplete`, `inputmode`, `maxlength`,
|
|
5
6
|
* `enterkeyhint`, `data-*`, `onchange`, ...) land on the `<textarea>`.
|
|
@@ -16,6 +17,16 @@ type Props = Omit<HTMLTextareaAttributes, 'class' | 'value' | 'id' | 'name' | 'p
|
|
|
16
17
|
disabled?: boolean;
|
|
17
18
|
loading?: boolean;
|
|
18
19
|
rows?: number;
|
|
20
|
+
/**
|
|
21
|
+
* The resize grip. `auto` (default): the browser's vertical grip with a mouse or
|
|
22
|
+
* trackpad, none on a touch screen, where dragging it scrolls the page. `vertical`
|
|
23
|
+
* shows it everywhere, `none` never. With `autogrow` there is no grip.
|
|
24
|
+
*/
|
|
25
|
+
resize?: 'auto' | 'none' | 'vertical';
|
|
26
|
+
/** The field grows with its content, from `rows` up to `maxRows`, and scrolls after that. */
|
|
27
|
+
autogrow?: boolean;
|
|
28
|
+
/** The tallest an `autogrow` field gets, in rows. Without it the field has no limit. */
|
|
29
|
+
maxRows?: number;
|
|
19
30
|
size?: SizeVariant;
|
|
20
31
|
variant?: SemanticVariant;
|
|
21
32
|
/** Status text under the field. Shown with a `success`, `warning` or `error` variant; for neutral help, use `hint`. */
|
|
@@ -24,6 +35,16 @@ type Props = Omit<HTMLTextareaAttributes, 'class' | 'value' | 'id' | 'name' | 'p
|
|
|
24
35
|
hint?: string;
|
|
25
36
|
/** An error under the field. It marks the field invalid and is announced; it wins over `variant` and `message`. */
|
|
26
37
|
error?: string;
|
|
38
|
+
/**
|
|
39
|
+
* A character count under the field, at the end of the line the hint is on. It needs
|
|
40
|
+
* `maxlength`; without one nothing is shown. It is part of the field's description
|
|
41
|
+
* (`aria-describedby`) and is not a live region, so it is not read out at every key.
|
|
42
|
+
*/
|
|
43
|
+
count?: boolean;
|
|
44
|
+
/** The count is shown from this many characters on. `0` (default): always. */
|
|
45
|
+
countFrom?: number;
|
|
46
|
+
/** The words of the count, for the page's language. Default: "12 / 2000". */
|
|
47
|
+
countText?: (count: TextareaCount) => string;
|
|
27
48
|
oninput?: (event: Event) => void;
|
|
28
49
|
/** Ids of other elements that describe the field. The hint and the message are added after them. */
|
|
29
50
|
"aria-describedby"?: string | null;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, tick } from "svelte";
|
|
3
|
+
import { cn } from "../util/cn.js";
|
|
4
|
+
import type { TimeChoice } from "../util/temporal-field.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One scrollable column of the time picker in TimeField: the hours, or the
|
|
8
|
+
* minutes. Internal: it is not exported from the package.
|
|
9
|
+
*
|
|
10
|
+
* A listbox of 44px rows. One row is the Tab stop (the chosen one, else
|
|
11
|
+
* the first); Up and Down, Home and End move between rows, Enter and
|
|
12
|
+
* Space choose the focused one, and typing digits jumps to a row. A row
|
|
13
|
+
* that is out of range stays focusable and says `aria-disabled`. With
|
|
14
|
+
* nothing chosen, `openAt` is the row the column stands at and the keyboard
|
|
15
|
+
* starts from; it is not marked chosen.
|
|
16
|
+
*/
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Accessible name of the listbox. */
|
|
19
|
+
label: string;
|
|
20
|
+
options: TimeChoice[];
|
|
21
|
+
/** The `value` of the chosen option, or `""`. */
|
|
22
|
+
selected: string;
|
|
23
|
+
onselect: (value: string) => void;
|
|
24
|
+
/** The `value` of the row to stand at while nothing is chosen. It is not chosen. */
|
|
25
|
+
openAt?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let { label, options, selected, onselect, openAt = "" }: Props = $props();
|
|
29
|
+
|
|
30
|
+
let box: HTMLDivElement | undefined = $state();
|
|
31
|
+
/** The row that takes Tab: where the keyboard was last, else the chosen one, else the first. */
|
|
32
|
+
let focusedValue = $state<string | null>(null);
|
|
33
|
+
|
|
34
|
+
const tabStop = $derived(
|
|
35
|
+
options.some((option) => option.value === focusedValue)
|
|
36
|
+
? focusedValue
|
|
37
|
+
: options.some((option) => option.value === selected)
|
|
38
|
+
? selected
|
|
39
|
+
: options.some((option) => option.value === openAt)
|
|
40
|
+
? openAt
|
|
41
|
+
: (options[0]?.value ?? null),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The chosen row, else the `openAt` one, is put in the middle of the
|
|
46
|
+
* column as it opens, and again when the choice changes (a new hour can move the minute). Measured
|
|
47
|
+
* from the two boxes, not `offsetTop`, which counts from the sheet. Set
|
|
48
|
+
* straight on `scrollTop`: nothing here scrolls smoothly, so there is no
|
|
49
|
+
* motion for `prefers-reduced-motion` to remove.
|
|
50
|
+
*/
|
|
51
|
+
function centreSelected() {
|
|
52
|
+
const column = box;
|
|
53
|
+
const row =
|
|
54
|
+
column?.querySelector<HTMLElement>('[aria-selected="true"]') ??
|
|
55
|
+
(openAt ? rows().find((element) => element.dataset.value === openAt) : undefined);
|
|
56
|
+
if (!column || !row || !column.clientHeight) return;
|
|
57
|
+
const offset = row.getBoundingClientRect().top - column.getBoundingClientRect().top;
|
|
58
|
+
column.scrollTop += offset - (column.clientHeight - row.offsetHeight) / 2;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onMount(centreSelected);
|
|
62
|
+
|
|
63
|
+
$effect(() => {
|
|
64
|
+
void selected;
|
|
65
|
+
void tick().then(centreSelected);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const rows = () => [...(box?.querySelectorAll<HTMLElement>('[role="option"]') ?? [])];
|
|
69
|
+
|
|
70
|
+
function focusRow(value: string | undefined) {
|
|
71
|
+
if (value === undefined) return;
|
|
72
|
+
focusedValue = value;
|
|
73
|
+
const row = rows().find((element) => element.dataset.value === value);
|
|
74
|
+
row?.focus();
|
|
75
|
+
row?.scrollIntoView?.({ block: "nearest" });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let typed = "";
|
|
79
|
+
let typedTimer: ReturnType<typeof setTimeout> | undefined;
|
|
80
|
+
|
|
81
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
82
|
+
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
83
|
+
const values = options.map((option) => option.value);
|
|
84
|
+
const current = values.indexOf((event.target as HTMLElement | null)?.closest<HTMLElement>("[data-value]")?.dataset.value ?? "");
|
|
85
|
+
if (event.key === "ArrowDown") {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
focusRow(values[Math.min(current + 1, values.length - 1)]);
|
|
88
|
+
} else if (event.key === "ArrowUp") {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
focusRow(values[Math.max(current - 1, 0)]);
|
|
91
|
+
} else if (event.key === "Home") {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
focusRow(values[0]);
|
|
94
|
+
} else if (event.key === "End") {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
focusRow(values[values.length - 1]);
|
|
97
|
+
} else if (event.key === "Enter" || event.key === " ") {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
const option = options[current];
|
|
100
|
+
if (option && !option.disabled) onselect(option.value);
|
|
101
|
+
} else if (/^[0-9]$/.test(event.key)) {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
typed += event.key;
|
|
104
|
+
clearTimeout(typedTimer);
|
|
105
|
+
typedTimer = setTimeout(() => (typed = ""), 800);
|
|
106
|
+
// "7" is 07, "30" is 30, "1" then "9" is 19. The value is the 24-hour one, whatever the label says.
|
|
107
|
+
const hit =
|
|
108
|
+
options.find((option) => option.value.startsWith(typed)) ??
|
|
109
|
+
options.find((option) => Number(option.value) === Number(typed)) ??
|
|
110
|
+
options.find((option) => option.value.startsWith(typed.slice(-1)));
|
|
111
|
+
if (hit) focusRow(hit.value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
117
|
+
<div
|
|
118
|
+
bind:this={box}
|
|
119
|
+
role="listbox"
|
|
120
|
+
aria-label={label}
|
|
121
|
+
tabindex="-1"
|
|
122
|
+
data-time-column
|
|
123
|
+
data-sheet-no-drag
|
|
124
|
+
class="h-44 min-w-0 flex-1 overflow-y-auto overscroll-contain rounded-control border border-input-border bg-input p-1"
|
|
125
|
+
onkeydown={handleKeydown}
|
|
126
|
+
>
|
|
127
|
+
{#each options as option (option.value)}
|
|
128
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
129
|
+
<div
|
|
130
|
+
role="option"
|
|
131
|
+
data-value={option.value}
|
|
132
|
+
aria-selected={option.value === selected}
|
|
133
|
+
aria-disabled={option.disabled ? "true" : undefined}
|
|
134
|
+
tabindex={option.value === tabStop ? 0 : -1}
|
|
135
|
+
class={cn(
|
|
136
|
+
"focus-ring flex h-11 shrink-0 cursor-pointer items-center justify-center rounded-control text-base transition-colors duration-(--duration-base) motion-reduce:transition-none focus:outline-none focus-visible:outline-none",
|
|
137
|
+
// Two class lists, never both: the hand-written colour classes
|
|
138
|
+
// sit outside the cascade layers, so a variant cannot beat them.
|
|
139
|
+
option.value === selected
|
|
140
|
+
? "bg-action-primary text-action-primary font-semibold"
|
|
141
|
+
: "text-body hover:bg-input-hover",
|
|
142
|
+
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:hover:bg-transparent",
|
|
143
|
+
)}
|
|
144
|
+
onfocus={() => (focusedValue = option.value)}
|
|
145
|
+
onclick={() => !option.disabled && onselect(option.value)}
|
|
146
|
+
>
|
|
147
|
+
{option.label}
|
|
148
|
+
</div>
|
|
149
|
+
{/each}
|
|
150
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { TimeChoice } from "../util/temporal-field.js";
|
|
2
|
+
/**
|
|
3
|
+
* One scrollable column of the time picker in TimeField: the hours, or the
|
|
4
|
+
* minutes. Internal: it is not exported from the package.
|
|
5
|
+
*
|
|
6
|
+
* A listbox of 44px rows. One row is the Tab stop (the chosen one, else
|
|
7
|
+
* the first); Up and Down, Home and End move between rows, Enter and
|
|
8
|
+
* Space choose the focused one, and typing digits jumps to a row. A row
|
|
9
|
+
* that is out of range stays focusable and says `aria-disabled`. With
|
|
10
|
+
* nothing chosen, `openAt` is the row the column stands at and the keyboard
|
|
11
|
+
* starts from; it is not marked chosen.
|
|
12
|
+
*/
|
|
13
|
+
interface Props {
|
|
14
|
+
/** Accessible name of the listbox. */
|
|
15
|
+
label: string;
|
|
16
|
+
options: TimeChoice[];
|
|
17
|
+
/** The `value` of the chosen option, or `""`. */
|
|
18
|
+
selected: string;
|
|
19
|
+
onselect: (value: string) => void;
|
|
20
|
+
/** The `value` of the row to stand at while nothing is chosen. It is not chosen. */
|
|
21
|
+
openAt?: string;
|
|
22
|
+
}
|
|
23
|
+
declare const TimeColumn: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type TimeColumn = ReturnType<typeof TimeColumn>;
|
|
25
|
+
export default TimeColumn;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
3
3
|
import type { SizeVariant } from "../types/variants.js";
|
|
4
|
+
import type { TimeFieldStrings } from "../util/temporal-field.js";
|
|
4
5
|
import TemporalField from "./TemporalField.svelte";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -11,15 +12,25 @@
|
|
|
11
12
|
* `HH:mm` (`HH:mm:ss` when `step` asks for seconds), or `""` while empty,
|
|
12
13
|
* whatever the field shows.
|
|
13
14
|
*
|
|
14
|
-
* What the field shows is the browser's decision
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* What the field shows is the browser's decision, unless it has a
|
|
16
|
+
* `locale`: without one, 24-hour or 12-hour with AM and PM, by the
|
|
17
|
+
* settings of the browser or device, not of the page. To show a time as
|
|
18
|
+
* text in the page's language, use `formatTime`:
|
|
17
19
|
*
|
|
18
20
|
* ```svelte
|
|
19
21
|
* <TimeField label="Starts" bind:value={time} step={300} />
|
|
20
22
|
* <p>{formatTime(time, "sv")}</p> <!-- 19:00 -->
|
|
21
23
|
* ```
|
|
22
24
|
*
|
|
25
|
+
* With `locale`, the field shows the value in that language ("18:30" in
|
|
26
|
+
* Swedish, never "06:30 PM") and its own picker opens: two columns in a
|
|
27
|
+
* BottomSheet, hours (written as the locale writes an hour) and minutes
|
|
28
|
+
* (every `step`, five minutes without one), and Done. A value that is not
|
|
29
|
+
* on the step is kept as its own minute. A `step` under 60 seconds, or
|
|
30
|
+
* `"any"`, uses the platform's picker instead, as does `picker="native"`.
|
|
31
|
+
* Until the page has run, and without scripts, it is the native input
|
|
32
|
+
* as above and submits as before.
|
|
33
|
+
*
|
|
23
34
|
* Other attributes (`autocomplete`, `data-*`, `onchange`, ...) land on the
|
|
24
35
|
* `<input>`.
|
|
25
36
|
*/
|
|
@@ -54,6 +65,26 @@
|
|
|
54
65
|
size?: SizeVariant;
|
|
55
66
|
/** Extra classes for the `<input>`. */
|
|
56
67
|
class?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Language of the field, as a BCP 47 tag (`sv`, `en-GB`) or a list of them. With it the
|
|
70
|
+
* field shows the value in that language, whatever the browser's is, and opens the
|
|
71
|
+
* library's own picker; see above. Left out, the field is the native input.
|
|
72
|
+
*/
|
|
73
|
+
locale?: string | string[];
|
|
74
|
+
/** With `locale`: how the value is shown, as options of `Intl.DateTimeFormat`. Default: formatTime gives for the locale. */
|
|
75
|
+
format?: Intl.DateTimeFormatOptions;
|
|
76
|
+
/** With `locale`: shown in the placeholder colour while the field is empty. */
|
|
77
|
+
placeholder?: string;
|
|
78
|
+
/** With `locale`: `library` (default) opens the library's time columns in a sheet; `native` opens the platform's own picker. */
|
|
79
|
+
picker?: "library" | "native";
|
|
80
|
+
/** With `locale`: the words the field says; replace any of them to translate. */
|
|
81
|
+
strings?: Partial<TimeFieldStrings>;
|
|
82
|
+
/**
|
|
83
|
+
* With `locale`, while the field is empty: the time the library's columns stand at when
|
|
84
|
+
* the sheet opens, as `HH:mm` (`"18:30"`). It is only where to scroll to: no row is
|
|
85
|
+
* chosen and `value` stays empty until the person chooses. Left out: now, on the step.
|
|
86
|
+
*/
|
|
87
|
+
openAt?: string;
|
|
57
88
|
};
|
|
58
89
|
|
|
59
90
|
let {
|
|
@@ -72,6 +103,12 @@
|
|
|
72
103
|
readonly = false,
|
|
73
104
|
size = "md",
|
|
74
105
|
class: className = "",
|
|
106
|
+
locale,
|
|
107
|
+
format,
|
|
108
|
+
placeholder,
|
|
109
|
+
picker = "library",
|
|
110
|
+
strings,
|
|
111
|
+
openAt,
|
|
75
112
|
...restProps
|
|
76
113
|
}: Props = $props();
|
|
77
114
|
|
|
@@ -104,5 +141,11 @@
|
|
|
104
141
|
{readonly}
|
|
105
142
|
{size}
|
|
106
143
|
class={className}
|
|
144
|
+
{locale}
|
|
145
|
+
{format}
|
|
146
|
+
{placeholder}
|
|
147
|
+
{picker}
|
|
148
|
+
{strings}
|
|
149
|
+
{openAt}
|
|
107
150
|
{...restProps}
|
|
108
151
|
/>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
2
2
|
import type { SizeVariant } from "../types/variants.js";
|
|
3
|
+
import type { TimeFieldStrings } from "../util/temporal-field.js";
|
|
3
4
|
/**
|
|
4
5
|
* A time of day, picked with the device's own time picker and styled
|
|
5
6
|
* like Input.
|
|
@@ -8,15 +9,25 @@ import type { SizeVariant } from "../types/variants.js";
|
|
|
8
9
|
* `HH:mm` (`HH:mm:ss` when `step` asks for seconds), or `""` while empty,
|
|
9
10
|
* whatever the field shows.
|
|
10
11
|
*
|
|
11
|
-
* What the field shows is the browser's decision
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* What the field shows is the browser's decision, unless it has a
|
|
13
|
+
* `locale`: without one, 24-hour or 12-hour with AM and PM, by the
|
|
14
|
+
* settings of the browser or device, not of the page. To show a time as
|
|
15
|
+
* text in the page's language, use `formatTime`:
|
|
14
16
|
*
|
|
15
17
|
* ```svelte
|
|
16
18
|
* <TimeField label="Starts" bind:value={time} step={300} />
|
|
17
19
|
* <p>{formatTime(time, "sv")}</p> <!-- 19:00 -->
|
|
18
20
|
* ```
|
|
19
21
|
*
|
|
22
|
+
* With `locale`, the field shows the value in that language ("18:30" in
|
|
23
|
+
* Swedish, never "06:30 PM") and its own picker opens: two columns in a
|
|
24
|
+
* BottomSheet, hours (written as the locale writes an hour) and minutes
|
|
25
|
+
* (every `step`, five minutes without one), and Done. A value that is not
|
|
26
|
+
* on the step is kept as its own minute. A `step` under 60 seconds, or
|
|
27
|
+
* `"any"`, uses the platform's picker instead, as does `picker="native"`.
|
|
28
|
+
* Until the page has run, and without scripts, it is the native input
|
|
29
|
+
* as above and submits as before.
|
|
30
|
+
*
|
|
20
31
|
* Other attributes (`autocomplete`, `data-*`, `onchange`, ...) land on the
|
|
21
32
|
* `<input>`.
|
|
22
33
|
*/
|
|
@@ -48,6 +59,26 @@ type Props = Omit<HTMLInputAttributes, "class" | "type" | "value" | "size" | "mi
|
|
|
48
59
|
size?: SizeVariant;
|
|
49
60
|
/** Extra classes for the `<input>`. */
|
|
50
61
|
class?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Language of the field, as a BCP 47 tag (`sv`, `en-GB`) or a list of them. With it the
|
|
64
|
+
* field shows the value in that language, whatever the browser's is, and opens the
|
|
65
|
+
* library's own picker; see above. Left out, the field is the native input.
|
|
66
|
+
*/
|
|
67
|
+
locale?: string | string[];
|
|
68
|
+
/** With `locale`: how the value is shown, as options of `Intl.DateTimeFormat`. Default: formatTime gives for the locale. */
|
|
69
|
+
format?: Intl.DateTimeFormatOptions;
|
|
70
|
+
/** With `locale`: shown in the placeholder colour while the field is empty. */
|
|
71
|
+
placeholder?: string;
|
|
72
|
+
/** With `locale`: `library` (default) opens the library's time columns in a sheet; `native` opens the platform's own picker. */
|
|
73
|
+
picker?: "library" | "native";
|
|
74
|
+
/** With `locale`: the words the field says; replace any of them to translate. */
|
|
75
|
+
strings?: Partial<TimeFieldStrings>;
|
|
76
|
+
/**
|
|
77
|
+
* With `locale`, while the field is empty: the time the library's columns stand at when
|
|
78
|
+
* the sheet opens, as `HH:mm` (`"18:30"`). It is only where to scroll to: no row is
|
|
79
|
+
* chosen and `value` stays empty until the person chooses. Left out: now, on the step.
|
|
80
|
+
*/
|
|
81
|
+
openAt?: string;
|
|
51
82
|
};
|
|
52
83
|
declare const TimeField: import("svelte").Component<Props, {}, "value">;
|
|
53
84
|
type TimeField = ReturnType<typeof TimeField>;
|
package/dist/atoms/Toast.svelte
CHANGED
|
@@ -33,15 +33,16 @@
|
|
|
33
33
|
let isVisible = $state(true);
|
|
34
34
|
|
|
35
35
|
const typeClasses: Record<NonNullable<Props['type']>, string> = {
|
|
36
|
-
success: 'border-success
|
|
37
|
-
error: 'border-error
|
|
38
|
-
warning: 'border-warning
|
|
39
|
-
|
|
36
|
+
success: 'border-success text-success-text',
|
|
37
|
+
error: 'border-error text-error-text',
|
|
38
|
+
warning: 'border-warning text-warning-text',
|
|
39
|
+
// The material has its own rim: no second edge on the neutral toast.
|
|
40
|
+
info: 'border-transparent text-body',
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
// `min(18rem, 100%)`: 18rem is wider than a 320px screen once the text is enlarged.
|
|
43
44
|
const cardClasses =
|
|
44
|
-
'box-border w-full min-w-[min(18rem,100%)] max-w-[min(24rem,calc(100vw-2rem))] overflow-hidden rounded-control border p-4
|
|
45
|
+
'box-border w-full min-w-[min(18rem,100%)] max-w-[min(24rem,calc(100vw-2rem))] overflow-hidden rounded-control border p-4 material-thick';
|
|
45
46
|
|
|
46
47
|
function closeToast(event: Event) {
|
|
47
48
|
isVisible = false;
|