@sectile/dom 0.4.0 → 0.6.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.
Files changed (71) hide show
  1. package/README.md +8 -0
  2. package/dist/calendar.d.ts +38 -88
  3. package/dist/calendar.d.ts.map +1 -1
  4. package/dist/calendar.js +160 -267
  5. package/dist/calendar.js.map +1 -1
  6. package/dist/date-field.d.ts +6 -6
  7. package/dist/date-field.d.ts.map +1 -1
  8. package/dist/date-field.js +6 -6
  9. package/dist/date-field.js.map +1 -1
  10. package/dist/date-picker.d.ts +11 -9
  11. package/dist/date-picker.d.ts.map +1 -1
  12. package/dist/date-picker.js +14 -12
  13. package/dist/date-picker.js.map +1 -1
  14. package/dist/date-range-field.d.ts +7 -7
  15. package/dist/date-range-field.d.ts.map +1 -1
  16. package/dist/date-range-field.js +7 -7
  17. package/dist/date-range-field.js.map +1 -1
  18. package/dist/date-range-picker.d.ts +10 -8
  19. package/dist/date-range-picker.d.ts.map +1 -1
  20. package/dist/date-range-picker.js +12 -11
  21. package/dist/date-range-picker.js.map +1 -1
  22. package/dist/date-time-field.d.ts +6 -6
  23. package/dist/date-time-field.d.ts.map +1 -1
  24. package/dist/date-time-field.js +6 -6
  25. package/dist/date-time-field.js.map +1 -1
  26. package/dist/date-time-picker.d.ts +10 -9
  27. package/dist/date-time-picker.d.ts.map +1 -1
  28. package/dist/date-time-picker.js +15 -13
  29. package/dist/date-time-picker.js.map +1 -1
  30. package/dist/date-time-range-picker.d.ts +10 -9
  31. package/dist/date-time-range-picker.d.ts.map +1 -1
  32. package/dist/date-time-range-picker.js +15 -13
  33. package/dist/date-time-range-picker.js.map +1 -1
  34. package/dist/index.d.ts +3 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal/floating-position.d.ts.map +1 -1
  39. package/dist/internal/floating-position.js +6 -2
  40. package/dist/internal/floating-position.js.map +1 -1
  41. package/dist/internal/reference-date.d.ts +3 -0
  42. package/dist/internal/reference-date.d.ts.map +1 -0
  43. package/dist/internal/reference-date.js +8 -0
  44. package/dist/internal/reference-date.js.map +1 -0
  45. package/dist/internal/result.d.ts +19 -0
  46. package/dist/internal/result.d.ts.map +1 -0
  47. package/dist/internal/result.js +8 -0
  48. package/dist/internal/result.js.map +1 -0
  49. package/dist/month-picker.d.ts +2 -2
  50. package/dist/month-picker.d.ts.map +1 -1
  51. package/dist/month-picker.js +1 -1
  52. package/dist/month-picker.js.map +1 -1
  53. package/dist/month-range-picker.d.ts +1 -1
  54. package/dist/month-range-picker.d.ts.map +1 -1
  55. package/dist/month-range-picker.js +1 -1
  56. package/dist/month-range-picker.js.map +1 -1
  57. package/dist/reorder.d.ts.map +1 -1
  58. package/dist/reorder.js.map +1 -1
  59. package/dist/time-field.d.ts +6 -6
  60. package/dist/time-field.d.ts.map +1 -1
  61. package/dist/time-field.js +6 -6
  62. package/dist/time-field.js.map +1 -1
  63. package/dist/time-range-field.d.ts +7 -7
  64. package/dist/time-range-field.d.ts.map +1 -1
  65. package/dist/time-range-field.js +6 -6
  66. package/dist/time-range-field.js.map +1 -1
  67. package/dist/virtual.d.ts +58 -0
  68. package/dist/virtual.d.ts.map +1 -0
  69. package/dist/virtual.js +322 -0
  70. package/dist/virtual.js.map +1 -0
  71. package/package.json +9 -2
package/README.md CHANGED
@@ -13,6 +13,14 @@ DOM bindings for Sectile interaction semantics.
13
13
 
14
14
  The package does not provide markup, components, styles, themes, or application data. Consumers choose the rendered structure and bind it to a Sectile connection.
15
15
 
