svseeds 0.6.0 → 0.7.1
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/README.md +1 -1
- package/_svseeds/Accordion.svelte +119 -83
- package/_svseeds/Accordion.svelte.d.ts +56 -27
- package/_svseeds/Button.svelte +116 -0
- package/_svseeds/Button.svelte.d.ts +55 -0
- package/_svseeds/Calendar.svelte +349 -0
- package/_svseeds/Calendar.svelte.d.ts +113 -0
- package/_svseeds/CheckField.svelte +228 -0
- package/_svseeds/CheckField.svelte.d.ts +80 -0
- package/_svseeds/ColorPicker.svelte +123 -0
- package/_svseeds/ColorPicker.svelte.d.ts +56 -0
- package/_svseeds/ComboBox.svelte +278 -0
- package/_svseeds/ComboBox.svelte.d.ts +72 -0
- package/_svseeds/ContextMenu.svelte +129 -0
- package/_svseeds/ContextMenu.svelte.d.ts +53 -0
- package/_svseeds/DarkToggle.svelte +125 -52
- package/_svseeds/DarkToggle.svelte.d.ts +38 -18
- package/_svseeds/DateField.svelte +278 -0
- package/_svseeds/DateField.svelte.d.ts +93 -0
- package/_svseeds/DateInput.svelte +347 -0
- package/_svseeds/DateInput.svelte.d.ts +105 -0
- package/_svseeds/Disclosure.svelte +233 -0
- package/_svseeds/Disclosure.svelte.d.ts +76 -0
- package/_svseeds/Drawer.svelte +179 -0
- package/_svseeds/Drawer.svelte.d.ts +62 -0
- package/_svseeds/FileField.svelte +276 -0
- package/_svseeds/FileField.svelte.d.ts +95 -0
- package/_svseeds/FileInput.svelte +279 -0
- package/_svseeds/FileInput.svelte.d.ts +103 -0
- package/_svseeds/HotkeyCapture.svelte +189 -0
- package/_svseeds/HotkeyCapture.svelte.d.ts +71 -0
- package/_svseeds/Image.svelte +211 -0
- package/_svseeds/Image.svelte.d.ts +83 -0
- package/_svseeds/MenuGroup.svelte +80 -0
- package/_svseeds/MenuGroup.svelte.d.ts +52 -0
- package/_svseeds/MenuItem.svelte +120 -0
- package/_svseeds/MenuItem.svelte.d.ts +63 -0
- package/_svseeds/MenuList.svelte +227 -0
- package/_svseeds/MenuList.svelte.d.ts +92 -0
- package/_svseeds/MenuSeparator.svelte +70 -0
- package/_svseeds/MenuSeparator.svelte.d.ts +44 -0
- package/_svseeds/Modal.svelte +121 -0
- package/_svseeds/Modal.svelte.d.ts +54 -0
- package/_svseeds/MonthPicker.svelte +172 -0
- package/_svseeds/MonthPicker.svelte.d.ts +66 -0
- package/_svseeds/NumberField.svelte +213 -0
- package/_svseeds/NumberField.svelte.d.ts +79 -0
- package/_svseeds/NumberInput.svelte +395 -0
- package/_svseeds/NumberInput.svelte.d.ts +95 -0
- package/_svseeds/Pagination.svelte +271 -0
- package/_svseeds/Pagination.svelte.d.ts +78 -0
- package/_svseeds/Popover.svelte +215 -0
- package/_svseeds/Popover.svelte.d.ts +79 -0
- package/_svseeds/ProgressTracker.svelte +124 -0
- package/_svseeds/{_ProgressTracker.svelte.d.ts → ProgressTracker.svelte.d.ts} +23 -12
- package/_svseeds/SelectField.svelte +213 -0
- package/_svseeds/{_SelectField.svelte.d.ts → SelectField.svelte.d.ts} +31 -20
- package/_svseeds/Slider.svelte +139 -0
- package/_svseeds/Slider.svelte.d.ts +75 -0
- package/_svseeds/Sortable.svelte +801 -0
- package/_svseeds/Sortable.svelte.d.ts +164 -0
- package/_svseeds/SortableGroup.svelte +87 -0
- package/_svseeds/SortableGroup.svelte.d.ts +52 -0
- package/_svseeds/Splitter.svelte +194 -0
- package/_svseeds/Splitter.svelte.d.ts +71 -0
- package/_svseeds/Tabs.svelte +230 -0
- package/_svseeds/Tabs.svelte.d.ts +72 -0
- package/_svseeds/TagsInput.svelte +260 -0
- package/_svseeds/TagsInput.svelte.d.ts +103 -0
- package/_svseeds/TagsInputField.svelte +118 -84
- package/_svseeds/TagsInputField.svelte.d.ts +31 -35
- package/_svseeds/TextField.svelte +238 -0
- package/_svseeds/TextField.svelte.d.ts +93 -0
- package/_svseeds/Toast.svelte +290 -0
- package/_svseeds/Toast.svelte.d.ts +87 -0
- package/_svseeds/Toggle.svelte +131 -0
- package/_svseeds/Toggle.svelte.d.ts +57 -0
- package/_svseeds/ToggleGroup.svelte +231 -0
- package/_svseeds/ToggleGroup.svelte.d.ts +76 -0
- package/_svseeds/ToggleGroupField.svelte +138 -66
- package/_svseeds/ToggleGroupField.svelte.d.ts +51 -29
- package/_svseeds/Tooltip.svelte +342 -0
- package/_svseeds/Tooltip.svelte.d.ts +74 -0
- package/_svseeds/WheelPicker.svelte +326 -0
- package/_svseeds/WheelPicker.svelte.d.ts +84 -0
- package/_svseeds/_core.d.ts +239 -0
- package/_svseeds/_core.js +290 -0
- package/index.d.ts +43 -25
- package/index.js +40 -22
- package/package.json +3 -3
- package/_svseeds/_Button.svelte +0 -92
- package/_svseeds/_Button.svelte.d.ts +0 -49
- package/_svseeds/_CheckField.svelte +0 -182
- package/_svseeds/_CheckField.svelte.d.ts +0 -71
- package/_svseeds/_ColorPicker.svelte +0 -114
- package/_svseeds/_ColorPicker.svelte.d.ts +0 -17
- package/_svseeds/_ComboBox.svelte +0 -163
- package/_svseeds/_ComboBox.svelte.d.ts +0 -52
- package/_svseeds/_ContextMenu.svelte +0 -82
- package/_svseeds/_ContextMenu.svelte.d.ts +0 -37
- package/_svseeds/_Disclosure.svelte +0 -152
- package/_svseeds/_Disclosure.svelte.d.ts +0 -48
- package/_svseeds/_Drawer.svelte +0 -136
- package/_svseeds/_Drawer.svelte.d.ts +0 -48
- package/_svseeds/_HotkeyCapture.svelte +0 -113
- package/_svseeds/_HotkeyCapture.svelte.d.ts +0 -34
- package/_svseeds/_Modal.svelte +0 -107
- package/_svseeds/_Modal.svelte.d.ts +0 -44
- package/_svseeds/_ProgressTracker.svelte +0 -101
- package/_svseeds/_SelectField.svelte +0 -190
- package/_svseeds/_Slider.svelte +0 -104
- package/_svseeds/_Slider.svelte.d.ts +0 -62
- package/_svseeds/_Sortable.svelte +0 -795
- package/_svseeds/_Sortable.svelte.d.ts +0 -111
- package/_svseeds/_Tabs.svelte +0 -118
- package/_svseeds/_Tabs.svelte.d.ts +0 -42
- package/_svseeds/_TagsInput.svelte +0 -155
- package/_svseeds/_TagsInput.svelte.d.ts +0 -81
- package/_svseeds/_TextField.svelte +0 -206
- package/_svseeds/_TextField.svelte.d.ts +0 -81
- package/_svseeds/_Toast.svelte +0 -258
- package/_svseeds/_Toast.svelte.d.ts +0 -14
- package/_svseeds/_Toggle.svelte +0 -112
- package/_svseeds/_Toggle.svelte.d.ts +0 -51
- package/_svseeds/_ToggleGroup.svelte +0 -102
- package/_svseeds/_ToggleGroup.svelte.d.ts +0 -49
- package/_svseeds/_Tooltip.svelte +0 -312
- package/_svseeds/_Tooltip.svelte.d.ts +0 -28
- package/_svseeds/core.d.ts +0 -229
- package/_svseeds/core.js +0 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
### Usage
|
|
4
|
+
Use standalone.
|
|
5
|
+
```svelte
|
|
6
|
+
<Calendar {...props} />
|
|
7
|
+
```
|
|
8
|
+
### Types
|
|
9
|
+
default value: *`(value)`*
|
|
10
|
+
```ts
|
|
11
|
+
type DayCtx = {
|
|
12
|
+
date: Temporal.PlainDate;
|
|
13
|
+
variant: string;
|
|
14
|
+
weekday: number;
|
|
15
|
+
today: boolean;
|
|
16
|
+
selected: boolean;
|
|
17
|
+
outside: boolean;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
type TransitionProp = {
|
|
21
|
+
fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
22
|
+
params?: unknown;
|
|
23
|
+
};
|
|
24
|
+
interface CalendarProps {
|
|
25
|
+
value?: Temporal.PlainDate; // bindable; selected date
|
|
26
|
+
display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
27
|
+
picking?: boolean; // bindable (false)
|
|
28
|
+
min?: Temporal.PlainDate;
|
|
29
|
+
max?: Temporal.PlainDate;
|
|
30
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
31
|
+
outsideDays?: boolean; // (false)
|
|
32
|
+
fixedWeeks?: boolean; // (false)
|
|
33
|
+
firstDayOfWeek?: number; // (0=Sun)
|
|
34
|
+
locale?: string;
|
|
35
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
36
|
+
prev?: Snippet<[string]>;
|
|
37
|
+
next?: Snippet<[string]>;
|
|
38
|
+
weekday?: Snippet<[number, string]>;
|
|
39
|
+
day?: Snippet<[DayCtx]>;
|
|
40
|
+
bottom?: Snippet<[string, () => void]>;
|
|
41
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
42
|
+
transition?: TransitionProp;
|
|
43
|
+
styling?: SVSClass;
|
|
44
|
+
variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
### Anatomy
|
|
48
|
+
```svelte
|
|
49
|
+
<div class="whole" role="application">
|
|
50
|
+
<div class="top">
|
|
51
|
+
<button class="left" />
|
|
52
|
+
<button class="label" />
|
|
53
|
+
<button class="right" />
|
|
54
|
+
</div>
|
|
55
|
+
<div class="middle">
|
|
56
|
+
<div class="aux" role="row">weekday headers</div>
|
|
57
|
+
<div class="main" role="grid">day cells</div>
|
|
58
|
+
MonthPicker while picking
|
|
59
|
+
</div>
|
|
60
|
+
<div class="bottom" conditional>{bottom}</div>
|
|
61
|
+
</div>
|
|
62
|
+
```
|
|
63
|
+
### Behavior
|
|
64
|
+
- Prev/next page `display`; the label toggles an embedded `_MonthPicker`.
|
|
65
|
+
- Enabled day clicks and Enter/Space select a single `Temporal.PlainDate`.
|
|
66
|
+
- Day cells expose `data-today`, `data-selected`, `data-outside`, `data-disabled`, and `data-weekday`.
|
|
67
|
+
- Arrow, Home/End, PageUp/PageDown, and Shift+PageUp/PageDown update roving focus.
|
|
68
|
+
-->
|
|
69
|
+
<script module lang="ts">
|
|
70
|
+
export type DayCtx = {
|
|
71
|
+
date: Temporal.PlainDate;
|
|
72
|
+
variant: string;
|
|
73
|
+
weekday: number;
|
|
74
|
+
today: boolean;
|
|
75
|
+
selected: boolean;
|
|
76
|
+
outside: boolean;
|
|
77
|
+
disabled: boolean;
|
|
78
|
+
};
|
|
79
|
+
export type TransitionProp = {
|
|
80
|
+
fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
81
|
+
params?: unknown;
|
|
82
|
+
};
|
|
83
|
+
export interface CalendarProps {
|
|
84
|
+
value?: Temporal.PlainDate;
|
|
85
|
+
display?: Temporal.PlainYearMonth;
|
|
86
|
+
picking?: boolean;
|
|
87
|
+
min?: Temporal.PlainDate;
|
|
88
|
+
max?: Temporal.PlainDate;
|
|
89
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
90
|
+
outsideDays?: boolean;
|
|
91
|
+
fixedWeeks?: boolean;
|
|
92
|
+
firstDayOfWeek?: number;
|
|
93
|
+
locale?: string;
|
|
94
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
95
|
+
prev?: Snippet<[string]>;
|
|
96
|
+
next?: Snippet<[string]>;
|
|
97
|
+
weekday?: Snippet<[number, string]>;
|
|
98
|
+
day?: Snippet<[DayCtx]>;
|
|
99
|
+
bottom?: Snippet<[string, () => void]>;
|
|
100
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
101
|
+
transition?: TransitionProp;
|
|
102
|
+
styling?: SVSClass;
|
|
103
|
+
variant?: SVSVariant;
|
|
104
|
+
}
|
|
105
|
+
export type CalendarReqdProps = never;
|
|
106
|
+
export type CalendarBindProps = "value" | "display" | "picking";
|
|
107
|
+
|
|
108
|
+
export const _CALENDAR_PRESET = "svs-calendar";
|
|
109
|
+
|
|
110
|
+
import { tick, untrack } from "svelte";
|
|
111
|
+
import { VARIANT, PARTS, _fnClass, shouldReduceMotion } from "./_core";
|
|
112
|
+
import MonthPicker from "./MonthPicker.svelte";
|
|
113
|
+
import type { Snippet } from "svelte";
|
|
114
|
+
import type { KeyboardEventHandler, MouseEventHandler } from "svelte/elements";
|
|
115
|
+
import type { SVSClass, SVSVariant } from "./_core";
|
|
116
|
+
import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<script lang="ts">
|
|
120
|
+
// prettier-ignore
|
|
121
|
+
let { value = $bindable(), display = $bindable(), picking = $bindable(false), min, max, isDisabled, outsideDays = false, fixedWeeks = false, firstDayOfWeek = 0, locale, label, prev, next, weekday, day, bottom, monthPicker, transition, styling, variant = VARIANT.NEUTRAL }: CalendarProps = $props();
|
|
122
|
+
|
|
123
|
+
// *** Initialize *** //
|
|
124
|
+
const cls = $derived(_fnClass(_CALENDAR_PRESET, styling));
|
|
125
|
+
const uid = $props.id();
|
|
126
|
+
const idCaption = `${uid}-caption`;
|
|
127
|
+
// svelte-ignore state_referenced_locally
|
|
128
|
+
if (display === undefined) display = (value ?? Temporal.Now.plainDateISO()).toPlainYearMonth();
|
|
129
|
+
const today = $derived(Temporal.Now.plainDateISO());
|
|
130
|
+
|
|
131
|
+
// *** States *** //
|
|
132
|
+
const initialDisplay = display as Temporal.PlainYearMonth;
|
|
133
|
+
let focused = $state(value && sameMonth(value, initialDisplay) ? value : firstOf(initialDisplay));
|
|
134
|
+
const firstWeekday = $derived(normWeekday(firstDayOfWeek));
|
|
135
|
+
const monthStart = $derived(firstOf(currentDisplay()));
|
|
136
|
+
const captionText = $derived(monthStart.toLocaleString(locale, { year: "numeric", month: "long" }));
|
|
137
|
+
const appLabel = $derived(`Calendar, ${captionText}`);
|
|
138
|
+
const weekdayLabels = $derived.by(() => {
|
|
139
|
+
const short = new Intl.DateTimeFormat(locale, { weekday: "narrow" });
|
|
140
|
+
const long = new Intl.DateTimeFormat(locale, { weekday: "long" });
|
|
141
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
142
|
+
const weekday = (firstWeekday + i) % 7;
|
|
143
|
+
const d = dateForWeekday(weekday);
|
|
144
|
+
return { weekday, text: short.format(d), full: long.format(d) };
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
const weeks = $derived.by(() => {
|
|
148
|
+
const first = monthStart;
|
|
149
|
+
const lead = (weekdayOf(first) - firstWeekday + 7) % 7;
|
|
150
|
+
const rows = fixedWeeks ? 6 : Math.ceil((lead + first.daysInMonth) / 7);
|
|
151
|
+
const start = first.subtract({ days: lead });
|
|
152
|
+
return Array.from({ length: rows }, (_, r) => Array.from({ length: 7 }, (_, c) => start.add({ days: r * 7 + c })));
|
|
153
|
+
});
|
|
154
|
+
const reduced = $derived(typeof window !== "undefined" && shouldReduceMotion());
|
|
155
|
+
const tfn = $derived(!reduced && transition?.fn ? transition.fn : noop);
|
|
156
|
+
const tparams = $derived(transition?.params as any);
|
|
157
|
+
|
|
158
|
+
// *** Reactive Handlers *** //
|
|
159
|
+
$effect.pre(() => {
|
|
160
|
+
display;
|
|
161
|
+
value;
|
|
162
|
+
untrack(() => syncFocus());
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
function syncFocus() {
|
|
166
|
+
const shown = currentDisplay();
|
|
167
|
+
if (sameMonth(focused, shown)) return;
|
|
168
|
+
focused = value && sameMonth(value, shown) ? value : firstOf(shown);
|
|
169
|
+
}
|
|
170
|
+
function currentDisplay(): Temporal.PlainYearMonth {
|
|
171
|
+
return display ?? initialDisplay;
|
|
172
|
+
}
|
|
173
|
+
function noop() {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
function firstOf(ym: Temporal.PlainYearMonth): Temporal.PlainDate {
|
|
177
|
+
return Temporal.PlainDate.from({ year: ym.year, month: ym.month, day: 1 });
|
|
178
|
+
}
|
|
179
|
+
function normWeekday(n: number): number {
|
|
180
|
+
return ((Math.trunc(n) % 7) + 7) % 7;
|
|
181
|
+
}
|
|
182
|
+
function weekdayOf(d: Temporal.PlainDate): number {
|
|
183
|
+
return d.dayOfWeek % 7;
|
|
184
|
+
}
|
|
185
|
+
function sameMonth(d: Temporal.PlainDate, ym: Temporal.PlainYearMonth): boolean {
|
|
186
|
+
return d.year === ym.year && d.month === ym.month;
|
|
187
|
+
}
|
|
188
|
+
function dateForWeekday(weekday: number): Date {
|
|
189
|
+
return new Date(Date.UTC(2023, 0, 1 + weekday));
|
|
190
|
+
}
|
|
191
|
+
function inRange(d: Temporal.PlainDate): boolean {
|
|
192
|
+
if (min && Temporal.PlainDate.compare(d, min) < 0) return false;
|
|
193
|
+
if (max && Temporal.PlainDate.compare(d, max) > 0) return false;
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
function disabled(d: Temporal.PlainDate): boolean {
|
|
197
|
+
return !inRange(d) || !!isDisabled?.(d);
|
|
198
|
+
}
|
|
199
|
+
function selected(d: Temporal.PlainDate): boolean {
|
|
200
|
+
return !!value?.equals(d);
|
|
201
|
+
}
|
|
202
|
+
function variantOf(d: Temporal.PlainDate): string {
|
|
203
|
+
if (disabled(d)) return VARIANT.INACTIVE;
|
|
204
|
+
if (selected(d)) return VARIANT.ACTIVE;
|
|
205
|
+
return VARIANT.NEUTRAL;
|
|
206
|
+
}
|
|
207
|
+
function ctx(d: Temporal.PlainDate): DayCtx {
|
|
208
|
+
return {
|
|
209
|
+
date: d,
|
|
210
|
+
variant: variantOf(d),
|
|
211
|
+
weekday: weekdayOf(d),
|
|
212
|
+
today: today.equals(d),
|
|
213
|
+
selected: selected(d),
|
|
214
|
+
outside: !sameMonth(d, currentDisplay()),
|
|
215
|
+
disabled: disabled(d),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function labelOf(d: Temporal.PlainDate): string {
|
|
219
|
+
return d.toLocaleString(locale, { dateStyle: "long" });
|
|
220
|
+
}
|
|
221
|
+
function displayOf(d: Temporal.PlainDate) {
|
|
222
|
+
display = d.toPlainYearMonth();
|
|
223
|
+
}
|
|
224
|
+
function prevMonth() {
|
|
225
|
+
display = currentDisplay().subtract({ months: 1 });
|
|
226
|
+
}
|
|
227
|
+
function nextMonth() {
|
|
228
|
+
display = currentDisplay().add({ months: 1 });
|
|
229
|
+
}
|
|
230
|
+
function select(d: Temporal.PlainDate) {
|
|
231
|
+
if (disabled(d)) return;
|
|
232
|
+
value = d;
|
|
233
|
+
focused = d;
|
|
234
|
+
if (!sameMonth(d, currentDisplay())) displayOf(d);
|
|
235
|
+
}
|
|
236
|
+
function setToday() {
|
|
237
|
+
displayOf(today);
|
|
238
|
+
select(today);
|
|
239
|
+
}
|
|
240
|
+
function move(d: Temporal.PlainDate) {
|
|
241
|
+
focused = d;
|
|
242
|
+
if (!sameMonth(d, currentDisplay())) displayOf(d);
|
|
243
|
+
tick().then(() => focusDate(d));
|
|
244
|
+
}
|
|
245
|
+
function focusDate(d: Temporal.PlainDate) {
|
|
246
|
+
const el = document.querySelector(`[data-svs-calendar="${uid}"] [data-date="${d.toString()}"]`) as HTMLElement | null;
|
|
247
|
+
el?.focus();
|
|
248
|
+
}
|
|
249
|
+
function weekEdge(toEnd: boolean): Temporal.PlainDate {
|
|
250
|
+
const offset = (weekdayOf(focused) - firstWeekday + 7) % 7;
|
|
251
|
+
return focused.add({ days: toEnd ? 6 - offset : -offset });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// *** Event Handlers *** //
|
|
255
|
+
const hprev: MouseEventHandler<HTMLButtonElement> = () => prevMonth();
|
|
256
|
+
const hnext: MouseEventHandler<HTMLButtonElement> = () => nextMonth();
|
|
257
|
+
const hlabel: MouseEventHandler<HTMLButtonElement> = () => (picking = !picking);
|
|
258
|
+
const hkeydown: KeyboardEventHandler<HTMLDivElement> = (ev) => {
|
|
259
|
+
let next: Temporal.PlainDate | undefined;
|
|
260
|
+
if (ev.key === "ArrowLeft") next = focused.subtract({ days: 1 });
|
|
261
|
+
else if (ev.key === "ArrowRight") next = focused.add({ days: 1 });
|
|
262
|
+
else if (ev.key === "ArrowUp") next = focused.subtract({ days: 7 });
|
|
263
|
+
else if (ev.key === "ArrowDown") next = focused.add({ days: 7 });
|
|
264
|
+
else if (ev.key === "Home") next = weekEdge(false);
|
|
265
|
+
else if (ev.key === "End") next = weekEdge(true);
|
|
266
|
+
else if (ev.key === "PageUp") next = focused.subtract(ev.shiftKey ? { years: 1 } : { months: 1 });
|
|
267
|
+
else if (ev.key === "PageDown") next = focused.add(ev.shiftKey ? { years: 1 } : { months: 1 });
|
|
268
|
+
else if (ev.key === "Enter" || ev.key === " ") select(focused);
|
|
269
|
+
else return;
|
|
270
|
+
ev.preventDefault();
|
|
271
|
+
if (next) move(next);
|
|
272
|
+
};
|
|
273
|
+
</script>
|
|
274
|
+
|
|
275
|
+
<!---------------------------------------->
|
|
276
|
+
|
|
277
|
+
<div class={cls(PARTS.WHOLE, variant)} role="application" aria-label={appLabel} data-svs-calendar={uid}>
|
|
278
|
+
<div class={cls(PARTS.TOP, variant)}>
|
|
279
|
+
<button class={cls(PARTS.LEFT, variant)} type="button" onclick={hprev}
|
|
280
|
+
>{#if prev}{@render prev(variant)}{:else}Previous{/if}</button
|
|
281
|
+
>
|
|
282
|
+
<button id={idCaption} class={cls(PARTS.LABEL, variant)} type="button" aria-expanded={picking} onclick={hlabel}>
|
|
283
|
+
{#if label}{@render label(currentDisplay(), variant, picking)}{:else}{captionText}{/if}
|
|
284
|
+
</button>
|
|
285
|
+
<button class={cls(PARTS.RIGHT, variant)} type="button" onclick={hnext}
|
|
286
|
+
>{#if next}{@render next(variant)}{:else}Next{/if}</button
|
|
287
|
+
>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
{#if picking}
|
|
291
|
+
<div class={cls(PARTS.MIDDLE, variant)} transition:tfn|local={tparams}>
|
|
292
|
+
<MonthPicker
|
|
293
|
+
{...monthPicker}
|
|
294
|
+
bind:value={() => currentDisplay(), (v) => (display = v)}
|
|
295
|
+
min={min?.toPlainYearMonth()}
|
|
296
|
+
max={max?.toPlainYearMonth()}
|
|
297
|
+
{variant}
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
{:else}
|
|
301
|
+
<div class={cls(PARTS.MIDDLE, variant)} transition:tfn|local={tparams}>
|
|
302
|
+
<div class={cls(PARTS.AUX, variant)} role="row">
|
|
303
|
+
{#each weekdayLabels as wl}
|
|
304
|
+
<span class={cls(PARTS.EXTRA, VARIANT.NEUTRAL)} role="columnheader" data-weekday={wl.weekday} aria-label={wl.full}>
|
|
305
|
+
{#if weekday}{@render weekday(wl.weekday, variant)}{:else}{wl.text}{/if}
|
|
306
|
+
</span>
|
|
307
|
+
{/each}
|
|
308
|
+
</div>
|
|
309
|
+
<div class={cls(PARTS.MAIN, variant)} role="grid" tabindex="-1" aria-labelledby={idCaption} onkeydown={hkeydown}>
|
|
310
|
+
{#each weeks as week}
|
|
311
|
+
<div role="row">
|
|
312
|
+
{#each week as cell}
|
|
313
|
+
{@const outside = !sameMonth(cell, currentDisplay())}
|
|
314
|
+
{#if outside && !outsideDays}
|
|
315
|
+
<span class={cls(PARTS.EXTRA, VARIANT.NEUTRAL)} role="gridcell" aria-hidden="true"></span>
|
|
316
|
+
{:else}
|
|
317
|
+
{@const c = ctx(cell)}
|
|
318
|
+
<button
|
|
319
|
+
class={cls(PARTS.EXTRA, c.variant)}
|
|
320
|
+
type="button"
|
|
321
|
+
role="gridcell"
|
|
322
|
+
tabindex={cell.equals(focused) ? 0 : -1}
|
|
323
|
+
aria-selected={c.selected}
|
|
324
|
+
aria-disabled={c.disabled || undefined}
|
|
325
|
+
aria-current={c.today ? "date" : undefined}
|
|
326
|
+
aria-label={labelOf(cell)}
|
|
327
|
+
data-date={cell.toString()}
|
|
328
|
+
data-today={c.today || undefined}
|
|
329
|
+
data-selected={c.selected || undefined}
|
|
330
|
+
data-outside={c.outside || undefined}
|
|
331
|
+
data-disabled={c.disabled || undefined}
|
|
332
|
+
data-weekday={c.weekday}
|
|
333
|
+
onfocus={() => (focused = cell)}
|
|
334
|
+
onclick={() => select(cell)}
|
|
335
|
+
>
|
|
336
|
+
{#if day}{@render day(c)}{:else}{cell.day}{/if}
|
|
337
|
+
</button>
|
|
338
|
+
{/if}
|
|
339
|
+
{/each}
|
|
340
|
+
</div>
|
|
341
|
+
{/each}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
{/if}
|
|
345
|
+
|
|
346
|
+
{#if bottom}
|
|
347
|
+
<div class={cls(PARTS.BOTTOM, variant)}>{@render bottom(variant, setToday)}</div>
|
|
348
|
+
{/if}
|
|
349
|
+
</div>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export type DayCtx = {
|
|
2
|
+
date: Temporal.PlainDate;
|
|
3
|
+
variant: string;
|
|
4
|
+
weekday: number;
|
|
5
|
+
today: boolean;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
outside: boolean;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type TransitionProp = {
|
|
11
|
+
fn?: (node: HTMLElement, params: any, options: {
|
|
12
|
+
direction: "in" | "out" | "both";
|
|
13
|
+
}) => import("svelte/transition").TransitionConfig;
|
|
14
|
+
params?: unknown;
|
|
15
|
+
};
|
|
16
|
+
export interface CalendarProps {
|
|
17
|
+
value?: Temporal.PlainDate;
|
|
18
|
+
display?: Temporal.PlainYearMonth;
|
|
19
|
+
picking?: boolean;
|
|
20
|
+
min?: Temporal.PlainDate;
|
|
21
|
+
max?: Temporal.PlainDate;
|
|
22
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
23
|
+
outsideDays?: boolean;
|
|
24
|
+
fixedWeeks?: boolean;
|
|
25
|
+
firstDayOfWeek?: number;
|
|
26
|
+
locale?: string;
|
|
27
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
28
|
+
prev?: Snippet<[string]>;
|
|
29
|
+
next?: Snippet<[string]>;
|
|
30
|
+
weekday?: Snippet<[number, string]>;
|
|
31
|
+
day?: Snippet<[DayCtx]>;
|
|
32
|
+
bottom?: Snippet<[string, () => void]>;
|
|
33
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
34
|
+
transition?: TransitionProp;
|
|
35
|
+
styling?: SVSClass;
|
|
36
|
+
variant?: SVSVariant;
|
|
37
|
+
}
|
|
38
|
+
export type CalendarReqdProps = never;
|
|
39
|
+
export type CalendarBindProps = "value" | "display" | "picking";
|
|
40
|
+
export declare const _CALENDAR_PRESET = "svs-calendar";
|
|
41
|
+
import type { Snippet } from "svelte";
|
|
42
|
+
import type { SVSClass, SVSVariant } from "./_core";
|
|
43
|
+
import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
44
|
+
/**
|
|
45
|
+
* ### Usage
|
|
46
|
+
* Use standalone.
|
|
47
|
+
* ```svelte
|
|
48
|
+
* <Calendar {...props} />
|
|
49
|
+
* ```
|
|
50
|
+
* ### Types
|
|
51
|
+
* default value: *`(value)`*
|
|
52
|
+
* ```ts
|
|
53
|
+
* type DayCtx = {
|
|
54
|
+
* date: Temporal.PlainDate;
|
|
55
|
+
* variant: string;
|
|
56
|
+
* weekday: number;
|
|
57
|
+
* today: boolean;
|
|
58
|
+
* selected: boolean;
|
|
59
|
+
* outside: boolean;
|
|
60
|
+
* disabled: boolean;
|
|
61
|
+
* };
|
|
62
|
+
* type TransitionProp = {
|
|
63
|
+
* fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
64
|
+
* params?: unknown;
|
|
65
|
+
* };
|
|
66
|
+
* interface CalendarProps {
|
|
67
|
+
* value?: Temporal.PlainDate; // bindable; selected date
|
|
68
|
+
* display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
69
|
+
* picking?: boolean; // bindable (false)
|
|
70
|
+
* min?: Temporal.PlainDate;
|
|
71
|
+
* max?: Temporal.PlainDate;
|
|
72
|
+
* isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
73
|
+
* outsideDays?: boolean; // (false)
|
|
74
|
+
* fixedWeeks?: boolean; // (false)
|
|
75
|
+
* firstDayOfWeek?: number; // (0=Sun)
|
|
76
|
+
* locale?: string;
|
|
77
|
+
* label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
78
|
+
* prev?: Snippet<[string]>;
|
|
79
|
+
* next?: Snippet<[string]>;
|
|
80
|
+
* weekday?: Snippet<[number, string]>;
|
|
81
|
+
* day?: Snippet<[DayCtx]>;
|
|
82
|
+
* bottom?: Snippet<[string, () => void]>;
|
|
83
|
+
* monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
84
|
+
* transition?: TransitionProp;
|
|
85
|
+
* styling?: SVSClass;
|
|
86
|
+
* variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
* ### Anatomy
|
|
90
|
+
* ```svelte
|
|
91
|
+
* <div class="whole" role="application">
|
|
92
|
+
* <div class="top">
|
|
93
|
+
* <button class="left" />
|
|
94
|
+
* <button class="label" />
|
|
95
|
+
* <button class="right" />
|
|
96
|
+
* </div>
|
|
97
|
+
* <div class="middle">
|
|
98
|
+
* <div class="aux" role="row">weekday headers</div>
|
|
99
|
+
* <div class="main" role="grid">day cells</div>
|
|
100
|
+
* MonthPicker while picking
|
|
101
|
+
* </div>
|
|
102
|
+
* <div class="bottom" conditional>{bottom}</div>
|
|
103
|
+
* </div>
|
|
104
|
+
* ```
|
|
105
|
+
* ### Behavior
|
|
106
|
+
* - Prev/next page `display`; the label toggles an embedded `_MonthPicker`.
|
|
107
|
+
* - Enabled day clicks and Enter/Space select a single `Temporal.PlainDate`.
|
|
108
|
+
* - Day cells expose `data-today`, `data-selected`, `data-outside`, `data-disabled`, and `data-weekday`.
|
|
109
|
+
* - Arrow, Home/End, PageUp/PageDown, and Shift+PageUp/PageDown update roving focus.
|
|
110
|
+
*/
|
|
111
|
+
declare const Calendar: import("svelte").Component<CalendarProps, {}, "display" | "value" | "picking">;
|
|
112
|
+
type Calendar = ReturnType<typeof Calendar>;
|
|
113
|
+
export default Calendar;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
### Usage
|
|
4
|
+
Use standalone.
|
|
5
|
+
```svelte
|
|
6
|
+
<CheckField {...props} />
|
|
7
|
+
```
|
|
8
|
+
### Types
|
|
9
|
+
default value: *`(value)`*
|
|
10
|
+
```ts
|
|
11
|
+
interface CheckFieldProps extends Omit<HTMLInputAttributes, "type" | "value" | "id"> {
|
|
12
|
+
options: SvelteMap<string, string> | Map<string, string>;
|
|
13
|
+
label?: string;
|
|
14
|
+
extra?: string;
|
|
15
|
+
aux?: Snippet<[string[], string, HTMLInputElement[]]>; // Snippet<[values,variant,elements]>
|
|
16
|
+
bottom?: string;
|
|
17
|
+
reserve?: boolean; // (false)
|
|
18
|
+
values?: string[]; // bindable
|
|
19
|
+
multiple?: boolean; // (true)
|
|
20
|
+
validations?: CheckFieldValidation[];
|
|
21
|
+
constraints?: CheckFieldConstraint[];
|
|
22
|
+
attach?: Attachment<HTMLInputElement>;
|
|
23
|
+
elements?: HTMLInputElement[]; // bindable
|
|
24
|
+
styling?: SVSClass;
|
|
25
|
+
variant?: SVSVariant; // bindable (VARIANT.NEUTRAL)
|
|
26
|
+
// class & other HTMLInputAttributes are passed to each <input> via ...rest (class is merged onto each input; required is applied dynamically)
|
|
27
|
+
}
|
|
28
|
+
type CheckFieldValidation = SVSFieldValidation<string[]>;
|
|
29
|
+
type CheckFieldConstraint = SVSFieldConstraint;
|
|
30
|
+
```
|
|
31
|
+
### Anatomy
|
|
32
|
+
```svelte
|
|
33
|
+
<div class="whole">
|
|
34
|
+
<div class="top" conditional: label or aux>
|
|
35
|
+
<span class="label" conditional>
|
|
36
|
+
{label}
|
|
37
|
+
<span class="extra" conditional>{extra}</span>
|
|
38
|
+
</span>
|
|
39
|
+
<span class="aux" conditional>{aux}</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="middle">
|
|
42
|
+
{#each options as { value, text }, i}
|
|
43
|
+
<label class="main">
|
|
44
|
+
<input class="left" {...rest} />
|
|
45
|
+
<span class="right">{text}</span>
|
|
46
|
+
</label>
|
|
47
|
+
{/each}
|
|
48
|
+
</div>
|
|
49
|
+
<div class="bottom" conditional: has text, or always when reserve>{bottom}</div>
|
|
50
|
+
</div>
|
|
51
|
+
```
|
|
52
|
+
-->
|
|
53
|
+
<script module lang="ts">
|
|
54
|
+
export interface CheckFieldProps extends Omit<HTMLInputAttributes, "type" | "value" | "id"> {
|
|
55
|
+
options: SvelteMap<string, string> | Map<string, string>;
|
|
56
|
+
label?: string;
|
|
57
|
+
extra?: string;
|
|
58
|
+
aux?: Snippet<[string[], string, HTMLInputElement[]]>; // Snippet<[values,variant,elements]>
|
|
59
|
+
bottom?: string;
|
|
60
|
+
reserve?: boolean; // (false)
|
|
61
|
+
values?: string[]; // bindable
|
|
62
|
+
multiple?: boolean; // (true)
|
|
63
|
+
validations?: CheckFieldValidation[];
|
|
64
|
+
constraints?: CheckFieldConstraint[];
|
|
65
|
+
attach?: Attachment<HTMLInputElement>;
|
|
66
|
+
elements?: HTMLInputElement[]; // bindable
|
|
67
|
+
styling?: SVSClass;
|
|
68
|
+
variant?: SVSVariant; // bindable (VARIANT.NEUTRAL)
|
|
69
|
+
}
|
|
70
|
+
export type CheckFieldReqdProps = "options";
|
|
71
|
+
export type CheckFieldBindProps = "values" | "variant" | "elements";
|
|
72
|
+
export type CheckFieldValidation = SVSFieldValidation<string[]>;
|
|
73
|
+
export type CheckFieldConstraint = SVSFieldConstraint;
|
|
74
|
+
|
|
75
|
+
export const _CHECK_FIELD_PRESET = "svs-check-field";
|
|
76
|
+
|
|
77
|
+
import { untrack, onMount } from "svelte";
|
|
78
|
+
import { VARIANT, PARTS, _fnClass, _isNeutral } from "./_core";
|
|
79
|
+
import type { Snippet } from "svelte";
|
|
80
|
+
import type { Attachment } from "svelte/attachments";
|
|
81
|
+
import type { SvelteMap } from "svelte/reactivity";
|
|
82
|
+
import type { HTMLInputAttributes, ChangeEventHandler, EventHandler } from "svelte/elements";
|
|
83
|
+
import type { SVSClass, SVSVariant, SVSFieldValidation, SVSFieldConstraint } from "./_core";
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<script lang="ts">
|
|
87
|
+
// prettier-ignore
|
|
88
|
+
let { options, label, extra, aux, bottom, reserve = false, values = $bindable([]), multiple = true, validations = [], constraints = [], name, onchange, oninvalid, attach, elements = $bindable([]), styling, variant = $bindable(VARIANT.NEUTRAL), class: c, required = false, ...rest }: CheckFieldProps = $props();
|
|
89
|
+
|
|
90
|
+
// *** Initialize *** //
|
|
91
|
+
const cls = $derived(_fnClass(_CHECK_FIELD_PRESET, styling));
|
|
92
|
+
const type = $derived(multiple ? "checkbox" : "radio");
|
|
93
|
+
const uid = $props.id();
|
|
94
|
+
const nm = $derived(name?.trim() ? name : `${uid}-name`);
|
|
95
|
+
const idLabel = $derived(label?.trim() ? `${uid}-label` : undefined);
|
|
96
|
+
const idDesc = $derived(bottom?.trim() ? `${uid}-desc` : undefined);
|
|
97
|
+
const idErr = $derived(idDesc ?? `${uid}-err`);
|
|
98
|
+
const roleGroup = $derived(multiple ? "group" : "radiogroup");
|
|
99
|
+
let errmsg = $state("");
|
|
100
|
+
const message = $derived(variant === VARIANT.INACTIVE ? errmsg || bottom : bottom);
|
|
101
|
+
|
|
102
|
+
// *** States *** //
|
|
103
|
+
let neutral = $state(_isNeutral(variant) ? variant : VARIANT.NEUTRAL);
|
|
104
|
+
$effect(() => {
|
|
105
|
+
neutral = _isNeutral(variant) ? variant : neutral;
|
|
106
|
+
});
|
|
107
|
+
const live = $derived(variant === VARIANT.INACTIVE ? "alert" : undefined);
|
|
108
|
+
const invalid = $derived(variant === VARIANT.INACTIVE ? true : undefined);
|
|
109
|
+
const idMsg = $derived(variant === VARIANT.INACTIVE && message?.trim() ? idErr : undefined);
|
|
110
|
+
const reqd = $derived(required && (!multiple || !values.length) ? true : undefined);
|
|
111
|
+
function shift(oninvalid: boolean = false, msg?: string) {
|
|
112
|
+
const vmsg = elements[0]?.validationMessage ?? "";
|
|
113
|
+
variant = msg ? VARIANT.INACTIVE : !values.length && !oninvalid ? neutral : vmsg ? VARIANT.INACTIVE : VARIANT.ACTIVE;
|
|
114
|
+
errmsg = msg ? msg : vmsg;
|
|
115
|
+
}
|
|
116
|
+
function verify() {
|
|
117
|
+
if (!elements[0]) return;
|
|
118
|
+
for (const v of validations) {
|
|
119
|
+
const msg = v({ value: values, validity: elements[0].validity, element: elements[0] });
|
|
120
|
+
if (msg) return elements[0].setCustomValidity(msg);
|
|
121
|
+
}
|
|
122
|
+
elements[0].setCustomValidity("");
|
|
123
|
+
}
|
|
124
|
+
function check(value: string): string {
|
|
125
|
+
if (!elements[0]) return "";
|
|
126
|
+
for (const c of constraints) {
|
|
127
|
+
const msg = c({ value, values, validity: elements[0].validity, element: elements[0] });
|
|
128
|
+
if (msg) return msg;
|
|
129
|
+
}
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// *** Reactive Handlers *** //
|
|
134
|
+
const opts = $derived(Array.from(options, ([value, text]) => ({ value, text, checked: values.includes(value) })));
|
|
135
|
+
$effect.pre(() => {
|
|
136
|
+
values;
|
|
137
|
+
untrack(() => validate(true));
|
|
138
|
+
});
|
|
139
|
+
function validate(effect?: boolean) {
|
|
140
|
+
if (effect && _isNeutral(variant)) return;
|
|
141
|
+
verify();
|
|
142
|
+
shift();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// *** Event Handlers *** //
|
|
146
|
+
const hchange: ChangeEventHandler<HTMLInputElement> = (ev) => {
|
|
147
|
+
onchange?.(ev);
|
|
148
|
+
const input = ev.currentTarget;
|
|
149
|
+
if (input.checked) {
|
|
150
|
+
const msg = check(input.value);
|
|
151
|
+
if (msg) {
|
|
152
|
+
input.checked = false;
|
|
153
|
+
shift(false, msg);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
values = elements.filter((el) => el.checked).map((el) => el.value);
|
|
158
|
+
validate();
|
|
159
|
+
};
|
|
160
|
+
const hinvalid: EventHandler<Event, HTMLInputElement> = (ev) => {
|
|
161
|
+
oninvalid?.(ev);
|
|
162
|
+
ev.preventDefault();
|
|
163
|
+
shift(true);
|
|
164
|
+
};
|
|
165
|
+
onMount(() => verify());
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
<!---------------------------------------->
|
|
169
|
+
|
|
170
|
+
{#if opts.length}
|
|
171
|
+
<div class={cls(PARTS.WHOLE, variant)} role="group" aria-labelledby={idLabel}>
|
|
172
|
+
{#if label?.trim() || aux}
|
|
173
|
+
<div class={cls(PARTS.TOP, variant)}>
|
|
174
|
+
{@render lbl()}
|
|
175
|
+
{#if aux}
|
|
176
|
+
<span class={cls(PARTS.AUX, variant)}>{@render aux(values, variant, elements)}</span>
|
|
177
|
+
{/if}
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
180
|
+
{@render main()}
|
|
181
|
+
{#if reserve || message?.trim()}
|
|
182
|
+
<div class={cls(PARTS.BOTTOM, variant)} id={idDesc ?? idErr} role={live}>{message}</div>
|
|
183
|
+
{/if}
|
|
184
|
+
</div>
|
|
185
|
+
{/if}
|
|
186
|
+
|
|
187
|
+
{#snippet lbl()}
|
|
188
|
+
{#if label?.trim()}
|
|
189
|
+
<span class={cls(PARTS.LABEL, variant)} id={idLabel}>
|
|
190
|
+
{label}
|
|
191
|
+
{#if extra?.trim()}
|
|
192
|
+
<span class={cls(PARTS.EXTRA, variant)}>{extra}</span>
|
|
193
|
+
{/if}
|
|
194
|
+
</span>
|
|
195
|
+
{/if}
|
|
196
|
+
{/snippet}
|
|
197
|
+
{#snippet main()}
|
|
198
|
+
<div
|
|
199
|
+
class={cls(PARTS.MIDDLE, variant)}
|
|
200
|
+
role={roleGroup}
|
|
201
|
+
aria-labelledby={idLabel}
|
|
202
|
+
aria-describedby={idDesc}
|
|
203
|
+
aria-invalid={!multiple ? invalid : undefined}
|
|
204
|
+
aria-errormessage={!multiple ? idMsg : undefined}
|
|
205
|
+
>
|
|
206
|
+
{#each opts as { value, text, checked }, i (value)}
|
|
207
|
+
{@const stat = checked ? VARIANT.ACTIVE : neutral}
|
|
208
|
+
<label class={cls(PARTS.MAIN, stat)}>
|
|
209
|
+
<input
|
|
210
|
+
bind:this={elements[i]}
|
|
211
|
+
class={[cls(PARTS.LEFT, stat), c]}
|
|
212
|
+
{...rest}
|
|
213
|
+
required={reqd}
|
|
214
|
+
aria-invalid={multiple ? invalid : undefined}
|
|
215
|
+
aria-errormessage={multiple ? idMsg : undefined}
|
|
216
|
+
{value}
|
|
217
|
+
name={nm}
|
|
218
|
+
{type}
|
|
219
|
+
{checked}
|
|
220
|
+
onchange={hchange}
|
|
221
|
+
oninvalid={hinvalid}
|
|
222
|
+
{@attach attach}
|
|
223
|
+
/>
|
|
224
|
+
<span class={cls(PARTS.RIGHT, stat)}>{text}</span>
|
|
225
|
+
</label>
|
|
226
|
+
{/each}
|
|
227
|
+
</div>
|
|
228
|
+
{/snippet}
|