@human-kit/svelte-components 1.0.0-alpha.1 → 1.0.0-alpha.2
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/dist/calendar/README.md +65 -0
- package/dist/calendar/TODO.md +109 -0
- package/dist/calendar/body-cell/calendar-body-cell.svelte +155 -0
- package/dist/calendar/body-cell/calendar-body-cell.svelte.d.ts +9 -0
- package/dist/calendar/grid/calendar-grid-month-scope.svelte +16 -0
- package/dist/calendar/grid/calendar-grid-month-scope.svelte.d.ts +8 -0
- package/dist/calendar/grid/calendar-grid.svelte +45 -0
- package/dist/calendar/grid/calendar-grid.svelte.d.ts +8 -0
- package/dist/calendar/grid/month-scope.d.ts +2 -0
- package/dist/calendar/grid/month-scope.js +8 -0
- package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte +13 -0
- package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte.d.ts +18 -0
- package/dist/calendar/grid-body/calendar-grid-body.svelte +36 -0
- package/dist/calendar/grid-body/calendar-grid-body.svelte.d.ts +8 -0
- package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte +13 -0
- package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte.d.ts +18 -0
- package/dist/calendar/grid-header/calendar-grid-header.svelte +31 -0
- package/dist/calendar/grid-header/calendar-grid-header.svelte.d.ts +8 -0
- package/dist/calendar/header-cell/calendar-header-cell-test.svelte +11 -0
- package/dist/calendar/header-cell/calendar-header-cell-test.svelte.d.ts +18 -0
- package/dist/calendar/header-cell/calendar-header-cell.svelte +16 -0
- package/dist/calendar/header-cell/calendar-header-cell.svelte.d.ts +8 -0
- package/dist/calendar/heading/calendar-heading.svelte +17 -0
- package/dist/calendar/heading/calendar-heading.svelte.d.ts +5 -0
- package/dist/calendar/index.d.ts +13 -0
- package/dist/calendar/index.js +13 -0
- package/dist/calendar/index.parts.d.ts +9 -0
- package/dist/calendar/index.parts.js +9 -0
- package/dist/calendar/root/calendar-root-bind-value-test.svelte +14 -0
- package/dist/calendar/root/calendar-root-bind-value-test.svelte.d.ts +3 -0
- package/dist/calendar/root/calendar-root-controlled-clear-test.svelte +20 -0
- package/dist/calendar/root/calendar-root-controlled-clear-test.svelte.d.ts +3 -0
- package/dist/calendar/root/calendar-root-test.svelte +67 -0
- package/dist/calendar/root/calendar-root-test.svelte.d.ts +12 -0
- package/dist/calendar/root/calendar-root.svelte +140 -0
- package/dist/calendar/root/calendar-root.svelte.d.ts +30 -0
- package/dist/calendar/root/context.d.ts +62 -0
- package/dist/calendar/root/context.js +724 -0
- package/dist/calendar/root/date-utils.d.ts +17 -0
- package/dist/calendar/root/date-utils.js +104 -0
- package/dist/calendar/trigger-next/calendar-trigger-next.svelte +38 -0
- package/dist/calendar/trigger-next/calendar-trigger-next.svelte.d.ts +8 -0
- package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte +38 -0
- package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte.d.ts +8 -0
- package/dist/combobox/README.md +40 -0
- package/dist/combobox/TODO.md +92 -92
- package/dist/combobox/button/README.md +15 -0
- package/dist/combobox/input/README.md +16 -0
- package/dist/combobox/item/README.md +27 -0
- package/dist/combobox/item-indicator/README.md +15 -0
- package/dist/combobox/list/README.md +27 -0
- package/dist/combobox/popover/README.md +13 -0
- package/dist/combobox/root/README.md +44 -0
- package/dist/combobox/tag/README.md +37 -0
- package/dist/combobox/tag-remove/README.md +14 -0
- package/dist/combobox/tags/README.md +23 -0
- package/dist/dialog/README.md +35 -0
- package/dist/dialog/content/README.md +16 -0
- package/dist/dialog/overlay/README.md +13 -0
- package/dist/dialog/portal/README.md +12 -0
- package/dist/dialog/root/README.md +53 -0
- package/dist/dialog/trigger/README.md +12 -0
- package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte +19 -0
- package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte.d.ts +18 -0
- package/dist/dialog/trigger/dialog-trigger.svelte +18 -6
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/listbox/README.md +26 -0
- package/dist/listbox/item/README.md +24 -0
- package/dist/listbox/root/README.md +40 -0
- package/dist/locale-provider/context.d.ts +8 -0
- package/dist/locale-provider/context.js +18 -0
- package/dist/locale-provider/index.d.ts +4 -0
- package/dist/locale-provider/index.js +4 -0
- package/dist/locale-provider/locale-provider-initial-value-test.svelte +15 -0
- package/dist/locale-provider/locale-provider-initial-value-test.svelte.d.ts +7 -0
- package/dist/locale-provider/locale-provider-test.svelte +20 -0
- package/dist/locale-provider/locale-provider-test.svelte.d.ts +6 -0
- package/dist/locale-provider/locale-provider-value-probe.svelte +22 -0
- package/dist/locale-provider/locale-provider-value-probe.svelte.d.ts +6 -0
- package/dist/locale-provider/locale-provider.svelte +23 -0
- package/dist/locale-provider/locale-provider.svelte.d.ts +8 -0
- package/dist/popover/README.md +32 -0
- package/dist/popover/content/README.md +25 -0
- package/dist/popover/root/README.md +30 -0
- package/dist/popover/trigger/README.md +23 -0
- package/dist/popover/trigger/popover-trigger-button.svelte +6 -3
- package/dist/popover/trigger/popover-trigger-button.svelte.d.ts +2 -3
- package/dist/popover/trigger/popover-trigger-multi-button-test.svelte +16 -0
- package/dist/popover/trigger/popover-trigger-multi-button-test.svelte.d.ts +18 -0
- package/dist/popover/trigger/popover-trigger.svelte +18 -6
- package/package.json +11 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Calendar
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
`Calendar` provides single-date and range selection (ISO `YYYY-MM-DD`) with keyboard navigation, controlled/uncontrolled state, and part-based composition.
|
|
6
|
+
|
|
7
|
+
## Usage Guidelines
|
|
8
|
+
|
|
9
|
+
- Use `Calendar.Root` as the stateful container.
|
|
10
|
+
- `selectionMode` supports `'single'` (default) and `'range'`.
|
|
11
|
+
- In controlled mode, use `value` with `onChange`; in uncontrolled mode, use `defaultValue`.
|
|
12
|
+
- In `single` mode, `value/defaultValue` is `YYYY-MM-DD`.
|
|
13
|
+
- In `range` mode, `value/defaultValue` is `{ start?: 'YYYY-MM-DD', end?: 'YYYY-MM-DD' }`.
|
|
14
|
+
- `visibleMonths` controls how many months are rendered and how paging behaves.
|
|
15
|
+
- `isDateUnavailable` marks specific days as non-focusable and non-selectable.
|
|
16
|
+
- Use `LocaleProvider` to localize month/day labels and first day of week.
|
|
17
|
+
- Keyboard navigation uses `Arrow` keys for day/week movement and `Home/End` for month edges.
|
|
18
|
+
|
|
19
|
+
## Accessibility
|
|
20
|
+
|
|
21
|
+
- Each `grid` exposes an accessible name using the visible month heading.
|
|
22
|
+
- Today exposes `aria-current="date"`.
|
|
23
|
+
- Unavailable cells expose `aria-disabled="true"` and are neither focusable nor selectable.
|
|
24
|
+
|
|
25
|
+
### Keyboard
|
|
26
|
+
|
|
27
|
+
- `ArrowRight/ArrowLeft`: move focus by +/- 1 day.
|
|
28
|
+
- `ArrowDown/ArrowUp`: move focus by +/- 7 days.
|
|
29
|
+
- `Home/End`: move focus to first/last day of month.
|
|
30
|
+
- `PageUp/PageDown`: move to previous/next month while trying to preserve day number.
|
|
31
|
+
- `Enter` or `Space`: select the focused date (if selectable).
|
|
32
|
+
- In `selectionMode="range"`, `Arrow/Page/Home/End` extend preview range while a range is pending.
|
|
33
|
+
- In `selectionMode="range"`, `Enter` or `Space` confirm the preview when pending.
|
|
34
|
+
|
|
35
|
+
## Internal Notes
|
|
36
|
+
|
|
37
|
+
- `PageUp/PageDown` try to preserve the day when crossing months; if no focusable target exists in the destination month and a range is pending, focus falls back to the reachable edge in the current month (aligned with `Home/End` behavior).
|
|
38
|
+
- In `selectionMode="range"`, the first click starts the range (`start`) and the second click confirms it (`end`), with automatic normalization for reversed selection order.
|
|
39
|
+
- In `selectionMode="range"`, hover updates a live preview before confirmation.
|
|
40
|
+
|
|
41
|
+
## Anatomy
|
|
42
|
+
|
|
43
|
+
- `Calendar.Root`
|
|
44
|
+
- `Calendar.TriggerPrevious`
|
|
45
|
+
- `Calendar.Heading`
|
|
46
|
+
- `Calendar.TriggerNext`
|
|
47
|
+
- `Calendar.Grid`
|
|
48
|
+
- `Calendar.GridHeader`
|
|
49
|
+
- `Calendar.HeaderCell`
|
|
50
|
+
- `Calendar.GridBody`
|
|
51
|
+
- `Calendar.BodyCell`
|
|
52
|
+
|
|
53
|
+
```svelte
|
|
54
|
+
<LocaleProvider locale="es-ES">
|
|
55
|
+
<Calendar.Root>
|
|
56
|
+
<Calendar.TriggerPrevious />
|
|
57
|
+
<Calendar.Heading />
|
|
58
|
+
<Calendar.TriggerNext />
|
|
59
|
+
<Calendar.Grid>
|
|
60
|
+
<Calendar.GridHeader />
|
|
61
|
+
<Calendar.GridBody />
|
|
62
|
+
</Calendar.Grid>
|
|
63
|
+
</Calendar.Root>
|
|
64
|
+
</LocaleProvider>
|
|
65
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Calendar TODO
|
|
2
|
+
|
|
3
|
+
## MVP v1
|
|
4
|
+
|
|
5
|
+
- [x] Controlled/uncontrolled root (`value`, `defaultValue`, `onChange`)
|
|
6
|
+
- [x] Paginated navigation via `visibleMonths`
|
|
7
|
+
- [x] Single-date selection
|
|
8
|
+
- [x] `isDisabled`, `isReadOnly`, `isDateUnavailable` states
|
|
9
|
+
- [x] Keyboard-accessible grid (baseline)
|
|
10
|
+
- [x] Exports + docs page + initial tests
|
|
11
|
+
|
|
12
|
+
## Future
|
|
13
|
+
|
|
14
|
+
- [x] Range selection
|
|
15
|
+
- [ ] Multi-select
|
|
16
|
+
- [ ] Non-Gregorian calendar configuration
|
|
17
|
+
- [ ] Advanced unavailable API (ranges/collections)
|
|
18
|
+
|
|
19
|
+
### Advanced unavailable API (details)
|
|
20
|
+
|
|
21
|
+
Goal: evolve from `isDateUnavailable?: (date) => boolean` to an expressive, composable, and performant API for blocking dates in real-world scenarios.
|
|
22
|
+
|
|
23
|
+
#### 1) Functional requirements
|
|
24
|
+
|
|
25
|
+
- [ ] Keep compatibility with the current `isDateUnavailable`.
|
|
26
|
+
- [ ] Support `Set` of specific dates (`YYYY-MM-DD`) with O(1) lookup.
|
|
27
|
+
- [ ] Support closed ranges `{ start, end }`.
|
|
28
|
+
- [ ] Support recurring rules (for example: weekends, weekdays, dayOfWeek, dayOfMonth).
|
|
29
|
+
- [ ] Support rule composition:
|
|
30
|
+
- [ ] `anyOf` (OR)
|
|
31
|
+
- [ ] `allOf` (AND)
|
|
32
|
+
- [ ] `except` (subtract dates/ranges)
|
|
33
|
+
- [ ] Support combined usage (function + set + ranges + recurring rules).
|
|
34
|
+
|
|
35
|
+
#### 2) Public API proposal
|
|
36
|
+
|
|
37
|
+
- [ ] Add optional `unavailable` prop in `Calendar.Root`.
|
|
38
|
+
- [ ] Base type design:
|
|
39
|
+
- [ ] `CalendarDateSet = Set<CalendarDateValue> | CalendarDateValue[]`
|
|
40
|
+
- [ ] `CalendarDateRange = { start: CalendarDateValue; end: CalendarDateValue }`
|
|
41
|
+
- [ ] `CalendarRecurringRule`
|
|
42
|
+
- [ ] `CalendarUnavailableRule = CalendarDateSet | CalendarDateRange | CalendarRecurringRule | ((date) => boolean)`
|
|
43
|
+
- [ ] `CalendarUnavailableConfig = CalendarUnavailableRule | { anyOf?: ...; allOf?: ...; except?: ... }`
|
|
44
|
+
- [ ] Document precedence rules:
|
|
45
|
+
- [ ] `except` first
|
|
46
|
+
- [ ] then `allOf`
|
|
47
|
+
- [ ] then `anyOf`
|
|
48
|
+
- [ ] fallback to `false`
|
|
49
|
+
- [ ] Clearly define how `unavailable` coexists with `isDateUnavailable` (for example: final OR for backward compatibility).
|
|
50
|
+
|
|
51
|
+
#### 3) Validation and normalization
|
|
52
|
+
|
|
53
|
+
- [ ] Validate invalid dates and reversed ranges in a robust way.
|
|
54
|
+
- [ ] Normalize ranges (`start <= end`).
|
|
55
|
+
- [ ] Silently ignore invalid entries or expose a dev warning (decide strategy).
|
|
56
|
+
- [ ] Avoid runtime throws for partial input; prefer tolerant behavior.
|
|
57
|
+
|
|
58
|
+
#### 4) Performance and caching
|
|
59
|
+
|
|
60
|
+
- [ ] Compile `unavailable` to an internal predicate once per config change.
|
|
61
|
+
- [ ] Cache results per visible date (`Map<CalendarDateValue, boolean>`).
|
|
62
|
+
- [ ] Invalidate cache only when these change:
|
|
63
|
+
- [ ] locale
|
|
64
|
+
- [ ] visibleMonths
|
|
65
|
+
- [ ] `isDateUnavailable`
|
|
66
|
+
- [ ] `unavailable`
|
|
67
|
+
- [ ] Avoid expensive recomputation during pending range selection.
|
|
68
|
+
- [ ] Keep low complexity in `isDateDisabled` (prefer amortized O(1)).
|
|
69
|
+
|
|
70
|
+
#### 5) Range mode integration
|
|
71
|
+
|
|
72
|
+
- [ ] `isRangePathSelectable` must use the same unavailable source of truth.
|
|
73
|
+
- [ ] If `except` exists, ensure endpoints and intermediate days follow the final composed rule.
|
|
74
|
+
- [ ] Keep mouse hover preview and keyboard preview consistent.
|
|
75
|
+
|
|
76
|
+
#### 6) Testing
|
|
77
|
+
|
|
78
|
+
- [ ] Unit tests for unavailable parser/normalizer.
|
|
79
|
+
- [ ] Compatibility tests with legacy `isDateUnavailable`.
|
|
80
|
+
- [ ] Tests by type:
|
|
81
|
+
- [ ] date sets
|
|
82
|
+
- [ ] ranges
|
|
83
|
+
- [ ] recurring rules
|
|
84
|
+
- [ ] `anyOf`/`allOf`/`except` composition
|
|
85
|
+
- [ ] Interaction tests (click/keyboard/range preview/confirm/cancel).
|
|
86
|
+
- [ ] Performance tests (no unnecessary recomputation during selection and focus).
|
|
87
|
+
|
|
88
|
+
#### 7) Documentation
|
|
89
|
+
|
|
90
|
+
- [ ] Calendar README section for "Advanced unavailable".
|
|
91
|
+
- [ ] Usage examples:
|
|
92
|
+
- [ ] block weekends
|
|
93
|
+
- [ ] block holidays (set)
|
|
94
|
+
- [ ] blackout by range
|
|
95
|
+
- [ ] composition with exceptions
|
|
96
|
+
- [ ] Migration notes from current `isDateUnavailable`.
|
|
97
|
+
|
|
98
|
+
#### 8) Demo
|
|
99
|
+
|
|
100
|
+
- [ ] Extend docs demo with unavailable strategy selector.
|
|
101
|
+
- [ ] Show derived state (active rule + visible blocked dates).
|
|
102
|
+
- [ ] Include composition example (`anyOf` + `except`).
|
|
103
|
+
|
|
104
|
+
#### 9) Completion criteria
|
|
105
|
+
|
|
106
|
+
- [ ] `bun typecheck` passes without errors.
|
|
107
|
+
- [ ] Calendar test suite is green.
|
|
108
|
+
- [ ] Backward compatibility is verified.
|
|
109
|
+
- [ ] API is documented with real examples.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { useCalendarContext } from '../root/context';
|
|
5
|
+
import { getCalendarMonthIndex } from '../grid/month-scope';
|
|
6
|
+
import { formatCalendarDate, getTodayUtcDate, parseCalendarDate } from '../root/date-utils';
|
|
7
|
+
|
|
8
|
+
type CalendarBodyCellProps = Omit<HTMLAttributes<HTMLTableCellElement>, 'children'> & {
|
|
9
|
+
date: string;
|
|
10
|
+
children?: Snippet<[string]>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let { date, children, class: className = '', ...restProps }: CalendarBodyCellProps = $props();
|
|
14
|
+
|
|
15
|
+
const calendar = useCalendarContext();
|
|
16
|
+
const layoutVersion = calendar.layoutVersion;
|
|
17
|
+
const selectionVersion = calendar.selectionVersion;
|
|
18
|
+
const monthIndex = getCalendarMonthIndex();
|
|
19
|
+
|
|
20
|
+
const parsedDate = $derived(parseCalendarDate(date));
|
|
21
|
+
const dayLabel = $derived(parsedDate ? String(parsedDate.getUTCDate()) : '');
|
|
22
|
+
const isSelected = $derived.by(() => {
|
|
23
|
+
void $layoutVersion;
|
|
24
|
+
void $selectionVersion;
|
|
25
|
+
if (calendar.isDisabled || calendar.isReadOnly) return false;
|
|
26
|
+
return calendar.isSelected(date);
|
|
27
|
+
});
|
|
28
|
+
const isRangeStart = $derived.by(() => {
|
|
29
|
+
void $layoutVersion;
|
|
30
|
+
void $selectionVersion;
|
|
31
|
+
if (calendar.isDisabled || calendar.isReadOnly) return false;
|
|
32
|
+
return calendar.isRangeStart(date);
|
|
33
|
+
});
|
|
34
|
+
const isRangeEnd = $derived.by(() => {
|
|
35
|
+
void $layoutVersion;
|
|
36
|
+
void $selectionVersion;
|
|
37
|
+
if (calendar.isDisabled || calendar.isReadOnly) return false;
|
|
38
|
+
return calendar.isRangeEnd(date);
|
|
39
|
+
});
|
|
40
|
+
const isInRange = $derived.by(() => {
|
|
41
|
+
void $layoutVersion;
|
|
42
|
+
void $selectionVersion;
|
|
43
|
+
if (calendar.isDisabled || calendar.isReadOnly) return false;
|
|
44
|
+
return calendar.isInRange(date);
|
|
45
|
+
});
|
|
46
|
+
const isFocused = $derived.by(() => {
|
|
47
|
+
void $selectionVersion;
|
|
48
|
+
return calendar.focusedValue === date;
|
|
49
|
+
});
|
|
50
|
+
const isDisabled = $derived.by(() => {
|
|
51
|
+
void $layoutVersion;
|
|
52
|
+
void $selectionVersion;
|
|
53
|
+
return calendar.isDateDisabled(date);
|
|
54
|
+
});
|
|
55
|
+
const isUnavailable = $derived.by(() => {
|
|
56
|
+
void $layoutVersion;
|
|
57
|
+
return calendar.isDateUnavailable(date);
|
|
58
|
+
});
|
|
59
|
+
const isOutsideMonth = $derived.by(() => {
|
|
60
|
+
void $layoutVersion;
|
|
61
|
+
return calendar.isOutsideVisibleRange(date, monthIndex);
|
|
62
|
+
});
|
|
63
|
+
const todayDate = formatCalendarDate(getTodayUtcDate());
|
|
64
|
+
const isToday = $derived(date === todayDate);
|
|
65
|
+
|
|
66
|
+
let gridCellElement: HTMLDivElement | undefined;
|
|
67
|
+
|
|
68
|
+
$effect(() => {
|
|
69
|
+
if (!isFocused || isDisabled) return;
|
|
70
|
+
if (!gridCellElement) return;
|
|
71
|
+
if (document.activeElement === gridCellElement) return;
|
|
72
|
+
gridCellElement.focus();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
$effect(() => {
|
|
76
|
+
if (!isDisabled) return;
|
|
77
|
+
if (!gridCellElement) return;
|
|
78
|
+
if (document.activeElement !== gridCellElement) return;
|
|
79
|
+
gridCellElement.blur();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
function handleClick() {
|
|
83
|
+
if (isDisabled) return;
|
|
84
|
+
calendar.setFocusedValue(date);
|
|
85
|
+
calendar.selectDate(date);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleFocus() {
|
|
89
|
+
if (isDisabled) return;
|
|
90
|
+
calendar.setFocusedValue(date);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function handleMousedown(event: MouseEvent) {
|
|
94
|
+
if (!isDisabled) return;
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleMouseenter() {
|
|
99
|
+
if (isDisabled) return;
|
|
100
|
+
calendar.setHoveredValue(date);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function handleMouseleave() {
|
|
104
|
+
if (isDisabled) return;
|
|
105
|
+
calendar.setHoveredValue(undefined);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
109
|
+
calendar.handleCellKeydown(event, date);
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<td
|
|
114
|
+
role="presentation"
|
|
115
|
+
data-selected={isSelected || undefined}
|
|
116
|
+
data-focused={isFocused || undefined}
|
|
117
|
+
data-disabled={isDisabled || undefined}
|
|
118
|
+
data-unavailable={isUnavailable || undefined}
|
|
119
|
+
data-outside-month={isOutsideMonth || undefined}
|
|
120
|
+
data-range-start={isRangeStart || undefined}
|
|
121
|
+
data-range-end={isRangeEnd || undefined}
|
|
122
|
+
data-in-range={isInRange || undefined}
|
|
123
|
+
{...restProps}
|
|
124
|
+
>
|
|
125
|
+
<div
|
|
126
|
+
bind:this={gridCellElement}
|
|
127
|
+
class={className}
|
|
128
|
+
role="gridcell"
|
|
129
|
+
tabindex={isDisabled ? -1 : isFocused ? 0 : -1}
|
|
130
|
+
data-selected={isSelected || undefined}
|
|
131
|
+
data-focused={isFocused || undefined}
|
|
132
|
+
data-disabled={isDisabled || undefined}
|
|
133
|
+
data-unavailable={isUnavailable || undefined}
|
|
134
|
+
data-outside-month={isOutsideMonth || undefined}
|
|
135
|
+
data-range-start={isRangeStart || undefined}
|
|
136
|
+
data-range-end={isRangeEnd || undefined}
|
|
137
|
+
data-in-range={isInRange || undefined}
|
|
138
|
+
aria-selected={isSelected}
|
|
139
|
+
aria-disabled={isDisabled || undefined}
|
|
140
|
+
aria-current={isToday ? 'date' : undefined}
|
|
141
|
+
aria-label={date}
|
|
142
|
+
onmousedown={handleMousedown}
|
|
143
|
+
onmouseenter={handleMouseenter}
|
|
144
|
+
onmouseleave={handleMouseleave}
|
|
145
|
+
onclick={handleClick}
|
|
146
|
+
onfocus={handleFocus}
|
|
147
|
+
onkeydown={handleKeydown}
|
|
148
|
+
>
|
|
149
|
+
{#if children}
|
|
150
|
+
{@render children(date)}
|
|
151
|
+
{:else}
|
|
152
|
+
{dayLabel}
|
|
153
|
+
{/if}
|
|
154
|
+
</div>
|
|
155
|
+
</td>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type CalendarBodyCellProps = Omit<HTMLAttributes<HTMLTableCellElement>, 'children'> & {
|
|
4
|
+
date: string;
|
|
5
|
+
children?: Snippet<[string]>;
|
|
6
|
+
};
|
|
7
|
+
declare const CalendarBodyCell: import("svelte").Component<CalendarBodyCellProps, {}, "">;
|
|
8
|
+
type CalendarBodyCell = ReturnType<typeof CalendarBodyCell>;
|
|
9
|
+
export default CalendarBodyCell;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { setCalendarMonthIndex } from './month-scope';
|
|
4
|
+
|
|
5
|
+
type CalendarGridMonthScopeProps = {
|
|
6
|
+
monthIndex: number;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const props: CalendarGridMonthScopeProps = $props();
|
|
11
|
+
setCalendarMonthIndex(props.monthIndex);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
{#if props.children}
|
|
15
|
+
{@render props.children()}
|
|
16
|
+
{/if}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type CalendarGridMonthScopeProps = {
|
|
3
|
+
monthIndex: number;
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
};
|
|
6
|
+
declare const CalendarGridMonthScope: import("svelte").Component<CalendarGridMonthScopeProps, {}, "">;
|
|
7
|
+
type CalendarGridMonthScope = ReturnType<typeof CalendarGridMonthScope>;
|
|
8
|
+
export default CalendarGridMonthScope;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { useCalendarContext } from '../root/context';
|
|
4
|
+
import CalendarGridMonthScope from './calendar-grid-month-scope.svelte';
|
|
5
|
+
|
|
6
|
+
type CalendarGridProps = {
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let { children, class: className = '' }: CalendarGridProps = $props();
|
|
12
|
+
const calendar = useCalendarContext();
|
|
13
|
+
const layoutVersion = calendar.layoutVersion;
|
|
14
|
+
const months = $derived.by(() => {
|
|
15
|
+
void $layoutVersion;
|
|
16
|
+
return calendar.months;
|
|
17
|
+
});
|
|
18
|
+
const selectionMode = $derived.by(() => {
|
|
19
|
+
void $layoutVersion;
|
|
20
|
+
return calendar.selectionMode;
|
|
21
|
+
});
|
|
22
|
+
const isReadOnly = $derived.by(() => {
|
|
23
|
+
void $layoutVersion;
|
|
24
|
+
return calendar.isReadOnly;
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class={className} data-calendar-grid>
|
|
29
|
+
{#each months as month (month.monthIndex)}
|
|
30
|
+
<div role="group" aria-label={month.heading} data-calendar-month={month.monthIndex}>
|
|
31
|
+
<CalendarGridMonthScope monthIndex={month.monthIndex}>
|
|
32
|
+
<table
|
|
33
|
+
role="grid"
|
|
34
|
+
aria-label={month.heading}
|
|
35
|
+
aria-readonly={isReadOnly || undefined}
|
|
36
|
+
aria-multiselectable={selectionMode === 'range' ? true : undefined}
|
|
37
|
+
>
|
|
38
|
+
{#if children}
|
|
39
|
+
{@render children()}
|
|
40
|
+
{/if}
|
|
41
|
+
</table>
|
|
42
|
+
</CalendarGridMonthScope>
|
|
43
|
+
</div>
|
|
44
|
+
{/each}
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type CalendarGridProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
class?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const CalendarGrid: import("svelte").Component<CalendarGridProps, {}, "">;
|
|
7
|
+
type CalendarGrid = ReturnType<typeof CalendarGrid>;
|
|
8
|
+
export default CalendarGrid;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
const MONTH_SCOPE_KEY = Symbol('calendar-month-scope');
|
|
3
|
+
export function setCalendarMonthIndex(monthIndex) {
|
|
4
|
+
setContext(MONTH_SCOPE_KEY, monthIndex);
|
|
5
|
+
}
|
|
6
|
+
export function getCalendarMonthIndex() {
|
|
7
|
+
return getContext(MONTH_SCOPE_KEY) ?? 0;
|
|
8
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Calendar from '../index';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<Calendar.Root defaultValue="2026-02-10" aria-label="Body custom test">
|
|
6
|
+
<Calendar.Grid>
|
|
7
|
+
<Calendar.GridBody>
|
|
8
|
+
{#snippet children(date)}
|
|
9
|
+
<Calendar.BodyCell {date}>{date}</Calendar.BodyCell>
|
|
10
|
+
{/snippet}
|
|
11
|
+
</Calendar.GridBody>
|
|
12
|
+
</Calendar.Grid>
|
|
13
|
+
</Calendar.Root>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const CalendarGridBodyCustomTest: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type CalendarGridBodyCustomTest = InstanceType<typeof CalendarGridBodyCustomTest>;
|
|
18
|
+
export default CalendarGridBodyCustomTest;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { useCalendarContext } from '../root/context';
|
|
4
|
+
import { getCalendarMonthIndex } from '../grid/month-scope';
|
|
5
|
+
import CalendarBodyCell from '../body-cell/calendar-body-cell.svelte';
|
|
6
|
+
|
|
7
|
+
type CalendarGridBodyProps = {
|
|
8
|
+
children?: Snippet<[string]>;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let { children, class: className = '' }: CalendarGridBodyProps = $props();
|
|
13
|
+
|
|
14
|
+
const calendar = useCalendarContext();
|
|
15
|
+
const layoutVersion = calendar.layoutVersion;
|
|
16
|
+
const monthIndex = getCalendarMonthIndex();
|
|
17
|
+
const month = $derived.by(() => {
|
|
18
|
+
void $layoutVersion;
|
|
19
|
+
return calendar.months[monthIndex];
|
|
20
|
+
});
|
|
21
|
+
const weeks = $derived(month?.weeks ?? []);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<tbody class={className}>
|
|
25
|
+
{#each weeks as week, weekIndex (weekIndex)}
|
|
26
|
+
<tr data-week={weekIndex}>
|
|
27
|
+
{#each week as day (day.date)}
|
|
28
|
+
{#if children}
|
|
29
|
+
{@render children(day.date)}
|
|
30
|
+
{:else}
|
|
31
|
+
<CalendarBodyCell date={day.date} />
|
|
32
|
+
{/if}
|
|
33
|
+
{/each}
|
|
34
|
+
</tr>
|
|
35
|
+
{/each}
|
|
36
|
+
</tbody>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type CalendarGridBodyProps = {
|
|
3
|
+
children?: Snippet<[string]>;
|
|
4
|
+
class?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const CalendarGridBody: import("svelte").Component<CalendarGridBodyProps, {}, "">;
|
|
7
|
+
type CalendarGridBody = ReturnType<typeof CalendarGridBody>;
|
|
8
|
+
export default CalendarGridBody;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Calendar from '../index';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<Calendar.Root defaultValue="2026-02-10" aria-label="Header custom test">
|
|
6
|
+
<Calendar.Grid>
|
|
7
|
+
<Calendar.GridHeader>
|
|
8
|
+
{#snippet children(day)}
|
|
9
|
+
<Calendar.HeaderCell>DAY-{day}</Calendar.HeaderCell>
|
|
10
|
+
{/snippet}
|
|
11
|
+
</Calendar.GridHeader>
|
|
12
|
+
</Calendar.Grid>
|
|
13
|
+
</Calendar.Root>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const CalendarGridHeaderCustomTest: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type CalendarGridHeaderCustomTest = InstanceType<typeof CalendarGridHeaderCustomTest>;
|
|
18
|
+
export default CalendarGridHeaderCustomTest;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { useCalendarContext } from '../root/context';
|
|
4
|
+
import CalendarHeaderCell from '../header-cell/calendar-header-cell.svelte';
|
|
5
|
+
|
|
6
|
+
type CalendarGridHeaderProps = {
|
|
7
|
+
children?: Snippet<[string]>;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let { children, class: className = '' }: CalendarGridHeaderProps = $props();
|
|
12
|
+
|
|
13
|
+
const calendar = useCalendarContext();
|
|
14
|
+
const layoutVersion = calendar.layoutVersion;
|
|
15
|
+
const weekdays = $derived.by(() => {
|
|
16
|
+
void $layoutVersion;
|
|
17
|
+
return calendar.weekdayLabels;
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<thead class={className}>
|
|
22
|
+
<tr>
|
|
23
|
+
{#each weekdays as day (day)}
|
|
24
|
+
{#if children}
|
|
25
|
+
{@render children(day)}
|
|
26
|
+
{:else}
|
|
27
|
+
<CalendarHeaderCell>{day}</CalendarHeaderCell>
|
|
28
|
+
{/if}
|
|
29
|
+
{/each}
|
|
30
|
+
</tr>
|
|
31
|
+
</thead>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type CalendarGridHeaderProps = {
|
|
3
|
+
children?: Snippet<[string]>;
|
|
4
|
+
class?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const CalendarGridHeader: import("svelte").Component<CalendarGridHeaderProps, {}, "">;
|
|
7
|
+
type CalendarGridHeader = ReturnType<typeof CalendarGridHeader>;
|
|
8
|
+
export default CalendarGridHeader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const CalendarHeaderCellTest: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type CalendarHeaderCellTest = InstanceType<typeof CalendarHeaderCellTest>;
|
|
18
|
+
export default CalendarHeaderCellTest;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
type CalendarHeaderCellProps = Omit<HTMLAttributes<HTMLTableCellElement>, 'children'> & {
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let { children, class: className = '', ...restProps }: CalendarHeaderCellProps = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<th role="columnheader" class={className} {...restProps}>
|
|
13
|
+
{#if children}
|
|
14
|
+
{@render children()}
|
|
15
|
+
{/if}
|
|
16
|
+
</th>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type CalendarHeaderCellProps = Omit<HTMLAttributes<HTMLTableCellElement>, 'children'> & {
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
};
|
|
6
|
+
declare const CalendarHeaderCell: import("svelte").Component<CalendarHeaderCellProps, {}, "">;
|
|
7
|
+
type CalendarHeaderCell = ReturnType<typeof CalendarHeaderCell>;
|
|
8
|
+
export default CalendarHeaderCell;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { useCalendarContext } from '../root/context';
|
|
4
|
+
|
|
5
|
+
type CalendarHeadingProps = Omit<HTMLAttributes<HTMLHeadingElement>, 'children'>;
|
|
6
|
+
|
|
7
|
+
let { class: className = '', ...restProps }: CalendarHeadingProps = $props();
|
|
8
|
+
|
|
9
|
+
const calendar = useCalendarContext();
|
|
10
|
+
const layoutVersion = calendar.layoutVersion;
|
|
11
|
+
const heading = $derived.by(() => {
|
|
12
|
+
void $layoutVersion;
|
|
13
|
+
return calendar.headingLabel;
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<h2 class={className} aria-live="polite" {...restProps}>{heading}</h2>
|