16
+ ## Virtualization
17
+
18
+ `@sectile/dom/virtual` connects the renderer-neutral `@sectile/virtual` strategies
19
+ to a scroll element. It coalesces scroll and resize work per animation frame,
20
+ batches item measurements, applies anchor correction before publishing the next
21
+ plan, and exposes manual measurement and mutation operations for track grids and
22
+ other non-linear layouts. Content and item style helpers remain opt-in.
23
+
16
24
  ## Floating surface positioning
17
25
 
18
26
  Popover and tooltip placement use Floating UI. Offset, collision flip and shift, available-size data, arrow positioning, detached-anchor hiding, and open-only automatic updates are enabled by default. Boundaries, padding, strategy, update observers, and the middleware queue remain configurable. Floating UI middleware is re-exported from both component entry points for custom positioning.
@@ -1,98 +1,48 @@
1
1
  import { type FacadeConnection } from '@sectile/core/adapter-runtime';
2
- import type { Result, StableID } from '@sectile/core';
3
- import { type CalendarCommand, type CalendarEvent, type CalendarPolicies, type CalendarState } from '@sectile/core/calendar';
4
- export type { CalendarPolicies } from '@sectile/core/calendar';
5
- import { type Grid } from '@sectile/core/grid';
6
- import { type RevisionResult, type RevisionSnapshot } from '@sectile/core/revision';
7
- export interface KeyboardInput {
8
- readonly key: string;
9
- readonly altKey?: boolean;
10
- readonly ctrlKey?: boolean;
11
- readonly metaKey?: boolean;
12
- }
13
- export type CalendarEffect<ID extends StableID = StableID> = {
14
- readonly type: 'focus-element';
15
- readonly id: ID;
16
- } | {
17
- readonly type: 'request-page';
18
- readonly direction: -1 | 1;
19
- readonly from: ID | null;
20
- };
21
- export interface CalendarValueChangeDetails<ID extends StableID = StableID> {
22
- readonly value: ID | null;
23
- readonly previousValue: ID | null;
24
- }
25
- export interface CalendarHighlightChangeDetails<ID extends StableID = StableID> {
26
- readonly value: ID | null;
27
- readonly previousValue: ID | null;
28
- }
29
- export interface CalendarControllerOptions<ID extends StableID = StableID> {
30
- readonly grid: Grid<ID>;
31
- readonly disabled?: boolean;
32
- readonly policies?: CalendarPolicies<ID>;
33
- readonly value?: ID | null;
34
- readonly defaultValue?: ID | null;
35
- readonly highlightedValue?: ID | null;
36
- readonly defaultHighlightedValue?: ID | null;
37
- readonly onValueChange?: (change: CalendarValueChangeDetails<ID>) => void;
38
- readonly onHighlightedValueChange?: (change: CalendarHighlightChangeDetails<ID>) => void;
39
- }
40
- export type CalendarControllerValueChangeHandler<ID extends StableID = StableID> = NonNullable<CalendarControllerOptions<ID>['onValueChange']>;
41
- export type CalendarControllerHighlightedValueChangeHandler<ID extends StableID = StableID> = NonNullable<CalendarControllerOptions<ID>['onHighlightedValueChange']>;
42
- export interface CalendarControlledValues<ID extends StableID = StableID> {
43
- readonly value?: ID | null;
44
- readonly highlightedValue?: ID | null;
45
- }
46
- export interface CalendarController<ID extends StableID = StableID> {
47
- readonly grid: Grid<ID>;
48
- getSnapshot(): RevisionSnapshot<CalendarState<ID>>;
49
- syncControlledValues(values: CalendarControlledValues<ID>): Result<RevisionSnapshot<CalendarState<ID>>>;
50
- handleKeyboardInput(input: KeyboardInput, expectedRevision?: number): RevisionResult<CalendarState<ID>, CalendarEffect<ID>>;
51
- handleEvent(event: CalendarEvent<ID>, expectedRevision?: number): RevisionResult<CalendarState<ID>, CalendarEffect<ID>>;
52
- }
53
- export interface CalendarPageRequestDetails<ID extends StableID = StableID> {
54
- readonly direction: -1 | 1;
55
- readonly from: ID | null;
56
- }
57
- export interface CalendarTransitionDetails<ID extends StableID = StableID> {
58
- readonly event: CalendarEvent<ID>;
59
- readonly result: RevisionResult<CalendarState<ID>, CalendarEffect<ID>>;
60
- }
61
- export interface CalendarConnectionOptions<ID extends StableID = StableID> {
62
- readonly controller: CalendarController<ID>;
2
+ import { type DOMTemporalResult } from './internal/result.js';
3
+ import type { RevisionSnapshot } from '@sectile/core/revision';
4
+ import { type CalendarEvent, type CalendarMonthValue, type CalendarPolicies, type CalendarState } from '@sectile/temporal/calendar';
5
+ import { type DateValue } from '@sectile/temporal/date-field';
6
+ export type { CalendarCommand, CalendarEvent, CalendarMonthValue, CalendarPolicies, CalendarState, CalendarView, CalendarViewMode, } from '@sectile/temporal/calendar';
7
+ export type { DateValue } from '@sectile/temporal/date-field';
8
+ export interface CalendarOptions {
63
9
  readonly root: HTMLElement;
10
+ readonly grid?: HTMLElement;
11
+ readonly policies?: CalendarPolicies;
12
+ readonly value?: DateValue | null;
13
+ readonly defaultValue?: DateValue | null;
14
+ readonly highlightedValue?: DateValue;
15
+ readonly defaultHighlightedValue?: DateValue;
16
+ readonly referenceDate?: DateValue;
64
17
  readonly disabled?: boolean;
65
- readonly onPageRequest?: (details: CalendarPageRequestDetails<ID>) => void;
66
- readonly onTransition?: (details: CalendarTransitionDetails<ID>) => void;
18
+ readonly readOnly?: boolean;
19
+ readonly required?: boolean;
20
+ readonly label?: string;
21
+ readonly onValueChange?: (value: DateValue | null) => void;
22
+ readonly onHighlightedValueChange?: (value: DateValue) => void;
67
23
  readonly onUpdate?: () => void;
68
24
  }
69
- export type CalendarConnectionPageRequestHandler<ID extends StableID = StableID> = NonNullable<CalendarConnectionOptions<ID>['onPageRequest']>;
70
- export type CalendarConnectionTransitionHandler<ID extends StableID = StableID> = NonNullable<CalendarConnectionOptions<ID>['onTransition']>;
71
- export type CalendarConnectionUpdateHandler<ID extends StableID = StableID> = NonNullable<CalendarConnectionOptions<ID>['onUpdate']>;
72
- export interface CalendarCellAttributes<ID extends StableID = StableID> {
73
- readonly id: ID;
74
- readonly rowIndex: number;
75
- readonly columnIndex: number;
76
- readonly disabled?: boolean;
77
- }
78
- export interface CalendarConnection<ID extends StableID = StableID> {
79
- readonly grid: Grid<ID>;
80
- getSnapshot(): RevisionSnapshot<CalendarState<ID>>;
81
- syncControlledValues(values: CalendarControlledValues<ID>): Result<RevisionSnapshot<CalendarState<ID>>>;
82
- setCalendarAttributes(label?: string): void;
83
- setCellAttributes(element: HTMLElement, attributes: CalendarCellAttributes<ID>): void;
84
- handleEvent(event: CalendarEvent<ID>): boolean;
25
+ export type CalendarValueChangeHandler = NonNullable<CalendarOptions['onValueChange']>;
26
+ export type CalendarHighlightedValueChangeHandler = NonNullable<CalendarOptions['onHighlightedValueChange']>;
27
+ export type CalendarUpdateHandler = NonNullable<CalendarOptions['onUpdate']>;
28
+ export interface CalendarControlledValues {
29
+ readonly value?: DateValue | null;
30
+ readonly highlightedValue?: DateValue;
31
+ }
32
+ export interface CalendarConnection {
33
+ getSnapshot(): RevisionSnapshot<CalendarState>;
34
+ getMonth(): readonly (readonly DateValue[])[];
35
+ getWeek(): readonly DateValue[];
36
+ getYear(): readonly (readonly CalendarMonthValue[])[];
37
+ syncControlledValues(values: CalendarControlledValues): DOMTemporalResult<RevisionSnapshot<CalendarState>>;
38
+ setCellAttributes(element: HTMLElement, value: DateValue): void;
39
+ handleEvent(event: CalendarEvent): boolean;
85
40
  handleKeyboardEvent(event: KeyboardEvent): boolean;
86
41
  focusCurrent(): void;
42
+ refresh(): void;
87
43
  disconnect(): void;
88
44
  }
89
- export type CalendarOptions<ID extends StableID = StableID> = Omit<CalendarControllerOptions<ID>, 'grid'> & Omit<CalendarConnectionOptions<ID>, 'controller'> & {
90
- readonly rows: readonly (readonly ID[])[];
91
- };
92
- export declare function createCalendarController<ID extends StableID>(options: CalendarControllerOptions<ID>): Result<CalendarController<ID>>;
93
- export declare function createCalendar<ID extends StableID>(options: CalendarOptions<ID>): FacadeConnection<CalendarConnection<ID>>;
94
- export declare function tryCreateCalendar<ID extends StableID>(options: CalendarOptions<ID>): Result<FacadeConnection<CalendarConnection<ID>>>;
95
- export declare function connectCalendar<ID extends StableID>(options: CalendarConnectionOptions<ID>): CalendarConnection<ID>;
96
- export declare function toCalendarEvent<ID extends StableID = StableID>(input: KeyboardInput): CalendarEvent<ID> | null;
97
- export declare function toCalendarEffect<ID extends StableID>(command: CalendarCommand<ID>): CalendarEffect<ID>;
45
+ export declare function createCalendar(options: CalendarOptions): FacadeConnection<CalendarConnection>;
46
+ export declare function tryCreateCalendar(options: CalendarOptions): DOMTemporalResult<FacadeConnection<CalendarConnection>>;
47
+ export declare function toCalendarEvent(input: Pick<KeyboardEvent, 'key' | 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'>): CalendarEvent | null;
98
48
  //# sourceMappingURL=calendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
package/dist/calendar.js CHANGED
@@ -1,296 +1,189 @@
1
- import { createFacadeConnection } from '@sectile/core/adapter-runtime';
1
+ import {} from '@sectile/core/adapter-runtime';
2
2
  import { unwrap } from '@sectile/core/result';
3
- import { tryCreateInteractionState, requireInteraction, } from '@sectile/core/interaction';
4
- import { applyCalendarEvent, tryCreateCalendarState, } from '@sectile/core/calendar';
5
- import { tryCreateGrid } from '@sectile/core/grid';
6
- import { tryCreateRevisionSnapshot, rejectRevisionInput, } from '@sectile/core/revision';
7
- import { applyControllerEvent, synchronizeControllerState } from '@sectile/core/adapter-runtime';
8
- import { findDelegatedID } from './internal/delegated-event.js';
3
+ import { createDOMTemporalController, createDOMTemporalFacadeConnection } from './internal/result.js';
4
+ import { applyCalendarEvent, calendarID, createCalendarMonth, createCalendarWeek, createCalendarYear, isCalendarValueAvailable, tryCreateCalendarState, } from '@sectile/temporal/calendar';
5
+ import { compareDateValues, parseDateValue } from '@sectile/temporal/date-field';
6
+ import { setDatePickerCellAvailability } from './internal/date-picker-cell.js';
9
7
  import { setInteractionAttributes } from './internal/interaction.js';
10
- export function createCalendarController(options) {
11
- const value = options.value !== undefined ? options.value : options.defaultValue ?? null;
12
- const current = options.highlightedValue !== undefined
13
- ? options.highlightedValue
14
- : options.defaultHighlightedValue ?? null;
15
- const initial = tryCreateCalendarState(options.grid, {
16
- selected: value === null ? [] : [value],
17
- anchor: value,
18
- current,
19
- });
20
- if (!initial.ok)
21
- return initial;
22
- const snapshot = tryCreateRevisionSnapshot(initial.value);
23
- if (!snapshot.ok)
24
- return snapshot;
25
- const interaction = tryCreateInteractionState(options);
26
- if (!interaction.ok)
27
- return interaction;
28
- return { ok: true, value: new DOMCalendarController(options, snapshot.value, interaction.value) };
29
- }
8
+ import { currentReferenceDate } from './internal/reference-date.js';
30
9
  export function createCalendar(options) {
31
10
  return unwrap(tryCreateCalendar(options));
32
11
  }
33
12
  export function tryCreateCalendar(options) {
34
- return createFacadeConnection(options, (options) => tryCreateCalendarConnection(options));
35
- }
36
- function tryCreateCalendarConnection(options) {
37
- const grid = tryCreateGrid(options.rows);
38
- if (!grid.ok)
39
- return grid;
40
- const controller = createCalendarController({ ...options, grid: grid.value });
41
- if (!controller.ok)
42
- return controller;
43
- return { ok: true, value: connectCalendar({ ...options, controller: controller.value }) };
44
- }
45
- export function connectCalendar(options) {
46
- return new DOMCalendarConnection(options);
13
+ return createDOMTemporalFacadeConnection(options, constructCalendar);
47
14
  }
48
- export function toCalendarEvent(input) {
49
- if (input.altKey === true || input.ctrlKey === true || input.metaKey === true)
50
- return null;
51
- if (input.key === 'ArrowLeft')
52
- return 'left';
53
- if (input.key === 'ArrowRight')
54
- return 'right';
55
- if (input.key === 'ArrowUp')
56
- return 'up';
57
- if (input.key === 'ArrowDown')
58
- return 'down';
59
- if (input.key === ' ' || input.key === 'Enter')
60
- return 'select';
61
- if (input.key === 'PageUp')
62
- return 'previous-page';
63
- if (input.key === 'PageDown')
64
- return 'next-page';
65
- return null;
66
- }
67
- export function toCalendarEffect(command) {
68
- return Object.freeze(command.type === 'focus'
69
- ? { type: 'focus-element', id: command.id }
70
- : { type: 'request-page', direction: command.direction, from: command.from });
15
+ function constructCalendar(options) {
16
+ const controls = {
17
+ value: options.value !== undefined,
18
+ highlighted: options.highlightedValue !== undefined,
19
+ };
20
+ const requestedValue = controls.value ? options.value : options.defaultValue;
21
+ const requestedHighlight = controls.highlighted ? options.highlightedValue : options.defaultHighlightedValue;
22
+ const initial = tryCreateCalendarState({
23
+ referenceDate: options.referenceDate ?? currentReferenceDate(),
24
+ ...(requestedValue === undefined ? {} : { value: requestedValue }),
25
+ ...(requestedHighlight === undefined ? {} : { highlighted: requestedHighlight }),
26
+ });
27
+ const runtime = createDOMTemporalController({
28
+ initial,
29
+ reducer: (state, event) => applyCalendarEvent(state, event, {
30
+ ...options.policies,
31
+ ...(options.required === undefined ? {} : { required: options.required }),
32
+ }),
33
+ reconcile: (previous, proposed) => tryCreateCalendarState({
34
+ value: controls.value ? previous.value : proposed.value,
35
+ highlighted: controls.highlighted ? previous.highlighted : proposed.highlighted,
36
+ view: controls.highlighted ? previous.view : proposed.view,
37
+ viewMode: proposed.viewMode,
38
+ }),
39
+ notify: (previous, proposed) => {
40
+ if (compareNullable(previous.value, proposed.value) !== 0)
41
+ options.onValueChange?.(proposed.value);
42
+ if (compareDateValues(previous.highlighted, proposed.highlighted) !== 0)
43
+ options.onHighlightedValueChange?.(proposed.highlighted);
44
+ },
45
+ toEffect: (command) => command,
46
+ interaction: options,
47
+ interactionIntent: calendarInteractionIntent,
48
+ });
49
+ return runtime.ok
50
+ ? { ok: true, value: new DOMCalendar(options, runtime.value, controls) }
51
+ : runtime;
71
52
  }
72
- class DOMCalendarConnection {
73
- grid;
74
- #controller;
75
- #root;
76
- #onPageRequest;
77
- #onTransition;
78
- #onUpdate;
79
- #handleKeydown;
80
- #handleClick;
81
- constructor(options) {
82
- this.#controller = options.controller;
83
- this.grid = options.controller.grid;
84
- this.#root = options.root;
85
- this.#onPageRequest = options.onPageRequest;
86
- this.#onTransition = options.onTransition;
87
- this.#onUpdate = options.onUpdate;
88
- setInteractionAttributes(this.#root, options);
89
- this.#handleKeydown = (event) => {
90
- if (this.handleKeyboardEvent(event))
91
- event.preventDefault();
92
- };
93
- this.#handleClick = (event) => {
94
- const id = findDelegatedID(event.target, this.#root, 'calendarId');
95
- if (id !== null)
96
- this.handleEvent({ type: 'select', id: id });
97
- };
98
- this.#root.addEventListener('keydown', this.#handleKeydown);
99
- this.#root.addEventListener('click', this.#handleClick);
100
- }
101
- getSnapshot() {
102
- return this.#controller.getSnapshot();
53
+ class DOMCalendar {
54
+ #options;
55
+ #grid;
56
+ #runtime;
57
+ #controls;
58
+ #keydown = (event) => {
59
+ if (this.handleKeyboardEvent(event))
60
+ event.preventDefault();
61
+ };
62
+ #click = (event) => {
63
+ const target = event.target instanceof Element ? event.target.closest('[data-calendar-id]') : null;
64
+ if (target === null || !this.#grid.contains(target))
65
+ return;
66
+ const parsed = parseDateValue(target.dataset['calendarId'] ?? '');
67
+ if (parsed.ok)
68
+ this.handleEvent({ type: 'select', value: parsed.value });
69
+ };
70
+ constructor(options, runtime, controls) {
71
+ this.#options = options;
72
+ this.#grid = options.grid ?? options.root;
73
+ this.#runtime = runtime;
74
+ this.#controls = controls;
75
+ this.#grid.addEventListener('keydown', this.#keydown);
76
+ this.#grid.addEventListener('click', this.#click);
77
+ this.#grid.setAttribute('role', 'grid');
78
+ setInteractionAttributes(this.#grid, options);
79
+ this.refresh();
80
+ }
81
+ getSnapshot() { return this.#runtime.getSnapshot(); }
82
+ getMonth() {
83
+ return createCalendarMonth(this.getSnapshot().state.view, this.#options.policies?.weekStartsOn);
84
+ }
85
+ getWeek() {
86
+ return createCalendarWeek(this.getSnapshot().state.highlighted, this.#options.policies?.weekStartsOn);
87
+ }
88
+ getYear() {
89
+ return createCalendarYear(this.getSnapshot().state.view.year);
103
90
  }
104
91
  syncControlledValues(values) {
105
- const result = this.#controller.syncControlledValues(values);
92
+ if (this.#controls.value !== (values.value !== undefined) || this.#controls.highlighted !== (values.highlightedValue !== undefined)) {
93
+ return {
94
+ ok: false,
95
+ error: {
96
+ class: 'construction',
97
+ code: 'controlled-shape-mismatch',
98
+ message: 'Controlled calendar values must preserve their construction-time shape.',
99
+ },
100
+ };
101
+ }
102
+ const state = this.getSnapshot().state;
103
+ const highlighted = this.#controls.highlighted ? values.highlightedValue : state.highlighted;
104
+ const result = this.#runtime.replace(tryCreateCalendarState({
105
+ value: this.#controls.value ? values.value : state.value,
106
+ highlighted,
107
+ view: { year: highlighted.year, month: highlighted.month },
108
+ viewMode: state.viewMode,
109
+ }));
106
110
  if (result.ok) {
107
- this.#onUpdate?.();
108
- this.focusCurrent();
111
+ this.refresh();
112
+ this.#options.onUpdate?.();
109
113
  }
110
114
  return result;
111
115
  }
112
- setCalendarAttributes(label) {
113
- this.#root.setAttribute('role', 'grid');
114
- this.#root.setAttribute('aria-rowcount', String(this.grid.rowCount));
115
- this.#root.setAttribute('aria-colcount', String(this.grid.columnCount));
116
- if (label === undefined)
117
- this.#root.removeAttribute('aria-label');
118
- else
119
- this.#root.setAttribute('aria-label', label);
120
- }
121
- setCellAttributes(element, attributes) {
122
- const state = this.#controller.getSnapshot().state;
123
- element.dataset['calendarId'] = String(attributes.id);
124
- element.tabIndex = state.cursor.current === attributes.id ? 0 : -1;
116
+ setCellAttributes(element, value) {
117
+ const state = this.getSnapshot().state;
118
+ element.dataset['calendarId'] = calendarID(value);
125
119
  element.setAttribute('role', 'gridcell');
126
- element.setAttribute('aria-rowindex', String(attributes.rowIndex));
127
- element.setAttribute('aria-colindex', String(attributes.columnIndex));
128
- element.setAttribute('aria-selected', String(state.selection.has(attributes.id)));
129
- if (attributes.disabled === true)
130
- element.setAttribute('aria-disabled', 'true');
131
- else
132
- element.removeAttribute('aria-disabled');
133
- }
134
- handleKeyboardEvent(event) {
135
- const input = {
136
- key: event.key,
137
- altKey: event.altKey,
138
- ctrlKey: event.ctrlKey,
139
- metaKey: event.metaKey,
140
- };
141
- const semanticEvent = toCalendarEvent(input);
142
- if (semanticEvent === null)
143
- return false;
144
- return this.handleEvent(semanticEvent);
120
+ element.setAttribute('aria-selected', String(state.value !== null && compareDateValues(state.value, value) === 0));
121
+ setDatePickerCellAvailability(element, isCalendarValueAvailable(value, this.#options.policies));
122
+ element.tabIndex = compareDateValues(state.highlighted, value) === 0 ? 0 : -1;
145
123
  }
146
124
  handleEvent(event) {
147
- const result = this.#controller.handleEvent(event);
148
- if (result.ok)
149
- this.#applyEffects(result.commands);
150
- this.#onTransition?.(Object.freeze({ event, result }));
151
- if (result.ok) {
152
- this.#onUpdate?.();
153
- this.focusCurrent();
154
- }
155
- return result.ok;
156
- }
157
- focusCurrent() {
158
- queueMicrotask(() => {
159
- const current = this.#controller.getSnapshot().state.cursor.current;
160
- if (current === null) {
161
- this.#root.focus();
162
- return;
163
- }
164
- for (const element of this.#root.querySelectorAll('[data-calendar-id]')) {
165
- if (element.dataset['calendarId'] !== String(current))
166
- continue;
167
- element.focus();
168
- return;
169
- }
170
- });
171
- }
172
- disconnect() {
173
- this.#root.removeEventListener('keydown', this.#handleKeydown);
174
- this.#root.removeEventListener('click', this.#handleClick);
175
- }
176
- #applyEffects(effects) {
177
- for (const effect of effects) {
178
- if (effect.type !== 'request-page')
179
- continue;
180
- this.#onPageRequest?.(Object.freeze({ direction: effect.direction, from: effect.from }));
125
+ const result = this.#runtime.handle(event);
126
+ if (!result.ok)
127
+ return false;
128
+ this.refresh();
129
+ this.#options.onUpdate?.();
130
+ if (result.commands.some((command) => command.type === 'highlight-changed')) {
131
+ queueMicrotask(() => this.focusCurrent());
181
132
  }
133
+ return true;
182
134
  }
183
- }
184
- class DOMCalendarController {
185
- grid;
186
- #grid;
187
- #policies;
188
- #valueControlled;
189
- #highlightControlled;
190
- #onValueChange;
191
- #onHighlightedValueChange;
192
- #interaction;
193
- #snapshot;
194
- constructor(options, snapshot, interaction) {
195
- this.grid = options.grid;
196
- this.#grid = options.grid;
197
- this.#policies = options.policies ?? {};
198
- this.#valueControlled = options.value !== undefined;
199
- this.#highlightControlled = options.highlightedValue !== undefined;
200
- this.#onValueChange = options.onValueChange;
201
- this.#onHighlightedValueChange = options.onHighlightedValueChange;
202
- this.#interaction = interaction;
203
- this.#snapshot = snapshot;
135
+ handleKeyboardEvent(event) {
136
+ const semantic = toCalendarEvent(event);
137
+ return semantic !== null && this.handleEvent(semantic);
204
138
  }
205
- getSnapshot() {
206
- return this.#snapshot;
139
+ focusCurrent() {
140
+ this.#grid.querySelector('[tabindex="0"]')?.focus();
207
141
  }
208
- syncControlledValues(values) {
209
- const error = controlledInputError(this.#valueControlled, this.#highlightControlled, values);
210
- if (error !== null)
211
- return { ok: false, error };
212
- const selected = this.#valueControlled
213
- ? values.value
214
- : selectedValue(this.#snapshot.state);
215
- const state = tryCreateCalendarState(this.#grid, {
216
- selected: selected === null ? [] : [selected],
217
- anchor: this.#valueControlled ? selected : this.#snapshot.state.selection.anchor,
218
- current: this.#highlightControlled
219
- ? values.highlightedValue
220
- : this.#snapshot.state.cursor.current,
142
+ refresh() {
143
+ if (this.#options.label === undefined)
144
+ this.#grid.removeAttribute('aria-label');
145
+ else
146
+ this.#grid.setAttribute('aria-label', this.#options.label);
147
+ this.#grid.querySelectorAll('[data-calendar-id]').forEach((element) => {
148
+ const parsed = parseDateValue(element.dataset['calendarId'] ?? '');
149
+ if (parsed.ok)
150
+ this.setCellAttributes(element, parsed.value);
221
151
  });
222
- const snapshot = synchronizeControllerState(this.#snapshot, state);
223
- if (!snapshot.ok)
224
- return snapshot;
225
- this.#snapshot = snapshot.value;
226
- return snapshot;
227
- }
228
- handleKeyboardInput(input, expectedRevision = this.#snapshot.revision) {
229
- const event = toCalendarEvent(input);
230
- if (event === null) {
231
- return rejectRevisionInput(this.#snapshot, {
232
- class: 'transition-rejection',
233
- code: 'unsupported-dom-key',
234
- message: 'DOM keyboard input does not map to a calendar semantic event.',
235
- details: { key: input.key },
236
- });
237
- }
238
- return this.handleEvent(event, expectedRevision);
239
152
  }
240
- handleEvent(event, expectedRevision = this.#snapshot.revision) {
241
- const permitted = requireInteraction(this.#interaction, 'navigate');
242
- if (!permitted.ok)
243
- return rejectRevisionInput(this.#snapshot, permitted.error);
244
- const result = applyControllerEvent(this.#snapshot, expectedRevision, event, (state, semanticEvent) => applyCalendarEvent(this.#grid, state, semanticEvent, this.#policies), (previous, proposed) => controlledState(this.#grid, previous, proposed, this.#valueControlled, this.#highlightControlled), (previous, proposed) => this.#notify(previous, proposed), toCalendarEffect);
245
- if (result.ok)
246
- this.#snapshot = result.snapshot;
247
- return result;
248
- }
249
- #notify(previous, proposed) {
250
- const previousValue = selectedValue(previous);
251
- const value = selectedValue(proposed);
252
- if (previousValue !== value) {
253
- this.#onValueChange?.(Object.freeze({ value, previousValue }));
254
- }
255
- if (previous.cursor.current !== proposed.cursor.current) {
256
- this.#onHighlightedValueChange?.(Object.freeze({
257
- value: proposed.cursor.current,
258
- previousValue: previous.cursor.current,
259
- }));
260
- }
153
+ disconnect() {
154
+ this.#grid.removeEventListener('keydown', this.#keydown);
155
+ this.#grid.removeEventListener('click', this.#click);
261
156
  }
262
157
  }
263
- function controlledState(grid, previous, proposed, valueControlled, highlightControlled) {
264
- return tryCreateCalendarState(grid, {
265
- selected: valueControlled ? previous.selection.selected : proposed.selection.selected,
266
- anchor: valueControlled ? previous.selection.anchor : proposed.selection.anchor,
267
- current: highlightControlled ? previous.cursor.current : proposed.cursor.current,
268
- });
158
+ export function toCalendarEvent(input) {
159
+ if (input.altKey || input.ctrlKey || input.metaKey)
160
+ return null;
161
+ if (input.key === 'ArrowLeft')
162
+ return 'previous-day';
163
+ if (input.key === 'ArrowRight')
164
+ return 'next-day';
165
+ if (input.key === 'ArrowUp')
166
+ return 'previous-week';
167
+ if (input.key === 'ArrowDown')
168
+ return 'next-week';
169
+ if (input.key === 'Home')
170
+ return 'start-of-week';
171
+ if (input.key === 'End')
172
+ return 'end-of-week';
173
+ if (input.key === 'PageUp')
174
+ return input.shiftKey ? 'previous-year' : 'previous-month';
175
+ if (input.key === 'PageDown')
176
+ return input.shiftKey ? 'next-year' : 'next-month';
177
+ if (input.key === 'Enter' || input.key === ' ')
178
+ return 'select-highlighted';
179
+ return null;
269
180
  }
270
- function selectedValue(state) {
271
- return state.selection.selected[0] ?? null;
181
+ function calendarInteractionIntent(event) {
182
+ if (event === 'select-highlighted')
183
+ return 'mutate';
184
+ return typeof event === 'object' && (event.type === 'select' || event.type === 'set-value') ? 'mutate' : 'navigate';
272
185
  }
273
- function controlledInputError(valueControlled, highlightControlled, values) {
274
- if (valueControlled !== (values.value !== undefined)) {
275
- return {
276
- class: 'construction',
277
- code: valueControlled ? 'controlled-value-required' : 'uncontrolled-value-update',
278
- message: valueControlled
279
- ? 'Controlled calendar selection sync requires value.'
280
- : 'Uncontrolled calendar selection cannot be synchronized externally.',
281
- };
282
- }
283
- if (highlightControlled !== (values.highlightedValue !== undefined)) {
284
- return {
285
- class: 'construction',
286
- code: highlightControlled
287
- ? 'controlled-highlighted-value-required'
288
- : 'uncontrolled-highlighted-value-update',
289
- message: highlightControlled
290
- ? 'Controlled calendar highlight sync requires highlightedValue.'
291
- : 'Uncontrolled calendar highlight cannot be synchronized externally.',
292
- };
293
- }
294
- return null;
186
+ function compareNullable(left, right) {
187
+ return left === null ? right === null ? 0 : -1 : right === null ? 1 : compareDateValues(left, right);
295
188
  }
296
189
  //# sourceMappingURL=calendar.js.map