svseeds 0.6.0 → 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 +2 -2
- package/_svseeds/Accordion.svelte +123 -82
- 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 +371 -0
- package/_svseeds/Calendar.svelte.d.ts +115 -0
- package/_svseeds/CheckField.svelte +232 -0
- package/_svseeds/CheckField.svelte.d.ts +80 -0
- package/_svseeds/ColorPicker.svelte +127 -0
- package/_svseeds/ColorPicker.svelte.d.ts +59 -0
- package/_svseeds/ComboBox.svelte +276 -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 +127 -54
- package/_svseeds/DarkToggle.svelte.d.ts +38 -18
- package/_svseeds/DateField.svelte +275 -0
- package/_svseeds/DateField.svelte.d.ts +93 -0
- package/_svseeds/DateInput.svelte +350 -0
- package/_svseeds/DateInput.svelte.d.ts +111 -0
- package/_svseeds/Disclosure.svelte +243 -0
- package/_svseeds/Disclosure.svelte.d.ts +76 -0
- package/_svseeds/Drawer.svelte +178 -0
- package/_svseeds/Drawer.svelte.d.ts +61 -0
- package/_svseeds/FileField.svelte +280 -0
- package/_svseeds/FileField.svelte.d.ts +95 -0
- package/_svseeds/FileInput.svelte +271 -0
- package/_svseeds/FileInput.svelte.d.ts +103 -0
- package/_svseeds/HotkeyCapture.svelte +190 -0
- package/_svseeds/HotkeyCapture.svelte.d.ts +74 -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 +123 -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 +210 -0
- package/_svseeds/NumberField.svelte.d.ts +79 -0
- package/_svseeds/NumberInput.svelte +381 -0
- package/_svseeds/NumberInput.svelte.d.ts +85 -0
- package/_svseeds/Pagination.svelte +269 -0
- package/_svseeds/Pagination.svelte.d.ts +81 -0
- package/_svseeds/Popover.svelte +261 -0
- package/_svseeds/Popover.svelte.d.ts +80 -0
- package/_svseeds/ProgressTracker.svelte +124 -0
- package/_svseeds/{_ProgressTracker.svelte.d.ts → ProgressTracker.svelte.d.ts} +23 -12
- package/_svseeds/SelectField.svelte +210 -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 +836 -0
- package/_svseeds/Sortable.svelte.d.ts +169 -0
- package/_svseeds/SortableGroup.svelte +90 -0
- package/_svseeds/SortableGroup.svelte.d.ts +55 -0
- package/_svseeds/Splitter.svelte +194 -0
- package/_svseeds/Splitter.svelte.d.ts +71 -0
- package/_svseeds/Tabs.svelte +233 -0
- package/_svseeds/Tabs.svelte.d.ts +75 -0
- package/_svseeds/TagsInput.svelte +250 -0
- package/_svseeds/TagsInput.svelte.d.ts +103 -0
- package/_svseeds/TagsInputField.svelte +120 -89
- package/_svseeds/TagsInputField.svelte.d.ts +31 -35
- package/_svseeds/TextField.svelte +235 -0
- package/_svseeds/TextField.svelte.d.ts +93 -0
- package/_svseeds/Toast.svelte +322 -0
- package/_svseeds/Toast.svelte.d.ts +109 -0
- package/_svseeds/Toggle.svelte +131 -0
- package/_svseeds/Toggle.svelte.d.ts +57 -0
- package/_svseeds/ToggleGroup.svelte +229 -0
- package/_svseeds/ToggleGroup.svelte.d.ts +79 -0
- package/_svseeds/ToggleGroupField.svelte +150 -78
- package/_svseeds/ToggleGroupField.svelte.d.ts +57 -35
- package/_svseeds/Tooltip.svelte +347 -0
- package/_svseeds/Tooltip.svelte.d.ts +74 -0
- package/_svseeds/WheelPicker.svelte +334 -0
- package/_svseeds/WheelPicker.svelte.d.ts +84 -0
- package/_svseeds/ZSortableA11y.svelte +1082 -0
- package/_svseeds/ZSortableA11y.svelte.d.ts +182 -0
- package/_svseeds/_core.d.ts +275 -0
- package/_svseeds/_core.js +359 -0
- package/index.d.ts +44 -25
- package/index.js +41 -22
- package/package.json +4 -4
- 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,371 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
### Usage
|
|
4
|
+
Use standalone.
|
|
5
|
+
```svelte
|
|
6
|
+
<Calendar {...props} />
|
|
7
|
+
```
|
|
8
|
+
### Types
|
|
9
|
+
default value: *`(value)`*
|
|
10
|
+
```ts
|
|
11
|
+
interface CalendarProps {
|
|
12
|
+
value?: Temporal.PlainDate; // bindable; selected date
|
|
13
|
+
display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
14
|
+
picking?: boolean; // bindable (false)
|
|
15
|
+
min?: Temporal.PlainDate;
|
|
16
|
+
max?: Temporal.PlainDate;
|
|
17
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
18
|
+
outsideDays?: boolean; // (false)
|
|
19
|
+
fixedWeeks?: boolean; // (false)
|
|
20
|
+
firstDayOfWeek?: number; // (0=Sun)
|
|
21
|
+
locale?: string;
|
|
22
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
23
|
+
left?: Snippet<[string]>;
|
|
24
|
+
right?: Snippet<[string]>;
|
|
25
|
+
weekday?: Snippet<[number, string]>;
|
|
26
|
+
day?: Snippet<[DayCtx]>;
|
|
27
|
+
bottom?: Snippet<[string, () => void]>;
|
|
28
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
29
|
+
transition?: TransitionProp;
|
|
30
|
+
styling?: SVSClass;
|
|
31
|
+
variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
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
|
+
```
|
|
47
|
+
### Anatomy
|
|
48
|
+
```svelte
|
|
49
|
+
<div class="whole" role="group">
|
|
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="main" role="grid">
|
|
57
|
+
<div class="aux" role="row">weekday headers</div>
|
|
58
|
+
day cells
|
|
59
|
+
</div>
|
|
60
|
+
MonthPicker while picking
|
|
61
|
+
</div>
|
|
62
|
+
<div class="bottom" conditional>{bottom}</div>
|
|
63
|
+
</div>
|
|
64
|
+
```
|
|
65
|
+
### Behavior
|
|
66
|
+
- Prev/next page `display`; the label toggles an embedded `_MonthPicker`.
|
|
67
|
+
- Enabled day clicks and Enter/Space select a single `Temporal.PlainDate`.
|
|
68
|
+
- Day cells expose `data-today`, `data-selected`, `data-outside`, `data-disabled`, and `data-weekday`.
|
|
69
|
+
- Arrow, Home/End, PageUp/PageDown, and Shift+PageUp/PageDown update roving focus.
|
|
70
|
+
-->
|
|
71
|
+
<script module lang="ts">
|
|
72
|
+
export interface CalendarProps {
|
|
73
|
+
value?: Temporal.PlainDate;
|
|
74
|
+
display?: Temporal.PlainYearMonth;
|
|
75
|
+
picking?: boolean;
|
|
76
|
+
min?: Temporal.PlainDate;
|
|
77
|
+
max?: Temporal.PlainDate;
|
|
78
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
79
|
+
outsideDays?: boolean;
|
|
80
|
+
fixedWeeks?: boolean;
|
|
81
|
+
firstDayOfWeek?: number;
|
|
82
|
+
locale?: string;
|
|
83
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
84
|
+
left?: Snippet<[string]>;
|
|
85
|
+
right?: Snippet<[string]>;
|
|
86
|
+
weekday?: Snippet<[number, string]>;
|
|
87
|
+
day?: Snippet<[DayCtx]>;
|
|
88
|
+
bottom?: Snippet<[string, () => void]>;
|
|
89
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
90
|
+
transition?: TransitionProp;
|
|
91
|
+
styling?: SVSClass;
|
|
92
|
+
variant?: SVSVariant;
|
|
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
|
+
};
|
|
107
|
+
export type CalendarReqdProps = never;
|
|
108
|
+
export type CalendarBindProps = "value" | "display" | "picking";
|
|
109
|
+
|
|
110
|
+
export const _CALENDAR_PRESET = "svs-calendar";
|
|
111
|
+
|
|
112
|
+
import { tick, untrack } from "svelte";
|
|
113
|
+
import { VARIANT, PARTS, _fnClass, shouldReduceMotion } from "./_core";
|
|
114
|
+
import MonthPicker from "./MonthPicker.svelte";
|
|
115
|
+
import type { Snippet } from "svelte";
|
|
116
|
+
import type { KeyboardEventHandler, MouseEventHandler } from "svelte/elements";
|
|
117
|
+
import type { SVSClass, SVSVariant } from "./_core";
|
|
118
|
+
import type { MonthPickerProps } from "./MonthPicker.svelte";
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<script lang="ts">
|
|
122
|
+
// prettier-ignore
|
|
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();
|
|
124
|
+
|
|
125
|
+
// *** Initialize *** //
|
|
126
|
+
const cls = $derived(_fnClass(_CALENDAR_PRESET, styling));
|
|
127
|
+
const uid = $props.id();
|
|
128
|
+
const idCaption = `${uid}-caption`;
|
|
129
|
+
// svelte-ignore state_referenced_locally
|
|
130
|
+
if (display === undefined) display = (value ?? Temporal.Now.plainDateISO()).toPlainYearMonth();
|
|
131
|
+
const today = $derived(Temporal.Now.plainDateISO());
|
|
132
|
+
|
|
133
|
+
// *** States *** //
|
|
134
|
+
const initialDisplay = display as Temporal.PlainYearMonth;
|
|
135
|
+
let focused = $state(value && sameMonth(value, initialDisplay) ? value : firstOf(initialDisplay));
|
|
136
|
+
let cells = $state<Record<string, HTMLElement>>({});
|
|
137
|
+
const firstWeekday = $derived(normWeekday(firstDayOfWeek));
|
|
138
|
+
const monthStart = $derived(firstOf(currentDisplay()));
|
|
139
|
+
const captionText = $derived(monthStart.toLocaleString(locale, { year: "numeric", month: "long" }));
|
|
140
|
+
const appLabel = $derived(`Calendar, ${captionText}`);
|
|
141
|
+
const weekdayLabels = $derived.by(() => {
|
|
142
|
+
const short = new Intl.DateTimeFormat(locale, { weekday: "narrow" });
|
|
143
|
+
const long = new Intl.DateTimeFormat(locale, { weekday: "long" });
|
|
144
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
145
|
+
const weekday = (firstWeekday + i) % 7;
|
|
146
|
+
const d = dateForWeekday(weekday);
|
|
147
|
+
return { weekday, text: short.format(d), full: long.format(d) };
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
const weeks = $derived.by(() => {
|
|
151
|
+
const first = monthStart;
|
|
152
|
+
const lead = (weekdayOf(first) - firstWeekday + 7) % 7;
|
|
153
|
+
const rows = fixedWeeks ? 6 : Math.ceil((lead + first.daysInMonth) / 7);
|
|
154
|
+
const start = first.subtract({ days: lead });
|
|
155
|
+
return Array.from({ length: rows }, (_, r) => Array.from({ length: 7 }, (_, c) => start.add({ days: r * 7 + c })));
|
|
156
|
+
});
|
|
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());
|
|
168
|
+
const tfn = $derived(!reduced && transition?.fn ? transition.fn : noop);
|
|
169
|
+
const tparams = $derived(transition?.params as any);
|
|
170
|
+
|
|
171
|
+
// *** Reactive Handlers *** //
|
|
172
|
+
$effect.pre(() => {
|
|
173
|
+
display;
|
|
174
|
+
value;
|
|
175
|
+
untrack(() => syncFocus());
|
|
176
|
+
});
|
|
177
|
+
$effect.pre(() => {
|
|
178
|
+
cellKeys;
|
|
179
|
+
untrack(() => trimCells(cellKeys));
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
function syncFocus() {
|
|
183
|
+
const shown = currentDisplay();
|
|
184
|
+
if (sameMonth(focused, shown)) return;
|
|
185
|
+
focused = value && sameMonth(value, shown) ? value : firstOf(shown);
|
|
186
|
+
}
|
|
187
|
+
function currentDisplay(): Temporal.PlainYearMonth {
|
|
188
|
+
return display ?? initialDisplay;
|
|
189
|
+
}
|
|
190
|
+
function noop() {
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
function firstOf(ym: Temporal.PlainYearMonth): Temporal.PlainDate {
|
|
194
|
+
return Temporal.PlainDate.from({ year: ym.year, month: ym.month, day: 1 });
|
|
195
|
+
}
|
|
196
|
+
function normWeekday(n: number): number {
|
|
197
|
+
return ((Math.trunc(n) % 7) + 7) % 7;
|
|
198
|
+
}
|
|
199
|
+
function weekdayOf(d: Temporal.PlainDate): number {
|
|
200
|
+
return d.dayOfWeek % 7;
|
|
201
|
+
}
|
|
202
|
+
function sameMonth(d: Temporal.PlainDate, ym: Temporal.PlainYearMonth): boolean {
|
|
203
|
+
return d.year === ym.year && d.month === ym.month;
|
|
204
|
+
}
|
|
205
|
+
function dateForWeekday(weekday: number): Date {
|
|
206
|
+
return new Date(Date.UTC(2023, 0, 1 + weekday));
|
|
207
|
+
}
|
|
208
|
+
function inRange(d: Temporal.PlainDate): boolean {
|
|
209
|
+
if (min && Temporal.PlainDate.compare(d, min) < 0) return false;
|
|
210
|
+
if (max && Temporal.PlainDate.compare(d, max) > 0) return false;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
function disabled(d: Temporal.PlainDate): boolean {
|
|
214
|
+
return !inRange(d) || !!isDisabled?.(d);
|
|
215
|
+
}
|
|
216
|
+
function selected(d: Temporal.PlainDate): boolean {
|
|
217
|
+
return !!value?.equals(d);
|
|
218
|
+
}
|
|
219
|
+
function variantOf(d: Temporal.PlainDate): string {
|
|
220
|
+
if (disabled(d)) return VARIANT.INACTIVE;
|
|
221
|
+
if (selected(d)) return VARIANT.ACTIVE;
|
|
222
|
+
return VARIANT.NEUTRAL;
|
|
223
|
+
}
|
|
224
|
+
function ctx(d: Temporal.PlainDate): DayCtx {
|
|
225
|
+
return {
|
|
226
|
+
date: d,
|
|
227
|
+
variant: variantOf(d),
|
|
228
|
+
weekday: weekdayOf(d),
|
|
229
|
+
today: today.equals(d),
|
|
230
|
+
selected: selected(d),
|
|
231
|
+
outside: !sameMonth(d, currentDisplay()),
|
|
232
|
+
disabled: disabled(d),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function labelOf(d: Temporal.PlainDate): string {
|
|
236
|
+
return d.toLocaleString(locale, { dateStyle: "long" });
|
|
237
|
+
}
|
|
238
|
+
function displayOf(d: Temporal.PlainDate) {
|
|
239
|
+
display = d.toPlainYearMonth();
|
|
240
|
+
}
|
|
241
|
+
function prevMonth() {
|
|
242
|
+
display = currentDisplay().subtract({ months: 1 });
|
|
243
|
+
}
|
|
244
|
+
function nextMonth() {
|
|
245
|
+
display = currentDisplay().add({ months: 1 });
|
|
246
|
+
}
|
|
247
|
+
function select(d: Temporal.PlainDate) {
|
|
248
|
+
if (disabled(d)) return;
|
|
249
|
+
value = d;
|
|
250
|
+
focused = d;
|
|
251
|
+
if (!sameMonth(d, currentDisplay())) displayOf(d);
|
|
252
|
+
}
|
|
253
|
+
function setToday() {
|
|
254
|
+
displayOf(today);
|
|
255
|
+
select(today);
|
|
256
|
+
}
|
|
257
|
+
function move(d: Temporal.PlainDate) {
|
|
258
|
+
focused = d;
|
|
259
|
+
if (!sameMonth(d, currentDisplay())) displayOf(d);
|
|
260
|
+
tick().then(() => focusDate(d));
|
|
261
|
+
}
|
|
262
|
+
function focusDate(d: Temporal.PlainDate) {
|
|
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
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function weekEdge(toEnd: boolean): Temporal.PlainDate {
|
|
271
|
+
const offset = (weekdayOf(focused) - firstWeekday + 7) % 7;
|
|
272
|
+
return focused.add({ days: toEnd ? 6 - offset : -offset });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// *** Event Handlers *** //
|
|
276
|
+
const hprev: MouseEventHandler<HTMLButtonElement> = () => prevMonth();
|
|
277
|
+
const hnext: MouseEventHandler<HTMLButtonElement> = () => nextMonth();
|
|
278
|
+
const hlabel: MouseEventHandler<HTMLButtonElement> = () => (picking = !picking);
|
|
279
|
+
const hkeydown: KeyboardEventHandler<HTMLDivElement> = (ev) => {
|
|
280
|
+
let next: Temporal.PlainDate | undefined;
|
|
281
|
+
if (ev.key === "ArrowLeft") next = focused.subtract({ days: 1 });
|
|
282
|
+
else if (ev.key === "ArrowRight") next = focused.add({ days: 1 });
|
|
283
|
+
else if (ev.key === "ArrowUp") next = focused.subtract({ days: 7 });
|
|
284
|
+
else if (ev.key === "ArrowDown") next = focused.add({ days: 7 });
|
|
285
|
+
else if (ev.key === "Home") next = weekEdge(false);
|
|
286
|
+
else if (ev.key === "End") next = weekEdge(true);
|
|
287
|
+
else if (ev.key === "PageUp") next = focused.subtract(ev.shiftKey ? { years: 1 } : { months: 1 });
|
|
288
|
+
else if (ev.key === "PageDown") next = focused.add(ev.shiftKey ? { years: 1 } : { months: 1 });
|
|
289
|
+
else if (ev.key === "Enter" || ev.key === " ") select(focused);
|
|
290
|
+
else return;
|
|
291
|
+
ev.preventDefault();
|
|
292
|
+
if (next) move(next);
|
|
293
|
+
};
|
|
294
|
+
</script>
|
|
295
|
+
|
|
296
|
+
<!---------------------------------------->
|
|
297
|
+
|
|
298
|
+
<div class={cls(PARTS.WHOLE, variant)} role="group" aria-label={appLabel} data-svs-calendar={uid}>
|
|
299
|
+
<div class={cls(PARTS.TOP, variant)}>
|
|
300
|
+
<button class={cls(PARTS.LEFT, variant)} type="button" onclick={hprev}
|
|
301
|
+
>{#if left}{@render left(variant)}{:else}Previous{/if}</button
|
|
302
|
+
>
|
|
303
|
+
<button id={idCaption} class={cls(PARTS.LABEL, variant)} type="button" aria-expanded={picking} onclick={hlabel}>
|
|
304
|
+
{#if label}{@render label(currentDisplay(), variant, picking)}{:else}{captionText}{/if}
|
|
305
|
+
</button>
|
|
306
|
+
<button class={cls(PARTS.RIGHT, variant)} type="button" onclick={hnext}
|
|
307
|
+
>{#if right}{@render right(variant)}{:else}Next{/if}</button
|
|
308
|
+
>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
{#if picking}
|
|
312
|
+
<div class={cls(PARTS.MIDDLE, variant)} transition:tfn|local={tparams}>
|
|
313
|
+
<MonthPicker
|
|
314
|
+
{...monthPicker}
|
|
315
|
+
bind:value={() => currentDisplay(), (v) => (display = v)}
|
|
316
|
+
min={min?.toPlainYearMonth()}
|
|
317
|
+
max={max?.toPlainYearMonth()}
|
|
318
|
+
{variant}
|
|
319
|
+
/>
|
|
320
|
+
</div>
|
|
321
|
+
{:else}
|
|
322
|
+
<div class={cls(PARTS.MIDDLE, variant)} transition:tfn|local={tparams}>
|
|
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>
|
|
331
|
+
{#each weeks as week}
|
|
332
|
+
<div role="row">
|
|
333
|
+
{#each week as cell}
|
|
334
|
+
{@const outside = !sameMonth(cell, currentDisplay())}
|
|
335
|
+
{#if outside && !outsideDays}
|
|
336
|
+
<span class={cls(PARTS.EXTRA, VARIANT.NEUTRAL)} role="gridcell" aria-hidden="true"></span>
|
|
337
|
+
{:else}
|
|
338
|
+
{@const c = ctx(cell)}
|
|
339
|
+
<button
|
|
340
|
+
class={cls(PARTS.EXTRA, c.variant)}
|
|
341
|
+
type="button"
|
|
342
|
+
role="gridcell"
|
|
343
|
+
tabindex={cell.equals(focused) ? 0 : -1}
|
|
344
|
+
bind:this={cells[cell.toString()]}
|
|
345
|
+
aria-selected={c.selected}
|
|
346
|
+
aria-disabled={c.disabled || undefined}
|
|
347
|
+
aria-current={c.today ? "date" : undefined}
|
|
348
|
+
aria-label={labelOf(cell)}
|
|
349
|
+
data-date={cell.toString()}
|
|
350
|
+
data-today={c.today || undefined}
|
|
351
|
+
data-selected={c.selected || undefined}
|
|
352
|
+
data-outside={c.outside || undefined}
|
|
353
|
+
data-disabled={c.disabled || undefined}
|
|
354
|
+
data-weekday={c.weekday}
|
|
355
|
+
onfocus={() => (focused = cell)}
|
|
356
|
+
onclick={() => select(cell)}
|
|
357
|
+
>
|
|
358
|
+
{#if day}{@render day(c)}{:else}{cell.day}{/if}
|
|
359
|
+
</button>
|
|
360
|
+
{/if}
|
|
361
|
+
{/each}
|
|
362
|
+
</div>
|
|
363
|
+
{/each}
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
{/if}
|
|
367
|
+
|
|
368
|
+
{#if bottom}
|
|
369
|
+
<div class={cls(PARTS.BOTTOM, variant)}>{@render bottom(variant, setToday)}</div>
|
|
370
|
+
{/if}
|
|
371
|
+
</div>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export interface CalendarProps {
|
|
2
|
+
value?: Temporal.PlainDate;
|
|
3
|
+
display?: Temporal.PlainYearMonth;
|
|
4
|
+
picking?: boolean;
|
|
5
|
+
min?: Temporal.PlainDate;
|
|
6
|
+
max?: Temporal.PlainDate;
|
|
7
|
+
isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
8
|
+
outsideDays?: boolean;
|
|
9
|
+
fixedWeeks?: boolean;
|
|
10
|
+
firstDayOfWeek?: number;
|
|
11
|
+
locale?: string;
|
|
12
|
+
label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
13
|
+
left?: Snippet<[string]>;
|
|
14
|
+
right?: Snippet<[string]>;
|
|
15
|
+
weekday?: Snippet<[number, string]>;
|
|
16
|
+
day?: Snippet<[DayCtx]>;
|
|
17
|
+
bottom?: Snippet<[string, () => void]>;
|
|
18
|
+
monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
19
|
+
transition?: TransitionProp;
|
|
20
|
+
styling?: SVSClass;
|
|
21
|
+
variant?: SVSVariant;
|
|
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
|
+
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
|
+
* interface CalendarProps {
|
|
54
|
+
* value?: Temporal.PlainDate; // bindable; selected date
|
|
55
|
+
* display?: Temporal.PlainYearMonth; // bindable; shown month
|
|
56
|
+
* picking?: boolean; // bindable (false)
|
|
57
|
+
* min?: Temporal.PlainDate;
|
|
58
|
+
* max?: Temporal.PlainDate;
|
|
59
|
+
* isDisabled?: (d: Temporal.PlainDate) => boolean;
|
|
60
|
+
* outsideDays?: boolean; // (false)
|
|
61
|
+
* fixedWeeks?: boolean; // (false)
|
|
62
|
+
* firstDayOfWeek?: number; // (0=Sun)
|
|
63
|
+
* locale?: string;
|
|
64
|
+
* label?: Snippet<[Temporal.PlainYearMonth, string, boolean]>;
|
|
65
|
+
* left?: Snippet<[string]>;
|
|
66
|
+
* right?: Snippet<[string]>;
|
|
67
|
+
* weekday?: Snippet<[number, string]>;
|
|
68
|
+
* day?: Snippet<[DayCtx]>;
|
|
69
|
+
* bottom?: Snippet<[string, () => void]>;
|
|
70
|
+
* monthPicker?: Omit<MonthPickerProps, "value" | "min" | "max" | "variant">;
|
|
71
|
+
* transition?: TransitionProp;
|
|
72
|
+
* styling?: SVSClass;
|
|
73
|
+
* variant?: SVSVariant; // (VARIANT.NEUTRAL)
|
|
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
|
+
* ```
|
|
89
|
+
* ### Anatomy
|
|
90
|
+
* ```svelte
|
|
91
|
+
* <div class="whole" role="group">
|
|
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="main" role="grid">
|
|
99
|
+
* <div class="aux" role="row">weekday headers</div>
|
|
100
|
+
* day cells
|
|
101
|
+
* </div>
|
|
102
|
+
* MonthPicker while picking
|
|
103
|
+
* </div>
|
|
104
|
+
* <div class="bottom" conditional>{bottom}</div>
|
|
105
|
+
* </div>
|
|
106
|
+
* ```
|
|
107
|
+
* ### Behavior
|
|
108
|
+
* - Prev/next page `display`; the label toggles an embedded `_MonthPicker`.
|
|
109
|
+
* - Enabled day clicks and Enter/Space select a single `Temporal.PlainDate`.
|
|
110
|
+
* - Day cells expose `data-today`, `data-selected`, `data-outside`, `data-disabled`, and `data-weekday`.
|
|
111
|
+
* - Arrow, Home/End, PageUp/PageDown, and Shift+PageUp/PageDown update roving focus.
|
|
112
|
+
*/
|
|
113
|
+
declare const Calendar: import("svelte").Component<CalendarProps, {}, "display" | "value" | "picking">;
|
|
114
|
+
type Calendar = ReturnType<typeof Calendar>;
|
|
115
|
+
export default Calendar;
|