foldkit 0.101.0 → 0.102.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +2 -1
  2. package/dist/canvas/view.d.ts +1 -1
  3. package/dist/canvas/view.d.ts.map +1 -1
  4. package/dist/canvas/view.js +5 -5
  5. package/dist/command/index.d.ts +71 -0
  6. package/dist/command/index.d.ts.map +1 -1
  7. package/dist/command/index.js +34 -1
  8. package/dist/command/public.d.ts +1 -1
  9. package/dist/command/public.d.ts.map +1 -1
  10. package/dist/command/public.js +1 -1
  11. package/dist/devTools/overlay.d.ts.map +1 -1
  12. package/dist/devTools/overlay.js +137 -110
  13. package/dist/dom/dom.d.ts +8 -11
  14. package/dist/dom/dom.d.ts.map +1 -1
  15. package/dist/dom/dom.js +8 -11
  16. package/dist/dom/elementMovement.d.ts +1 -3
  17. package/dist/dom/elementMovement.d.ts.map +1 -1
  18. package/dist/dom/elementMovement.js +1 -3
  19. package/dist/dom/inert.d.ts +2 -4
  20. package/dist/dom/inert.d.ts.map +1 -1
  21. package/dist/dom/inert.js +2 -4
  22. package/dist/dom/scrollLock.d.ts +2 -2
  23. package/dist/dom/scrollLock.js +2 -2
  24. package/dist/dom/waitForAnimation.d.ts +1 -1
  25. package/dist/dom/waitForAnimation.js +1 -1
  26. package/dist/html/boundary.d.ts +98 -0
  27. package/dist/html/boundary.d.ts.map +1 -0
  28. package/dist/html/boundary.js +176 -0
  29. package/dist/html/childAttribute.d.ts +44 -0
  30. package/dist/html/childAttribute.d.ts.map +1 -0
  31. package/dist/html/childAttribute.js +34 -0
  32. package/dist/html/index.d.ts +70 -23
  33. package/dist/html/index.d.ts.map +1 -1
  34. package/dist/html/index.js +639 -575
  35. package/dist/html/lazy.d.ts +12 -7
  36. package/dist/html/lazy.d.ts.map +1 -1
  37. package/dist/html/lazy.js +30 -11
  38. package/dist/html/public.d.ts +2 -2
  39. package/dist/html/public.d.ts.map +1 -1
  40. package/dist/html/public.js +1 -1
  41. package/dist/html/runtimeSingleton.d.ts +72 -0
  42. package/dist/html/runtimeSingleton.d.ts.map +1 -0
  43. package/dist/html/runtimeSingleton.js +112 -0
  44. package/dist/html/submodel.d.ts +98 -0
  45. package/dist/html/submodel.d.ts.map +1 -0
  46. package/dist/html/submodel.js +190 -0
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -0
  50. package/dist/render/render.d.ts +1 -1
  51. package/dist/render/render.js +1 -1
  52. package/dist/runtime/messagePriority.d.ts +5 -1
  53. package/dist/runtime/messagePriority.d.ts.map +1 -1
  54. package/dist/runtime/messagePriority.js +25 -4
  55. package/dist/runtime/runtime.d.ts +3 -1
  56. package/dist/runtime/runtime.d.ts.map +1 -1
  57. package/dist/runtime/runtime.js +123 -67
  58. package/dist/submodel/public.d.ts +4 -0
  59. package/dist/submodel/public.d.ts.map +1 -0
  60. package/dist/submodel/public.js +1 -0
  61. package/dist/submodel/submodel.d.ts +32 -0
  62. package/dist/submodel/submodel.d.ts.map +1 -0
  63. package/dist/submodel/submodel.js +1 -0
  64. package/dist/test/apps/disabledButton.d.ts +4 -5
  65. package/dist/test/apps/disabledButton.d.ts.map +1 -1
  66. package/dist/test/apps/disabledButton.js +16 -16
  67. package/dist/test/scene.d.ts +8 -8
  68. package/dist/test/scene.d.ts.map +1 -1
  69. package/dist/test/scene.js +25 -13
  70. package/dist/test/story.d.ts +15 -8
  71. package/dist/test/story.d.ts.map +1 -1
  72. package/dist/test/story.js +21 -9
  73. package/dist/ui/animation/index.d.ts +30 -14
  74. package/dist/ui/animation/index.d.ts.map +1 -1
  75. package/dist/ui/animation/index.js +9 -19
  76. package/dist/ui/animation/public.d.ts +2 -2
  77. package/dist/ui/animation/public.d.ts.map +1 -1
  78. package/dist/ui/animation/public.js +1 -1
  79. package/dist/ui/calendar/index.d.ts +199 -84
  80. package/dist/ui/calendar/index.d.ts.map +1 -1
  81. package/dist/ui/calendar/index.js +129 -140
  82. package/dist/ui/calendar/public.d.ts +2 -2
  83. package/dist/ui/calendar/public.d.ts.map +1 -1
  84. package/dist/ui/calendar/public.js +1 -1
  85. package/dist/ui/checkbox/index.d.ts +93 -21
  86. package/dist/ui/checkbox/index.d.ts.map +1 -1
  87. package/dist/ui/checkbox/index.js +62 -33
  88. package/dist/ui/checkbox/public.d.ts +2 -2
  89. package/dist/ui/checkbox/public.d.ts.map +1 -1
  90. package/dist/ui/checkbox/public.js +1 -1
  91. package/dist/ui/combobox/multi.d.ts +35 -91
  92. package/dist/ui/combobox/multi.d.ts.map +1 -1
  93. package/dist/ui/combobox/multi.js +34 -17
  94. package/dist/ui/combobox/multiPublic.d.ts +2 -2
  95. package/dist/ui/combobox/multiPublic.d.ts.map +1 -1
  96. package/dist/ui/combobox/multiPublic.js +1 -1
  97. package/dist/ui/combobox/public.d.ts +3 -3
  98. package/dist/ui/combobox/public.d.ts.map +1 -1
  99. package/dist/ui/combobox/public.js +2 -2
  100. package/dist/ui/combobox/shared.d.ts +56 -31
  101. package/dist/ui/combobox/shared.d.ts.map +1 -1
  102. package/dist/ui/combobox/shared.js +333 -322
  103. package/dist/ui/combobox/single.d.ts +46 -93
  104. package/dist/ui/combobox/single.d.ts.map +1 -1
  105. package/dist/ui/combobox/single.js +44 -17
  106. package/dist/ui/datePicker/index.d.ts +256 -48
  107. package/dist/ui/datePicker/index.d.ts.map +1 -1
  108. package/dist/ui/datePicker/index.js +149 -104
  109. package/dist/ui/datePicker/public.d.ts +2 -2
  110. package/dist/ui/datePicker/public.d.ts.map +1 -1
  111. package/dist/ui/datePicker/public.js +1 -1
  112. package/dist/ui/dialog/index.d.ts +95 -39
  113. package/dist/ui/dialog/index.d.ts.map +1 -1
  114. package/dist/ui/dialog/index.js +71 -62
  115. package/dist/ui/dialog/public.d.ts +2 -2
  116. package/dist/ui/dialog/public.d.ts.map +1 -1
  117. package/dist/ui/dialog/public.js +1 -1
  118. package/dist/ui/disclosure/index.d.ts +71 -31
  119. package/dist/ui/disclosure/index.d.ts.map +1 -1
  120. package/dist/ui/disclosure/index.js +57 -62
  121. package/dist/ui/disclosure/public.d.ts +2 -2
  122. package/dist/ui/disclosure/public.d.ts.map +1 -1
  123. package/dist/ui/disclosure/public.js +1 -1
  124. package/dist/ui/dragAndDrop/index.d.ts +6 -6
  125. package/dist/ui/dragAndDrop/index.d.ts.map +1 -1
  126. package/dist/ui/dragAndDrop/index.js +7 -7
  127. package/dist/ui/dragAndDrop/public.d.ts +1 -1
  128. package/dist/ui/dragAndDrop/public.d.ts.map +1 -1
  129. package/dist/ui/dragAndDrop/public.js +1 -1
  130. package/dist/ui/fileDrop/index.d.ts +42 -46
  131. package/dist/ui/fileDrop/index.d.ts.map +1 -1
  132. package/dist/ui/fileDrop/index.js +30 -46
  133. package/dist/ui/fileDrop/public.d.ts +2 -2
  134. package/dist/ui/fileDrop/public.d.ts.map +1 -1
  135. package/dist/ui/fileDrop/public.js +1 -1
  136. package/dist/ui/listbox/multi.d.ts +39 -84
  137. package/dist/ui/listbox/multi.d.ts.map +1 -1
  138. package/dist/ui/listbox/multi.js +38 -20
  139. package/dist/ui/listbox/multiPublic.d.ts +2 -2
  140. package/dist/ui/listbox/multiPublic.d.ts.map +1 -1
  141. package/dist/ui/listbox/multiPublic.js +1 -1
  142. package/dist/ui/listbox/public.d.ts +3 -3
  143. package/dist/ui/listbox/public.d.ts.map +1 -1
  144. package/dist/ui/listbox/public.js +2 -2
  145. package/dist/ui/listbox/shared.d.ts +71 -30
  146. package/dist/ui/listbox/shared.d.ts.map +1 -1
  147. package/dist/ui/listbox/shared.js +319 -296
  148. package/dist/ui/listbox/single.d.ts +57 -85
  149. package/dist/ui/listbox/single.d.ts.map +1 -1
  150. package/dist/ui/listbox/single.js +48 -24
  151. package/dist/ui/menu/index.d.ts +80 -36
  152. package/dist/ui/menu/index.d.ts.map +1 -1
  153. package/dist/ui/menu/index.js +117 -86
  154. package/dist/ui/menu/public.d.ts +2 -2
  155. package/dist/ui/menu/public.d.ts.map +1 -1
  156. package/dist/ui/menu/public.js +1 -1
  157. package/dist/ui/popover/index.d.ts +117 -44
  158. package/dist/ui/popover/index.d.ts.map +1 -1
  159. package/dist/ui/popover/index.js +88 -101
  160. package/dist/ui/popover/public.d.ts +2 -2
  161. package/dist/ui/popover/public.d.ts.map +1 -1
  162. package/dist/ui/popover/public.js +1 -1
  163. package/dist/ui/radioGroup/index.d.ts +122 -45
  164. package/dist/ui/radioGroup/index.d.ts.map +1 -1
  165. package/dist/ui/radioGroup/index.js +111 -72
  166. package/dist/ui/radioGroup/public.d.ts +2 -2
  167. package/dist/ui/radioGroup/public.d.ts.map +1 -1
  168. package/dist/ui/radioGroup/public.js +1 -1
  169. package/dist/ui/slider/index.d.ts +72 -34
  170. package/dist/ui/slider/index.d.ts.map +1 -1
  171. package/dist/ui/slider/index.js +40 -49
  172. package/dist/ui/slider/public.d.ts +2 -2
  173. package/dist/ui/slider/public.d.ts.map +1 -1
  174. package/dist/ui/slider/public.js +1 -1
  175. package/dist/ui/switch/index.d.ts +74 -21
  176. package/dist/ui/switch/index.d.ts.map +1 -1
  177. package/dist/ui/switch/index.js +62 -33
  178. package/dist/ui/switch/public.d.ts +2 -2
  179. package/dist/ui/switch/public.d.ts.map +1 -1
  180. package/dist/ui/switch/public.js +1 -1
  181. package/dist/ui/tabs/index.d.ts +107 -45
  182. package/dist/ui/tabs/index.d.ts.map +1 -1
  183. package/dist/ui/tabs/index.js +99 -81
  184. package/dist/ui/tabs/public.d.ts +2 -2
  185. package/dist/ui/tabs/public.d.ts.map +1 -1
  186. package/dist/ui/tabs/public.js +1 -1
  187. package/dist/ui/toast/index.d.ts +93 -109
  188. package/dist/ui/toast/index.d.ts.map +1 -1
  189. package/dist/ui/toast/index.js +16 -29
  190. package/dist/ui/toast/schema.d.ts +15 -4
  191. package/dist/ui/toast/schema.d.ts.map +1 -1
  192. package/dist/ui/toast/schema.js +11 -4
  193. package/dist/ui/toast/update.d.ts +36 -18
  194. package/dist/ui/toast/update.d.ts.map +1 -1
  195. package/dist/ui/toast/update.js +33 -14
  196. package/dist/ui/tooltip/index.d.ts +94 -42
  197. package/dist/ui/tooltip/index.d.ts.map +1 -1
  198. package/dist/ui/tooltip/index.js +64 -73
  199. package/dist/ui/tooltip/public.d.ts +2 -2
  200. package/dist/ui/tooltip/public.d.ts.map +1 -1
  201. package/dist/ui/tooltip/public.js +1 -1
  202. package/dist/ui/virtualList/index.d.ts +18 -41
  203. package/dist/ui/virtualList/index.d.ts.map +1 -1
  204. package/dist/ui/virtualList/index.js +17 -37
  205. package/dist/ui/virtualList/public.d.ts +2 -2
  206. package/dist/ui/virtualList/public.d.ts.map +1 -1
  207. package/dist/ui/virtualList/public.js +1 -1
  208. package/dist/vdom.d.ts +3 -2
  209. package/dist/vdom.d.ts.map +1 -1
  210. package/dist/vdom.js +44 -0
  211. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { Array, Effect, Match as M, Option, Schema as S, pipe } from 'effect';
