@human-kit/svelte-components 1.0.0-alpha.2 → 1.0.0-alpha.4
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/FOCUS_STATE_CONTRACT.md +63 -0
- package/dist/FOCUS_STATE_REVIEW_TEMPLATE.md +70 -0
- package/dist/calendar/README.md +2 -1
- package/dist/calendar/TODO.md +21 -107
- package/dist/calendar/body-cell/README.md +15 -0
- package/dist/calendar/body-cell/calendar-body-cell.svelte +116 -41
- package/dist/calendar/grid/README.md +13 -0
- package/dist/calendar/grid-body/README.md +13 -0
- package/dist/calendar/grid-header/README.md +13 -0
- package/dist/calendar/header-cell/README.md +14 -0
- package/dist/calendar/heading/README.md +13 -0
- package/dist/calendar/root/README.md +24 -0
- package/dist/calendar/root/calendar-root-test.svelte +4 -0
- package/dist/calendar/root/calendar-root-test.svelte.d.ts +1 -0
- package/dist/calendar/root/calendar-root.svelte +3 -0
- package/dist/calendar/root/calendar-root.svelte.d.ts +1 -0
- package/dist/calendar/root/context.d.ts +4 -0
- package/dist/calendar/root/context.js +28 -25
- package/dist/calendar/root/date-utils.d.ts +1 -1
- package/dist/calendar/root/date-utils.js +16 -26
- package/dist/calendar/trigger-next/README.md +14 -0
- package/dist/calendar/trigger-previous/README.md +14 -0
- package/dist/clock/README.md +75 -0
- package/dist/clock/axis/README.md +24 -0
- package/dist/clock/axis/clock-axis.svelte +37 -0
- package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
- package/dist/clock/index.d.ts +10 -0
- package/dist/clock/index.js +10 -0
- package/dist/clock/index.parts.d.ts +4 -0
- package/dist/clock/index.parts.js +4 -0
- package/dist/clock/root/README.md +38 -0
- package/dist/clock/root/clock-root-test.svelte +62 -0
- package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
- package/dist/clock/root/clock-root.svelte +329 -0
- package/dist/clock/root/clock-root.svelte.d.ts +25 -0
- package/dist/clock/root/context.d.ts +22 -0
- package/dist/clock/root/context.js +15 -0
- package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
- package/dist/clock/root/resolve-visible-columns.js +16 -0
- package/dist/clock/root/time-utils.d.ts +48 -0
- package/dist/clock/root/time-utils.js +314 -0
- package/dist/clock/root/wheel-options.d.ts +17 -0
- package/dist/clock/root/wheel-options.js +63 -0
- package/dist/clock/wheel-column/README.md +25 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
- package/dist/clock/wheel-item/README.md +17 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
- package/dist/combobox/TODO.md +28 -175
- package/dist/combobox/button/combobox-button.svelte +2 -0
- package/dist/combobox/root/combobox.svelte +30 -0
- package/dist/datepicker/README.md +100 -0
- package/dist/datepicker/TODO.md +28 -0
- package/dist/datepicker/calendar/README.md +19 -0
- package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte +60 -0
- package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte.d.ts +3 -0
- package/dist/datepicker/calendar/date-picker-calendar.svelte +65 -0
- package/dist/datepicker/calendar/date-picker-calendar.svelte.d.ts +10 -0
- package/dist/datepicker/index.d.ts +18 -0
- package/dist/datepicker/index.js +18 -0
- package/dist/datepicker/index.parts.d.ts +14 -0
- package/dist/datepicker/index.parts.js +14 -0
- package/dist/datepicker/input/README.md +15 -0
- package/dist/datepicker/input/date-picker-input.svelte +108 -0
- package/dist/datepicker/input/date-picker-input.svelte.d.ts +11 -0
- package/dist/datepicker/internal/strict-props.d.ts +2 -0
- package/dist/datepicker/internal/strict-props.js +28 -0
- package/dist/datepicker/popover/README.md +20 -0
- package/dist/datepicker/popover/date-picker-popover-handler-test.svelte +57 -0
- package/dist/datepicker/popover/date-picker-popover-handler-test.svelte.d.ts +3 -0
- package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte +45 -0
- package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte.d.ts +18 -0
- package/dist/datepicker/popover/date-picker-popover.svelte +87 -0
- package/dist/datepicker/popover/date-picker-popover.svelte.d.ts +7 -0
- package/dist/datepicker/root/README.md +38 -0
- package/dist/datepicker/root/context.d.ts +43 -0
- package/dist/datepicker/root/context.js +15 -0
- package/dist/datepicker/root/date-picker-bindable-empty-test.svelte +24 -0
- package/dist/datepicker/root/date-picker-bindable-empty-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-bindable-test.svelte +41 -0
- package/dist/datepicker/root/date-picker-bindable-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-empty-test.svelte +47 -0
- package/dist/datepicker/root/date-picker-empty-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-locale-typing-test.svelte +47 -0
- package/dist/datepicker/root/date-picker-locale-typing-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-open-cancel-test.svelte +54 -0
- package/dist/datepicker/root/date-picker-open-cancel-test.svelte.d.ts +8 -0
- package/dist/datepicker/root/date-picker-root.svelte +495 -0
- package/dist/datepicker/root/date-picker-root.svelte.d.ts +24 -0
- package/dist/datepicker/root/date-picker-test.svelte +86 -0
- package/dist/datepicker/root/date-picker-test.svelte.d.ts +13 -0
- package/dist/datepicker/root/date-utils.d.ts +17 -0
- package/dist/datepicker/root/date-utils.js +138 -0
- package/dist/datepicker/root/draft-evaluation.d.ts +13 -0
- package/dist/datepicker/root/draft-evaluation.js +56 -0
- package/dist/datepicker/root/focus-controller.d.ts +3 -0
- package/dist/datepicker/root/focus-controller.js +15 -0
- package/dist/datepicker/root/open-change.d.ts +5 -0
- package/dist/datepicker/root/open-change.js +13 -0
- package/dist/datepicker/root/open-controller.d.ts +7 -0
- package/dist/datepicker/root/open-controller.js +15 -0
- package/dist/datepicker/root/segment-controller.d.ts +8 -0
- package/dist/datepicker/root/segment-controller.js +53 -0
- package/dist/datepicker/root/segment-state.d.ts +18 -0
- package/dist/datepicker/root/segment-state.js +134 -0
- package/dist/datepicker/root/value-commit.d.ts +4 -0
- package/dist/datepicker/root/value-commit.js +8 -0
- package/dist/datepicker/segment/README.md +14 -0
- package/dist/datepicker/segment/date-picker-segment.svelte +319 -0
- package/dist/datepicker/segment/date-picker-segment.svelte.d.ts +9 -0
- package/dist/datepicker/trigger/README.md +14 -0
- package/dist/datepicker/trigger/date-picker-trigger.svelte +110 -0
- package/dist/datepicker/trigger/date-picker-trigger.svelte.d.ts +9 -0
- package/dist/dialog/content/dialog-content.svelte +6 -6
- package/dist/dialog/root/context.d.ts +2 -1
- package/dist/dialog/root/dialog-root.svelte +9 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/listbox/root/listbox.svelte +44 -0
- package/dist/popover/README.md +10 -0
- package/dist/popover/content/popover-content-standalone-test.svelte +28 -0
- package/dist/popover/content/popover-content-standalone-test.svelte.d.ts +6 -0
- package/dist/popover/content/popover-content-test.svelte +2 -1
- package/dist/popover/content/popover-content-test.svelte.d.ts +2 -1
- package/dist/popover/content/popover-content.svelte +91 -18
- package/dist/popover/content/popover-content.svelte.d.ts +5 -1
- package/dist/popover/index.d.ts +1 -1
- package/dist/popover/index.js +1 -3
- package/dist/popover/root/README.md +10 -15
- package/dist/popover/root/context.d.ts +16 -7
- package/dist/popover/root/context.js +0 -2
- package/dist/popover/root/focus-state.d.ts +4 -0
- package/dist/popover/root/focus-state.js +33 -0
- package/dist/popover/root/popover-root.svelte +90 -17
- package/dist/popover/root/popover-root.svelte.d.ts +2 -1
- package/dist/popover/root/popover-test.svelte +2 -1
- package/dist/popover/root/popover-test.svelte.d.ts +2 -1
- package/dist/popover/trigger/popover-trigger-button.svelte +4 -4
- package/dist/popover/trigger/popover-trigger.svelte +1 -1
- package/dist/portal/portal.svelte +3 -1
- package/dist/primitives/click-outside.d.ts +1 -1
- package/dist/primitives/click-outside.js +1 -1
- package/dist/primitives/focus-trap.d.ts +7 -2
- package/dist/primitives/focus-trap.js +50 -17
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/input-modality.d.ts +7 -0
- package/dist/primitives/input-modality.js +125 -0
- package/dist/test-utils/focus-contract.d.ts +3 -0
- package/dist/test-utils/focus-contract.js +26 -0
- package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
- package/dist/timepicker/README.md +97 -0
- package/dist/timepicker/TODO.md +86 -0
- package/dist/timepicker/clock/README.md +14 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
- package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
- package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
- package/dist/timepicker/index.d.ts +14 -0
- package/dist/timepicker/index.js +14 -0
- package/dist/timepicker/index.parts.d.ts +8 -0
- package/dist/timepicker/index.parts.js +8 -0
- package/dist/timepicker/input/README.md +15 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/input/time-picker-input.svelte +109 -0
- package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
- package/dist/timepicker/internal/strict-props.d.ts +4 -0
- package/dist/timepicker/internal/strict-props.js +51 -0
- package/dist/timepicker/popover/README.md +20 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
- package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
- package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
- package/dist/timepicker/root/README.md +42 -0
- package/dist/timepicker/root/context.d.ts +51 -0
- package/dist/timepicker/root/context.js +15 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-root.svelte +625 -0
- package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
- package/dist/timepicker/root/time-picker-test.svelte +72 -0
- package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
- package/dist/timepicker/root/time-utils.d.ts +1 -0
- package/dist/timepicker/root/time-utils.js +3 -0
- package/dist/timepicker/segment/README.md +14 -0
- package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
- package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
- package/dist/timepicker/trigger/README.md +14 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
- package/dist/utils/date-only.d.ts +11 -0
- package/dist/utils/date-only.js +53 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +16 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Focus State Contract
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
Focus-state contract for composed and interactive library components.
|
|
6
|
+
|
|
7
|
+
## Canonical Attributes
|
|
8
|
+
|
|
9
|
+
- `data-focused`: focused interactive element (real focus or logical focus for active item).
|
|
10
|
+
- `data-focus-visible`: visible focus based on keyboard/screen-reader modality.
|
|
11
|
+
- `data-focus-within`: any descendant inside the composed container has focus.
|
|
12
|
+
|
|
13
|
+
## Serialization Rules
|
|
14
|
+
|
|
15
|
+
1. Attributes are serialized as presence (`"true"`) or absence.
|
|
16
|
+
2. Never serialize `"false"`.
|
|
17
|
+
|
|
18
|
+
## Invariants
|
|
19
|
+
|
|
20
|
+
1. On containers, `data-focus-visible` implies `data-focus-within`.
|
|
21
|
+
2. On items, `data-focus-visible` implies `data-focused`.
|
|
22
|
+
3. On external blur (focus leaves scope), clear container `data-focus-within` and `data-focus-visible`.
|
|
23
|
+
|
|
24
|
+
## Modality
|
|
25
|
+
|
|
26
|
+
- Keyboard/SR: may activate `data-focus-visible`.
|
|
27
|
+
- Pointer: should not activate `data-focus-visible` by default.
|
|
28
|
+
|
|
29
|
+
Canonical implementation lives in `primitives/input-modality.ts`:
|
|
30
|
+
|
|
31
|
+
- `trackInteractionModality(event, target)` records input modality transitions.
|
|
32
|
+
- `shouldShowFocusVisible(target)` resolves whether `data-focus-visible` should be shown.
|
|
33
|
+
- `focusWithModality(target, modality)` atomically sets modality + programmatic focus restore.
|
|
34
|
+
- Keep explicit `trackInteractionModality` calls in component keyboard/pointer handlers to guarantee deterministic modality updates before local focus-state logic runs.
|
|
35
|
+
|
|
36
|
+
## Restore focus
|
|
37
|
+
|
|
38
|
+
On overlay/popover close, transient trigger state is allowed:
|
|
39
|
+
|
|
40
|
+
- `escape-key` => `data-focused=true` and `data-focus-visible=true`.
|
|
41
|
+
- `outside-press` => `data-focused=true` and `data-focus-visible` absent.
|
|
42
|
+
|
|
43
|
+
## Recommended Implementation
|
|
44
|
+
|
|
45
|
+
- Native visual baseline: `:focus`, `:focus-visible`, `:focus-within`.
|
|
46
|
+
- Use `data-*` for composed state and restore semantics.
|
|
47
|
+
- Avoid overengineering: centralize minimal synchronization utilities and validate with contract tests.
|
|
48
|
+
|
|
49
|
+
## Operational Template
|
|
50
|
+
|
|
51
|
+
- Use `FOCUS_STATE_REVIEW_TEMPLATE.md` for PR/release reviews (modality matrix + component status + checklist).
|
|
52
|
+
|
|
53
|
+
## Component Coverage
|
|
54
|
+
|
|
55
|
+
The following components implement this contract:
|
|
56
|
+
|
|
57
|
+
- **Popover** — trigger + content, restore focus on close.
|
|
58
|
+
- **Dialog** — trigger + overlay/content, nested stack support.
|
|
59
|
+
- **DatePicker** — segment spinbuttons, trigger, popover (calendar).
|
|
60
|
+
- **TimePicker** — segment spinbuttons, trigger, popover (scrollable columns). Follows the same contract as DatePicker.
|
|
61
|
+
- **Calendar** — grid cells with roving tabindex.
|
|
62
|
+
- **ComboBox** — input + listbox with virtual focus.
|
|
63
|
+
- **ListBox** — items with roving tabindex.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Focus State Review Template
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Template to validate focus-related changes without adding extra CI guardrails.
|
|
6
|
+
Fill this out for PRs that modify interaction, keyboard navigation, overlays, or focus-based styling.
|
|
7
|
+
|
|
8
|
+
## 1) Review Metadata
|
|
9
|
+
|
|
10
|
+
- PR/Branch:
|
|
11
|
+
- Component(s):
|
|
12
|
+
- Reviewer:
|
|
13
|
+
- Date:
|
|
14
|
+
- Verified browsers:
|
|
15
|
+
- [ ] Chromium
|
|
16
|
+
- [ ] Firefox
|
|
17
|
+
- [ ] WebKit/Safari
|
|
18
|
+
|
|
19
|
+
## 2) Minimum Modality Matrix (per component)
|
|
20
|
+
|
|
21
|
+
> Mark **OK/NA/FAIL** and add a short note when failing.
|
|
22
|
+
|
|
23
|
+
| Scenario | What to validate | Status | Note |
|
|
24
|
+
| ---------------------------------- | --------------------------------------------------------------------------- | ------ | ---- |
|
|
25
|
+
| Keyboard (Tab/Arrow/Home/End/Page) | `data-*` and ARIA stay in sync with logical focus | | |
|
|
26
|
+
| Pointer (click/mousedown) | do not elevate `data-focus-visible` by default | | |
|
|
27
|
+
| External blur | transient state cleanup (`data-focus-within`, `data-focus-visible`) | | |
|
|
28
|
+
| Close restore (`escape-key`) | trigger: `data-focused=true`, `data-focus-visible=true` (when applicable) | | |
|
|
29
|
+
| Close restore (`outside-press`) | trigger: `data-focused=true`, `data-focus-visible` absent (when applicable) | | |
|
|
30
|
+
| Programmatic focus | does not break invariants or leave stale state | | |
|
|
31
|
+
|
|
32
|
+
## 3) Required Invariants
|
|
33
|
+
|
|
34
|
+
- [ ] Never serialize `'false'` for `data-focused`, `data-focus-visible`, `data-focus-within`.
|
|
35
|
+
- [ ] On containers: `data-focus-visible => data-focus-within`.
|
|
36
|
+
- [ ] On items: `data-focus-visible => data-focused`.
|
|
37
|
+
- [ ] No visible desync between real focus, logical focus, and `data-*` attributes.
|
|
38
|
+
|
|
39
|
+
## 4) Component Status (living status)
|
|
40
|
+
|
|
41
|
+
> Use one row per component touched or audited.
|
|
42
|
+
|
|
43
|
+
| Component | Status | Current coverage | Residual risk | Owner |
|
|
44
|
+
| ---------- | --------------------------------- | ---------------- | --------------- | ----- |
|
|
45
|
+
| DatePicker | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
46
|
+
| Popover | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
47
|
+
| ListBox | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
48
|
+
| ComboBox | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
49
|
+
| Dialog | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
50
|
+
| Calendar | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
51
|
+
|
|
52
|
+
## 5) PR Checklist
|
|
53
|
+
|
|
54
|
+
- [ ] If focus/keyboard/overlay behavior changed, tests were added or updated.
|
|
55
|
+
- [ ] Shared helper was used: `src/lib/test-utils/focus-contract.ts`.
|
|
56
|
+
- [ ] Focused suites for affected components were run.
|
|
57
|
+
- [ ] If the change was cross-cutting, full package test suite was run.
|
|
58
|
+
|
|
59
|
+
## 6) Suggested Commands
|
|
60
|
+
|
|
61
|
+
- Focused (example):
|
|
62
|
+
- `bun run test -- --run src/lib/datepicker src/lib/popover src/lib/listbox src/lib/combobox src/lib/dialog src/lib/calendar`
|
|
63
|
+
- Full:
|
|
64
|
+
- `bun run test -- --run`
|
|
65
|
+
|
|
66
|
+
## 7) Final Decision
|
|
67
|
+
|
|
68
|
+
- Final status: [ ] Approved [ ] Approved with risk [ ] Blocked
|
|
69
|
+
- Residual risk summary:
|
|
70
|
+
- Follow-up actions:
|
package/dist/calendar/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- In `single` mode, `value/defaultValue` is `YYYY-MM-DD`.
|
|
13
13
|
- In `range` mode, `value/defaultValue` is `{ start?: 'YYYY-MM-DD', end?: 'YYYY-MM-DD' }`.
|
|
14
14
|
- `visibleMonths` controls how many months are rendered and how paging behaves.
|
|
15
|
+
- `showOutsideDays` controls whether days outside the current month are shown; default is `false`.
|
|
15
16
|
- `isDateUnavailable` marks specific days as non-focusable and non-selectable.
|
|
16
17
|
- Use `LocaleProvider` to localize month/day labels and first day of week.
|
|
17
18
|
- Keyboard navigation uses `Arrow` keys for day/week movement and `Home/End` for month edges.
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
|
|
21
22
|
- Each `grid` exposes an accessible name using the visible month heading.
|
|
22
23
|
- Today exposes `aria-current="date"`.
|
|
23
|
-
- Unavailable cells expose `aria-disabled="true"
|
|
24
|
+
- Unavailable cells expose `aria-disabled="true"`. According to ARIA Grid specifications, disabled cells remain focusable so that screen reader users can spatially navigate and discover them, but they are not selectable.
|
|
24
25
|
|
|
25
26
|
### Keyboard
|
|
26
27
|
|
package/dist/calendar/TODO.md
CHANGED
|
@@ -1,109 +1,23 @@
|
|
|
1
1
|
# Calendar TODO
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- [x]
|
|
10
|
-
- [x]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- [x]
|
|
15
|
-
- [ ]
|
|
16
|
-
- [ ]
|
|
17
|
-
- [ ]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Track Calendar work with a single mandatory TODO format.
|
|
6
|
+
|
|
7
|
+
## Backlog
|
|
8
|
+
|
|
9
|
+
- [x] [M][P0][Area: Core API][Owner: Unassigned][Target: Done] Implement controlled/uncontrolled root (`value`, `defaultValue`, `onChange`).
|
|
10
|
+
- [x] [M][P0][Area: Navigation][Owner: Unassigned][Target: Done] Implement paginated navigation via `visibleMonths`.
|
|
11
|
+
- [x] [M][P0][Area: Selection][Owner: Unassigned][Target: Done] Implement single-date selection.
|
|
12
|
+
- [x] [M][P0][Area: State][Owner: Unassigned][Target: Done] Support `isDisabled`, `isReadOnly`, and `isDateUnavailable`.
|
|
13
|
+
- [x] [M][P0][Area: Accessibility][Owner: Unassigned][Target: Done] Deliver keyboard-accessible grid baseline.
|
|
14
|
+
- [x] [M][P0][Area: Delivery][Owner: Unassigned][Target: Done] Ship exports, docs page, and initial tests.
|
|
15
|
+
- [x] [S][P1][Area: Selection][Owner: Unassigned][Target: Done] Implement range selection.
|
|
16
|
+
- [ ] [S][P1][Area: Selection][Owner: Unassigned][Target: TBD] Implement multi-select mode.
|
|
17
|
+
- [ ] [C][P2][Area: Internationalization][Owner: Unassigned][Target: TBD] Add non-Gregorian calendar configuration.
|
|
18
|
+
- [ ] [S][P1][Area: Availability API][Owner: Unassigned][Target: TBD] Add advanced unavailable API (set/ranges/rules).
|
|
19
|
+
- [ ] [M][P0][Area: Availability API][Owner: Unassigned][Target: TBD] Keep backward compatibility with `isDateUnavailable`.
|
|
20
|
+
- [ ] [M][P0][Area: Performance][Owner: Unassigned][Target: TBD] Compile unavailability config to cached predicate with proper invalidation.
|
|
21
|
+
- [ ] [M][P0][Area: Testing][Owner: Unassigned][Target: TBD] Add coverage for unavailable parser, composition rules, and interaction flows.
|
|
22
|
+
- [ ] [S][P1][Area: Documentation][Owner: Unassigned][Target: TBD] Document advanced unavailable API with migration examples.
|
|
23
|
+
- [ ] [C][P2][Area: Demo][Owner: Unassigned][Target: TBD] Extend docs demo with unavailable strategy examples.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Calendar BodyCell
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.BodyCell
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.BodyCell`
|
|
8
|
+
Description: Wrapper part for date grid cells inside `Calendar.GridBody`.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| -------------- | -------------------------------------- | ----------- | --------------------------------------------------------------- |
|
|
12
|
+
| `date` | `string` | `required` | Calendar date value rendered by the cell (`YYYY-MM-DD`). |
|
|
13
|
+
| `children` | `Snippet<[string]>` | `undefined` | Optional custom renderer receiving the day label text. |
|
|
14
|
+
| `class` | `string` | `''` | CSS class names for the inner gridcell element. |
|
|
15
|
+
| `...restProps` | `HTMLAttributes<HTMLTableCellElement>` | `-` | Additional attributes forwarded to the outer table cell (`td`). |
|
|
@@ -1,9 +1,45 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
const ariaDateFormatterCache: Record<string, Intl.DateTimeFormat> = Object.create(null);
|
|
3
|
+
|
|
4
|
+
function getAriaDateFormatter(locale: string): Intl.DateTimeFormat {
|
|
5
|
+
let formatter = ariaDateFormatterCache[locale];
|
|
6
|
+
if (!formatter) {
|
|
7
|
+
formatter = new Intl.DateTimeFormat(locale, {
|
|
8
|
+
dateStyle: 'full',
|
|
9
|
+
timeZone: 'UTC'
|
|
10
|
+
});
|
|
11
|
+
ariaDateFormatterCache[locale] = formatter;
|
|
12
|
+
}
|
|
13
|
+
return formatter;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatAriaDateLabel(locale: string, date: string): string {
|
|
17
|
+
const [yearText, monthText, dayText] = date.split('-');
|
|
18
|
+
const year = Number(yearText);
|
|
19
|
+
const month = Number(monthText);
|
|
20
|
+
const day = Number(dayText);
|
|
21
|
+
|
|
22
|
+
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
23
|
+
return date;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
27
|
+
if (Number.isNaN(parsed.getTime())) return date;
|
|
28
|
+
|
|
29
|
+
return getAriaDateFormatter(locale).format(parsed);
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
1
33
|
<script lang="ts">
|
|
2
34
|
import type { Snippet } from 'svelte';
|
|
3
35
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
36
|
import { useCalendarContext } from '../root/context';
|
|
5
37
|
import { getCalendarMonthIndex } from '../grid/month-scope';
|
|
6
38
|
import { formatCalendarDate, getTodayUtcDate, parseCalendarDate } from '../root/date-utils';
|
|
39
|
+
import {
|
|
40
|
+
shouldShowFocusVisible,
|
|
41
|
+
trackInteractionModality
|
|
42
|
+
} from '../../primitives/input-modality';
|
|
7
43
|
|
|
8
44
|
type CalendarBodyCellProps = Omit<HTMLAttributes<HTMLTableCellElement>, 'children'> & {
|
|
9
45
|
date: string;
|
|
@@ -47,6 +83,11 @@
|
|
|
47
83
|
void $selectionVersion;
|
|
48
84
|
return calendar.focusedValue === date;
|
|
49
85
|
});
|
|
86
|
+
const isFocusVisible = $derived.by(() => {
|
|
87
|
+
void $selectionVersion;
|
|
88
|
+
return calendar.focusVisible;
|
|
89
|
+
});
|
|
90
|
+
const isVisuallyFocused = $derived(isFocused && isFocusVisible);
|
|
50
91
|
const isDisabled = $derived.by(() => {
|
|
51
92
|
void $layoutVersion;
|
|
52
93
|
void $selectionVersion;
|
|
@@ -56,56 +97,76 @@
|
|
|
56
97
|
void $layoutVersion;
|
|
57
98
|
return calendar.isDateUnavailable(date);
|
|
58
99
|
});
|
|
100
|
+
const isAriaDisabled = $derived(isDisabled || isUnavailable);
|
|
59
101
|
const isOutsideMonth = $derived.by(() => {
|
|
60
102
|
void $layoutVersion;
|
|
61
103
|
return calendar.isOutsideVisibleRange(date, monthIndex);
|
|
62
104
|
});
|
|
105
|
+
const showOutsideDays = $derived.by(() => {
|
|
106
|
+
void $layoutVersion;
|
|
107
|
+
return calendar.showOutsideDays;
|
|
108
|
+
});
|
|
109
|
+
const hidesOutsideDay = $derived(isOutsideMonth && !showOutsideDays);
|
|
110
|
+
const isSelectionDisabled = $derived(isDisabled || hidesOutsideDay);
|
|
111
|
+
const isFocusDisabled = $derived(calendar.isDisabled || hidesOutsideDay);
|
|
63
112
|
const todayDate = formatCalendarDate(getTodayUtcDate());
|
|
64
113
|
const isToday = $derived(date === todayDate);
|
|
114
|
+
const ariaDateLabel = $derived.by(() => {
|
|
115
|
+
void $layoutVersion;
|
|
116
|
+
return formatAriaDateLabel(calendar.locale, date);
|
|
117
|
+
});
|
|
65
118
|
|
|
66
|
-
let gridCellElement
|
|
119
|
+
let gridCellElement = $state<HTMLDivElement | undefined>(undefined);
|
|
67
120
|
|
|
68
121
|
$effect(() => {
|
|
69
|
-
if (!isFocused ||
|
|
122
|
+
if (!isFocused || isFocusDisabled) return;
|
|
70
123
|
if (!gridCellElement) return;
|
|
71
124
|
if (document.activeElement === gridCellElement) return;
|
|
72
125
|
gridCellElement.focus();
|
|
73
126
|
});
|
|
74
127
|
|
|
75
128
|
$effect(() => {
|
|
76
|
-
if (!
|
|
129
|
+
if (!isFocusDisabled) return;
|
|
77
130
|
if (!gridCellElement) return;
|
|
78
131
|
if (document.activeElement !== gridCellElement) return;
|
|
79
132
|
gridCellElement.blur();
|
|
80
133
|
});
|
|
81
134
|
|
|
82
135
|
function handleClick() {
|
|
83
|
-
if (
|
|
136
|
+
if (isFocusDisabled) return;
|
|
84
137
|
calendar.setFocusedValue(date);
|
|
85
|
-
|
|
138
|
+
if (!isSelectionDisabled) {
|
|
139
|
+
calendar.selectDate(date);
|
|
140
|
+
}
|
|
86
141
|
}
|
|
87
142
|
|
|
88
143
|
function handleFocus() {
|
|
89
|
-
if (
|
|
144
|
+
if (isFocusDisabled) return;
|
|
90
145
|
calendar.setFocusedValue(date);
|
|
146
|
+
calendar.setFocusVisible(shouldShowFocusVisible(gridCellElement ?? null));
|
|
91
147
|
}
|
|
92
148
|
|
|
93
149
|
function handleMousedown(event: MouseEvent) {
|
|
94
|
-
|
|
95
|
-
|
|
150
|
+
trackInteractionModality(event, gridCellElement ?? null);
|
|
151
|
+
calendar.setFocusVisible(false);
|
|
152
|
+
if (isSelectionDisabled) {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
}
|
|
96
155
|
}
|
|
97
156
|
|
|
98
157
|
function handleMouseenter() {
|
|
99
|
-
if (
|
|
158
|
+
if (isFocusDisabled) return;
|
|
100
159
|
calendar.setHoveredValue(date);
|
|
101
160
|
}
|
|
102
161
|
|
|
103
162
|
function handleMouseleave() {
|
|
104
|
-
if (
|
|
163
|
+
if (isFocusDisabled) return;
|
|
105
164
|
calendar.setHoveredValue(undefined);
|
|
106
165
|
}
|
|
107
166
|
|
|
108
167
|
function handleKeydown(event: KeyboardEvent) {
|
|
168
|
+
if (isFocusDisabled) return;
|
|
169
|
+
trackInteractionModality(event, gridCellElement ?? null);
|
|
109
170
|
calendar.handleCellKeydown(event, date);
|
|
110
171
|
}
|
|
111
172
|
</script>
|
|
@@ -114,7 +175,8 @@
|
|
|
114
175
|
role="presentation"
|
|
115
176
|
data-selected={isSelected || undefined}
|
|
116
177
|
data-focused={isFocused || undefined}
|
|
117
|
-
data-
|
|
178
|
+
data-focus-visible={isVisuallyFocused || undefined}
|
|
179
|
+
data-disabled={isAriaDisabled || undefined}
|
|
118
180
|
data-unavailable={isUnavailable || undefined}
|
|
119
181
|
data-outside-month={isOutsideMonth || undefined}
|
|
120
182
|
data-range-start={isRangeStart || undefined}
|
|
@@ -122,34 +184,47 @@
|
|
|
122
184
|
data-in-range={isInRange || undefined}
|
|
123
185
|
{...restProps}
|
|
124
186
|
>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
187
|
+
{#if hidesOutsideDay}
|
|
188
|
+
<div
|
|
189
|
+
class={className}
|
|
190
|
+
role="presentation"
|
|
191
|
+
data-disabled={true}
|
|
192
|
+
data-outside-month={true}
|
|
193
|
+
aria-hidden="true"
|
|
194
|
+
></div>
|
|
195
|
+
{:else}
|
|
196
|
+
<div
|
|
197
|
+
bind:this={gridCellElement}
|
|
198
|
+
class={className}
|
|
199
|
+
role="gridcell"
|
|
200
|
+
tabindex={isFocusDisabled ? -1 : isFocused ? 0 : -1}
|
|
201
|
+
data-selected={isSelected || undefined}
|
|
202
|
+
data-focused={isFocused || undefined}
|
|
203
|
+
data-focus-visible={isVisuallyFocused || undefined}
|
|
204
|
+
data-disabled={isAriaDisabled || hidesOutsideDay || undefined}
|
|
205
|
+
data-unavailable={isUnavailable || undefined}
|
|
206
|
+
data-outside-month={isOutsideMonth || undefined}
|
|
207
|
+
data-range-start={isRangeStart || undefined}
|
|
208
|
+
data-range-end={isRangeEnd || undefined}
|
|
209
|
+
data-in-range={isInRange || undefined}
|
|
210
|
+
data-date={date}
|
|
211
|
+
aria-selected={isSelected}
|
|
212
|
+
aria-disabled={isAriaDisabled || hidesOutsideDay || undefined}
|
|
213
|
+
aria-current={isToday ? 'date' : undefined}
|
|
214
|
+
aria-label={ariaDateLabel}
|
|
215
|
+
style={isVisuallyFocused ? undefined : 'outline: none;'}
|
|
216
|
+
onmousedown={handleMousedown}
|
|
217
|
+
onmouseenter={handleMouseenter}
|
|
218
|
+
onmouseleave={handleMouseleave}
|
|
219
|
+
onclick={handleClick}
|
|
220
|
+
onfocus={handleFocus}
|
|
221
|
+
onkeydown={handleKeydown}
|
|
222
|
+
>
|
|
223
|
+
{#if children}
|
|
224
|
+
{@render children(date)}
|
|
225
|
+
{:else}
|
|
226
|
+
{dayLabel}
|
|
227
|
+
{/if}
|
|
228
|
+
</div>
|
|
229
|
+
{/if}
|
|
155
230
|
</td>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Calendar Grid
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.Grid
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.Grid`
|
|
8
|
+
Description: Calendar month table container rendered by `Calendar.Root`.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| ---------- | --------- | ----------- | --------------------------------------------- |
|
|
12
|
+
| `children` | `Snippet` | `undefined` | Optional custom month-grid content. |
|
|
13
|
+
| `class` | `string` | `''` | CSS class names for the outer grid container. |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Calendar GridBody
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.GridBody
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.GridBody`
|
|
8
|
+
Description: TBody container for week rows in `Calendar.Grid`.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| ---------- | ------------------- | ----------- | -------------------------------------------------------- |
|
|
12
|
+
| `children` | `Snippet<[string]>` | `undefined` | Optional custom renderer receiving each day date string. |
|
|
13
|
+
| `class` | `string` | `''` | CSS class names for the `tbody` element. |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Calendar GridHeader
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.GridHeader
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.GridHeader`
|
|
8
|
+
Description: Header row group for weekday labels in `Calendar.Grid`.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| ---------- | ------------------- | ----------- | ---------------------------------------------------------------- |
|
|
12
|
+
| `children` | `Snippet<[string]>` | `undefined` | Optional custom renderer receiving each localized weekday label. |
|
|
13
|
+
| `class` | `string` | `''` | CSS class names for the `thead` element. |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Calendar HeaderCell
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.HeaderCell
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.HeaderCell`
|
|
8
|
+
Description: Weekday heading cell part used inside `Calendar.GridHeader`.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| -------------- | -------------------------------------- | ----------- | -------------------------------------------------- |
|
|
12
|
+
| `children` | `Snippet` | `undefined` | Optional custom header cell content. |
|
|
13
|
+
| `class` | `string` | `''` | CSS class names for the header cell element. |
|
|
14
|
+
| `...restProps` | `HTMLAttributes<HTMLTableCellElement>` | `-` | Additional attributes forwarded to the table cell. |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Calendar Heading
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.Heading
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.Heading`
|
|
8
|
+
Description: Label part that displays the currently visible calendar period.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| -------------- | ------------------------------------ | ------- | ------------------------------------------------------- |
|
|
12
|
+
| `class` | `string` | `''` | CSS class names for the heading element (`h2`). |
|
|
13
|
+
| `...restProps` | `HTMLAttributes<HTMLHeadingElement>` | `-` | Additional attributes forwarded to the heading element. |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Calendar Root
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Calendar.Root
|
|
6
|
+
|
|
7
|
+
Name: `Calendar.Root`
|
|
8
|
+
Description: Root state container for date grid rendering, navigation, selection, and locale-aware formatting.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| ------------------- | ---------------------------------------------------------- | ----------- | ------------------------------------------------------------------- |
|
|
12
|
+
| `selectionMode` | `'single' \| 'range'` | `'single'` | Selection behavior and value shape. |
|
|
13
|
+
| `value` | `CalendarDateValue \| CalendarRangeValue \| undefined` | `bindable` | Controlled selected value (single date or `{ start, end }`). |
|
|
14
|
+
| `defaultValue` | `CalendarDateValue \| CalendarRangeValue \| undefined` | `undefined` | Initial value for uncontrolled mode. |
|
|
15
|
+
| `onChange` | `(value: CalendarDateValue \| CalendarRangeValue) => void` | `undefined` | Called when selection changes. |
|
|
16
|
+
| `visibleMonths` | `number` | `1` | Number of month grids rendered simultaneously. |
|
|
17
|
+
| `showOutsideDays` | `boolean` | `false` | Whether days outside the visible month remain rendered/interactive. |
|
|
18
|
+
| `isDateUnavailable` | `(date: string) => boolean` | `undefined` | Marks specific dates as unavailable. |
|
|
19
|
+
| `isDisabled` | `boolean` | `false` | Disables interaction and navigation. |
|
|
20
|
+
| `isReadOnly` | `boolean` | `false` | Keeps navigation while preventing selection updates. |
|
|
21
|
+
| `children` | `Snippet` | `undefined` | Composed `Calendar` parts. |
|
|
22
|
+
| `class` | `string` | `''` | CSS class names for the root wrapper. |
|
|
23
|
+
| `id` | `string` | `undefined` | Optional root id. |
|
|
24
|
+
| `aria-label` | `string` | `undefined` | Accessible label for the root wrapper. |
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
type Props = {
|
|
6
6
|
selectionMode?: CalendarSelectionMode;
|
|
7
7
|
visibleMonths?: number;
|
|
8
|
+
showOutsideDays?: boolean;
|
|
8
9
|
isDisabled?: boolean;
|
|
9
10
|
isReadOnly?: boolean;
|
|
10
11
|
defaultValue?: CalendarValue;
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
let {
|
|
15
16
|
selectionMode = 'single',
|
|
16
17
|
visibleMonths = 1,
|
|
18
|
+
showOutsideDays = false,
|
|
17
19
|
isDisabled = false,
|
|
18
20
|
isReadOnly = false,
|
|
19
21
|
defaultValue,
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
<Calendar.Root
|
|
33
35
|
selectionMode="range"
|
|
34
36
|
{visibleMonths}
|
|
37
|
+
{showOutsideDays}
|
|
35
38
|
{isDisabled}
|
|
36
39
|
{isReadOnly}
|
|
37
40
|
defaultValue={rangeDefaultValue}
|
|
@@ -50,6 +53,7 @@
|
|
|
50
53
|
<Calendar.Root
|
|
51
54
|
selectionMode="single"
|
|
52
55
|
{visibleMonths}
|
|
56
|
+
{showOutsideDays}
|
|
53
57
|
{isDisabled}
|
|
54
58
|
{isReadOnly}
|
|
55
59
|
defaultValue={singleDefaultValue}
|