@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.
- package/README.md +8 -0
- package/dist/calendar.d.ts +38 -88
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +160 -267
- package/dist/calendar.js.map +1 -1
- package/dist/date-field.d.ts +6 -6
- package/dist/date-field.d.ts.map +1 -1
- package/dist/date-field.js +6 -6
- package/dist/date-field.js.map +1 -1
- package/dist/date-picker.d.ts +11 -9
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +14 -12
- package/dist/date-picker.js.map +1 -1
- package/dist/date-range-field.d.ts +7 -7
- package/dist/date-range-field.d.ts.map +1 -1
- package/dist/date-range-field.js +7 -7
- package/dist/date-range-field.js.map +1 -1
- package/dist/date-range-picker.d.ts +10 -8
- package/dist/date-range-picker.d.ts.map +1 -1
- package/dist/date-range-picker.js +12 -11
- package/dist/date-range-picker.js.map +1 -1
- package/dist/date-time-field.d.ts +6 -6
- package/dist/date-time-field.d.ts.map +1 -1
- package/dist/date-time-field.js +6 -6
- package/dist/date-time-field.js.map +1 -1
- package/dist/date-time-picker.d.ts +10 -9
- package/dist/date-time-picker.d.ts.map +1 -1
- package/dist/date-time-picker.js +15 -13
- package/dist/date-time-picker.js.map +1 -1
- package/dist/date-time-range-picker.d.ts +10 -9
- package/dist/date-time-range-picker.d.ts.map +1 -1
- package/dist/date-time-range-picker.js +15 -13
- package/dist/date-time-range-picker.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/floating-position.d.ts.map +1 -1
- package/dist/internal/floating-position.js +6 -2
- package/dist/internal/floating-position.js.map +1 -1
- package/dist/internal/reference-date.d.ts +3 -0
- package/dist/internal/reference-date.d.ts.map +1 -0
- package/dist/internal/reference-date.js +8 -0
- package/dist/internal/reference-date.js.map +1 -0
- package/dist/internal/result.d.ts +19 -0
- package/dist/internal/result.d.ts.map +1 -0
- package/dist/internal/result.js +8 -0
- package/dist/internal/result.js.map +1 -0
- package/dist/month-picker.d.ts +2 -2
- package/dist/month-picker.d.ts.map +1 -1
- package/dist/month-picker.js +1 -1
- package/dist/month-picker.js.map +1 -1
- package/dist/month-range-picker.d.ts +1 -1
- package/dist/month-range-picker.d.ts.map +1 -1
- package/dist/month-range-picker.js +1 -1
- package/dist/month-range-picker.js.map +1 -1
- package/dist/reorder.d.ts.map +1 -1
- package/dist/reorder.js.map +1 -1
- package/dist/time-field.d.ts +6 -6
- package/dist/time-field.d.ts.map +1 -1
- package/dist/time-field.js +6 -6
- package/dist/time-field.js.map +1 -1
- package/dist/time-range-field.d.ts +7 -7
- package/dist/time-range-field.d.ts.map +1 -1
- package/dist/time-range-field.js +6 -6
- package/dist/time-range-field.js.map +1 -1
- package/dist/virtual.d.ts +58 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +322 -0
- package/dist/virtual.js.map +1 -0
- 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.
|
package/dist/calendar.d.ts
CHANGED
|
@@ -1,98 +1,48 @@
|
|
|
1
1
|
import { type FacadeConnection } from '@sectile/core/adapter-runtime';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import { type
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
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
|
|
66
|
-
readonly
|
|
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
|
|
70
|
-
export type
|
|
71
|
-
export type
|
|
72
|
-
export interface
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
readonly
|
|
80
|
-
|
|
81
|
-
syncControlledValues(values: CalendarControlledValues
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
90
|
-
|
|
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
|
package/dist/calendar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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 {
|
|
1
|
+
import {} from '@sectile/core/adapter-runtime';
|
|
2
2
|
import { unwrap } from '@sectile/core/result';
|
|
3
|
-
import {
|
|
4
|
-
import { applyCalendarEvent, tryCreateCalendarState, } from '@sectile/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
this.#
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return this
|
|
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
|
-
|
|
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
|
|
108
|
-
this.
|
|
111
|
+
this.refresh();
|
|
112
|
+
this.#options.onUpdate?.();
|
|
109
113
|
}
|
|
110
114
|
return result;
|
|
111
115
|
}
|
|
112
|
-
|
|
113
|
-
this
|
|
114
|
-
|
|
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-
|
|
127
|
-
element
|
|
128
|
-
element.
|
|
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.#
|
|
148
|
-
if (result.ok)
|
|
149
|
-
|
|
150
|
-
this
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
206
|
-
|
|
139
|
+
focusCurrent() {
|
|
140
|
+
this.#grid.querySelector('[tabindex="0"]')?.focus();
|
|
207
141
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
271
|
-
|
|
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
|
|
274
|
-
|
|
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
|