1
+ import { Array, Effect, Function, Match as M, Number, Option, Schema as S, pipe, } from 'effect';
2
2
  import * as Calendar from '../../calendar/index.js';
3
3
  import * as Command from '../../command/index.js';
4
4
  import * as Dom from '../../dom/index.js';
5
5
  import { OptionExt } from '../../effectExtensions/index.js';
6
- import { createLazy, html, } from '../../html/index.js';
6
+ import { childAttributes, defineView, html, } from '../../html/index.js';
7
7
  import { m } from '../../message/index.js';
8
8
  import { evo } from '../../struct/index.js';
9
9
  // MODEL
@@ -87,19 +87,21 @@ export const Message = S.Union([
87
87
  // OUT MESSAGE
88
88
  /** Emitted when the visible month changes due to navigation. Consumers of an
89
89
  * inline calendar may use this to load month-scoped data (holidays, events).
90
- *
91
- * Date selection does NOT use OutMessage consumers subscribe via the
92
- * `onSelectedDate` callback in `ViewConfig`, matching the Listbox/Popover
93
- * controlled-component pattern. Use `Calendar.selectDate(model, date)` to
94
- * write back when handling the callback. */
90
+ * A click that commits a date in a different month emits `SelectedDate`, not
91
+ * `ChangedViewMonth`. The parent infers the month change from the date. */
95
92
  export const ChangedViewMonth = m('ChangedViewMonth', {
96
93
  year: S.Int,
97
94
  month: S.Int,
98
95
  });
99
- /** The calendar's OutMessage. Only one variant `ChangedViewMonth` which
100
- * fires when navigation shifts the visible month. Date selection goes through
101
- * the `onSelectedDate` ViewConfig callback, not OutMessage. */
102
- export const OutMessage = ChangedViewMonth;
96
+ /** Emitted when the user commits a date selection via click or keyboard. The
97
+ * calendar's internal state already reflects the new selection by the time
98
+ * this fires; consumers react by lifting the date into their domain state
99
+ * (closing a popover, advancing a form step, etc.). */
100
+ export const SelectedDate = m('SelectedDate', {
101
+ date: Calendar.CalendarDate,
102
+ });
103
+ /** Union of the calendar's OutMessages. */
104
+ export const OutMessage = S.Union([ChangedViewMonth, SelectedDate]);
103
105
  /** Creates an initial calendar model. The view month defaults to the month
104
106
  * of the initial selected date, or today if no date is pre-selected. */
105
107
  export const init = (config) => {
@@ -134,30 +136,50 @@ export const FocusGrid = Command.define('FocusGrid', { id: S.String }, Completed
134
136
  *
135
137
  * Equivalent to dispatching `ClickedDay({ date })` through `update`. */
136
138
  export const selectDate = (model, date) => update(model, ClickedDay({ date }));
137
- /** Sets the minimum selectable date. Pass `Option.none()` to remove the
138
- * minimum. Use this when the minimum derives from other Model state (e.g. a
139
- * start date field whose current selection constrains an end date picker).
139
+ /** Reflects an externally-sourced selected date onto the model without
140
+ * emitting an OutMessage. When a date is given, sets the selection and
141
+ * moves the view to the date's month so it stays visible, mirroring
142
+ * `selectDate`'s state change minus the `SelectedDate` announcement. Pass
143
+ * `Option.none()` to clear the selection (the view is left where it is).
144
+ * Use this to mirror external truth (a URL parameter, a saved draft) onto
145
+ * the calendar. Contrast with `selectDate`, a user or programmatic
146
+ * *choice* that emits `SelectedDate`. Returns the model directly because
147
+ * it produces no commands and no OutMessage. */
148
+ export const reflectSelectedDate = Function.dual(2, (model, maybeDate) => Option.match(maybeDate, {
149
+ onNone: () => evo(model, { maybeSelectedDate: () => Option.none() }),
150
+ onSome: date => evo(model, {
151
+ maybeSelectedDate: () => Option.some(date),
152
+ maybeFocusedDate: () => Option.some(date),
153
+ viewYear: () => date.year,
154
+ viewMonth: () => date.month,
155
+ }),
156
+ }));
157
+ /** Reflects the minimum selectable date onto the model. Pass `Option.none()`
158
+ * to remove the minimum. Use this when the minimum derives from other Model
159
+ * state (e.g. a start date field whose current selection constrains an end
160
+ * date picker).
140
161
  *
141
- * Does NOT reconcile the current selection if a previously-selected date
162
+ * Does NOT reconcile the current selection. If a previously-selected date
142
163
  * is now below the new minimum, it remains selected. Callers should clear or
143
164
  * reassign the selection explicitly if their domain requires it. */
144
- export const setMinDate = (model, maybeMinDate) => evo(model, { maybeMinDate: () => maybeMinDate });
145
- /** Sets the maximum selectable date. Pass `Option.none()` to remove the
146
- * maximum. Does NOT reconcile the current selection. */
147
- export const setMaxDate = (model, maybeMaxDate) => evo(model, { maybeMaxDate: () => maybeMaxDate });
148
- /** Sets the list of individually-disabled dates. Pass an empty array to
149
- * clear. Does NOT reconcile the current selection. */
150
- export const setDisabledDates = (model, disabledDates) => evo(model, { disabledDates: () => disabledDates });
151
- /** Sets the days of the week that are disabled (e.g. weekends). Pass an
165
+ export const reflectMinDate = Function.dual(2, (model, maybeMinDate) => evo(model, { maybeMinDate: () => maybeMinDate }));
166
+ /** Reflects the maximum selectable date onto the model. Pass `Option.none()`
167
+ * to remove the maximum. Does NOT reconcile the current selection. */
168
+ export const reflectMaxDate = Function.dual(2, (model, maybeMaxDate) => evo(model, { maybeMaxDate: () => maybeMaxDate }));
169
+ /** Reflects the list of individually-disabled dates onto the model. Pass an
152
170
  * empty array to clear. Does NOT reconcile the current selection. */
153
- export const setDisabledDaysOfWeek = (model, disabledDaysOfWeek) => evo(model, { disabledDaysOfWeek: () => disabledDaysOfWeek });
171
+ export const reflectDisabledDates = Function.dual(2, (model, disabledDates) => evo(model, { disabledDates: () => disabledDates }));
172
+ /** Reflects the days of the week that are disabled (e.g. weekends) onto the
173
+ * model. Pass an empty array to clear. Does NOT reconcile the current
174
+ * selection. */
175
+ export const reflectDisabledDaysOfWeek = Function.dual(2, (model, disabledDaysOfWeek) => evo(model, { disabledDaysOfWeek: () => disabledDaysOfWeek }));
154
176
  /** Returns the calendar to Days mode regardless of current depth. Useful for
155
177
  * standalone (non-popovered) consumers that want to wire their own back-out
156
- * gesture. Popovered consumers like `Ui.DatePicker` don't need this Escape
178
+ * gesture. Popovered consumers like `Ui.DatePicker` don't need this. Escape
157
179
  * closes the popover, and the calendar resets to Days on next open.
158
180
  *
159
181
  * Reconciles `maybeFocusedDate` to a date inside the visible (`viewYear`,
160
- * `viewMonth`) Months/Years navigation can leave the cursor on a date
182
+ * `viewMonth`). Months/Years navigation can leave the cursor on a date
161
183
  * outside the days grid (paged-away year, etc.), which would otherwise
162
184
  * cause `aria-activedescendant` to point at a non-rendered cell and the
163
185
  * next ArrowLeft to jump to the cursor's stale year. */
@@ -221,18 +243,17 @@ const isCommitKey = (key) => key === 'Enter' || key === ' ';
221
243
  const currentOrFallbackFocus = (model) => Option.getOrElse(model.maybeFocusedDate, () => Calendar.make(model.viewYear, model.viewMonth, 1));
222
244
  /** Applies a date selection to the model: commits the selection, moves the
223
245
  * cursor onto the date, and syncs the view month if the selection crosses a
224
- * month boundary. Emits `ChangedViewMonth` only when the commit crosses a
225
- * month boundary. */
246
+ * month boundary. Always emits `SelectedDate` carrying the committed date;
247
+ * the parent infers month transitions from the date itself rather than from
248
+ * a separate `ChangedViewMonth` signal that would race with the selection. */
226
249
  const commitSelection = (model, date) => {
227
- const crossedMonth = date.year !== model.viewYear || date.month !== model.viewMonth;
228
250
  const nextModel = evo(model, {
229
251
  maybeSelectedDate: () => Option.some(date),
230
252
  maybeFocusedDate: () => Option.some(date),
231
253
  viewYear: () => date.year,
232
254
  viewMonth: () => date.month,
233
255
  });
234
- const maybeOutMessage = OptionExt.when(crossedMonth, ChangedViewMonth({ year: date.year, month: date.month }));
235
- return [nextModel, maybeOutMessage];
256
+ return [nextModel, Option.some(SelectedDate({ date }))];
236
257
  };
237
258
  /** Applies a focus move to the model, clamping to the allowed range and
238
259
  * skipping disabled dates. Emits `ChangedViewMonth` if the move crossed a
@@ -292,7 +313,7 @@ const resolveMonthsKey = (key) => M.value(key).pipe(M.withReturnType(), M.when('
292
313
  * one window) navigation. */
293
314
  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);
294
315
  /** Applies a months-grid focus shift, updating `maybeFocusedDate` and
295
- * `viewYear` to reflect the new focused date. `viewMonth` is preserved
316
+ * `viewYear` to reflect the new focused date. `viewMonth` is preserved.
296
317
  * Months mode keyboard navigation moves the cursor without committing. */
297
318
  const applyMonthsFocusShift = (model, monthShift) => {
298
319
  const focused = currentOrFallbackFocus(model);
@@ -491,76 +512,58 @@ const isMonthDisabled = (model, year, month) => {
491
512
  * year or entirely above the maximum date's year. */
492
513
  const isYearDisabled = (model, year) => Option.exists(model.maybeMinDate, min => year < min.year) ||
493
514
  Option.exists(model.maybeMaxDate, max => year > max.year);
494
- const buildDaysAttributes = (config) => {
515
+ const buildDaysAttributes = (model, viewInputs) => {
495
516
  const h = html();
496
- const { model, toParentMessage, onSelectedDate } = config;
497
517
  const { id, viewYear, viewMonth, maybeFocusedDate, maybeSelectedDate, today, locale, isGridFocused, } = model;
498
- const dispatchSelectedDate = (date) => onSelectedDate !== undefined
499
- ? onSelectedDate(date)
500
- : toParentMessage(ClickedDay({ date }));
501
- const previousMonthLabel = config.previousMonthLabel ?? 'Previous month';
502
- const nextMonthLabel = config.nextMonthLabel ?? 'Next month';
503
- const headingButtonLabel = config.daysHeadingButtonLabel ?? 'Switch to month picker';
518
+ const previousMonthLabel = viewInputs.previousMonthLabel ?? 'Previous month';
519
+ const nextMonthLabel = viewInputs.nextMonthLabel ?? 'Next month';
520
+ const headingButtonLabel = viewInputs.daysHeadingButtonLabel ?? 'Switch to month picker';
504
521
  const headingText = `${locale.monthNames[viewMonth - 1]} ${viewYear}`;
505
522
  const rotatedDayNames = rotateDayNames(DAY_NAMES_SUNDAY_FIRST, locale.firstDayOfWeek);
506
523
  const rotatedShortDayNames = rotateDayNames(locale.shortDayNames, locale.firstDayOfWeek);
507
524
  const { gridStart, weeks: weeksDates } = buildGrid(viewYear, viewMonth, locale.firstDayOfWeek);
508
- const rootAttributes = [
509
- h.Id(id),
510
- h.Key('Days'),
511
- ];
525
+ const rootAttributes = [h.Id(id), h.Key('Days')];
512
526
  const previousMonthButton = [
513
527
  h.Type('button'),
514
528
  h.AriaLabel(previousMonthLabel),
515
- h.OnClick(toParentMessage(ClickedPreviousMonthButton())),
529
+ h.OnClick(ClickedPreviousMonthButton()),
516
530
  ];
517
531
  const nextMonthButton = [
518
532
  h.Type('button'),
519
533
  h.AriaLabel(nextMonthLabel),
520
- h.OnClick(toParentMessage(ClickedNextMonthButton())),
534
+ h.OnClick(ClickedNextMonthButton()),
521
535
  ];
522
536
  const headingButton = [
523
537
  h.Type('button'),
524
538
  h.AriaLabel(headingButtonLabel),
525
- h.OnClick(toParentMessage(ClickedHeading())),
539
+ h.OnClick(ClickedHeading()),
526
540
  ];
527
541
  const handleKeyDown = (key, modifiers) => {
528
542
  if (!NAV_KEYS.has(key)) {
529
543
  return Option.none();
530
544
  }
531
- if (isCommitKey(key) && onSelectedDate !== undefined) {
532
- return pipe(maybeFocusedDate, Option.filter(date => !isDateDisabled(model, date)), Option.map(onSelectedDate));
545
+ if (isCommitKey(key)) {
546
+ const maybeCommit = pipe(maybeFocusedDate, Option.filter(date => !isDateDisabled(model, date)), Option.map(date => ClickedDay({ date })));
547
+ if (Option.isSome(maybeCommit)) {
548
+ return maybeCommit;
549
+ }
533
550
  }
534
- return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
551
+ return Option.some(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey }));
535
552
  };
536
553
  const activeDescendantAttributes = pipe(maybeFocusedDate, Option.map(date => h.AriaActiveDescendant(dayCellId(id, date))), Option.toArray);
537
554
  const gridAttributes = [
538
555
  h.Id(gridId(id)),
539
556
  h.Role('grid'),
540
557
  h.AriaLabel(`Calendar, ${headingText}`),
541
- h.AriaRowcount(WEEKS_IN_GRID + 1),
558
+ h.AriaRowcount(Number.increment(WEEKS_IN_GRID)),
542
559
  h.AriaColcount(DAYS_IN_WEEK),
543
560
  h.Tabindex(0),
544
- h.OnFocus(toParentMessage(FocusedGrid())),
545
- h.OnBlur(toParentMessage(BlurredGrid())),
561
+ h.OnFocus(FocusedGrid()),
562
+ h.OnBlur(BlurredGrid()),
546
563
  h.OnKeyDownPreventDefault(handleKeyDown),
547
564
  ...activeDescendantAttributes,
548
565
  ];
549
- const headerRowAttributes = [
550
- h.Role('row'),
551
- h.AriaRowindex(1),
552
- ];
553
- const columnHeaders = Array.zipWith(rotatedShortDayNames, rotatedDayNames, (name, fullName) => ({
554
- name,
555
- fullName,
556
- })).map(({ name, fullName }, columnIndex) => ({
557
- name,
558
- attributes: [
559
- h.Role('columnheader'),
560
- h.AriaLabel(fullName),
561
- h.AriaColindex(columnIndex + 1),
562
- ],
563
- }));
566
+ const headerRowAttributes = [h.Role('row'), h.AriaRowindex(1)];
564
567
  const buildDayCell = (date, columnIndex) => {
565
568
  const isSelected = Option.exists(maybeSelectedDate, Calendar.isEqual(date));
566
569
  const isFocused = Option.exists(maybeFocusedDate, Calendar.isEqual(date));
@@ -578,7 +581,7 @@ const buildDaysAttributes = (config) => {
578
581
  h.Id(dayCellId(id, date)),
579
582
  h.Role('gridcell'),
580
583
  h.AriaSelected(isSelected),
581
- h.AriaColindex(columnIndex + 1),
584
+ h.AriaColindex(Number.increment(columnIndex)),
582
585
  ...stateDataAttributes,
583
586
  ];
584
587
  const buttonAttributes = [
@@ -586,13 +589,13 @@ const buildDaysAttributes = (config) => {
586
589
  h.Tabindex(-1),
587
590
  h.AriaLabel(Calendar.formatAriaLabel(date, locale)),
588
591
  h.AriaDisabled(isDisabled),
589
- ...(isDisabled ? [] : [h.OnClick(dispatchSelectedDate(date))]),
592
+ ...(isDisabled ? [] : [h.OnClick(ClickedDay({ date }))]),
590
593
  ];
591
594
  return {
592
595
  date,
593
596
  label: String(date.day),
594
- cellAttributes,
595
- buttonAttributes,
597
+ cellAttributes: childAttributes(cellAttributes),
598
+ buttonAttributes: childAttributes(buttonAttributes),
596
599
  isSelected,
597
600
  isFocused: isFocused && isGridFocused,
598
601
  isToday,
@@ -603,41 +606,45 @@ const buildDaysAttributes = (config) => {
603
606
  const weeks = weeksDates.map((weekDates, weekIndex) => {
604
607
  const weekStart = Calendar.addDays(gridStart, weekIndex * DAYS_IN_WEEK);
605
608
  return {
606
- attributes: [
609
+ attributes: childAttributes([
607
610
  h.Role('row'),
608
611
  h.AriaRowindex(weekIndex + 2),
609
612
  h.AriaLabel(`Week of ${Calendar.formatLong(weekStart, locale)}`),
610
- ],
613
+ ]),
611
614
  cells: weekDates.map(buildDayCell),
612
615
  };
613
616
  });
617
+ const wrappedColumnHeaders = Array.zipWith(rotatedShortDayNames, rotatedDayNames, (name, fullName) => ({ name, fullName })).map(({ name, fullName }, columnIndex) => ({
618
+ name,
619
+ attributes: childAttributes([
620
+ h.Role('columnheader'),
621
+ h.AriaLabel(fullName),
622
+ h.AriaColindex(Number.increment(columnIndex)),
623
+ ]),
624
+ }));
614
625
  return {
615
626
  _tag: 'Days',
616
- root: rootAttributes,
617
- previousMonthButton,
618
- nextMonthButton,
619
- headingButton,
627
+ root: childAttributes(rootAttributes),
628
+ previousMonthButton: childAttributes(previousMonthButton),
629
+ nextMonthButton: childAttributes(nextMonthButton),
630
+ headingButton: childAttributes(headingButton),
620
631
  heading: { id: headingId(id), text: headingText },
621
- grid: gridAttributes,
622
- headerRow: headerRowAttributes,
623
- columnHeaders,
632
+ grid: childAttributes(gridAttributes),
633
+ headerRow: childAttributes(headerRowAttributes),
634
+ columnHeaders: wrappedColumnHeaders,
624
635
  weeks,
625
636
  };
626
637
  };
627
- const buildMonthsAttributes = (config) => {
638
+ const buildMonthsAttributes = (model, viewInputs) => {
628
639
  const h = html();
629
- const { model, toParentMessage } = config;
630
640
  const { id, viewYear, viewMonth, maybeFocusedDate, today, locale, isGridFocused, } = model;
631
- const headingButtonLabel = config.monthsHeadingButtonLabel ?? 'Switch to year picker';
641
+ const headingButtonLabel = viewInputs.monthsHeadingButtonLabel ?? 'Switch to year picker';
632
642
  const headingText = `${viewYear}`;
633
- const rootAttributes = [
634
- h.Id(id),
635
- h.Key('Months'),
636
- ];
643
+ const rootAttributes = [h.Id(id), h.Key('Months')];
637
644
  const headingButton = [
638
645
  h.Type('button'),
639
646
  h.AriaLabel(headingButtonLabel),
640
- h.OnClick(toParentMessage(ClickedHeading())),
647
+ h.OnClick(ClickedHeading()),
641
648
  ];
642
649
  const focusedMonth = Option.match(maybeFocusedDate, {
643
650
  onNone: () => viewMonth,
@@ -648,10 +655,10 @@ const buildMonthsAttributes = (config) => {
648
655
  return Option.none();
649
656
  }
650
657
  else if (isCommitKey(key)) {
651
- return OptionExt.when(!isMonthDisabled(model, viewYear, focusedMonth), toParentMessage(SelectedMonth({ month: focusedMonth })));
658
+ return OptionExt.when(!isMonthDisabled(model, viewYear, focusedMonth), SelectedMonth({ month: focusedMonth }));
652
659
  }
653
660
  else {
654
- return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
661
+ return Option.some(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey }));
655
662
  }
656
663
  };
657
664
  const activeDescendantAttributes = [
@@ -662,8 +669,8 @@ const buildMonthsAttributes = (config) => {
662
669
  h.Role('grid'),
663
670
  h.AriaLabel(`Month picker, ${headingText}`),
664
671
  h.Tabindex(0),
665
- h.OnFocus(toParentMessage(FocusedGrid())),
666
- h.OnBlur(toParentMessage(BlurredGrid())),
672
+ h.OnFocus(FocusedGrid()),
673
+ h.OnBlur(BlurredGrid()),
667
674
  h.OnKeyDownPreventDefault(handleKeyDown),
668
675
  ...activeDescendantAttributes,
669
676
  ];
@@ -691,38 +698,35 @@ const buildMonthsAttributes = (config) => {
691
698
  h.Tabindex(-1),
692
699
  h.AriaLabel(`${label} ${viewYear}`),
693
700
  h.AriaDisabled(isDisabled),
694
- ...(isDisabled
695
- ? []
696
- : [h.OnClick(toParentMessage(SelectedMonth({ month })))]),
701
+ ...(isDisabled ? [] : [h.OnClick(SelectedMonth({ month }))]),
697
702
  ];
698
703
  return {
699
704
  month,
700
705
  label,
701
706
  shortLabel,
702
- cellAttributes,
703
- buttonAttributes,
707
+ cellAttributes: childAttributes(cellAttributes),
708
+ buttonAttributes: childAttributes(buttonAttributes),
704
709
  isSelected,
705
710
  isFocused: isFocused && isGridFocused,
706
711
  isCurrentMonth,
707
712
  isDisabled,
708
713
  };
709
714
  };
710
- const cells = Array.makeBy(MONTHS_IN_YEAR, monthIndex => buildMonthCell(monthIndex + 1));
715
+ const cells = Array.makeBy(MONTHS_IN_YEAR, monthIndex => buildMonthCell(Number.increment(monthIndex)));
711
716
  return {
712
717
  _tag: 'Months',
713
- root: rootAttributes,
714
- headingButton,
718
+ root: childAttributes(rootAttributes),
719
+ headingButton: childAttributes(headingButton),
715
720
  heading: { id: headingId(id), text: headingText },
716
- grid: gridAttributes,
721
+ grid: childAttributes(gridAttributes),
717
722
  cells,
718
723
  };
719
724
  };
720
- const buildYearsAttributes = (config) => {
725
+ const buildYearsAttributes = (model, viewInputs) => {
721
726
  const h = html();
722
- const { model, toParentMessage } = config;
723
727
  const { id, viewYear, maybeFocusedDate, today, isGridFocused } = model;
724
- const previousYearsPageLabel = config.previousYearsPageLabel ?? 'Previous 12 years';
725
- const nextYearsPageLabel = config.nextYearsPageLabel ?? 'Next 12 years';
728
+ const previousYearsPageLabel = viewInputs.previousYearsPageLabel ?? 'Previous 12 years';
729
+ const nextYearsPageLabel = viewInputs.nextYearsPageLabel ?? 'Next 12 years';
726
730
  const cursorYear = Option.match(maybeFocusedDate, {
727
731
  onNone: () => viewYear,
728
732
  onSome: date => date.year,
@@ -730,19 +734,16 @@ const buildYearsAttributes = (config) => {
730
734
  const pageStart = yearsPageStart(cursorYear);
731
735
  const pageEnd = pageStart + YEARS_PAGE_SIZE - 1;
732
736
  const headingText = `${pageStart}–${pageEnd}`;
733
- const rootAttributes = [
734
- h.Id(id),
735
- h.Key('Years'),
736
- ];
737
+ const rootAttributes = [h.Id(id), h.Key('Years')];
737
738
  const previousPageButton = [
738
739
  h.Type('button'),
739
740
  h.AriaLabel(previousYearsPageLabel),
740
- h.OnClick(toParentMessage(PagedYears({ direction: -1 }))),
741
+ h.OnClick(PagedYears({ direction: -1 })),
741
742
  ];
742
743
  const nextPageButton = [
743
744
  h.Type('button'),
744
745
  h.AriaLabel(nextYearsPageLabel),
745
- h.OnClick(toParentMessage(PagedYears({ direction: 1 }))),
746
+ h.OnClick(PagedYears({ direction: 1 })),
746
747
  ];
747
748
  const focusedYear = cursorYear;
748
749
  const handleKeyDown = (key, modifiers) => {
@@ -750,10 +751,10 @@ const buildYearsAttributes = (config) => {
750
751
  return Option.none();
751
752
  }
752
753
  else if (isCommitKey(key)) {
753
- return OptionExt.when(!isYearDisabled(model, focusedYear), toParentMessage(SelectedYear({ year: focusedYear })));
754
+ return OptionExt.when(!isYearDisabled(model, focusedYear), SelectedYear({ year: focusedYear }));
754
755
  }
755
756
  else {
756
- return Option.some(toParentMessage(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey })));
757
+ return Option.some(PressedKeyOnGrid({ key, isShift: modifiers.shiftKey }));
757
758
  }
758
759
  };
759
760
  const activeDescendantAttributes = [
@@ -764,8 +765,8 @@ const buildYearsAttributes = (config) => {
764
765
  h.Role('grid'),
765
766
  h.AriaLabel(`Year picker, ${headingText}`),
766
767
  h.Tabindex(0),
767
- h.OnFocus(toParentMessage(FocusedGrid())),
768
- h.OnBlur(toParentMessage(BlurredGrid())),
768
+ h.OnFocus(FocusedGrid()),
769
+ h.OnBlur(BlurredGrid()),
769
770
  h.OnKeyDownPreventDefault(handleKeyDown),
770
771
  ...activeDescendantAttributes,
771
772
  ];
@@ -792,15 +793,13 @@ const buildYearsAttributes = (config) => {
792
793
  h.Tabindex(-1),
793
794
  h.AriaLabel(label),
794
795
  h.AriaDisabled(isDisabled),
795
- ...(isDisabled
796
- ? []
797
- : [h.OnClick(toParentMessage(SelectedYear({ year })))]),
796
+ ...(isDisabled ? [] : [h.OnClick(SelectedYear({ year }))]),
798
797
  ];
799
798
  return {
800
799
  year,
801
800
  label,
802
- cellAttributes,
803
- buttonAttributes,
801
+ cellAttributes: childAttributes(cellAttributes),
802
+ buttonAttributes: childAttributes(buttonAttributes),
804
803
  isSelected,
805
804
  isFocused: isFocused && isGridFocused,
806
805
  isCurrentYear,
@@ -810,26 +809,16 @@ const buildYearsAttributes = (config) => {
810
809
  const cells = Array.makeBy(YEARS_PAGE_SIZE, offset => buildYearCell(pageStart + offset));
811
810
  return {
812
811
  _tag: 'Years',
813
- root: rootAttributes,
814
- previousPageButton,
815
- nextPageButton,
812
+ root: childAttributes(rootAttributes),
813
+ previousPageButton: childAttributes(previousPageButton),
814
+ nextPageButton: childAttributes(nextPageButton),
816
815
  heading: { id: headingId(id), text: headingText },
817
- grid: gridAttributes,
816
+ grid: childAttributes(gridAttributes),
818
817
  cells,
819
818
  };
820
819
  };
821
- /** Renders an accessible calendar. Builds mode-specific ARIA attribute
822
- * groups and derived cell data, then delegates layout to the consumer's
823
- * `toView` callback. The variant of `CalendarAttributes` passed to `toView`
824
- * matches `model.viewMode`. */
825
- 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));
826
- /** Creates a memoized calendar view. Static config is captured in a closure;
827
- * only `model` and `toParentMessage` are compared per render via `createLazy`. */
828
- export const lazy = (staticConfig) => {
829
- const lazyView = createLazy();
830
- return (model, toParentMessage) => lazyView((currentModel, currentToParentMessage) => view({
831
- ...staticConfig,
832
- model: currentModel,
833
- toParentMessage: currentToParentMessage,
834
- }), [model, toParentMessage]);
835
- };
820
+ /** Renders an accessible calendar. Publishes mode-specific ARIA attribute
821
+ * bundles + derived cell data, then delegates layout to the consumer's
822
+ * `toView` callback. The variant of `CalendarAttributes` passed to
823
+ * `toView` matches `model.viewMode`. */
824
+ export const view = defineView((model, viewInputs) => viewInputs.toView(M.value(model.viewMode).pipe(M.withReturnType(), M.when('Days', () => buildDaysAttributes(model, viewInputs)), M.when('Months', () => buildMonthsAttributes(model, viewInputs)), M.when('Years', () => buildYearsAttributes(model, viewInputs)), M.exhaustive)));
@@ -1,3 +1,3 @@
1
- export { init, update, view, lazy, 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';
1
+ export { init, update, view, selectDate, reflectSelectedDate, reflectMinDate, reflectMaxDate, reflectDisabledDates, reflectDisabledDaysOfWeek, dropToDays, Model, ViewMode, Message, OutMessage, ChangedViewMonth, SelectedDate, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton, ClickedHeading, SelectedMonth, SelectedYear, PagedYears, FocusedGrid, BlurredGrid, RefreshedToday, CompletedFocusGrid, FocusGrid, } from './index.js';
2
+ export type { InitConfig, ViewInputs, 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,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
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,UAAU,EACV,KAAK,EACL,QAAQ,EACR,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,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, 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';
1
+ export { init, update, view, selectDate, reflectSelectedDate, reflectMinDate, reflectMaxDate, reflectDisabledDates, reflectDisabledDaysOfWeek, dropToDays, Model, ViewMode, Message, OutMessage, ChangedViewMonth, SelectedDate, ClickedDay, PressedKeyOnGrid, ClickedPreviousMonthButton, ClickedNextMonthButton, ClickedHeading, SelectedMonth, SelectedYear, PagedYears, FocusedGrid, BlurredGrid, RefreshedToday, CompletedFocusGrid, FocusGrid, } from './index.js';