svseeds 0.7.1 → 0.8.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/README.md +1 -1
- package/_svseeds/Accordion.svelte +10 -5
- package/_svseeds/Accordion.svelte.d.ts +3 -3
- package/_svseeds/Calendar.svelte +69 -47
- package/_svseeds/Calendar.svelte.d.ts +37 -35
- package/_svseeds/CheckField.svelte +19 -15
- package/_svseeds/ColorPicker.svelte +8 -4
- package/_svseeds/ColorPicker.svelte.d.ts +6 -3
- package/_svseeds/ComboBox.svelte +5 -7
- package/_svseeds/DarkToggle.svelte +2 -2
- package/_svseeds/DateField.svelte +11 -14
- package/_svseeds/DateInput.svelte +26 -23
- package/_svseeds/DateInput.svelte.d.ts +22 -16
- package/_svseeds/Disclosure.svelte +17 -7
- package/_svseeds/Drawer.svelte +1 -2
- package/_svseeds/Drawer.svelte.d.ts +1 -2
- package/_svseeds/FileField.svelte +19 -15
- package/_svseeds/FileInput.svelte +3 -11
- package/_svseeds/HotkeyCapture.svelte +3 -2
- package/_svseeds/HotkeyCapture.svelte.d.ts +3 -0
- package/_svseeds/Modal.svelte +5 -3
- package/_svseeds/NumberField.svelte +11 -14
- package/_svseeds/NumberInput.svelte +39 -53
- package/_svseeds/NumberInput.svelte.d.ts +11 -21
- package/_svseeds/Pagination.svelte +29 -31
- package/_svseeds/Pagination.svelte.d.ts +16 -13
- package/_svseeds/Popover.svelte +64 -18
- package/_svseeds/Popover.svelte.d.ts +16 -15
- package/_svseeds/SelectField.svelte +10 -13
- package/_svseeds/Sortable.svelte +43 -8
- package/_svseeds/Sortable.svelte.d.ts +8 -3
- package/_svseeds/SortableGroup.svelte +8 -5
- package/_svseeds/SortableGroup.svelte.d.ts +6 -3
- package/_svseeds/Tabs.svelte +22 -19
- package/_svseeds/Tabs.svelte.d.ts +8 -5
- package/_svseeds/TagsInput.svelte +3 -13
- package/_svseeds/TagsInputField.svelte +11 -14
- package/_svseeds/TextField.svelte +10 -13
- package/_svseeds/Toast.svelte +38 -6
- package/_svseeds/Toast.svelte.d.ts +24 -2
- package/_svseeds/Toggle.svelte +1 -1
- package/_svseeds/ToggleGroup.svelte +39 -41
- package/_svseeds/ToggleGroup.svelte.d.ts +13 -10
- package/_svseeds/ToggleGroupField.svelte +29 -29
- package/_svseeds/ToggleGroupField.svelte.d.ts +12 -12
- package/_svseeds/Tooltip.svelte +12 -7
- package/_svseeds/Tooltip.svelte.d.ts +7 -7
- package/_svseeds/WheelPicker.svelte +21 -13
- package/_svseeds/ZSortableA11y.svelte +1082 -0
- package/_svseeds/ZSortableA11y.svelte.d.ts +182 -0
- package/_svseeds/_core.d.ts +37 -1
- package/_svseeds/_core.js +70 -1
- package/index.d.ts +10 -9
- package/index.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/svseeds)
|
|
4
4
|
[](https://jsr.io/@svseeds/ui)
|
|
5
|
-
[](LICENSE
|
|
5
|
+
[](LICENSE)
|
|
6
6
|
|
|
7
7
|
SvSeeds is a collection of essential functional components for Svelte development. These components are intentionally designed with minimal styling, giving you complete freedom to apply your own styles based on your project requirements. For specialized needs, you can download the components as .svelte files through our CLI tool and customize their functionality directly.
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
value: string; // REQUIRED, unique within `items`. Addresses `current`.
|
|
25
25
|
label: string | Snippet<[boolean, string]> | AccordionComponent;
|
|
26
26
|
panel: Snippet<[string]> | AccordionComponent;
|
|
27
|
-
|
|
27
|
+
inactive?: string | boolean; // (false) reason string OR true; forwarded to Disclosure
|
|
28
28
|
};
|
|
29
29
|
```
|
|
30
30
|
`value`s must be unique within `items`. Accordion is exclusive: at most one item is open at a time.
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
{children}
|
|
37
37
|
{:else}
|
|
38
38
|
{#each items as item}
|
|
39
|
-
<Disclosure id={item.value} label={item.label} inactive={item.
|
|
39
|
+
<Disclosure id={item.value} label={item.label} inactive={item.inactive}>
|
|
40
40
|
{item.panel}
|
|
41
41
|
</Disclosure>
|
|
42
42
|
{/each}
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
value: string; // REQUIRED, unique within `items`. Addresses `current`.
|
|
64
64
|
label: string | Snippet<[boolean, string]> | AccordionComponent;
|
|
65
65
|
panel: Snippet<[string]> | AccordionComponent;
|
|
66
|
-
|
|
66
|
+
inactive?: string | boolean; // (false) reason string OR true; forwarded to Disclosure
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
export const _ACCORDION_PRESET = "svs-accordion";
|
|
@@ -71,6 +71,9 @@
|
|
|
71
71
|
function isComponent(x: unknown): x is AccordionComponent {
|
|
72
72
|
return typeof x === "object" && x !== null && "component" in x;
|
|
73
73
|
}
|
|
74
|
+
function isInactive(x?: string | boolean) {
|
|
75
|
+
return x === true || (typeof x === "string" && x.trim().length > 0);
|
|
76
|
+
}
|
|
74
77
|
|
|
75
78
|
import { untrack } from "svelte";
|
|
76
79
|
import { VARIANT, PARTS, _fnClass, _omit } from "./_core";
|
|
@@ -104,7 +107,9 @@
|
|
|
104
107
|
},
|
|
105
108
|
};
|
|
106
109
|
_setDisclosureContext(ctx);
|
|
107
|
-
const selected = $derived(
|
|
110
|
+
const selected = $derived(
|
|
111
|
+
children || !items ? current : items.some((it) => it.value === current && !isInactive(it.inactive)) ? current : undefined,
|
|
112
|
+
);
|
|
108
113
|
|
|
109
114
|
// *** Initialize Child Props *** //
|
|
110
115
|
const childProps = $derived({
|
|
@@ -133,7 +138,7 @@
|
|
|
133
138
|
{#snippet panel(variant: string)}
|
|
134
139
|
{@render panelContent(item.panel, variant)}
|
|
135
140
|
{/snippet}
|
|
136
|
-
<Disclosure id={item.value} {label} children={panel} inactive={item.
|
|
141
|
+
<Disclosure id={item.value} {label} children={panel} inactive={item.inactive} {variant} {...childProps} />
|
|
137
142
|
{/each}
|
|
138
143
|
</div>
|
|
139
144
|
{/if}
|
|
@@ -16,7 +16,7 @@ export type AccordionItem = {
|
|
|
16
16
|
value: string;
|
|
17
17
|
label: string | Snippet<[boolean, string]> | AccordionComponent;
|
|
18
18
|
panel: Snippet<[string]> | AccordionComponent;
|
|
19
|
-
|
|
19
|
+
inactive?: string | boolean;
|
|
20
20
|
};
|
|
21
21
|
export declare const _ACCORDION_PRESET = "svs-accordion";
|
|
22
22
|
import type { Component, Snippet } from "svelte";
|
|
@@ -47,7 +47,7 @@ import type { DisclosureProps, DisclosureReqdProps, DisclosureBindProps } from "
|
|
|
47
47
|
* value: string; // REQUIRED, unique within `items`. Addresses `current`.
|
|
48
48
|
* label: string | Snippet<[boolean, string]> | AccordionComponent;
|
|
49
49
|
* panel: Snippet<[string]> | AccordionComponent;
|
|
50
|
-
*
|
|
50
|
+
* inactive?: string | boolean; // (false) reason string OR true; forwarded to Disclosure
|
|
51
51
|
* };
|
|
52
52
|
* ```
|
|
53
53
|
* `value`s must be unique within `items`. Accordion is exclusive: at most one item is open at a time.
|
|
@@ -59,7 +59,7 @@ import type { DisclosureProps, DisclosureReqdProps, DisclosureBindProps } from "
|
|
|
59
59
|
* {children}
|
|
60
60
|
* {:else}
|
|
61
61
|
* {#each items as item}
|
|
62
|
-
* <Disclosure id={item.value} label={item.label} inactive={item.
|
|
62
|
+
* <Disclosure id={item.value} label={item.label} inactive={item.inactive}>
|
|
63
63
|
* {item.panel}
|
|
64
64
|
* </Disclosure>
|
|
65
65
|
* {/each}
|
package/_svseeds/Calendar.svelte
CHANGED
|
@@ -8,19 +8,6 @@
|
|
|
8
8
|
### Types
|
|
9
9
|
default value: *`(value)`*
|
|
10
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
11
|
interface CalendarProps {
|
|
25
12
|
value?: Temporal.PlainDate; // bindable; selected date
|
|
26
13
|
display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
@@ -33,8 +20,8 @@
|
|
|
33
20
|
firstDayOfWeek?: number; // (0=Sun)
|
|
34
21
|
locale?: string;
|
|
35
22
|
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
left?: Snippet<[string]>;
|
|
24
|
+
right?: Snippet<[string]>;
|
|
38
25
|
weekday?: Snippet<[number, string]>;
|
|
39
26
|
day?: Snippet<[DayCtx]>;
|
|
40
27
|
bottom?: Snippet<[string, () => void]>;
|
|
@@ -43,18 +30,33 @@
|
|
|
43
30
|
styling?: SVSClass;
|
|
44
31
|
variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
45
32
|
}
|
|
33
|
+
type DayCtx = {
|
|
34
|
+
date: Temporal.PlainDate;
|
|
35
|
+
variant: string;
|
|
36
|
+
weekday: number;
|
|
37
|
+
today: boolean;
|
|
38
|
+
selected: boolean;
|
|
39
|
+
outside: boolean;
|
|
40
|
+
disabled: boolean;
|
|
41
|
+
};
|
|
42
|
+
type TransitionProp = {
|
|
43
|
+
fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
44
|
+
params?: unknown;
|
|
45
|
+
};
|
|
46
46
|
```
|
|
47
47
|
### Anatomy
|
|
48
48
|
```svelte
|
|
49
|
-
<div class="whole" role="
|
|
49
|
+
<div class="whole" role="group">
|
|
50
50
|
<div class="top">
|
|
51
51
|
<button class="left" />
|
|
52
52
|
<button class="label" />
|
|
53
53
|
<button class="right" />
|
|
54
54
|
</div>
|
|
55
55
|
<div class="middle">
|
|
56
|
-
<div class="
|
|
57
|
-
|
|
56
|
+
<div class="main" role="grid">
|
|
57
|
+
<div class="aux" role="row">weekday headers</div>
|
|
58
|
+
day cells
|
|
59
|
+
</div>
|
|
58
60
|
MonthPicker while picking
|
|
59
61
|
</div>
|
|
60
62
|
<div class="bottom" conditional>{bottom}</div>
|
|
@@ -67,19 +69,6 @@
|
|
|
67
69
|
- Arrow, Home/End, PageUp/PageDown, and Shift+PageUp/PageDown update roving focus.
|
|
68
70
|
-->
|
|
69
71
|
<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
72
|
export interface CalendarProps {
|
|
84
73
|
value?: Temporal.PlainDate;
|
|
85
74
|
display?: Temporal.PlainYearMonth;
|
|
@@ -92,8 +81,8 @@
|
|
|
92
81
|
firstDayOfWeek?: number;
|
|
93
82
|
locale?: string;
|
|
94
83
|
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
left?: Snippet<[string]>;
|
|
85
|
+
right?: Snippet<[string]>;
|
|
97
86
|
weekday?: Snippet<[number, string]>;
|
|
98
87
|
day?: Snippet<[DayCtx]>;
|
|
99
88
|
bottom?: Snippet<[string, () => void]>;
|
|
@@ -102,6 +91,19 @@
|
|
|
102
91
|
styling?: SVSClass;
|
|
103
92
|
variant?: SVSVariant;
|
|
104
93
|
}
|
|
94
|
+
export type DayCtx = {
|
|
95
|
+
date: Temporal.PlainDate;
|
|
96
|
+
variant: string;
|
|
97
|
+
weekday: number;
|
|
98
|
+
today: boolean;
|
|
99
|
+
selected: boolean;
|
|
100
|
+
outside: boolean;
|
|
101
|
+
disabled: boolean;
|
|
102
|
+
};
|
|
103
|
+
export type TransitionProp = {
|
|
104
|
+
fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
105
|
+
params?: unknown;
|
|
106
|
+
};
|
|
105
107
|
export type CalendarReqdProps = never;
|
|
106
108
|
export type CalendarBindProps = "value" | "display" | "picking";
|
|
107
109
|
|
|
@@ -118,7 +120,7 @@
|
|
|
118
120
|
|
|
119
121
|
<script lang="ts">
|
|
120
122
|
// prettier-ignore
|
|
121
|
-
let { value = $bindable(), display = $bindable(), picking = $bindable(false), min, max, isDisabled, outsideDays = false, fixedWeeks = false, firstDayOfWeek = 0, locale, label,
|
|
123
|
+
let { value = $bindable(), display = $bindable(), picking = $bindable(false), min, max, isDisabled, outsideDays = false, fixedWeeks = false, firstDayOfWeek = 0, locale, label, left, right, weekday, day, bottom, monthPicker, transition, styling, variant = VARIANT.NEUTRAL }: CalendarProps = $props();
|
|
122
124
|
|
|
123
125
|
// *** Initialize *** //
|
|
124
126
|
const cls = $derived(_fnClass(_CALENDAR_PRESET, styling));
|
|
@@ -131,6 +133,7 @@
|
|
|
131
133
|
// *** States *** //
|
|
132
134
|
const initialDisplay = display as Temporal.PlainYearMonth;
|
|
133
135
|
let focused = $state(value && sameMonth(value, initialDisplay) ? value : firstOf(initialDisplay));
|
|
136
|
+
let cells = $state<Record<string, HTMLElement>>({});
|
|
134
137
|
const firstWeekday = $derived(normWeekday(firstDayOfWeek));
|
|
135
138
|
const monthStart = $derived(firstOf(currentDisplay()));
|
|
136
139
|
const captionText = $derived(monthStart.toLocaleString(locale, { year: "numeric", month: "long" }));
|
|
@@ -151,7 +154,17 @@
|
|
|
151
154
|
const start = first.subtract({ days: lead });
|
|
152
155
|
return Array.from({ length: rows }, (_, r) => Array.from({ length: 7 }, (_, c) => start.add({ days: r * 7 + c })));
|
|
153
156
|
});
|
|
154
|
-
const
|
|
157
|
+
const cellKeys = $derived.by(() => {
|
|
158
|
+
const shown = currentDisplay();
|
|
159
|
+
const keys = new Set<string>();
|
|
160
|
+
for (const week of weeks) {
|
|
161
|
+
for (const cell of week) {
|
|
162
|
+
if (outsideDays || sameMonth(cell, shown)) keys.add(cell.toString());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return keys;
|
|
166
|
+
});
|
|
167
|
+
const reduced = $derived(shouldReduceMotion());
|
|
155
168
|
const tfn = $derived(!reduced && transition?.fn ? transition.fn : noop);
|
|
156
169
|
const tparams = $derived(transition?.params as any);
|
|
157
170
|
|
|
@@ -161,6 +174,10 @@
|
|
|
161
174
|
value;
|
|
162
175
|
untrack(() => syncFocus());
|
|
163
176
|
});
|
|
177
|
+
$effect.pre(() => {
|
|
178
|
+
cellKeys;
|
|
179
|
+
untrack(() => trimCells(cellKeys));
|
|
180
|
+
});
|
|
164
181
|
|
|
165
182
|
function syncFocus() {
|
|
166
183
|
const shown = currentDisplay();
|
|
@@ -243,8 +260,12 @@
|
|
|
243
260
|
tick().then(() => focusDate(d));
|
|
244
261
|
}
|
|
245
262
|
function focusDate(d: Temporal.PlainDate) {
|
|
246
|
-
|
|
247
|
-
|
|
263
|
+
cells[d.toString()]?.focus();
|
|
264
|
+
}
|
|
265
|
+
function trimCells(keys: Set<string>) {
|
|
266
|
+
for (const key of Object.keys(cells)) {
|
|
267
|
+
if (!keys.has(key)) delete cells[key];
|
|
268
|
+
}
|
|
248
269
|
}
|
|
249
270
|
function weekEdge(toEnd: boolean): Temporal.PlainDate {
|
|
250
271
|
const offset = (weekdayOf(focused) - firstWeekday + 7) % 7;
|
|
@@ -274,16 +295,16 @@
|
|
|
274
295
|
|
|
275
296
|
<!---------------------------------------->
|
|
276
297
|
|
|
277
|
-
<div class={cls(PARTS.WHOLE, variant)} role="
|
|
298
|
+
<div class={cls(PARTS.WHOLE, variant)} role="group" aria-label={appLabel} data-svs-calendar={uid}>
|
|
278
299
|
<div class={cls(PARTS.TOP, variant)}>
|
|
279
300
|
<button class={cls(PARTS.LEFT, variant)} type="button" onclick={hprev}
|
|
280
|
-
>{#if
|
|
301
|
+
>{#if left}{@render left(variant)}{:else}Previous{/if}</button
|
|
281
302
|
>
|
|
282
303
|
<button id={idCaption} class={cls(PARTS.LABEL, variant)} type="button" aria-expanded={picking} onclick={hlabel}>
|
|
283
304
|
{#if label}{@render label(currentDisplay(), variant, picking)}{:else}{captionText}{/if}
|
|
284
305
|
</button>
|
|
285
306
|
<button class={cls(PARTS.RIGHT, variant)} type="button" onclick={hnext}
|
|
286
|
-
>{#if
|
|
307
|
+
>{#if right}{@render right(variant)}{:else}Next{/if}</button
|
|
287
308
|
>
|
|
288
309
|
</div>
|
|
289
310
|
|
|
@@ -299,14 +320,14 @@
|
|
|
299
320
|
</div>
|
|
300
321
|
{:else}
|
|
301
322
|
<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
323
|
<div class={cls(PARTS.MAIN, variant)} role="grid" tabindex="-1" aria-labelledby={idCaption} onkeydown={hkeydown}>
|
|
324
|
+
<div class={cls(PARTS.AUX, variant)} role="row">
|
|
325
|
+
{#each weekdayLabels as wl}
|
|
326
|
+
<span class={cls(PARTS.EXTRA, VARIANT.NEUTRAL)} role="columnheader" data-weekday={wl.weekday} aria-label={wl.full}>
|
|
327
|
+
{#if weekday}{@render weekday(wl.weekday, variant)}{:else}{wl.text}{/if}
|
|
328
|
+
</span>
|
|
329
|
+
{/each}
|
|
330
|
+
</div>
|
|
310
331
|
{#each weeks as week}
|
|
311
332
|
<div role="row">
|
|
312
333
|
{#each week as cell}
|
|
@@ -320,6 +341,7 @@
|
|
|
320
341
|
type="button"
|
|
321
342
|
role="gridcell"
|
|
322
343
|
tabindex={cell.equals(focused) ? 0 : -1}
|
|
344
|
+
bind:this={cells[cell.toString()]}
|
|
323
345
|
aria-selected={c.selected}
|
|
324
346
|
aria-disabled={c.disabled || undefined}
|
|
325
347
|
aria-current={c.today ? "date" : undefined}
|
|
@@ -1,18 +1,3 @@
|
|
|
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
1
|
export interface CalendarProps {
|
|
17
2
|
value?: Temporal.PlainDate;
|
|
18
3
|
display?: Temporal.PlainYearMonth;
|
|
@@ -25,8 +10,8 @@ export interface CalendarProps {
|
|
|
25
10
|
firstDayOfWeek?: number;
|
|
26
11
|
locale?: string;
|
|
27
12
|
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
left?: Snippet<[string]>;
|
|
14
|
+
right?: Snippet<[string]>;
|
|
30
15
|
weekday?: Snippet<[number, string]>;
|
|
31
16
|
day?: Snippet<[DayCtx]>;
|
|
32
17
|
bottom?: Snippet<[string, () => void]>;
|
|
@@ -35,6 +20,21 @@ export interface CalendarProps {
|
|
|
35
20
|
styling?: SVSClass;
|
|
36
21
|
variant?: SVSVariant;
|
|
37
22
|
}
|
|
23
|
+
export type DayCtx = {
|
|
24
|
+
date: Temporal.PlainDate;
|
|
25
|
+
variant: string;
|
|
26
|
+
weekday: number;
|
|
27
|
+
today: boolean;
|
|
28
|
+
selected: boolean;
|
|
29
|
+
outside: boolean;
|
|
30
|
+
disabled: boolean;
|
|
31
|
+
};
|
|
32
|
+
export type TransitionProp = {
|
|
33
|
+
fn?: (node: HTMLElement, params: any, options: {
|
|
34
|
+
direction: "in" | "out" | "both";
|
|
35
|
+
}) => import("svelte/transition").TransitionConfig;
|
|
36
|
+
params?: unknown;
|
|
37
|
+
};
|
|
38
38
|
export type CalendarReqdProps = never;
|
|
39
39
|
export type CalendarBindProps = "value" | "display" | "picking";
|
|
40
40
|
export declare const _CALENDAR_PRESET = "svs-calendar";
|
|
@@ -50,19 +50,6 @@ import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
|
50
50
|
* ### Types
|
|
51
51
|
* default value: *`(value)`*
|
|
52
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
53
|
* interface CalendarProps {
|
|
67
54
|
* value?: Temporal.PlainDate; // bindable; selected date
|
|
68
55
|
* display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
@@ -75,8 +62,8 @@ import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
|
75
62
|
* firstDayOfWeek?: number; // (0=Sun)
|
|
76
63
|
* locale?: string;
|
|
77
64
|
* label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
78
|
-
*
|
|
79
|
-
*
|
|
65
|
+
* left?: Snippet<[string]>;
|
|
66
|
+
* right?: Snippet<[string]>;
|
|
80
67
|
* weekday?: Snippet<[number, string]>;
|
|
81
68
|
* day?: Snippet<[DayCtx]>;
|
|
82
69
|
* bottom?: Snippet<[string, () => void]>;
|
|
@@ -85,18 +72,33 @@ import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
|
85
72
|
* styling?: SVSClass;
|
|
86
73
|
* variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
87
74
|
* }
|
|
75
|
+
* type DayCtx = {
|
|
76
|
+
* date: Temporal.PlainDate;
|
|
77
|
+
* variant: string;
|
|
78
|
+
* weekday: number;
|
|
79
|
+
* today: boolean;
|
|
80
|
+
* selected: boolean;
|
|
81
|
+
* outside: boolean;
|
|
82
|
+
* disabled: boolean;
|
|
83
|
+
* };
|
|
84
|
+
* type TransitionProp = {
|
|
85
|
+
* fn?: (node: HTMLElement, params: any, options: { direction: "in" | "out" | "both" }) => import("svelte/transition").TransitionConfig;
|
|
86
|
+
* params?: unknown;
|
|
87
|
+
* };
|
|
88
88
|
* ```
|
|
89
89
|
* ### Anatomy
|
|
90
90
|
* ```svelte
|
|
91
|
-
* <div class="whole" role="
|
|
91
|
+
* <div class="whole" role="group">
|
|
92
92
|
* <div class="top">
|
|
93
93
|
* <button class="left" />
|
|
94
94
|
* <button class="label" />
|
|
95
95
|
* <button class="right" />
|
|
96
96
|
* </div>
|
|
97
97
|
* <div class="middle">
|
|
98
|
-
* <div class="
|
|
99
|
-
*
|
|
98
|
+
* <div class="main" role="grid">
|
|
99
|
+
* <div class="aux" role="row">weekday headers</div>
|
|
100
|
+
* day cells
|
|
101
|
+
* </div>
|
|
100
102
|
* MonthPicker while picking
|
|
101
103
|
* </div>
|
|
102
104
|
* <div class="bottom" conditional>{bottom}</div>
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
export const _CHECK_FIELD_PRESET = "svs-check-field";
|
|
76
76
|
|
|
77
77
|
import { untrack, onMount } from "svelte";
|
|
78
|
-
import { VARIANT, PARTS, _fnClass, _isNeutral } from "./_core";
|
|
78
|
+
import { VARIANT, PARTS, _fieldAria, _fieldIds, _fieldMessage, _fnClass, _isNeutral, _verify } from "./_core";
|
|
79
79
|
import type { Snippet } from "svelte";
|
|
80
80
|
import type { Attachment } from "svelte/attachments";
|
|
81
81
|
import type { SvelteMap } from "svelte/reactivity";
|
|
@@ -92,21 +92,23 @@
|
|
|
92
92
|
const type = $derived(multiple ? "checkbox" : "radio");
|
|
93
93
|
const uid = $props.id();
|
|
94
94
|
const nm = $derived(name?.trim() ? name : `${uid}-name`);
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
95
|
+
const ids = $derived(_fieldIds(uid, label, bottom));
|
|
96
|
+
const idLabel = $derived(ids.idLabel);
|
|
97
|
+
const idDesc = $derived(ids.idDesc);
|
|
98
|
+
const idErr = $derived(ids.idErr);
|
|
98
99
|
const roleGroup = $derived(multiple ? "group" : "radiogroup");
|
|
99
100
|
let errmsg = $state("");
|
|
100
|
-
const message = $derived(variant
|
|
101
|
+
const message = $derived(_fieldMessage(variant, errmsg, bottom));
|
|
101
102
|
|
|
102
103
|
// *** States *** //
|
|
103
104
|
let neutral = $state(_isNeutral(variant) ? variant : VARIANT.NEUTRAL);
|
|
104
105
|
$effect(() => {
|
|
105
106
|
neutral = _isNeutral(variant) ? variant : neutral;
|
|
106
107
|
});
|
|
107
|
-
const
|
|
108
|
+
const aria = $derived(_fieldAria(variant, message, idErr));
|
|
109
|
+
const live = $derived(aria.live);
|
|
108
110
|
const invalid = $derived(variant === VARIANT.INACTIVE ? true : undefined);
|
|
109
|
-
const idMsg = $derived(
|
|
111
|
+
const idMsg = $derived(aria.idMsg);
|
|
110
112
|
const reqd = $derived(required && (!multiple || !values.length) ? true : undefined);
|
|
111
113
|
function shift(oninvalid: boolean = false, msg?: string) {
|
|
112
114
|
const vmsg = elements[0]?.validationMessage ?? "";
|
|
@@ -114,12 +116,7 @@
|
|
|
114
116
|
errmsg = msg ? msg : vmsg;
|
|
115
117
|
}
|
|
116
118
|
function verify() {
|
|
117
|
-
|
|
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("");
|
|
119
|
+
_verify(elements[0], validations, values);
|
|
123
120
|
}
|
|
124
121
|
function check(value: string): string {
|
|
125
122
|
if (!elements[0]) return "";
|
|
@@ -132,10 +129,17 @@
|
|
|
132
129
|
|
|
133
130
|
// *** Reactive Handlers *** //
|
|
134
131
|
const opts = $derived(Array.from(options, ([value, text]) => ({ value, text, checked: values.includes(value) })));
|
|
132
|
+
$effect.pre(() => {
|
|
133
|
+
opts.length;
|
|
134
|
+
untrack(() => trimElements(opts.length));
|
|
135
|
+
});
|
|
135
136
|
$effect.pre(() => {
|
|
136
137
|
values;
|
|
137
138
|
untrack(() => validate(true));
|
|
138
139
|
});
|
|
140
|
+
function trimElements(length: number) {
|
|
141
|
+
if (elements.length > length) elements.length = length;
|
|
142
|
+
}
|
|
139
143
|
function validate(effect?: boolean) {
|
|
140
144
|
if (effect && _isNeutral(variant)) return;
|
|
141
145
|
verify();
|
|
@@ -154,7 +158,7 @@
|
|
|
154
158
|
return;
|
|
155
159
|
}
|
|
156
160
|
}
|
|
157
|
-
values = elements.filter((el) => el
|
|
161
|
+
values = elements.filter((el) => el?.checked).map((el) => el.value);
|
|
158
162
|
validate();
|
|
159
163
|
};
|
|
160
164
|
const hinvalid: EventHandler<Event, HTMLInputElement> = (ev) => {
|
|
@@ -179,7 +183,7 @@
|
|
|
179
183
|
{/if}
|
|
180
184
|
{@render main()}
|
|
181
185
|
{#if reserve || message?.trim()}
|
|
182
|
-
<div class={cls(PARTS.BOTTOM, variant)} id={
|
|
186
|
+
<div class={cls(PARTS.BOTTOM, variant)} id={idErr} role={live}>{message}</div>
|
|
183
187
|
{/if}
|
|
184
188
|
</div>
|
|
185
189
|
{/if}
|
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
### Types
|
|
9
9
|
default value: *`(value)`*
|
|
10
10
|
```ts
|
|
11
|
-
interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value"> {
|
|
11
|
+
interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha" | "aria-label"> {
|
|
12
12
|
value?: string; // bindable ("#000000")
|
|
13
13
|
alpha?: number; // (1)
|
|
14
14
|
checkered?: boolean; // (true)
|
|
15
|
+
ariaLabel?: string;
|
|
15
16
|
attach?: Attachment<HTMLInputElement>;
|
|
16
17
|
element?: HTMLInputElement; // bindable
|
|
17
18
|
styling?: SVSClass;
|
|
18
19
|
variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
20
|
+
// ariaLabel is the recommended accessible-name prop; aria-labelledby may be passed via ...rest
|
|
19
21
|
// class & other HTMLInputAttributes are passed to <input> via ...rest (class is merged onto the control)
|
|
20
22
|
}
|
|
21
23
|
```
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
<label class="whole">
|
|
25
27
|
<div class="middle"> // this middle element can provide the default transparency background when checkered is true
|
|
26
28
|
<div class="main"> // this main element is color sample and positions the transparent, focusable native input overlay
|
|
27
|
-
<input {...rest} type="color" />
|
|
29
|
+
<input aria-label {...rest} type="color" />
|
|
28
30
|
</div>
|
|
29
31
|
</div>
|
|
30
32
|
</label>
|
|
@@ -36,10 +38,11 @@
|
|
|
36
38
|
```
|
|
37
39
|
-->
|
|
38
40
|
<script module lang="ts">
|
|
39
|
-
export interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha"> {
|
|
41
|
+
export interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha" | "aria-label"> {
|
|
40
42
|
value?: string; // bindable ("#000000")
|
|
41
43
|
alpha?: number; // (1)
|
|
42
44
|
checkered?: boolean; // (true)
|
|
45
|
+
ariaLabel?: string;
|
|
43
46
|
attach?: Attachment<HTMLInputElement>;
|
|
44
47
|
element?: HTMLInputElement; // bindable
|
|
45
48
|
styling?: SVSClass;
|
|
@@ -89,7 +92,7 @@
|
|
|
89
92
|
|
|
90
93
|
<script lang="ts">
|
|
91
94
|
// prettier-ignore
|
|
92
|
-
let { value = $bindable(DEFAULT_COLOR), alpha = 1, checkered = true, attach, element = $bindable(), styling, variant = VARIANT.NEUTRAL, class: c, ...rest }: ColorPickerProps = $props();
|
|
95
|
+
let { value = $bindable(DEFAULT_COLOR), alpha = 1, checkered = true, ariaLabel, attach, element = $bindable(), styling, variant = VARIANT.NEUTRAL, class: c, ...rest }: ColorPickerProps = $props();
|
|
93
96
|
|
|
94
97
|
// *** Initialize *** //
|
|
95
98
|
const cls = $derived(_fnClass(_COLOR_PICKER_PRESET, styling));
|
|
@@ -113,6 +116,7 @@
|
|
|
113
116
|
bind:value
|
|
114
117
|
bind:this={element}
|
|
115
118
|
class={c}
|
|
119
|
+
aria-label={ariaLabel}
|
|
116
120
|
{...rest}
|
|
117
121
|
type="color"
|
|
118
122
|
style="position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha"> {
|
|
1
|
+
export interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha" | "aria-label"> {
|
|
2
2
|
value?: string;
|
|
3
3
|
alpha?: number;
|
|
4
4
|
checkered?: boolean;
|
|
5
|
+
ariaLabel?: string;
|
|
5
6
|
attach?: Attachment<HTMLInputElement>;
|
|
6
7
|
element?: HTMLInputElement;
|
|
7
8
|
styling?: SVSClass;
|
|
@@ -24,14 +25,16 @@ import type { SVSClass, SVSVariant } from "./_core";
|
|
|
24
25
|
* ### Types
|
|
25
26
|
* default value: *`(value)`*
|
|
26
27
|
* ```ts
|
|
27
|
-
* interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value"> {
|
|
28
|
+
* interface ColorPickerProps extends Omit<HTMLInputAttributes, "type" | "value" | "alpha" | "aria-label"> {
|
|
28
29
|
* value?: string; // bindable ("#000000")
|
|
29
30
|
* alpha?: number; // (1)
|
|
30
31
|
* checkered?: boolean; // (true)
|
|
32
|
+
* ariaLabel?: string;
|
|
31
33
|
* attach?: Attachment<HTMLInputElement>;
|
|
32
34
|
* element?: HTMLInputElement; // bindable
|
|
33
35
|
* styling?: SVSClass;
|
|
34
36
|
* variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
37
|
+
* // ariaLabel is the recommended accessible-name prop; aria-labelledby may be passed via ...rest
|
|
35
38
|
* // class & other HTMLInputAttributes are passed to <input> via ...rest (class is merged onto the control)
|
|
36
39
|
* }
|
|
37
40
|
* ```
|
|
@@ -40,7 +43,7 @@ import type { SVSClass, SVSVariant } from "./_core";
|
|
|
40
43
|
* <label class="whole">
|
|
41
44
|
* <div class="middle"> // this middle element can provide the default transparency background when checkered is true
|
|
42
45
|
* <div class="main"> // this main element is color sample and positions the transparent, focusable native input overlay
|
|
43
|
-
* <input {...rest} type="color" />
|
|
46
|
+
* <input aria-label {...rest} type="color" />
|
|
44
47
|
* </div>
|
|
45
48
|
* </div>
|
|
46
49
|
* </label>
|
package/_svseeds/ComboBox.svelte
CHANGED
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
export const [_getComboBoxContext, _setComboBoxContext] = _createContext<ComboBoxContext>();
|
|
73
73
|
|
|
74
74
|
import { tick } from "svelte";
|
|
75
|
-
import { VARIANT, PARTS, _fnClass, _createContext } from "./_core";
|
|
75
|
+
import { VARIANT, PARTS, _detectOverflow, _fnClass, _createContext } from "./_core";
|
|
76
76
|
import type { Snippet } from "svelte";
|
|
77
77
|
import type { Attachment } from "svelte/attachments";
|
|
78
78
|
import type { SvelteSet } from "svelte/reactivity";
|
|
@@ -141,9 +141,7 @@
|
|
|
141
141
|
overflow = { x: false, y: false };
|
|
142
142
|
await tick();
|
|
143
143
|
if (!listElem || typeof window === "undefined") return;
|
|
144
|
-
|
|
145
|
-
overflow.x = window.innerWidth < rect.right;
|
|
146
|
-
overflow.y = window.innerHeight < rect.bottom;
|
|
144
|
+
overflow = _detectOverflow(listElem);
|
|
147
145
|
}
|
|
148
146
|
function apply() {
|
|
149
147
|
if (!expanded) return;
|
|
@@ -171,9 +169,9 @@
|
|
|
171
169
|
onclick?.(ev);
|
|
172
170
|
if (!expanded) open();
|
|
173
171
|
};
|
|
174
|
-
const hinput:
|
|
172
|
+
const hinput: EventHandler<InputEvent, HTMLInputElement> = (ev) => {
|
|
175
173
|
oninput?.(ev);
|
|
176
|
-
if (
|
|
174
|
+
if (ev.isComposing) return;
|
|
177
175
|
typed = true;
|
|
178
176
|
selected = effOptions?.has(effValue) ? view.indexOf(effValue) : NA;
|
|
179
177
|
};
|
|
@@ -243,7 +241,7 @@
|
|
|
243
241
|
onblur={hblur}
|
|
244
242
|
onclick={hclick}
|
|
245
243
|
onkeydown={hkeydown}
|
|
246
|
-
oninput={hinput}
|
|
244
|
+
oninput={hinput as FormEventHandler<HTMLInputElement>}
|
|
247
245
|
{@attach attach}
|
|
248
246
|
/>
|
|
249
247
|
{#if extra}
|