foldkit 0.78.0 → 0.80.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 +1 -0
- package/dist/ui/calendar/index.d.ts +117 -28
- package/dist/ui/calendar/index.d.ts.map +1 -1
- package/dist/ui/calendar/index.js +393 -105
- package/dist/ui/calendar/public.d.ts +2 -2
- package/dist/ui/calendar/public.d.ts.map +1 -1
- package/dist/ui/calendar/public.js +1 -1
- package/dist/ui/datePicker/index.d.ts +5 -2
- package/dist/ui/datePicker/index.d.ts.map +1 -1
- package/dist/ui/datePicker/index.js +8 -2
- package/dist/ui/virtualList/index.d.ts +42 -1
- package/dist/ui/virtualList/index.d.ts.map +1 -1
- package/dist/ui/virtualList/index.js +89 -15
- package/dist/ui/virtualList/public.d.ts +1 -1
- package/dist/ui/virtualList/public.d.ts.map +1 -1
- package/dist/ui/virtualList/public.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,6 +170,7 @@ This is what makes Foldkit unusually AI-friendly. The same property that makes t
|
|
|
170
170
|
- **[Stopwatch](https://foldkit.dev/example-apps/stopwatch)** — Timer with start/stop/reset
|
|
171
171
|
- **[Crash View](https://foldkit.dev/example-apps/crash-view)** — Custom crash fallback UI with crash reporting
|
|
172
172
|
- **[Form](https://foldkit.dev/example-apps/form)** — Form validation with async email checking
|
|
173
|
+
- **[Job Application](https://foldkit.dev/example-apps/job-application)** — Multi-step form with cross-field validation, file uploads, and per-step error indicators
|
|
173
174
|
- **[Weather](https://foldkit.dev/example-apps/weather)** — HTTP requests with async state handling
|
|
174
175
|
- **[Routing](https://foldkit.dev/example-apps/routing)** — URL routing with parser combinators
|
|
175
176
|
- **[Query Sync](https://foldkit.dev/example-apps/query-sync)** — URL query parameter sync with filtering and sorting
|
|
@@ -3,9 +3,15 @@ import * as Calendar from '../../calendar/index.js';
|
|
|
3
3
|
import type { CalendarDate } from '../../calendar/index.js';
|
|
4
4
|
import * as Command from '../../command/index.js';
|
|
5
5
|
import { type Attribute, type Html } from '../../html/index.js';
|
|
6
|
+
/** Which grid the calendar is currently displaying. `Days` is the standard
|
|
7
|
+
* 6×7 day grid; `Months` is a 3×4 month-name grid for fast month jumps;
|
|
8
|
+
* `Years` is a 3×4 year grid paged in 12-year windows for fast year jumps. */
|
|
9
|
+
export declare const ViewMode: S.Literal<["Days", "Months", "Years"]>;
|
|
10
|
+
export type ViewMode = typeof ViewMode.Type;
|
|
6
11
|
/** Schema for the calendar component's state. Tracks the visible month/year,
|
|
7
|
-
* the keyboard-focused and user-selected dates,
|
|
8
|
-
* governs navigation (locale, min/max, disabled
|
|
12
|
+
* the keyboard-focused and user-selected dates, the active view mode, and
|
|
13
|
+
* the configuration that governs navigation (locale, min/max, disabled
|
|
14
|
+
* days). */
|
|
9
15
|
export declare const Model: S.Struct<{
|
|
10
16
|
id: typeof S.String;
|
|
11
17
|
today: S.filter<S.Struct<{
|
|
@@ -15,6 +21,7 @@ export declare const Model: S.Struct<{
|
|
|
15
21
|
}>>;
|
|
16
22
|
viewYear: typeof S.Int;
|
|
17
23
|
viewMonth: S.filter<typeof S.Int>;
|
|
24
|
+
viewMode: S.Literal<["Days", "Months", "Years"]>;
|
|
18
25
|
maybeFocusedDate: S.OptionFromSelf<S.filter<S.Struct<{
|
|
19
26
|
year: typeof S.Int;
|
|
20
27
|
month: S.filter<typeof S.Int>;
|
|
@@ -65,18 +72,30 @@ export declare const PressedKeyOnGrid: import("../../schema/index.js").CallableT
|
|
|
65
72
|
key: typeof S.String;
|
|
66
73
|
isShift: typeof S.Boolean;
|
|
67
74
|
}>;
|
|
68
|
-
/** Sent when the user clicks the previous-month navigation button
|
|
75
|
+
/** Sent when the user clicks the previous-month navigation button in Days
|
|
76
|
+
* mode. (The Years mode prev/next-page buttons dispatch `PagedYears`.) */
|
|
69
77
|
export declare const ClickedPreviousMonthButton: import("../../schema/index.js").CallableTaggedStruct<"ClickedPreviousMonthButton", {}>;
|
|
70
|
-
/** Sent when the user clicks the next-month navigation button
|
|
78
|
+
/** Sent when the user clicks the next-month navigation button in Days
|
|
79
|
+
* mode. (The Years mode prev/next-page buttons dispatch `PagedYears`.) */
|
|
71
80
|
export declare const ClickedNextMonthButton: import("../../schema/index.js").CallableTaggedStruct<"ClickedNextMonthButton", {}>;
|
|
72
|
-
/** Sent when the user
|
|
73
|
-
|
|
81
|
+
/** Sent when the user clicks the calendar heading. Zooms out one mode
|
|
82
|
+
* level: Days → Months, Months → Years. Terminal in Years mode. */
|
|
83
|
+
export declare const ClickedHeading: import("../../schema/index.js").CallableTaggedStruct<"ClickedHeading", {}>;
|
|
84
|
+
/** Sent when the user picks a month from the months grid. Jumps the view
|
|
85
|
+
* to that month and returns the calendar to Days mode. */
|
|
86
|
+
export declare const SelectedMonth: import("../../schema/index.js").CallableTaggedStruct<"SelectedMonth", {
|
|
74
87
|
month: typeof S.Int;
|
|
75
88
|
}>;
|
|
76
|
-
/** Sent when the user picks a year from the
|
|
77
|
-
|
|
89
|
+
/** Sent when the user picks a year from the years grid. Jumps the view to
|
|
90
|
+
* that year and transitions the calendar to Months mode for further drilling. */
|
|
91
|
+
export declare const SelectedYear: import("../../schema/index.js").CallableTaggedStruct<"SelectedYear", {
|
|
78
92
|
year: typeof S.Int;
|
|
79
93
|
}>;
|
|
94
|
+
/** Sent when the user pages the years grid forward or backward by one
|
|
95
|
+
* window. Direction is `1` for next, `-1` for previous. */
|
|
96
|
+
export declare const PagedYears: import("../../schema/index.js").CallableTaggedStruct<"PagedYears", {
|
|
97
|
+
direction: S.Literal<[1, -1]>;
|
|
98
|
+
}>;
|
|
80
99
|
/** Sent when the grid container receives DOM focus. */
|
|
81
100
|
export declare const FocusedGrid: import("../../schema/index.js").CallableTaggedStruct<"FocusedGrid", {}>;
|
|
82
101
|
/** Sent when the grid container loses DOM focus. */
|
|
@@ -101,10 +120,12 @@ export declare const Message: S.Union<[import("../../schema/index.js").CallableT
|
|
|
101
120
|
}>, import("../../schema/index.js").CallableTaggedStruct<"PressedKeyOnGrid", {
|
|
102
121
|
key: typeof S.String;
|
|
103
122
|
isShift: typeof S.Boolean;
|
|
104
|
-
}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedPreviousMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedNextMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"
|
|
123
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedPreviousMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedNextMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedHeading", {}>, import("../../schema/index.js").CallableTaggedStruct<"SelectedMonth", {
|
|
105
124
|
month: typeof S.Int;
|
|
106
|
-
}>, import("../../schema/index.js").CallableTaggedStruct<"
|
|
125
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"SelectedYear", {
|
|
107
126
|
year: typeof S.Int;
|
|
127
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"PagedYears", {
|
|
128
|
+
direction: S.Literal<[1, -1]>;
|
|
108
129
|
}>, import("../../schema/index.js").CallableTaggedStruct<"FocusedGrid", {}>, import("../../schema/index.js").CallableTaggedStruct<"BlurredGrid", {}>, import("../../schema/index.js").CallableTaggedStruct<"RefreshedToday", {
|
|
109
130
|
today: S.filter<S.Struct<{
|
|
110
131
|
year: typeof S.Int;
|
|
@@ -183,6 +204,17 @@ export declare const setDisabledDates: (model: Model, disabledDates: ReadonlyArr
|
|
|
183
204
|
/** Sets the days of the week that are disabled (e.g. weekends). Pass an
|
|
184
205
|
* empty array to clear. Does NOT reconcile the current selection. */
|
|
185
206
|
export declare const setDisabledDaysOfWeek: (model: Model, disabledDaysOfWeek: ReadonlyArray<Calendar.DayOfWeek>) => Model;
|
|
207
|
+
/** Returns the calendar to Days mode regardless of current depth. Useful for
|
|
208
|
+
* standalone (non-popovered) consumers that want to wire their own back-out
|
|
209
|
+
* gesture. Popovered consumers like `Ui.DatePicker` don't need this — Escape
|
|
210
|
+
* closes the popover, and the calendar resets to Days on next open.
|
|
211
|
+
*
|
|
212
|
+
* Reconciles `maybeFocusedDate` to a date inside the visible (`viewYear`,
|
|
213
|
+
* `viewMonth`) — Months/Years navigation can leave the cursor on a date
|
|
214
|
+
* outside the days grid (paged-away year, etc.), which would otherwise
|
|
215
|
+
* cause `aria-activedescendant` to point at a non-rendered cell and the
|
|
216
|
+
* next ArrowLeft to jump to the cursor's stale year. */
|
|
217
|
+
export declare const dropToDays: (model: Model) => Model;
|
|
186
218
|
/** Processes a calendar message and returns the next model, commands, and
|
|
187
219
|
* optional OutMessage. */
|
|
188
220
|
export declare const update: (model: Model, message: Message) => UpdateReturn;
|
|
@@ -198,39 +230,93 @@ export type DayCell<ParentMessage> = Readonly<{
|
|
|
198
230
|
isInViewMonth: boolean;
|
|
199
231
|
isDisabled: boolean;
|
|
200
232
|
}>;
|
|
201
|
-
/** A column header for the grid's first row (day-of-week labels). */
|
|
233
|
+
/** A column header for the day grid's first row (day-of-week labels). */
|
|
202
234
|
export type ColumnHeader<ParentMessage> = Readonly<{
|
|
203
235
|
name: string;
|
|
204
236
|
attributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
205
237
|
}>;
|
|
206
|
-
/** A single week row in the
|
|
207
|
-
*
|
|
238
|
+
/** A single week row in the day grid, carrying its own row attributes (role,
|
|
239
|
+
* aria-rowindex) alongside its 7 day cells. */
|
|
208
240
|
export type Week<ParentMessage> = Readonly<{
|
|
209
241
|
attributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
210
242
|
cells: ReadonlyArray<DayCell<ParentMessage>>;
|
|
211
243
|
}>;
|
|
212
|
-
/**
|
|
213
|
-
*
|
|
214
|
-
|
|
244
|
+
/** Information about a single month cell in the rendered months grid.
|
|
245
|
+
* `label` is the locale-aware full month name (e.g. "September"); `shortLabel`
|
|
246
|
+
* is the locale-aware abbreviation (e.g. "Sep"). Render whichever fits the
|
|
247
|
+
* cell — never substring `label` to abbreviate, since that's not safe across
|
|
248
|
+
* locales. */
|
|
249
|
+
export type MonthCell<ParentMessage> = Readonly<{
|
|
250
|
+
month: number;
|
|
251
|
+
label: string;
|
|
252
|
+
shortLabel: string;
|
|
253
|
+
cellAttributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
254
|
+
buttonAttributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
255
|
+
isSelected: boolean;
|
|
256
|
+
isFocused: boolean;
|
|
257
|
+
isCurrentMonth: boolean;
|
|
258
|
+
isDisabled: boolean;
|
|
259
|
+
}>;
|
|
260
|
+
/** Information about a single year cell in the rendered years grid. */
|
|
261
|
+
export type YearCell<ParentMessage> = Readonly<{
|
|
262
|
+
year: number;
|
|
263
|
+
label: string;
|
|
264
|
+
cellAttributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
265
|
+
buttonAttributes: ReadonlyArray<Attribute<ParentMessage>>;
|
|
266
|
+
isSelected: boolean;
|
|
267
|
+
isFocused: boolean;
|
|
268
|
+
isCurrentYear: boolean;
|
|
269
|
+
isDisabled: boolean;
|
|
270
|
+
}>;
|
|
271
|
+
/** Attributes provided to the consumer when rendering the day grid. */
|
|
272
|
+
export type DaysModeAttributes<ParentMessage> = Readonly<{
|
|
273
|
+
_tag: 'Days';
|
|
215
274
|
root: ReadonlyArray<Attribute<ParentMessage>>;
|
|
216
275
|
previousMonthButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
217
276
|
nextMonthButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
277
|
+
headingButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
218
278
|
heading: Readonly<{
|
|
219
279
|
id: string;
|
|
220
280
|
text: string;
|
|
221
281
|
}>;
|
|
222
|
-
monthSelect: ReadonlyArray<Attribute<ParentMessage>>;
|
|
223
|
-
monthOptions: ReadonlyArray<Readonly<{
|
|
224
|
-
value: number;
|
|
225
|
-
label: string;
|
|
226
|
-
}>>;
|
|
227
|
-
yearSelect: ReadonlyArray<Attribute<ParentMessage>>;
|
|
228
|
-
yearOptions: ReadonlyArray<number>;
|
|
229
282
|
grid: ReadonlyArray<Attribute<ParentMessage>>;
|
|
230
283
|
headerRow: ReadonlyArray<Attribute<ParentMessage>>;
|
|
231
284
|
columnHeaders: ReadonlyArray<ColumnHeader<ParentMessage>>;
|
|
232
285
|
weeks: ReadonlyArray<Week<ParentMessage>>;
|
|
233
286
|
}>;
|
|
287
|
+
/** Attributes provided to the consumer when rendering the months grid. The
|
|
288
|
+
* 12 cells are pre-built in calendar (locale-ordered) — the consumer arranges
|
|
289
|
+
* them in whatever grid layout they prefer (3×4 is the typical choice). */
|
|
290
|
+
export type MonthsModeAttributes<ParentMessage> = Readonly<{
|
|
291
|
+
_tag: 'Months';
|
|
292
|
+
root: ReadonlyArray<Attribute<ParentMessage>>;
|
|
293
|
+
headingButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
294
|
+
heading: Readonly<{
|
|
295
|
+
id: string;
|
|
296
|
+
text: string;
|
|
297
|
+
}>;
|
|
298
|
+
grid: ReadonlyArray<Attribute<ParentMessage>>;
|
|
299
|
+
cells: ReadonlyArray<MonthCell<ParentMessage>>;
|
|
300
|
+
}>;
|
|
301
|
+
/** Attributes provided to the consumer when rendering the years grid. The
|
|
302
|
+
* 12 cells span one paged window; prev/next buttons page by 12 years. */
|
|
303
|
+
export type YearsModeAttributes<ParentMessage> = Readonly<{
|
|
304
|
+
_tag: 'Years';
|
|
305
|
+
root: ReadonlyArray<Attribute<ParentMessage>>;
|
|
306
|
+
previousPageButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
307
|
+
nextPageButton: ReadonlyArray<Attribute<ParentMessage>>;
|
|
308
|
+
heading: Readonly<{
|
|
309
|
+
id: string;
|
|
310
|
+
text: string;
|
|
311
|
+
}>;
|
|
312
|
+
grid: ReadonlyArray<Attribute<ParentMessage>>;
|
|
313
|
+
cells: ReadonlyArray<YearCell<ParentMessage>>;
|
|
314
|
+
}>;
|
|
315
|
+
/** Discriminated union of attribute groups and derived data the calendar
|
|
316
|
+
* component provides to the consumer's `toView` callback. The variant
|
|
317
|
+
* matches `model.viewMode` — pattern-match on `_tag` with `M.tagsExhaustive`
|
|
318
|
+
* to render each mode. */
|
|
319
|
+
export type CalendarAttributes<ParentMessage> = DaysModeAttributes<ParentMessage> | MonthsModeAttributes<ParentMessage> | YearsModeAttributes<ParentMessage>;
|
|
234
320
|
/** Configuration for rendering a calendar with `view`. */
|
|
235
321
|
export type ViewConfig<ParentMessage> = Readonly<{
|
|
236
322
|
model: Model;
|
|
@@ -245,12 +331,15 @@ export type ViewConfig<ParentMessage> = Readonly<{
|
|
|
245
331
|
onSelectedDate?: (date: CalendarDate) => ParentMessage;
|
|
246
332
|
previousMonthLabel?: string;
|
|
247
333
|
nextMonthLabel?: string;
|
|
248
|
-
|
|
249
|
-
|
|
334
|
+
previousYearsPageLabel?: string;
|
|
335
|
+
nextYearsPageLabel?: string;
|
|
336
|
+
daysHeadingButtonLabel?: string;
|
|
337
|
+
monthsHeadingButtonLabel?: string;
|
|
250
338
|
}>;
|
|
251
|
-
/** Renders an accessible calendar
|
|
252
|
-
*
|
|
253
|
-
*
|
|
339
|
+
/** Renders an accessible calendar. Builds mode-specific ARIA attribute
|
|
340
|
+
* groups and derived cell data, then delegates layout to the consumer's
|
|
341
|
+
* `toView` callback. The variant of `CalendarAttributes` passed to `toView`
|
|
342
|
+
* matches `model.viewMode`. */
|
|
254
343
|
export declare const view: <ParentMessage>(config: ViewConfig<ParentMessage>) => Html;
|
|
255
344
|
/** Creates a memoized calendar view. Static config is captured in a closure;
|
|
256
345
|
* only `model` and `toParentMessage` are compared per render via `createLazy`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,MAAM,EAAE,MAAM,IAAI,CAAC,EAAQ,MAAM,QAAQ,CAAA;AAE7E,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AAEjD,OAAO,EACL,KAAK,SAAS,EACd,KAAK,IAAI,EAGV,MAAM,qBAAqB,CAAA;AAO5B;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,14 +7,20 @@ import { m } from '../../message/index.js';
|
|
|
7
7
|
import { evo } from '../../struct/index.js';
|
|
8
8
|
import * as Task from '../../task/index.js';
|
|
9
9
|
// MODEL
|
|
10
|
+
/** Which grid the calendar is currently displaying. `Days` is the standard
|
|
11
|
+
* 6×7 day grid; `Months` is a 3×4 month-name grid for fast month jumps;
|
|
12
|
+
* `Years` is a 3×4 year grid paged in 12-year windows for fast year jumps. */
|
|
13
|
+
export const ViewMode = S.Literal('Days', 'Months', 'Years');
|
|
10
14
|
/** Schema for the calendar component's state. Tracks the visible month/year,
|
|
11
|
-
* the keyboard-focused and user-selected dates,
|
|
12
|
-
* governs navigation (locale, min/max, disabled
|
|
15
|
+
* the keyboard-focused and user-selected dates, the active view mode, and
|
|
16
|
+
* the configuration that governs navigation (locale, min/max, disabled
|
|
17
|
+
* days). */
|
|
13
18
|
export const Model = S.Struct({
|
|
14
19
|
id: S.String,
|
|
15
20
|
today: Calendar.CalendarDate,
|
|
16
21
|
viewYear: S.Int,
|
|
17
22
|
viewMonth: S.Int.pipe(S.between(1, 12)),
|
|
23
|
+
viewMode: ViewMode,
|
|
18
24
|
maybeFocusedDate: S.OptionFromSelf(Calendar.CalendarDate),
|
|
19
25
|
maybeSelectedDate: S.OptionFromSelf(Calendar.CalendarDate),
|
|
20
26
|
isGridFocused: S.Boolean,
|
|
@@ -33,18 +39,24 @@ export const PressedKeyOnGrid = m('PressedKeyOnGrid', {
|
|
|
33
39
|
key: S.String,
|
|
34
40
|
isShift: S.Boolean,
|
|
35
41
|
});
|
|
36
|
-
/** Sent when the user clicks the previous-month navigation button
|
|
42
|
+
/** Sent when the user clicks the previous-month navigation button in Days
|
|
43
|
+
* mode. (The Years mode prev/next-page buttons dispatch `PagedYears`.) */
|
|
37
44
|
export const ClickedPreviousMonthButton = m('ClickedPreviousMonthButton');
|
|
38
|
-
/** Sent when the user clicks the next-month navigation button
|
|
45
|
+
/** Sent when the user clicks the next-month navigation button in Days
|
|
46
|
+
* mode. (The Years mode prev/next-page buttons dispatch `PagedYears`.) */
|
|
39
47
|
export const ClickedNextMonthButton = m('ClickedNextMonthButton');
|
|
40
|
-
/** Sent when the user
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
/** Sent when the user clicks the calendar heading. Zooms out one mode
|
|
49
|
+
* level: Days → Months, Months → Years. Terminal in Years mode. */
|
|
50
|
+
export const ClickedHeading = m('ClickedHeading');
|
|
51
|
+
/** Sent when the user picks a month from the months grid. Jumps the view
|
|
52
|
+
* to that month and returns the calendar to Days mode. */
|
|
53
|
+
export const SelectedMonth = m('SelectedMonth', { month: S.Int });
|
|
54
|
+
/** Sent when the user picks a year from the years grid. Jumps the view to
|
|
55
|
+
* that year and transitions the calendar to Months mode for further drilling. */
|
|
56
|
+
export const SelectedYear = m('SelectedYear', { year: S.Int });
|
|
57
|
+
/** Sent when the user pages the years grid forward or backward by one
|
|
58
|
+
* window. Direction is `1` for next, `-1` for previous. */
|
|
59
|
+
export const PagedYears = m('PagedYears', { direction: S.Literal(1, -1) });
|
|
48
60
|
/** Sent when the grid container receives DOM focus. */
|
|
49
61
|
export const FocusedGrid = m('FocusedGrid');
|
|
50
62
|
/** Sent when the grid container loses DOM focus. */
|
|
@@ -56,7 +68,7 @@ export const RefreshedToday = m('RefreshedToday', {
|
|
|
56
68
|
/** Sent when a FocusGrid command completes. */
|
|
57
69
|
export const CompletedFocusGrid = m('CompletedFocusGrid');
|
|
58
70
|
/** Union of all messages the calendar component can produce. */
|
|
59
|
-
export const Message = S.Union(ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton,
|
|
71
|
+
export const Message = S.Union(ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton, ClickedHeading, SelectedMonth, SelectedYear, PagedYears, FocusedGrid, BlurredGrid, RefreshedToday, CompletedFocusGrid);
|
|
60
72
|
// OUT MESSAGE
|
|
61
73
|
/** Emitted when the visible month changes due to navigation. Consumers of an
|
|
62
74
|
* inline calendar may use this to load month-scoped data (holidays, events).
|
|
@@ -83,6 +95,7 @@ export const init = (config) => {
|
|
|
83
95
|
today: config.today,
|
|
84
96
|
viewYear: initialFocus.year,
|
|
85
97
|
viewMonth: initialFocus.month,
|
|
98
|
+
viewMode: 'Days',
|
|
86
99
|
maybeFocusedDate: Option.some(initialFocus),
|
|
87
100
|
maybeSelectedDate: maybeInitialSelectedDate,
|
|
88
101
|
isGridFocused: false,
|
|
@@ -126,25 +139,34 @@ export const setDisabledDates = (model, disabledDates) => evo(model, { disabledD
|
|
|
126
139
|
/** Sets the days of the week that are disabled (e.g. weekends). Pass an
|
|
127
140
|
* empty array to clear. Does NOT reconcile the current selection. */
|
|
128
141
|
export const setDisabledDaysOfWeek = (model, disabledDaysOfWeek) => evo(model, { disabledDaysOfWeek: () => disabledDaysOfWeek });
|
|
142
|
+
/** Returns the calendar to Days mode regardless of current depth. Useful for
|
|
143
|
+
* standalone (non-popovered) consumers that want to wire their own back-out
|
|
144
|
+
* gesture. Popovered consumers like `Ui.DatePicker` don't need this — Escape
|
|
145
|
+
* closes the popover, and the calendar resets to Days on next open.
|
|
146
|
+
*
|
|
147
|
+
* Reconciles `maybeFocusedDate` to a date inside the visible (`viewYear`,
|
|
148
|
+
* `viewMonth`) — Months/Years navigation can leave the cursor on a date
|
|
149
|
+
* outside the days grid (paged-away year, etc.), which would otherwise
|
|
150
|
+
* cause `aria-activedescendant` to point at a non-rendered cell and the
|
|
151
|
+
* next ArrowLeft to jump to the cursor's stale year. */
|
|
152
|
+
export const dropToDays = (model) => {
|
|
153
|
+
const focusedDay = Option.match(model.maybeFocusedDate, {
|
|
154
|
+
onNone: () => 1,
|
|
155
|
+
onSome: date => Math.min(date.day, Calendar.daysInMonth(model.viewYear, model.viewMonth)),
|
|
156
|
+
});
|
|
157
|
+
return evo(model, {
|
|
158
|
+
viewMode: () => 'Days',
|
|
159
|
+
maybeFocusedDate: () => Option.some(Calendar.make(model.viewYear, model.viewMonth, focusedDay)),
|
|
160
|
+
});
|
|
161
|
+
};
|
|
129
162
|
const DAY_SKIP_CAP = 31;
|
|
130
163
|
const MONTH_SKIP_CAP = 12;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (aboveMax) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
if (model.disabledDaysOfWeek.includes(Calendar.dayOfWeek(date))) {
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
if (model.disabledDates.some(Calendar.isEqual(date))) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
return false;
|
|
147
|
-
};
|
|
164
|
+
/** Number of years per Years-mode page. A 3×4 grid renders one window. */
|
|
165
|
+
const YEARS_PAGE_SIZE = 12;
|
|
166
|
+
const isDateDisabled = (model, date) => Option.exists(model.maybeMinDate, min => Calendar.isBefore(date, min)) ||
|
|
167
|
+
Option.exists(model.maybeMaxDate, max => Calendar.isAfter(date, max)) ||
|
|
168
|
+
model.disabledDaysOfWeek.includes(Calendar.dayOfWeek(date)) ||
|
|
169
|
+
model.disabledDates.some(Calendar.isEqual(date));
|
|
148
170
|
/** Walks from `start` in `direction`, returning the first non-disabled date
|
|
149
171
|
* within `cap` steps. Falls back to `start` if every candidate is disabled. */
|
|
150
172
|
const skipDisabled = (model, start, direction, cap) => pipe(cap, Array.makeBy(step => Calendar.addDays(start, step * direction)), Array.findFirst(date => !isDateDisabled(model, date)), Option.getOrElse(() => start));
|
|
@@ -241,14 +263,52 @@ const applyViewMonthChange = (model, year, month, direction) => {
|
|
|
241
263
|
});
|
|
242
264
|
return [nextModel, [], Option.some(ChangedViewMonth({ year, month }))];
|
|
243
265
|
};
|
|
244
|
-
/** Direction the user moved when jumping to a new view month via
|
|
245
|
-
* Used by `skipDisabled` so a forward jump skips forward through
|
|
246
|
-
* dates and a backward jump skips backward. */
|
|
247
|
-
const
|
|
266
|
+
/** Direction the user moved when jumping to a new view year/month via grid
|
|
267
|
+
* selection. Used by `skipDisabled` so a forward jump skips forward through
|
|
268
|
+
* disabled dates and a backward jump skips backward. */
|
|
269
|
+
const jumpDirection = (model, year, month) => {
|
|
248
270
|
const next = Calendar.make(year, month, 1);
|
|
249
271
|
const current = Calendar.make(model.viewYear, model.viewMonth, 1);
|
|
250
272
|
return Calendar.isAfter(next, current) ? 1 : -1;
|
|
251
273
|
};
|
|
274
|
+
/** Maps a keyboard key to a months-grid focus shift (in months). Months
|
|
275
|
+
* mode supports horizontal (±1), vertical (±row width), and PageUp/Down
|
|
276
|
+
* (±12) navigation. */
|
|
277
|
+
const resolveMonthsKey = (key) => M.value(key).pipe(M.withReturnType(), M.when('ArrowLeft', () => -1), M.when('ArrowRight', () => 1), M.when('ArrowUp', () => -MONTHS_GRID_COLUMNS), M.when('ArrowDown', () => MONTHS_GRID_COLUMNS), M.when('PageUp', () => -MONTHS_IN_YEAR), M.when('PageDown', () => MONTHS_IN_YEAR), M.option);
|
|
278
|
+
/** Maps a keyboard key to a years-grid focus shift (in years). Years mode
|
|
279
|
+
* supports horizontal (±1), vertical (±row width), and PageUp/Down (±12 =
|
|
280
|
+
* one window) navigation. */
|
|
281
|
+
const resolveYearsKey = (key) => M.value(key).pipe(M.withReturnType(), M.when('ArrowLeft', () => -1), M.when('ArrowRight', () => 1), M.when('ArrowUp', () => -YEARS_GRID_COLUMNS), M.when('ArrowDown', () => YEARS_GRID_COLUMNS), M.when('PageUp', () => -YEARS_PAGE_SIZE), M.when('PageDown', () => YEARS_PAGE_SIZE), M.option);
|
|
282
|
+
/** Applies a months-grid focus shift, updating `maybeFocusedDate` and
|
|
283
|
+
* `viewYear` to reflect the new focused date. `viewMonth` is preserved —
|
|
284
|
+
* Months mode keyboard navigation moves the cursor without committing. */
|
|
285
|
+
const applyMonthsFocusShift = (model, monthShift) => {
|
|
286
|
+
const focused = currentOrFallbackFocus(model);
|
|
287
|
+
const nextFocus = Calendar.addMonths(focused, monthShift);
|
|
288
|
+
return [
|
|
289
|
+
evo(model, {
|
|
290
|
+
maybeFocusedDate: () => Option.some(nextFocus),
|
|
291
|
+
viewYear: () => nextFocus.year,
|
|
292
|
+
}),
|
|
293
|
+
[],
|
|
294
|
+
Option.none(),
|
|
295
|
+
];
|
|
296
|
+
};
|
|
297
|
+
/** Applies a years-grid focus shift, updating only `maybeFocusedDate`.
|
|
298
|
+
* `viewYear` is preserved so the "selected" highlight (`year === viewYear`)
|
|
299
|
+
* stays on the calendar's centered year while the cursor moves freely. The
|
|
300
|
+
* visible 12-year page is derived from the cursor in the view layer. */
|
|
301
|
+
const applyYearsFocusShift = (model, yearShift) => {
|
|
302
|
+
const focused = currentOrFallbackFocus(model);
|
|
303
|
+
const nextFocus = Calendar.addYears(focused, yearShift);
|
|
304
|
+
return [
|
|
305
|
+
evo(model, {
|
|
306
|
+
maybeFocusedDate: () => Option.some(nextFocus),
|
|
307
|
+
}),
|
|
308
|
+
[],
|
|
309
|
+
Option.none(),
|
|
310
|
+
];
|
|
311
|
+
};
|
|
252
312
|
/** Processes a calendar message and returns the next model, commands, and
|
|
253
313
|
* optional OutMessage. */
|
|
254
314
|
export const update = (model, message) => M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
|
|
@@ -256,26 +316,38 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
|
|
|
256
316
|
if (isDateDisabled(model, date)) {
|
|
257
317
|
return [model, [], Option.none()];
|
|
258
318
|
}
|
|
259
|
-
|
|
260
|
-
|
|
319
|
+
else {
|
|
320
|
+
const [nextModel, maybeOutMessage] = commitSelection(model, date);
|
|
321
|
+
return [nextModel, [], maybeOutMessage];
|
|
322
|
+
}
|
|
261
323
|
},
|
|
262
|
-
PressedKeyOnGrid: ({ key, isShift }) => {
|
|
324
|
+
PressedKeyOnGrid: ({ key, isShift }) => M.value(model.viewMode).pipe(withUpdateReturn, M.when('Days', () => {
|
|
263
325
|
const focused = currentOrFallbackFocus(model);
|
|
264
326
|
if (isCommitKey(key)) {
|
|
265
327
|
if (isDateDisabled(model, focused)) {
|
|
266
328
|
return [model, [], Option.none()];
|
|
267
329
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
return Option.match(resolveNavigationKey(key, isShift, focused, model.locale.firstDayOfWeek), {
|
|
272
|
-
onNone: () => [model, [], Option.none()],
|
|
273
|
-
onSome: ([candidate, direction, cap]) => {
|
|
274
|
-
const [nextModel, maybeOutMessage] = applyFocusMove(model, candidate, direction, cap);
|
|
330
|
+
else {
|
|
331
|
+
const [nextModel, maybeOutMessage] = commitSelection(model, focused);
|
|
275
332
|
return [nextModel, [], maybeOutMessage];
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
return Option.match(resolveNavigationKey(key, isShift, focused, model.locale.firstDayOfWeek), {
|
|
337
|
+
onNone: () => [model, [], Option.none()],
|
|
338
|
+
onSome: ([candidate, direction, cap]) => {
|
|
339
|
+
const [nextModel, maybeOutMessage] = applyFocusMove(model, candidate, direction, cap);
|
|
340
|
+
return [nextModel, [], maybeOutMessage];
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}), M.when('Months', () => Option.match(resolveMonthsKey(key), {
|
|
345
|
+
onNone: () => [model, [], Option.none()],
|
|
346
|
+
onSome: shift => applyMonthsFocusShift(model, shift),
|
|
347
|
+
})), M.when('Years', () => Option.match(resolveYearsKey(key), {
|
|
348
|
+
onNone: () => [model, [], Option.none()],
|
|
349
|
+
onSome: shift => applyYearsFocusShift(model, shift),
|
|
350
|
+
})), M.exhaustive),
|
|
279
351
|
ClickedPreviousMonthButton: () => {
|
|
280
352
|
const next = Calendar.subtractMonths(Calendar.make(model.viewYear, model.viewMonth, 1), 1);
|
|
281
353
|
return applyViewMonthChange(model, next.year, next.month, -1);
|
|
@@ -284,8 +356,42 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
|
|
|
284
356
|
const next = Calendar.addMonths(Calendar.make(model.viewYear, model.viewMonth, 1), 1);
|
|
285
357
|
return applyViewMonthChange(model, next.year, next.month, 1);
|
|
286
358
|
},
|
|
287
|
-
|
|
288
|
-
|
|
359
|
+
ClickedHeading: () => M.value(model.viewMode).pipe(withUpdateReturn, M.when('Days', () => [
|
|
360
|
+
evo(model, { viewMode: () => 'Months' }),
|
|
361
|
+
[focusGrid(model.id)],
|
|
362
|
+
Option.none(),
|
|
363
|
+
]), M.when('Months', () => [
|
|
364
|
+
evo(model, { viewMode: () => 'Years' }),
|
|
365
|
+
[focusGrid(model.id)],
|
|
366
|
+
Option.none(),
|
|
367
|
+
]), M.when('Years', () => [model, [], Option.none()]), M.exhaustive),
|
|
368
|
+
SelectedMonth: ({ month }) => {
|
|
369
|
+
if (isMonthDisabled(model, model.viewYear, month)) {
|
|
370
|
+
return [model, [], Option.none()];
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
const [nextModel, commands, maybeOutMessage] = applyViewMonthChange(model, model.viewYear, month, jumpDirection(model, model.viewYear, month));
|
|
374
|
+
return [
|
|
375
|
+
evo(nextModel, { viewMode: () => 'Days' }),
|
|
376
|
+
[...commands, focusGrid(model.id)],
|
|
377
|
+
maybeOutMessage,
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
SelectedYear: ({ year }) => {
|
|
382
|
+
if (isYearDisabled(model, year)) {
|
|
383
|
+
return [model, [], Option.none()];
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
const [nextModel, commands, maybeOutMessage] = applyViewMonthChange(model, year, model.viewMonth, jumpDirection(model, year, model.viewMonth));
|
|
387
|
+
return [
|
|
388
|
+
evo(nextModel, { viewMode: () => 'Months' }),
|
|
389
|
+
[...commands, focusGrid(model.id)],
|
|
390
|
+
maybeOutMessage,
|
|
391
|
+
];
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
PagedYears: ({ direction }) => applyYearsFocusShift(model, direction * YEARS_PAGE_SIZE),
|
|
289
395
|
FocusedGrid: () => [
|
|
290
396
|
evo(model, { isGridFocused: () => true }),
|
|
291
397
|
[],
|
|
@@ -305,7 +411,9 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
|
|
|
305
411
|
}));
|
|
306
412
|
// VIEW
|
|
307
413
|
const headingId = (modelId) => `${modelId}-heading`;
|
|
308
|
-
const
|
|
414
|
+
const dayCellId = (modelId, date) => `${modelId}-cell-${date.year}-${date.month}-${date.day}`;
|
|
415
|
+
const monthCellId = (modelId, month) => `${modelId}-cell-month-${month}`;
|
|
416
|
+
const yearCellId = (modelId, year) => `${modelId}-cell-year-${year}`;
|
|
309
417
|
const DAY_NAMES_SUNDAY_FIRST = [
|
|
310
418
|
'Sunday',
|
|
311
419
|
'Monday',
|
|
@@ -342,24 +450,6 @@ const buildGrid = (viewYear, viewMonth, firstDayOfWeek) => {
|
|
|
342
450
|
const weeks = Array.makeBy(WEEKS_IN_GRID, weekIndex => Array.makeBy(DAYS_IN_WEEK, dayIndex => Calendar.addDays(gridStart, weekIndex * DAYS_IN_WEEK + dayIndex)));
|
|
343
451
|
return { gridStart, weeks };
|
|
344
452
|
};
|
|
345
|
-
const YEAR_RANGE_LOOKAHEAD = 10;
|
|
346
|
-
const YEAR_RANGE_LOOKBEHIND = 100;
|
|
347
|
-
/** Computes the inclusive year range the year dropdown should expose,
|
|
348
|
-
* derived from min/max constraints if set, else a sensible default window
|
|
349
|
-
* around today. */
|
|
350
|
-
const resolveYearRange = (model) => {
|
|
351
|
-
const defaultStart = model.today.year - YEAR_RANGE_LOOKBEHIND;
|
|
352
|
-
const defaultEnd = model.today.year + YEAR_RANGE_LOOKAHEAD;
|
|
353
|
-
const start = Option.match(model.maybeMinDate, {
|
|
354
|
-
onNone: () => defaultStart,
|
|
355
|
-
onSome: min => min.year,
|
|
356
|
-
});
|
|
357
|
-
const end = Option.match(model.maybeMaxDate, {
|
|
358
|
-
onNone: () => defaultEnd,
|
|
359
|
-
onSome: max => max.year,
|
|
360
|
-
});
|
|
361
|
-
return [start, end];
|
|
362
|
-
};
|
|
363
453
|
const NAV_KEYS = new Set([
|
|
364
454
|
'ArrowLeft',
|
|
365
455
|
'ArrowRight',
|
|
@@ -372,52 +462,55 @@ const NAV_KEYS = new Set([
|
|
|
372
462
|
'Enter',
|
|
373
463
|
' ',
|
|
374
464
|
]);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
465
|
+
const MONTHS_GRID_COLUMNS = 3;
|
|
466
|
+
const YEARS_GRID_COLUMNS = 3;
|
|
467
|
+
const MONTHS_IN_YEAR = 12;
|
|
468
|
+
/** Returns the start year of the 12-year window the years grid renders. */
|
|
469
|
+
const yearsPageStart = (viewYear) => Math.floor(viewYear / YEARS_PAGE_SIZE) * YEARS_PAGE_SIZE;
|
|
470
|
+
/** A month range is fully disabled when its last day is below the minimum
|
|
471
|
+
* or its first day is above the maximum. */
|
|
472
|
+
const isMonthDisabled = (model, year, month) => {
|
|
473
|
+
const monthStart = Calendar.make(year, month, 1);
|
|
474
|
+
const monthEnd = Calendar.make(year, month, Calendar.daysInMonth(year, month));
|
|
475
|
+
return (Option.exists(model.maybeMinDate, min => Calendar.isBefore(monthEnd, min)) ||
|
|
476
|
+
Option.exists(model.maybeMaxDate, max => Calendar.isAfter(monthStart, max)));
|
|
477
|
+
};
|
|
478
|
+
/** A year is fully disabled when it falls entirely below the minimum date's
|
|
479
|
+
* year or entirely above the maximum date's year. */
|
|
480
|
+
const isYearDisabled = (model, year) => Option.exists(model.maybeMinDate, min => year < min.year) ||
|
|
481
|
+
Option.exists(model.maybeMaxDate, max => year > max.year);
|
|
482
|
+
const buildDaysAttributes = (config) => {
|
|
483
|
+
const { AriaActiveDescendant, AriaColcount, AriaColindex, AriaDisabled, AriaLabel, AriaRowcount, AriaRowindex, AriaSelected, DataAttribute, Id, Key, OnBlur, OnClick, OnFocus, OnKeyDownPreventDefault, Role, Tabindex, Type, } = html();
|
|
484
|
+
const { model, toParentMessage, onSelectedDate } = config;
|
|
381
485
|
const { id, viewYear, viewMonth, maybeFocusedDate, maybeSelectedDate, today, locale, isGridFocused, } = model;
|
|
382
|
-
/** Returns the parent message to dispatch when the user commits a date. In
|
|
383
|
-
* controlled mode (when `onSelectedDate` is provided), dispatches the
|
|
384
|
-
* callback directly. In uncontrolled mode, routes through the internal
|
|
385
|
-
* `ClickedDay` message so the calendar's own update manages selection. */
|
|
386
486
|
const dispatchSelectedDate = (date) => onSelectedDate !== undefined
|
|
387
487
|
? onSelectedDate(date)
|
|
388
488
|
: toParentMessage(ClickedDay({ date }));
|
|
389
489
|
const previousMonthLabel = config.previousMonthLabel ?? 'Previous month';
|
|
390
490
|
const nextMonthLabel = config.nextMonthLabel ?? 'Next month';
|
|
391
|
-
const
|
|
392
|
-
const yearSelectLabel = config.yearSelectLabel ?? 'Select year';
|
|
491
|
+
const headingButtonLabel = config.daysHeadingButtonLabel ?? 'Switch to month picker';
|
|
393
492
|
const headingText = `${locale.monthNames[viewMonth - 1]} ${viewYear}`;
|
|
394
|
-
const monthOptions = locale.monthNames.map((label, index) => ({
|
|
395
|
-
value: index + 1,
|
|
396
|
-
label,
|
|
397
|
-
}));
|
|
398
|
-
const [yearRangeStart, yearRangeEnd] = resolveYearRange(model);
|
|
399
|
-
const yearOptions = Array.makeBy(Math.max(0, yearRangeEnd - yearRangeStart + 1), index => yearRangeStart + index);
|
|
400
493
|
const rotatedDayNames = rotateDayNames(DAY_NAMES_SUNDAY_FIRST, locale.firstDayOfWeek);
|
|
401
494
|
const rotatedShortDayNames = rotateDayNames(locale.shortDayNames, locale.firstDayOfWeek);
|
|
402
495
|
const { gridStart, weeks: weeksDates } = buildGrid(viewYear, viewMonth, locale.firstDayOfWeek);
|
|
403
|
-
const rootAttributes = [
|
|
404
|
-
|
|
496
|
+
const rootAttributes = [
|
|
497
|
+
Id(id),
|
|
498
|
+
Key('Days'),
|
|
499
|
+
];
|
|
500
|
+
const previousMonthButton = [
|
|
405
501
|
Type('button'),
|
|
406
502
|
AriaLabel(previousMonthLabel),
|
|
407
503
|
OnClick(toParentMessage(ClickedPreviousMonthButton())),
|
|
408
504
|
];
|
|
409
|
-
const
|
|
505
|
+
const nextMonthButton = [
|
|
410
506
|
Type('button'),
|
|
411
507
|
AriaLabel(nextMonthLabel),
|
|
412
508
|
OnClick(toParentMessage(ClickedNextMonthButton())),
|
|
413
509
|
];
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const yearSelectAttributes = [
|
|
419
|
-
AriaLabel(yearSelectLabel),
|
|
420
|
-
OnChange(value => toParentMessage(SelectedYearFromDropdown({ year: Number(value) }))),
|
|
510
|
+
const headingButton = [
|
|
511
|
+
Type('button'),
|
|
512
|
+
AriaLabel(headingButtonLabel),
|
|
513
|
+
OnClick(toParentMessage(ClickedHeading())),
|
|
421
514
|
];
|
|
422
515
|
const handleKeyDown = (key, modifiers) => {
|
|
423
516
|
if (!NAV_KEYS.has(key)) {
|
|
@@ -428,7 +521,7 @@ export const view = (config) => {
|
|
|
428
521
|
}
|
|
429
522
|
return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
|
|
430
523
|
};
|
|
431
|
-
const activeDescendantAttributes = pipe(maybeFocusedDate, Option.map(date => AriaActiveDescendant(
|
|
524
|
+
const activeDescendantAttributes = pipe(maybeFocusedDate, Option.map(date => AriaActiveDescendant(dayCellId(id, date))), Option.toArray);
|
|
432
525
|
const gridAttributes = [
|
|
433
526
|
Id(gridId(id)),
|
|
434
527
|
Role('grid'),
|
|
@@ -470,7 +563,7 @@ export const view = (config) => {
|
|
|
470
563
|
OptionExt.when(isDisabled, DataAttribute('disabled', '')),
|
|
471
564
|
]);
|
|
472
565
|
const cellAttributes = [
|
|
473
|
-
Id(
|
|
566
|
+
Id(dayCellId(id, date)),
|
|
474
567
|
Role('gridcell'),
|
|
475
568
|
AriaSelected(isSelected),
|
|
476
569
|
AriaColindex(columnIndex + 1),
|
|
@@ -506,21 +599,216 @@ export const view = (config) => {
|
|
|
506
599
|
cells: weekDates.map(buildDayCell),
|
|
507
600
|
};
|
|
508
601
|
});
|
|
509
|
-
return
|
|
602
|
+
return {
|
|
603
|
+
_tag: 'Days',
|
|
510
604
|
root: rootAttributes,
|
|
511
|
-
previousMonthButton
|
|
512
|
-
nextMonthButton
|
|
605
|
+
previousMonthButton,
|
|
606
|
+
nextMonthButton,
|
|
607
|
+
headingButton,
|
|
513
608
|
heading: { id: headingId(id), text: headingText },
|
|
514
|
-
monthSelect: monthSelectAttributes,
|
|
515
|
-
monthOptions,
|
|
516
|
-
yearSelect: yearSelectAttributes,
|
|
517
|
-
yearOptions,
|
|
518
609
|
grid: gridAttributes,
|
|
519
610
|
headerRow: headerRowAttributes,
|
|
520
611
|
columnHeaders,
|
|
521
612
|
weeks,
|
|
613
|
+
};
|
|
614
|
+
};
|
|
615
|
+
const buildMonthsAttributes = (config) => {
|
|
616
|
+
const { AriaActiveDescendant, AriaDisabled, AriaLabel, AriaSelected, DataAttribute, Id, Key, OnBlur, OnClick, OnFocus, OnKeyDownPreventDefault, Role, Tabindex, Type, } = html();
|
|
617
|
+
const { model, toParentMessage } = config;
|
|
618
|
+
const { id, viewYear, viewMonth, maybeFocusedDate, today, locale, isGridFocused, } = model;
|
|
619
|
+
const headingButtonLabel = config.monthsHeadingButtonLabel ?? 'Switch to year picker';
|
|
620
|
+
const headingText = `${viewYear}`;
|
|
621
|
+
const rootAttributes = [
|
|
622
|
+
Id(id),
|
|
623
|
+
Key('Months'),
|
|
624
|
+
];
|
|
625
|
+
const headingButton = [
|
|
626
|
+
Type('button'),
|
|
627
|
+
AriaLabel(headingButtonLabel),
|
|
628
|
+
OnClick(toParentMessage(ClickedHeading())),
|
|
629
|
+
];
|
|
630
|
+
const focusedMonth = Option.match(maybeFocusedDate, {
|
|
631
|
+
onNone: () => viewMonth,
|
|
632
|
+
onSome: date => (date.year === viewYear ? date.month : viewMonth),
|
|
522
633
|
});
|
|
634
|
+
const handleKeyDown = (key, modifiers) => {
|
|
635
|
+
if (!NAV_KEYS.has(key)) {
|
|
636
|
+
return Option.none();
|
|
637
|
+
}
|
|
638
|
+
else if (isCommitKey(key)) {
|
|
639
|
+
return OptionExt.when(!isMonthDisabled(model, viewYear, focusedMonth), toParentMessage(SelectedMonth({ month: focusedMonth })));
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
const activeDescendantAttributes = [
|
|
646
|
+
AriaActiveDescendant(monthCellId(id, focusedMonth)),
|
|
647
|
+
];
|
|
648
|
+
const gridAttributes = [
|
|
649
|
+
Id(gridId(id)),
|
|
650
|
+
Role('grid'),
|
|
651
|
+
AriaLabel(`Month picker, ${headingText}`),
|
|
652
|
+
Tabindex(0),
|
|
653
|
+
OnFocus(toParentMessage(FocusedGrid())),
|
|
654
|
+
OnBlur(toParentMessage(BlurredGrid())),
|
|
655
|
+
OnKeyDownPreventDefault(handleKeyDown),
|
|
656
|
+
...activeDescendantAttributes,
|
|
657
|
+
];
|
|
658
|
+
const buildMonthCell = (month) => {
|
|
659
|
+
const label = locale.monthNames[month - 1] ?? String(month);
|
|
660
|
+
const shortLabel = locale.shortMonthNames[month - 1] ?? label;
|
|
661
|
+
const isSelected = month === viewMonth;
|
|
662
|
+
const isFocused = month === focusedMonth;
|
|
663
|
+
const isCurrentMonth = today.year === viewYear && today.month === month;
|
|
664
|
+
const isDisabled = isMonthDisabled(model, viewYear, month);
|
|
665
|
+
const stateDataAttributes = Array.getSomes([
|
|
666
|
+
OptionExt.when(isCurrentMonth, DataAttribute('today', '')),
|
|
667
|
+
OptionExt.when(isSelected, DataAttribute('selected', '')),
|
|
668
|
+
OptionExt.when(isFocused && isGridFocused, DataAttribute('focused', '')),
|
|
669
|
+
OptionExt.when(isDisabled, DataAttribute('disabled', '')),
|
|
670
|
+
]);
|
|
671
|
+
const cellAttributes = [
|
|
672
|
+
Id(monthCellId(id, month)),
|
|
673
|
+
Role('gridcell'),
|
|
674
|
+
AriaSelected(isSelected),
|
|
675
|
+
...stateDataAttributes,
|
|
676
|
+
];
|
|
677
|
+
const buttonAttributes = [
|
|
678
|
+
Type('button'),
|
|
679
|
+
Tabindex(-1),
|
|
680
|
+
AriaLabel(`${label} ${viewYear}`),
|
|
681
|
+
AriaDisabled(isDisabled),
|
|
682
|
+
...(isDisabled
|
|
683
|
+
? []
|
|
684
|
+
: [OnClick(toParentMessage(SelectedMonth({ month })))]),
|
|
685
|
+
];
|
|
686
|
+
return {
|
|
687
|
+
month,
|
|
688
|
+
label,
|
|
689
|
+
shortLabel,
|
|
690
|
+
cellAttributes,
|
|
691
|
+
buttonAttributes,
|
|
692
|
+
isSelected,
|
|
693
|
+
isFocused: isFocused && isGridFocused,
|
|
694
|
+
isCurrentMonth,
|
|
695
|
+
isDisabled,
|
|
696
|
+
};
|
|
697
|
+
};
|
|
698
|
+
const cells = Array.makeBy(MONTHS_IN_YEAR, monthIndex => buildMonthCell(monthIndex + 1));
|
|
699
|
+
return {
|
|
700
|
+
_tag: 'Months',
|
|
701
|
+
root: rootAttributes,
|
|
702
|
+
headingButton,
|
|
703
|
+
heading: { id: headingId(id), text: headingText },
|
|
704
|
+
grid: gridAttributes,
|
|
705
|
+
cells,
|
|
706
|
+
};
|
|
707
|
+
};
|
|
708
|
+
const buildYearsAttributes = (config) => {
|
|
709
|
+
const { AriaActiveDescendant, AriaDisabled, AriaLabel, AriaSelected, DataAttribute, Id, Key, OnBlur, OnClick, OnFocus, OnKeyDownPreventDefault, Role, Tabindex, Type, } = html();
|
|
710
|
+
const { model, toParentMessage } = config;
|
|
711
|
+
const { id, viewYear, maybeFocusedDate, today, isGridFocused } = model;
|
|
712
|
+
const previousYearsPageLabel = config.previousYearsPageLabel ?? 'Previous 12 years';
|
|
713
|
+
const nextYearsPageLabel = config.nextYearsPageLabel ?? 'Next 12 years';
|
|
714
|
+
const cursorYear = Option.match(maybeFocusedDate, {
|
|
715
|
+
onNone: () => viewYear,
|
|
716
|
+
onSome: date => date.year,
|
|
717
|
+
});
|
|
718
|
+
const pageStart = yearsPageStart(cursorYear);
|
|
719
|
+
const pageEnd = pageStart + YEARS_PAGE_SIZE - 1;
|
|
720
|
+
const headingText = `${pageStart}–${pageEnd}`;
|
|
721
|
+
const rootAttributes = [
|
|
722
|
+
Id(id),
|
|
723
|
+
Key('Years'),
|
|
724
|
+
];
|
|
725
|
+
const previousPageButton = [
|
|
726
|
+
Type('button'),
|
|
727
|
+
AriaLabel(previousYearsPageLabel),
|
|
728
|
+
OnClick(toParentMessage(PagedYears({ direction: -1 }))),
|
|
729
|
+
];
|
|
730
|
+
const nextPageButton = [
|
|
731
|
+
Type('button'),
|
|
732
|
+
AriaLabel(nextYearsPageLabel),
|
|
733
|
+
OnClick(toParentMessage(PagedYears({ direction: 1 }))),
|
|
734
|
+
];
|
|
735
|
+
const focusedYear = cursorYear;
|
|
736
|
+
const handleKeyDown = (key, modifiers) => {
|
|
737
|
+
if (!NAV_KEYS.has(key)) {
|
|
738
|
+
return Option.none();
|
|
739
|
+
}
|
|
740
|
+
else if (isCommitKey(key)) {
|
|
741
|
+
return OptionExt.when(!isYearDisabled(model, focusedYear), toParentMessage(SelectedYear({ year: focusedYear })));
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
const activeDescendantAttributes = [
|
|
748
|
+
AriaActiveDescendant(yearCellId(id, focusedYear)),
|
|
749
|
+
];
|
|
750
|
+
const gridAttributes = [
|
|
751
|
+
Id(gridId(id)),
|
|
752
|
+
Role('grid'),
|
|
753
|
+
AriaLabel(`Year picker, ${headingText}`),
|
|
754
|
+
Tabindex(0),
|
|
755
|
+
OnFocus(toParentMessage(FocusedGrid())),
|
|
756
|
+
OnBlur(toParentMessage(BlurredGrid())),
|
|
757
|
+
OnKeyDownPreventDefault(handleKeyDown),
|
|
758
|
+
...activeDescendantAttributes,
|
|
759
|
+
];
|
|
760
|
+
const buildYearCell = (year) => {
|
|
761
|
+
const label = String(year);
|
|
762
|
+
const isSelected = year === viewYear;
|
|
763
|
+
const isFocused = year === focusedYear;
|
|
764
|
+
const isCurrentYear = today.year === year;
|
|
765
|
+
const isDisabled = isYearDisabled(model, year);
|
|
766
|
+
const stateDataAttributes = Array.getSomes([
|
|
767
|
+
OptionExt.when(isCurrentYear, DataAttribute('today', '')),
|
|
768
|
+
OptionExt.when(isSelected, DataAttribute('selected', '')),
|
|
769
|
+
OptionExt.when(isFocused && isGridFocused, DataAttribute('focused', '')),
|
|
770
|
+
OptionExt.when(isDisabled, DataAttribute('disabled', '')),
|
|
771
|
+
]);
|
|
772
|
+
const cellAttributes = [
|
|
773
|
+
Id(yearCellId(id, year)),
|
|
774
|
+
Role('gridcell'),
|
|
775
|
+
AriaSelected(isSelected),
|
|
776
|
+
...stateDataAttributes,
|
|
777
|
+
];
|
|
778
|
+
const buttonAttributes = [
|
|
779
|
+
Type('button'),
|
|
780
|
+
Tabindex(-1),
|
|
781
|
+
AriaLabel(label),
|
|
782
|
+
AriaDisabled(isDisabled),
|
|
783
|
+
...(isDisabled ? [] : [OnClick(toParentMessage(SelectedYear({ year })))]),
|
|
784
|
+
];
|
|
785
|
+
return {
|
|
786
|
+
year,
|
|
787
|
+
label,
|
|
788
|
+
cellAttributes,
|
|
789
|
+
buttonAttributes,
|
|
790
|
+
isSelected,
|
|
791
|
+
isFocused: isFocused && isGridFocused,
|
|
792
|
+
isCurrentYear,
|
|
793
|
+
isDisabled,
|
|
794
|
+
};
|
|
795
|
+
};
|
|
796
|
+
const cells = Array.makeBy(YEARS_PAGE_SIZE, offset => buildYearCell(pageStart + offset));
|
|
797
|
+
return {
|
|
798
|
+
_tag: 'Years',
|
|
799
|
+
root: rootAttributes,
|
|
800
|
+
previousPageButton,
|
|
801
|
+
nextPageButton,
|
|
802
|
+
heading: { id: headingId(id), text: headingText },
|
|
803
|
+
grid: gridAttributes,
|
|
804
|
+
cells,
|
|
805
|
+
};
|
|
523
806
|
};
|
|
807
|
+
/** Renders an accessible calendar. Builds mode-specific ARIA attribute
|
|
808
|
+
* groups and derived cell data, then delegates layout to the consumer's
|
|
809
|
+
* `toView` callback. The variant of `CalendarAttributes` passed to `toView`
|
|
810
|
+
* matches `model.viewMode`. */
|
|
811
|
+
export const view = (config) => config.toView(M.value(config.model.viewMode).pipe(M.withReturnType(), M.when('Days', () => buildDaysAttributes(config)), M.when('Months', () => buildMonthsAttributes(config)), M.when('Years', () => buildYearsAttributes(config)), M.exhaustive));
|
|
524
812
|
/** Creates a memoized calendar view. Static config is captured in a closure;
|
|
525
813
|
* only `model` and `toParentMessage` are compared per render via `createLazy`. */
|
|
526
814
|
export const lazy = (staticConfig) => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { init, update, view, lazy, focusGrid, selectDate, setMinDate, setMaxDate, setDisabledDates, setDisabledDaysOfWeek, Model, Message, OutMessage, ChangedViewMonth, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton,
|
|
2
|
-
export type { InitConfig, ViewConfig, CalendarAttributes, DayCell, ColumnHeader, Week, } from './index.js';
|
|
1
|
+
export { init, update, view, lazy, focusGrid, selectDate, setMinDate, setMaxDate, setDisabledDates, setDisabledDaysOfWeek, dropToDays, Model, ViewMode, Message, OutMessage, ChangedViewMonth, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton, ClickedHeading, SelectedMonth, SelectedYear, PagedYears, FocusedGrid, BlurredGrid, RefreshedToday, CompletedFocusGrid, FocusGrid, } from './index.js';
|
|
2
|
+
export type { InitConfig, ViewConfig, CalendarAttributes, DaysModeAttributes, MonthsModeAttributes, YearsModeAttributes, DayCell, ColumnHeader, Week, MonthCell, YearCell, } from './index.js';
|
|
3
3
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,qBAAqB,EACrB,UAAU,EACV,KAAK,EACL,QAAQ,EACR,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,cAAc,EACd,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,SAAS,GACV,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { init, update, view, lazy, focusGrid, selectDate, setMinDate, setMaxDate, setDisabledDates, setDisabledDaysOfWeek, Model, Message, OutMessage, ChangedViewMonth, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton,
|
|
1
|
+
export { init, update, view, lazy, focusGrid, selectDate, setMinDate, setMaxDate, setDisabledDates, setDisabledDaysOfWeek, dropToDays, Model, ViewMode, Message, OutMessage, ChangedViewMonth, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton, ClickedHeading, SelectedMonth, SelectedYear, PagedYears, FocusedGrid, BlurredGrid, RefreshedToday, CompletedFocusGrid, FocusGrid, } from './index.js';
|
|
@@ -24,6 +24,7 @@ export declare const Model: S.Struct<{
|
|
|
24
24
|
}>>;
|
|
25
25
|
viewYear: typeof S.Int;
|
|
26
26
|
viewMonth: S.filter<typeof S.Int>;
|
|
27
|
+
viewMode: S.Literal<["Days", "Months", "Years"]>;
|
|
27
28
|
maybeFocusedDate: S.OptionFromSelf<S.filter<S.Struct<{
|
|
28
29
|
year: typeof S.Int;
|
|
29
30
|
month: S.filter<typeof S.Int>;
|
|
@@ -85,10 +86,12 @@ export declare const GotCalendarMessage: import("../../schema/index.js").Callabl
|
|
|
85
86
|
}>, import("../../schema/index.js").CallableTaggedStruct<"PressedKeyOnGrid", {
|
|
86
87
|
key: typeof S.String;
|
|
87
88
|
isShift: typeof S.Boolean;
|
|
88
|
-
}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedPreviousMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedNextMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"
|
|
89
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedPreviousMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedNextMonthButton", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedHeading", {}>, import("../../schema/index.js").CallableTaggedStruct<"SelectedMonth", {
|
|
89
90
|
month: typeof S.Int;
|
|
90
|
-
}>, import("../../schema/index.js").CallableTaggedStruct<"
|
|
91
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"SelectedYear", {
|
|
91
92
|
year: typeof S.Int;
|
|
93
|
+
}>, import("../../schema/index.js").CallableTaggedStruct<"PagedYears", {
|
|
94
|
+
direction: S.Literal<[1, -1]>;
|
|
92
95
|
}>, import("../../schema/index.js").CallableTaggedStruct<"FocusedGrid", {}>, import("../../schema/index.js").CallableTaggedStruct<"BlurredGrid", {}>, import("../../schema/index.js").CallableTaggedStruct<"RefreshedToday", {
|
|
93
96
|
today: S.filter<S.Struct<{
|
|
94
97
|
year: typeof S.Int;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/datePicker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhE,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EACL,KAAK,SAAS,EACd,KAAK,IAAI,EAGV,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,KAAK,UAAU,MAAM,sBAAsB,CAAA;AAKlD;;mDAEmD;AACnD,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/datePicker/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -108,7 +108,10 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
|
|
|
108
108
|
Opened: () => {
|
|
109
109
|
const [nextPopover, popoverCommands] = Popover.open(model.popover);
|
|
110
110
|
return [
|
|
111
|
-
evo(model, {
|
|
111
|
+
evo(model, {
|
|
112
|
+
popover: () => nextPopover,
|
|
113
|
+
calendar: () => UiCalendar.dropToDays(model.calendar),
|
|
114
|
+
}),
|
|
112
115
|
mapPopoverCommands(popoverCommands),
|
|
113
116
|
Option.none(),
|
|
114
117
|
];
|
|
@@ -116,7 +119,10 @@ export const update = (model, message) => M.value(message).pipe(withUpdateReturn
|
|
|
116
119
|
Closed: () => {
|
|
117
120
|
const [nextPopover, popoverCommands] = Popover.close(model.popover);
|
|
118
121
|
return [
|
|
119
|
-
evo(model, {
|
|
122
|
+
evo(model, {
|
|
123
|
+
popover: () => nextPopover,
|
|
124
|
+
calendar: () => UiCalendar.dropToDays(model.calendar),
|
|
125
|
+
}),
|
|
120
126
|
mapPopoverCommands(popoverCommands),
|
|
121
127
|
Option.none(),
|
|
122
128
|
];
|
|
@@ -68,8 +68,26 @@ export declare const update: (model: Model, message: Message) => readonly [Model
|
|
|
68
68
|
*
|
|
69
69
|
* Should be called after the container has rendered. If the container is not
|
|
70
70
|
* yet in the DOM the Command silently no-ops (the model still transitions
|
|
71
|
-
* through `ScrollingToIndex` → `Idle` via the version-matched completion).
|
|
71
|
+
* through `ScrollingToIndex` → `Idle` via the version-matched completion).
|
|
72
|
+
*
|
|
73
|
+
* Assumes uniform row heights: target scroll position is computed as
|
|
74
|
+
* `index * model.rowHeightPx`. For variable-height rows, use
|
|
75
|
+
* `scrollToIndexVariable`. */
|
|
72
76
|
export declare const scrollToIndex: (model: Model, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
|
|
77
|
+
/** Variable-height counterpart of `scrollToIndex`. Walks the heights of items
|
|
78
|
+
* before `index` to compute the target `scrollTop`. Use this when rendering
|
|
79
|
+
* the list with `itemToRowHeightPx`; use `scrollToIndex` for uniform heights.
|
|
80
|
+
*
|
|
81
|
+
* Out-of-range indices clamp to the corresponding edge: negative or zero
|
|
82
|
+
* scrolls to the top, indices past the end scroll past the last row.
|
|
83
|
+
*
|
|
84
|
+
* Note: when restoring `initialScrollTop` on the first measurement of a
|
|
85
|
+
* variable-height list, the runtime falls back to uniform-height math (using
|
|
86
|
+
* `model.rowHeightPx`) because items aren't reachable from the `update`
|
|
87
|
+
* function. Consumers who need an accurate initial scroll on a
|
|
88
|
+
* variable-height list should call `scrollToIndexVariable` after the first
|
|
89
|
+
* `MeasuredContainer` arrives. */
|
|
90
|
+
export declare const scrollToIndexVariable: <Item>(model: Model, items: ReadonlyArray<Item>, itemToRowHeightPx: (item: Item, index: number) => number, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
|
|
73
91
|
/** Slice of the data array that the view should render, plus the spacer
|
|
74
92
|
* heights that keep the scrollbar physically correct. The first row in the
|
|
75
93
|
* slice corresponds to data index `startIndex`. */
|
|
@@ -82,10 +100,25 @@ export type VisibleWindow = Readonly<{
|
|
|
82
100
|
/** Computes the visible slice of a data array given the current scroll
|
|
83
101
|
* position, container height, row height, and an overscan buffer.
|
|
84
102
|
*
|
|
103
|
+
* Assumes uniform row heights via `model.rowHeightPx`. For variable-height
|
|
104
|
+
* rows, use `visibleWindowVariable`.
|
|
105
|
+
*
|
|
85
106
|
* Returns `Option.none()` when the container has not yet been measured;
|
|
86
107
|
* callers should render a placeholder (or `Html.empty`) and wait for the
|
|
87
108
|
* first `MeasuredContainer` message. */
|
|
88
109
|
export declare const visibleWindow: (model: Model, itemCount: number, overscan: number) => Option.Option<VisibleWindow>;
|
|
110
|
+
/** Variable-height counterpart of `visibleWindow`. Walks the heights of every
|
|
111
|
+
* item to build a prefix-sum array, then locates the visible slice with two
|
|
112
|
+
* linear searches.
|
|
113
|
+
*
|
|
114
|
+
* Cost is O(N) per call, walking the whole `items` array once to build the
|
|
115
|
+
* prefix sums. For lists in the 10k-item range, this comfortably fits inside
|
|
116
|
+
* a 60Hz scroll budget. Larger lists or hotter scroll paths can layer a
|
|
117
|
+
* prefix-sum cache invalidated when items change; that lives behind the same
|
|
118
|
+
* return shape so consumers don't have to know.
|
|
119
|
+
*
|
|
120
|
+
* Returns `Option.none()` when the container has not yet been measured. */
|
|
121
|
+
export declare const visibleWindowVariable: <Item>(model: Model, items: ReadonlyArray<Item>, itemToRowHeightPx: (item: Item, index: number) => number, overscan: number) => Option.Option<VisibleWindow>;
|
|
89
122
|
/** Schema describing the subscription dependencies for container scroll and
|
|
90
123
|
* resize tracking. */
|
|
91
124
|
export declare const SubscriptionDeps: S.Struct<{
|
|
@@ -150,6 +183,14 @@ export type ViewConfig<Message, Item> = Readonly<{
|
|
|
150
183
|
items: ReadonlyArray<Item>;
|
|
151
184
|
itemToKey: (item: Item, index: number) => string;
|
|
152
185
|
itemToView: (item: Item, index: number) => Html;
|
|
186
|
+
/** Optional per-item row height in pixels. When provided, the list renders
|
|
187
|
+
* with variable-height rows: each row's wrapper takes the height returned
|
|
188
|
+
* by this callback, and scroll math walks the items to compute the visible
|
|
189
|
+
* slice and spacers. When absent, all rows use `model.rowHeightPx`. Use
|
|
190
|
+
* this for tables with wrapping cells, taller detail rows, or any list
|
|
191
|
+
* where rows differ. Prefer the uniform `rowHeightPx` path when row
|
|
192
|
+
* heights are stable: it avoids the per-render walk over `items`. */
|
|
193
|
+
itemToRowHeightPx?: (item: Item, index: number) => number;
|
|
153
194
|
/** Number of rows rendered above and below the visible viewport. Higher
|
|
154
195
|
* values smooth out fast scroll at the cost of mounting more DOM. Default
|
|
155
196
|
* is 5; react-window uses 1 and react-virtualized uses 3. Pick a value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,MAAM,EACN,MAAM,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Array, Effect, Match as M, Number, Option, Schema as S, Stream, } from 'effect';
|
|
1
|
+
import { Array, Effect, Match as M, Number, Option, Schema as S, Stream, pipe, } from 'effect';
|
|
2
2
|
import * as Command from '../../command/index.js';
|
|
3
3
|
import { createLazy, html, } from '../../html/index.js';
|
|
4
4
|
import { m } from '../../message/index.js';
|
|
@@ -110,6 +110,16 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
|
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
112
|
}));
|
|
113
|
+
const buildScrollToIndex = (model, index, targetScrollTop) => {
|
|
114
|
+
const nextVersion = Number.increment(model.pendingScrollVersion);
|
|
115
|
+
return [
|
|
116
|
+
evo(model, {
|
|
117
|
+
pendingScrollVersion: () => nextVersion,
|
|
118
|
+
pendingScroll: () => ScrollingToIndex({ index, version: nextVersion }),
|
|
119
|
+
}),
|
|
120
|
+
[applyScroll(model.id, targetScrollTop, nextVersion)],
|
|
121
|
+
];
|
|
122
|
+
};
|
|
113
123
|
/** Programmatically scrolls the container so the row at `index` is visible.
|
|
114
124
|
* Returns the next model and a Command that mutates `element.scrollTop`. The
|
|
115
125
|
* natural scroll event then flows back through `ScrolledContainer` and the
|
|
@@ -121,22 +131,42 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
|
|
|
121
131
|
*
|
|
122
132
|
* Should be called after the container has rendered. If the container is not
|
|
123
133
|
* yet in the DOM the Command silently no-ops (the model still transitions
|
|
124
|
-
* through `ScrollingToIndex` → `Idle` via the version-matched completion).
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
* through `ScrollingToIndex` → `Idle` via the version-matched completion).
|
|
135
|
+
*
|
|
136
|
+
* Assumes uniform row heights: target scroll position is computed as
|
|
137
|
+
* `index * model.rowHeightPx`. For variable-height rows, use
|
|
138
|
+
* `scrollToIndexVariable`. */
|
|
139
|
+
export const scrollToIndex = (model, index) => buildScrollToIndex(model, index, index * model.rowHeightPx);
|
|
140
|
+
/** Variable-height counterpart of `scrollToIndex`. Walks the heights of items
|
|
141
|
+
* before `index` to compute the target `scrollTop`. Use this when rendering
|
|
142
|
+
* the list with `itemToRowHeightPx`; use `scrollToIndex` for uniform heights.
|
|
143
|
+
*
|
|
144
|
+
* Out-of-range indices clamp to the corresponding edge: negative or zero
|
|
145
|
+
* scrolls to the top, indices past the end scroll past the last row.
|
|
146
|
+
*
|
|
147
|
+
* Note: when restoring `initialScrollTop` on the first measurement of a
|
|
148
|
+
* variable-height list, the runtime falls back to uniform-height math (using
|
|
149
|
+
* `model.rowHeightPx`) because items aren't reachable from the `update`
|
|
150
|
+
* function. Consumers who need an accurate initial scroll on a
|
|
151
|
+
* variable-height list should call `scrollToIndexVariable` after the first
|
|
152
|
+
* `MeasuredContainer` arrives. */
|
|
153
|
+
export const scrollToIndexVariable = (model, items, itemToRowHeightPx, index) => {
|
|
154
|
+
const cumulativeOffsets = prefixSum(items, itemToRowHeightPx);
|
|
155
|
+
const targetScrollTop = pipe(cumulativeOffsets, Array.get(Math.max(0, index)), Option.getOrElse(() => lastOrZero(cumulativeOffsets)));
|
|
156
|
+
return buildScrollToIndex(model, index, targetScrollTop);
|
|
135
157
|
};
|
|
136
158
|
const clampIndex = (index, itemCount) => Math.max(0, Math.min(index, itemCount));
|
|
159
|
+
const prefixSum = (items, itemToRowHeightPx) => {
|
|
160
|
+
const heights = Array.map(items, itemToRowHeightPx);
|
|
161
|
+
return Array.scan(heights, 0, (cumulative, height) => cumulative + height);
|
|
162
|
+
};
|
|
163
|
+
const lastOrZero = (values) => pipe(values, Array.last, Option.getOrElse(() => 0));
|
|
137
164
|
/** Computes the visible slice of a data array given the current scroll
|
|
138
165
|
* position, container height, row height, and an overscan buffer.
|
|
139
166
|
*
|
|
167
|
+
* Assumes uniform row heights via `model.rowHeightPx`. For variable-height
|
|
168
|
+
* rows, use `visibleWindowVariable`.
|
|
169
|
+
*
|
|
140
170
|
* Returns `Option.none()` when the container has not yet been measured;
|
|
141
171
|
* callers should render a placeholder (or `Html.empty`) and wait for the
|
|
142
172
|
* first `MeasuredContainer` message. */
|
|
@@ -157,6 +187,41 @@ export const visibleWindow = (model, itemCount, overscan) => M.value(model.measu
|
|
|
157
187
|
});
|
|
158
188
|
},
|
|
159
189
|
}));
|
|
190
|
+
/** Variable-height counterpart of `visibleWindow`. Walks the heights of every
|
|
191
|
+
* item to build a prefix-sum array, then locates the visible slice with two
|
|
192
|
+
* linear searches.
|
|
193
|
+
*
|
|
194
|
+
* Cost is O(N) per call, walking the whole `items` array once to build the
|
|
195
|
+
* prefix sums. For lists in the 10k-item range, this comfortably fits inside
|
|
196
|
+
* a 60Hz scroll budget. Larger lists or hotter scroll paths can layer a
|
|
197
|
+
* prefix-sum cache invalidated when items change; that lives behind the same
|
|
198
|
+
* return shape so consumers don't have to know.
|
|
199
|
+
*
|
|
200
|
+
* Returns `Option.none()` when the container has not yet been measured. */
|
|
201
|
+
export const visibleWindowVariable = (model, items, itemToRowHeightPx, overscan) => M.value(model.measurement).pipe(M.withReturnType(), M.tagsExhaustive({
|
|
202
|
+
Unmeasured: () => Option.none(),
|
|
203
|
+
Measured: ({ containerHeight }) => {
|
|
204
|
+
const itemCount = items.length;
|
|
205
|
+
const cumulativeOffsets = prefixSum(items, itemToRowHeightPx);
|
|
206
|
+
const totalHeight = lastOrZero(cumulativeOffsets);
|
|
207
|
+
const firstVisibleIndex = pipe(cumulativeOffsets, Array.findFirstIndex(Number.greaterThan(model.scrollTop)), Option.match({
|
|
208
|
+
onNone: () => itemCount,
|
|
209
|
+
onSome: index => Math.max(0, index - 1),
|
|
210
|
+
}));
|
|
211
|
+
const lastVisibleIndex = pipe(cumulativeOffsets, Array.findFirstIndex(Number.greaterThanOrEqualTo(model.scrollTop + containerHeight)), Option.getOrElse(() => itemCount));
|
|
212
|
+
const startIndex = clampIndex(firstVisibleIndex - overscan, itemCount);
|
|
213
|
+
const endIndex = clampIndex(lastVisibleIndex + overscan, itemCount);
|
|
214
|
+
const topSpacerHeight = pipe(cumulativeOffsets, Array.get(startIndex), Option.getOrElse(() => 0));
|
|
215
|
+
const offsetAtEnd = pipe(cumulativeOffsets, Array.get(endIndex), Option.getOrElse(() => totalHeight));
|
|
216
|
+
const bottomSpacerHeight = totalHeight - offsetAtEnd;
|
|
217
|
+
return Option.some({
|
|
218
|
+
startIndex,
|
|
219
|
+
endIndex,
|
|
220
|
+
topSpacerHeight,
|
|
221
|
+
bottomSpacerHeight,
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
}));
|
|
160
225
|
// SUBSCRIPTION
|
|
161
226
|
const containerElement = (id) => Option.fromNullable(document.getElementById(id));
|
|
162
227
|
/** Schema describing the subscription dependencies for container scroll and
|
|
@@ -278,7 +343,7 @@ const DEFAULT_OVERSCAN = 5;
|
|
|
278
343
|
* count of currently mounted rows. */
|
|
279
344
|
export const view = (config) => {
|
|
280
345
|
const { AriaPosinset, AriaSetsize, Class, DataAttribute, Id, Role, Style, keyed, } = html();
|
|
281
|
-
const { model, items, itemToKey, itemToView, overscan = DEFAULT_OVERSCAN, rowElement = 'li', className, attributes = [], } = config;
|
|
346
|
+
const { model, items, itemToKey, itemToView, itemToRowHeightPx, overscan = DEFAULT_OVERSCAN, rowElement = 'li', className, attributes = [], } = config;
|
|
282
347
|
const containerAttributes = [
|
|
283
348
|
Id(model.id),
|
|
284
349
|
Role('list'),
|
|
@@ -293,7 +358,13 @@ export const view = (config) => {
|
|
|
293
358
|
...attributes,
|
|
294
359
|
];
|
|
295
360
|
const renderContainer = (children) => keyed('ul')(model.id, containerAttributes, children);
|
|
296
|
-
|
|
361
|
+
const maybeWindow = itemToRowHeightPx !== undefined
|
|
362
|
+
? visibleWindowVariable(model, items, itemToRowHeightPx, overscan)
|
|
363
|
+
: visibleWindow(model, items.length, overscan);
|
|
364
|
+
const rowHeightFor = (item, dataIndex) => itemToRowHeightPx !== undefined
|
|
365
|
+
? itemToRowHeightPx(item, dataIndex)
|
|
366
|
+
: model.rowHeightPx;
|
|
367
|
+
return Option.match(maybeWindow, {
|
|
297
368
|
onNone: () => renderContainer([]),
|
|
298
369
|
onSome: ({ startIndex, endIndex, topSpacerHeight, bottomSpacerHeight }) => {
|
|
299
370
|
const visibleItems = items.slice(startIndex, endIndex);
|
|
@@ -306,7 +377,10 @@ export const view = (config) => {
|
|
|
306
377
|
DataAttribute('virtual-list-item-index', String(dataIndex)),
|
|
307
378
|
AriaSetsize(items.length),
|
|
308
379
|
AriaPosinset(dataIndex + 1),
|
|
309
|
-
Style({
|
|
380
|
+
Style({
|
|
381
|
+
height: `${rowHeightFor(item, dataIndex)}px`,
|
|
382
|
+
display: 'grid',
|
|
383
|
+
}),
|
|
310
384
|
], [itemToView(item, dataIndex)]);
|
|
311
385
|
});
|
|
312
386
|
return renderContainer([topSpacer, ...renderedRows, bottomSpacer]);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { init, update, scrollToIndex, view, lazy, subscriptions, visibleWindow, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, SubscriptionDeps, } from './index.js';
|
|
1
|
+
export { init, update, scrollToIndex, scrollToIndexVariable, view, lazy, subscriptions, visibleWindow, visibleWindowVariable, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, SubscriptionDeps, } from './index.js';
|
|
2
2
|
export type { InitConfig, ViewConfig, VisibleWindow } from './index.js';
|
|
3
3
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,IAAI,EACJ,IAAI,EACJ,aAAa,EACb,aAAa,EACb,KAAK,EACL,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,qBAAqB,EACrB,IAAI,EACJ,IAAI,EACJ,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,KAAK,EACL,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { init, update, scrollToIndex, view, lazy, subscriptions, visibleWindow, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, SubscriptionDeps, } from './index.js';
|
|
1
|
+
export { init, update, scrollToIndex, scrollToIndexVariable, view, lazy, subscriptions, visibleWindow, visibleWindowVariable, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, SubscriptionDeps, } from './index.js';
|