@k2b/ui 0.44.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/browser/actions/Tabs.js +3 -4
  2. package/dist/browser/actions/Tabs.js.map +1 -1
  3. package/dist/browser/actions/menu-sheet.js +2 -3
  4. package/dist/browser/actions/menu-sheet.js.map +1 -1
  5. package/dist/browser/actions/phone-sheet.js +4 -0
  6. package/dist/browser/actions/phone-sheet.js.map +1 -0
  7. package/dist/browser/content/DataTable.js +47 -51
  8. package/dist/browser/content/DataTable.js.map +1 -1
  9. package/dist/browser/feedback/dialog-core.js +5 -1
  10. package/dist/browser/feedback/dialog-core.js.map +1 -1
  11. package/dist/browser/feedback/dialog-tone.js +26 -0
  12. package/dist/browser/feedback/dialog-tone.js.map +1 -0
  13. package/dist/browser/feedback/index.js.map +1 -1
  14. package/dist/browser/feedback/prompt-confirm.js +223 -0
  15. package/dist/browser/feedback/prompt-confirm.js.map +1 -0
  16. package/dist/browser/feedback/prompts.js +195 -343
  17. package/dist/browser/feedback/prompts.js.map +1 -1
  18. package/dist/browser/inputs/ChoiceList.js +45 -17
  19. package/dist/browser/inputs/ChoiceList.js.map +1 -1
  20. package/dist/browser/inputs/DatePicker.js +1647 -334
  21. package/dist/browser/inputs/DatePicker.js.map +1 -1
  22. package/dist/browser/inputs/TextInput.js +5 -4
  23. package/dist/browser/inputs/TextInput.js.map +1 -1
  24. package/dist/browser/inputs/date-input-messages.js +128 -0
  25. package/dist/browser/inputs/date-input-messages.js.map +1 -0
  26. package/dist/browser/inputs/date-input-status.js +132 -0
  27. package/dist/browser/inputs/date-input-status.js.map +1 -0
  28. package/dist/browser/inputs/date-input.js +334 -0
  29. package/dist/browser/inputs/date-input.js.map +1 -0
  30. package/dist/browser/inputs/date-sheet.js +49 -0
  31. package/dist/browser/inputs/date-sheet.js.map +1 -0
  32. package/dist/browser/internal/reveal-in-scroller.js +28 -0
  33. package/dist/browser/internal/reveal-in-scroller.js.map +1 -0
  34. package/dist/browser/intl/messages.js +6 -0
  35. package/dist/browser/intl/messages.js.map +1 -1
  36. package/dist/browser/layout/AppWorkspace.js +536 -512
  37. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  38. package/dist/browser/layout/BottomSheet.js +3 -0
  39. package/dist/browser/layout/BottomSheet.js.map +1 -1
  40. package/dist/browser/layout/PanelDialog.js +167 -132
  41. package/dist/browser/layout/PanelDialog.js.map +1 -1
  42. package/dist/browser/layout/Settings.js +175 -139
  43. package/dist/browser/layout/Settings.js.map +1 -1
  44. package/dist/browser/layout/SettingsModal.js +2 -4
  45. package/dist/browser/layout/SettingsModal.js.map +1 -1
  46. package/dist/browser/layout/index.js.map +1 -1
  47. package/dist/browser/surfaces/StatusBadge.js +47 -14
  48. package/dist/browser/surfaces/StatusBadge.js.map +1 -1
  49. package/dist/browser/text-editor/TextEditor.js +7 -0
  50. package/dist/browser/text-editor/TextEditor.js.map +1 -1
  51. package/dist/ssr/actions/Tabs.js +3 -4
  52. package/dist/ssr/actions/Tabs.js.map +1 -1
  53. package/dist/ssr/actions/menu-sheet.js +2 -3
  54. package/dist/ssr/actions/menu-sheet.js.map +1 -1
  55. package/dist/ssr/actions/phone-sheet.js +4 -0
  56. package/dist/ssr/actions/phone-sheet.js.map +1 -0
  57. package/dist/ssr/content/DataTable.js +1 -2
  58. package/dist/ssr/content/DataTable.js.map +1 -1
  59. package/dist/ssr/feedback/dialog-core.js +5 -1
  60. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  61. package/dist/ssr/feedback/dialog-tone.js +26 -0
  62. package/dist/ssr/feedback/dialog-tone.js.map +1 -0
  63. package/dist/ssr/feedback/index.js.map +1 -1
  64. package/dist/ssr/feedback/prompt-confirm.js +123 -0
  65. package/dist/ssr/feedback/prompt-confirm.js.map +1 -0
  66. package/dist/ssr/feedback/prompts.js +60 -132
  67. package/dist/ssr/feedback/prompts.js.map +1 -1
  68. package/dist/ssr/inputs/ChoiceList.js +29 -9
  69. package/dist/ssr/inputs/ChoiceList.js.map +1 -1
  70. package/dist/ssr/inputs/DatePicker.js +1183 -166
  71. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  72. package/dist/ssr/inputs/TextInput.js +5 -4
  73. package/dist/ssr/inputs/TextInput.js.map +1 -1
  74. package/dist/ssr/inputs/date-input-messages.js +128 -0
  75. package/dist/ssr/inputs/date-input-messages.js.map +1 -0
  76. package/dist/ssr/inputs/date-input-status.js +132 -0
  77. package/dist/ssr/inputs/date-input-status.js.map +1 -0
  78. package/dist/ssr/inputs/date-input.js +334 -0
  79. package/dist/ssr/inputs/date-input.js.map +1 -0
  80. package/dist/ssr/inputs/date-sheet.js +44 -0
  81. package/dist/ssr/inputs/date-sheet.js.map +1 -0
  82. package/dist/ssr/internal/reveal-in-scroller.js +28 -0
  83. package/dist/ssr/internal/reveal-in-scroller.js.map +1 -0
  84. package/dist/ssr/intl/messages.js +6 -0
  85. package/dist/ssr/intl/messages.js.map +1 -1
  86. package/dist/ssr/layout/AppWorkspace.js +44 -23
  87. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  88. package/dist/ssr/layout/BottomSheet.js +3 -0
  89. package/dist/ssr/layout/BottomSheet.js.map +1 -1
  90. package/dist/ssr/layout/PanelDialog.js +48 -30
  91. package/dist/ssr/layout/PanelDialog.js.map +1 -1
  92. package/dist/ssr/layout/Settings.js +34 -18
  93. package/dist/ssr/layout/Settings.js.map +1 -1
  94. package/dist/ssr/layout/SettingsModal.js +2 -4
  95. package/dist/ssr/layout/SettingsModal.js.map +1 -1
  96. package/dist/ssr/layout/index.js.map +1 -1
  97. package/dist/ssr/surfaces/StatusBadge.js +26 -8
  98. package/dist/ssr/surfaces/StatusBadge.js.map +1 -1
  99. package/dist/ssr/text-editor/TextEditor.js +7 -0
  100. package/dist/ssr/text-editor/TextEditor.js.map +1 -1
  101. package/dist/styles.css +1 -1
  102. package/dist/types/actions/menu-sheet.d.ts +1 -3
  103. package/dist/types/actions/phone-sheet.d.ts +3 -0
  104. package/dist/types/feedback/dialog-tone.d.ts +10 -0
  105. package/dist/types/feedback/index.d.ts +1 -0
  106. package/dist/types/feedback/prompt-confirm.d.ts +19 -0
  107. package/dist/types/feedback/prompts.d.ts +4 -8
  108. package/dist/types/inputs/ChoiceList.d.ts +7 -3
  109. package/dist/types/inputs/DatePicker.d.ts +13 -3
  110. package/dist/types/inputs/TextInput.d.ts +1 -1
  111. package/dist/types/inputs/date-input-messages.d.ts +64 -0
  112. package/dist/types/inputs/date-input-status.d.ts +35 -0
  113. package/dist/types/inputs/date-input.d.ts +90 -0
  114. package/dist/types/inputs/date-sheet.d.ts +14 -0
  115. package/dist/types/internal/reveal-in-scroller.d.ts +6 -0
  116. package/dist/types/intl/messages.d.ts +3 -0
  117. package/dist/types/layout/AppWorkspace.d.ts +14 -0
  118. package/dist/types/layout/BottomSheet.d.ts +3 -0
  119. package/dist/types/layout/PanelDialog.d.ts +3 -0
  120. package/dist/types/layout/Settings.d.ts +17 -1
  121. package/dist/types/layout/index.d.ts +1 -1
  122. package/dist/types/surfaces/StatusBadge.d.ts +5 -0
  123. package/package.json +1 -1
@@ -18,32 +18,44 @@ var _tmpl$ = ["<i class=\"", "\" aria-hidden=\"true\"></i>"],
18
18
  _tmpl$0 = ["<button type=\"button\">", "</button>"],
19
19
  _tmpl$1 = ["<div class=\"k2b-date-weekdays\" aria-hidden=\"true\">", "", "</div>"],
20
20
  _tmpl$10 = ["<div class=\"k2b-date-grid\" role=\"grid\">", "", "</div>"],
21
- _tmpl$11 = ["<section class=\"k2b-date-calendar\"", "><header class=\"k2b-date-calendar__header\"><button type=\"button\" class=\"k2b-date-calendar__nav\"", "><i class=\"ti ti-chevron-left\" aria-hidden=\"true\"></i></button><button type=\"button\" class=\"k2b-date-calendar__title\">", "</button><button type=\"button\" class=\"k2b-date-calendar__nav\"", "><i class=\"ti ti-chevron-right\" aria-hidden=\"true\"></i></button></header>", "</section>"],
22
- _tmpl$12 = ["<div class=\"k2b-date-months\" role=\"group\" aria-label=\"", "\">", "</div>"],
23
- _tmpl$13 = ["<button type=\"button\"", ">", "</button>"],
24
- _tmpl$14 = ["<span class=\"k2b-date-weekend\" style=\"", "\"></span>"],
25
- _tmpl$15 = ["<span", ">", "</span>"],
26
- _tmpl$16 = ["<span class=\"k2b-date-weekend\" aria-hidden=\"true\" style=\"", "\"></span>"],
27
- _tmpl$17 = ["<div class=\"k2b-date-week\" role=\"row\">", "</div>"],
28
- _tmpl$18 = ["<button type=\"button\" role=\"gridcell\"", ">", "</button>"],
29
- _tmpl$19 = ["<span>", "</span>"],
30
- _tmpl$20 = ["<label class=\"k2b-date-time\">", "<span class=\"k2b-date-time__control\"><input type=\"text\" inputmode=\"numeric\"", " placeholder=\"09:00\"", "><i class=\"ti ti-clock\" aria-hidden=\"true\"></i></span></label>"],
31
- _tmpl$21 = ["<div class=\"k2b-date-popover__body\">", "", "</div>"],
32
- _tmpl$22 = ["<button type=\"button\" class=\"k2b-date-optional-time__remove\"", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
33
- _tmpl$23 = ["<div class=\"k2b-date-optional-time\"", ">", "</div>"],
34
- _tmpl$24 = ["<div class=\"k2b-date-actions\">", "<button type=\"button\" class=\"k2b-date-apply\"", ">", "</button></div>"],
35
- _tmpl$25 = ["<button type=\"button\" class=\"k2b-date-optional-time__add\"><i class=\"ti ti-clock-plus\" aria-hidden=\"true\"></i>", "</button>"],
36
- _tmpl$26 = ["<span class=\"k2b-date-range-value\"><span>", "</span><i class=\"ti ti-arrow-narrow-right\" aria-hidden=\"true\"></i><span>", "</span></span>"],
37
- _tmpl$27 = ["<span class=\"k2b-date-duration\"><i class=\"ti ti-hourglass-low\" aria-hidden=\"true\"></i>", "</span>"],
38
- _tmpl$28 = ["<div class=\"k2b-date-range-times\">", "", "</div>"],
39
- _tmpl$29 = ["<div class=\"k2b-date-durations\" role=\"group\"", ">", "</div>"];
21
+ _tmpl$11 = ["<footer class=\"k2b-date-calendar__limits\"><span>", "</span><span>", "</span></footer>"],
22
+ _tmpl$12 = ["<section class=\"k2b-date-calendar\"", "><header class=\"k2b-date-calendar__header\"><button type=\"button\" class=\"k2b-date-calendar__nav\"", "><i class=\"ti ti-chevron-left\" aria-hidden=\"true\"></i></button><button type=\"button\" class=\"k2b-date-calendar__title\">", "</button><button type=\"button\" class=\"k2b-date-calendar__nav\"", "><i class=\"ti ti-chevron-right\" aria-hidden=\"true\"></i></button></header>", "", "</section>"],
23
+ _tmpl$13 = ["<div class=\"k2b-date-months\" role=\"group\" aria-label=\"", "\">", "</div>"],
24
+ _tmpl$14 = ["<button type=\"button\"", ">", "</button>"],
25
+ _tmpl$15 = ["<span class=\"k2b-date-weekend\" style=\"", "\"></span>"],
26
+ _tmpl$16 = ["<span", ">", "</span>"],
27
+ _tmpl$17 = ["<span class=\"k2b-date-weekend\" aria-hidden=\"true\" style=\"", "\"></span>"],
28
+ _tmpl$18 = ["<div class=\"k2b-date-week\" role=\"row\">", "</div>"],
29
+ _tmpl$19 = ["<button type=\"button\" role=\"gridcell\"", "", ">", "</button>"],
30
+ _tmpl$20 = ["<span>", "</span>"],
31
+ _tmpl$21 = ["<label class=\"k2b-date-time\">", "<span class=\"k2b-date-time__control\"><input type=\"text\" inputmode=\"numeric\"", " placeholder=\"09:00\"", "><i class=\"ti ti-clock\" aria-hidden=\"true\"></i></span></label>"],
32
+ _tmpl$22 = ["<p", " class=\"k2b-date-status\"", ">", "</p>"],
33
+ _tmpl$23 = ["<span class=\"k2b-sr-only\" aria-live=\"polite\">", "</span>"],
34
+ _tmpl$24 = ["<button type=\"button\" class=\"k2b-input-shell__clear k2b-input-clear-action k2b-date-field__clear\"", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
35
+ _tmpl$25 = ["<button type=\"button\" class=\"k2b-date-field__toggle\" tabindex=\"-1\"", "><i class=\"ti ti-chevron-down\" aria-hidden=\"true\"></i></button>"],
36
+ _tmpl$26 = ["<div class=\"k2b-date-popover__body\">", "", "</div>"],
37
+ _tmpl$27 = ["<div class=\"k2b-date-picker k2b-date-field\"", "><div class=\"k2b-input-shell\"", "><span class=\"k2b-input-shell__icon k2b-date-field__icon\" aria-hidden=\"true\"><i class=\"ti ti-calendar\"></i></span>", "", "", "<div", " class=\"k2b-date-popover\" popover=\"manual\" role=\"dialog\"", ">", "</div></div>", "</div>"],
38
+ _tmpl$28 = "<span class=\"k2b-date-field__lock\" aria-hidden=\"true\"><i class=\"ti ti-lock\"></i></span>",
39
+ _tmpl$29 = ["<div class=\"k2b-date-actions\">", "<button type=\"button\" class=\"k2b-date-apply\"", ">", "</button></div>"],
40
+ _tmpl$30 = ["<span class=\"k2b-date-duration\"><i class=\"ti ti-hourglass-low\" aria-hidden=\"true\"></i>", "</span>"],
41
+ _tmpl$31 = ["<div class=\"k2b-date-picker k2b-date-field\" data-range=\"true\"", "><div class=\"k2b-input-shell\" role=\"group\"", "><span class=\"k2b-input-shell__icon k2b-date-field__icon\" aria-hidden=\"true\"><i class=\"ti ti-calendar-stats\"></i></span>", "<i class=\"ti ti-arrow-narrow-right k2b-date-field__arrow\" aria-hidden=\"true\"></i>", "<span class=\"k2b-date-field__spacer\"></span>", "", "<div", " class=\"k2b-date-popover\" popover=\"manual\" role=\"dialog\"", ">", "</div></div>", "</div>"],
42
+ _tmpl$32 = ["<button type=\"button\" class=\"k2b-date-optional-time__remove\"", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
43
+ _tmpl$33 = ["<div class=\"k2b-date-optional-time\"", ">", "</div>"],
44
+ _tmpl$34 = ["<button type=\"button\" class=\"k2b-date-optional-time__add\"><i class=\"ti ti-clock-plus\" aria-hidden=\"true\"></i>", "</button>"],
45
+ _tmpl$35 = ["<span class=\"k2b-date-range-value\"><span>", "</span><i class=\"ti ti-arrow-narrow-right\" aria-hidden=\"true\"></i><span>", "</span></span>"],
46
+ _tmpl$36 = ["<div class=\"k2b-date-range-times\">", "", "</div>"],
47
+ _tmpl$37 = ["<div class=\"k2b-date-durations\" role=\"group\"", ">", "</div>"];
40
48
  import { dates } from "@k2b/stdlib";
41
- import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js";
49
+ import { batch, createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from "solid-js";
50
+ import { prefersMenuSheet } from "../actions/phone-sheet";
42
51
  import { createFieldMeta, Field, fieldControlAria, fieldDescribedBy } from "../internal/field";
43
52
  import { returnFocus, ringOnReturn } from "../internal/focus-return";
44
53
  import { useDateConfigLocale } from "../intl/locale";
45
54
  import { resolveUiMessages, useUiMessages } from "../intl/messages";
46
55
  import { isWeekendKey } from "../intl/weekend";
56
+ import { dateInputPattern, formatDateInput, parseDateInput } from "./date-input";
57
+ import { resolveDateInputMessages } from "./date-input-messages";
58
+ import { draftStatusText, errorText, formatDateLong, limitErrorText, rangeStatusText, restStatusText, spokenDateText, typingStatusText } from "./date-input-status";
47
59
  import { dateKey, displayDate, filterTimeInput, formatDateOnlyRangeDuration, formatDateTimeValue, inRange, isCompleteTime, isDateKey, isRangeEdge, monthDate, monthNames, msUntilNextDay, normalizeTimeInput, parseDateValue, pickerContext, pickRangeDay, previewRange, rangeAnchor, resolveFocusDay, splitDateTime, toDateTimeValue, weekendBands, yearMonth } from "./date-picker";
48
60
  import { commitFieldValue, resolveMaybeAccessor } from "./field-contract";
49
61
  const popoverIsOpen = popover => {
@@ -54,10 +66,18 @@ const popoverIsOpen = popover => {
54
66
  return false;
55
67
  }
56
68
  };
57
- export const placeDatePopover = (trigger, popover, wide) => {
69
+
70
+ /** `below` is a line under the trigger that the panel must not cover, such as a typed field's status line. */
71
+ export const placeDatePopover = (trigger, popover, wide, below) => {
58
72
  const margin = 8;
59
73
  const gap = 4;
60
- const rect = trigger.getBoundingClientRect();
74
+ const triggerRect = trigger.getBoundingClientRect();
75
+ const belowRect = below?.getBoundingClientRect();
76
+ const rect = {
77
+ left: triggerRect.left,
78
+ top: triggerRect.top,
79
+ bottom: Math.max(triggerRect.bottom, belowRect?.height ? belowRect.bottom : 0)
80
+ };
61
81
  // Keep the panel at its designed width instead of stretching to a full-width
62
82
  // trigger; it only ever shrinks to fit a narrow viewport.
63
83
  const preferredWidth = wide ? 440 : 336;
@@ -234,7 +254,19 @@ function DatePickerPanel(props) {
234
254
  const [view, setView] = createSignal("days");
235
255
  const context = createMemo(() => pickerContext(props.dateConfig));
236
256
  const month = createMemo(() => yearMonth(props.visibleMonth(), context()));
237
- const weeks = createMemo(() => dates.getMonthGrid(month().year, month().month, context()));
257
+ // Always six weeks: typing moves an open calendar between months, and a month with fewer weeks would resize it.
258
+ const weeks = createMemo(() => {
259
+ const grid = [...dates.getMonthGrid(month().year, month().month, context())];
260
+ while (grid.length < 6) {
261
+ const last = grid[grid.length - 1].at(-1);
262
+ grid.push(Array.from({
263
+ length: 7
264
+ }, (_, index) => dates.addDays(last, index + 1, context())));
265
+ }
266
+ return grid;
267
+ });
268
+ const blocked = key => Boolean(props.min && key < props.min || props.max && key > props.max);
269
+ const inputMessages = createMemo(() => resolveDateInputMessages(context().locale));
238
270
  const weekdays = createMemo(() => dates.weekdays(context()));
239
271
  const months = createMemo(() => monthNames(context()));
240
272
  const dayNameFormatter = createMemo(() => new Intl.DateTimeFormat(context().locale, {
@@ -291,7 +323,7 @@ function DatePickerPanel(props) {
291
323
  let calendar;
292
324
  // Days that belong to the visible month; the roving tabindex must land on one
293
325
  // of them or the grid becomes unreachable by keyboard after month navigation.
294
- const monthDayKeys = createMemo(() => dayRows().flat().filter(day => !day.outside).map(day => day.key));
326
+ const monthDayKeys = createMemo(() => dayRows().flat().filter(day => !day.outside && !blocked(day.key)).map(day => day.key));
295
327
  // Remembers where arrow-key navigation left off so Tab returns to that day.
296
328
  const [focusedKey, setFocusedKey] = createSignal();
297
329
  const defaultFocus = createMemo(() => resolveFocusDay(monthDayKeys(), focusedKey() || props.focusDate?.() || props.selected?.() || todayKey()));
@@ -299,8 +331,18 @@ function DatePickerPanel(props) {
299
331
  const moveYear = delta => props.setVisibleMonth(monthDate(month().year + delta, month().month, context()));
300
332
  const previousLabel = () => view() === "days" ? messages().previousMonth : messages().previousYear;
301
333
  const nextLabel = () => view() === "days" ? messages().nextMonth : messages().nextYear;
334
+ // A step whose whole month (or year) lies outside min/max is not offered.
335
+ const stepBlocked = delta => {
336
+ if (view() === "days") {
337
+ const target = dateKey(dates.addMonths(props.visibleMonth(), delta, context()), context()).slice(0, 7);
338
+ return Boolean(props.min && target < props.min.slice(0, 7) || props.max && target > props.max.slice(0, 7));
339
+ }
340
+ const year = String(month().year + delta);
341
+ return Boolean(props.min && year < props.min.slice(0, 4) || props.max && year > props.max.slice(0, 4));
342
+ };
302
343
  const focusDate = date => {
303
344
  const targetKey = dateKey(date, context());
345
+ if (blocked(targetKey)) return;
304
346
  if (!dates.isSameMonth(date, props.visibleMonth(), context())) props.setVisibleMonth(date);
305
347
  queueMicrotask(() => calendar?.querySelector(`[data-date-day="${targetKey}"]`)?.focus());
306
348
  };
@@ -330,7 +372,7 @@ function DatePickerPanel(props) {
330
372
  focusDate(dates.addDays(day, offset, context()));
331
373
  }
332
374
  };
333
- return _$ssr(_tmpl$11, _$ssrAttribute("aria-label", _$escape(messages().calendar, true), false), _$ssrAttribute("aria-label", _$escape(previousLabel(), true), false), _$escape(_$createComponent(Show, {
375
+ return _$ssr(_tmpl$12, _$ssrAttribute("aria-label", _$escape(messages().calendar, true), false), _$ssrAttribute("disabled", stepBlocked(-1), true) + _$ssrAttribute("aria-label", _$escape(previousLabel(), true), false), _$escape(_$createComponent(Show, {
334
376
  get when() {
335
377
  return view() === "days";
336
378
  },
@@ -340,16 +382,16 @@ function DatePickerPanel(props) {
340
382
  get children() {
341
383
  return dates.formatMonthYear(props.visibleMonth(), context());
342
384
  }
343
- })), _$ssrAttribute("aria-label", _$escape(nextLabel(), true), false), _$escape(_$createComponent(Show, {
385
+ })), _$ssrAttribute("disabled", stepBlocked(1), true) + _$ssrAttribute("aria-label", _$escape(nextLabel(), true), false), _$escape(_$createComponent(Show, {
344
386
  get when() {
345
387
  return view() === "days";
346
388
  },
347
389
  get fallback() {
348
- return _$ssr(_tmpl$12, `Months in ${_$escape(month().year, true)}`, _$escape(_$createComponent(For, {
390
+ return _$ssr(_tmpl$13, `Months in ${_$escape(month().year, true)}`, _$escape(_$createComponent(For, {
349
391
  get each() {
350
392
  return months();
351
393
  },
352
- children: (name, index) => _$ssr(_tmpl$13, _$ssrAttribute("aria-pressed", _$escape(index(), true) === _$escape(month().month, true), false) + _$ssrAttribute("data-selected", index() === month().month ? "true" : _$escape(undefined, true), false), _$escape(name))
394
+ children: (name, index) => _$ssr(_tmpl$14, _$ssrAttribute("aria-pressed", _$escape(index(), true) === _$escape(month().month, true), false) + _$ssrAttribute("data-selected", index() === month().month ? "true" : _$escape(undefined, true), false), _$escape(name))
353
395
  })));
354
396
  },
355
397
  get children() {
@@ -357,22 +399,22 @@ function DatePickerPanel(props) {
357
399
  get each() {
358
400
  return bands();
359
401
  },
360
- children: band => _$ssr(_tmpl$14, _$ssrStyleProperty("grid-column:", `${_$escape(band.start, true) + 1} / span ${_$escape(band.span, true)}`))
402
+ children: band => _$ssr(_tmpl$15, _$ssrStyleProperty("grid-column:", `${_$escape(band.start, true) + 1} / span ${_$escape(band.span, true)}`))
361
403
  })), _$escape(_$createComponent(For, {
362
404
  get each() {
363
405
  return weekdays();
364
406
  },
365
- children: (day, index) => _$ssr(_tmpl$15, _$ssrAttribute("data-weekend", weekendColumns()[index()] ? "true" : _$escape(undefined, true), false), _$escape(day))
407
+ children: (day, index) => _$ssr(_tmpl$16, _$ssrAttribute("data-weekend", weekendColumns()[index()] ? "true" : _$escape(undefined, true), false), _$escape(day))
366
408
  }))), _$ssr(_tmpl$10, _$escape(_$createComponent(For, {
367
409
  get each() {
368
410
  return bands();
369
411
  },
370
- children: band => _$ssr(_tmpl$16, _$ssrStyleProperty("grid-column:", `${_$escape(band.start, true) + 1} / span ${_$escape(band.span, true)}`) + _$ssrStyleProperty(";grid-row:", `1 / span ${_$escape(dayRows().length, true)}`))
412
+ children: band => _$ssr(_tmpl$17, _$ssrStyleProperty("grid-column:", `${_$escape(band.start, true) + 1} / span ${_$escape(band.span, true)}`) + _$ssrStyleProperty(";grid-row:", `1 / span ${_$escape(dayRows().length, true)}`))
371
413
  })), _$escape(_$createComponent(For, {
372
414
  get each() {
373
415
  return dayRows();
374
416
  },
375
- children: week => _$ssr(_tmpl$17, _$escape(_$createComponent(For, {
417
+ children: week => _$ssr(_tmpl$18, _$escape(_$createComponent(For, {
376
418
  each: week,
377
419
  children: (cell, cellIndex) => {
378
420
  const selected = () => props.selected?.() === cell.key;
@@ -383,64 +425,1007 @@ function DatePickerPanel(props) {
383
425
  const active = () => selected() || isRangeEdge(cell.key, range());
384
426
  const focusable = () => cell.key === defaultFocus();
385
427
  const today = () => cell.key === todayKey();
386
- return _$ssr(_tmpl$18, _$ssrAttribute("data-date-day", _$escape(cell.key, true), false) + _$ssrAttribute("data-date-focus", focusable() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", cell.outside ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-weekend", weekendColumns()[cellIndex()] ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-in-range", inRange(cell.key, range()) && !active() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-current", today() ? "date" : _$escape(undefined, true), false) + _$ssrAttribute("aria-label", today() ? _$escape(todayLabel(cell.label), true) : _$escape(cell.label, true), false) + _$ssrAttribute("aria-selected", _$escape(active(), true), false) + _$ssrAttribute("tabindex", focusable() ? 0 : -1, false), _$escape(cell.number));
428
+ const label = () => {
429
+ const name = today() ? todayLabel(cell.label) : cell.label;
430
+ return blocked(cell.key) ? inputMessages().notSelectable({
431
+ date: name
432
+ }) : name;
433
+ };
434
+ return _$ssr(_tmpl$19, _$ssrAttribute("data-date-day", _$escape(cell.key, true), false) + _$ssrAttribute("data-date-focus", focusable() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", cell.outside ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-weekend", weekendColumns()[cellIndex()] ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-in-range", inRange(cell.key, range()) && !active() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-current", today() ? "date" : _$escape(undefined, true), false) + _$ssrAttribute("aria-label", _$escape(label(), true), false) + _$ssrAttribute("aria-selected", _$escape(active(), true), false), _$ssrAttribute("disabled", blocked(cell.key), true) + _$ssrAttribute("tabindex", focusable() ? 0 : -1, false), _$escape(cell.number));
387
435
  }
388
436
  })))
389
437
  })))];
390
438
  }
439
+ })), _$escape(_$createComponent(Show, {
440
+ get when() {
441
+ return props.min || props.max;
442
+ },
443
+ get children() {
444
+ return _$ssr(_tmpl$11, props.min ? _$escape(inputMessages().limitFrom({
445
+ date: formatDateInput(props.min, context().locale)
446
+ })) : "", props.max ? _$escape(inputMessages().limitUntil({
447
+ date: formatDateInput(props.max, context().locale)
448
+ })) : "");
449
+ }
391
450
  })));
392
451
  }
393
452
  function TimeInput(props) {
394
453
  const messages = useUiMessages();
395
- return _$ssr(_tmpl$20, _$escape(_$createComponent(Show, {
454
+ return _$ssr(_tmpl$21, _$escape(_$createComponent(Show, {
396
455
  get when() {
397
456
  return props.label;
398
457
  },
399
458
  get children() {
400
- return _$ssr(_tmpl$19, _$escape(props.label));
459
+ return _$ssr(_tmpl$20, _$escape(props.label));
401
460
  }
402
461
  })), _$ssrAttribute("value", _$escape(props.time, true), false), _$ssrAttribute("aria-label", props.label ? _$escape(messages().timeLabel({
403
462
  label: props.label
404
463
  }), true) : _$escape(messages().time, true), false));
405
464
  }
465
+
466
+ /** The calendar popover of a typed field: it lives inside the field's shell, so focus in the calendar keeps the field's focus edge. */
467
+ function createCalendarPopover(options) {
468
+ const [open, setOpen] = createSignal(false);
469
+ let popover;
470
+ const place = () => {
471
+ const anchor = options.anchor();
472
+ if (anchor && popover && open()) placeDatePopover(anchor, popover, options.wide(), options.below());
473
+ };
474
+ const show = () => {
475
+ if (!popover || popoverIsOpen(popover)) return;
476
+ setOpen(true);
477
+ popover.showPopover();
478
+ place();
479
+ };
480
+ const close = () => {
481
+ if (popoverIsOpen(popover)) popover?.hidePopover();
482
+ setOpen(false);
483
+ };
484
+ createEffect(() => {
485
+ if (!open()) return;
486
+ window.addEventListener("resize", place);
487
+ window.addEventListener("scroll", place, true);
488
+ onCleanup(() => {
489
+ window.removeEventListener("resize", place);
490
+ window.removeEventListener("scroll", place, true);
491
+ });
492
+ });
493
+ onCleanup(close);
494
+ return {
495
+ open,
496
+ show,
497
+ close,
498
+ place,
499
+ ref: element => {
500
+ popover = element;
501
+ // The browser also hides a popover itself, for example when it leaves the top layer.
502
+ element.addEventListener("toggle", () => setOpen(popoverIsOpen(element)));
503
+ },
504
+ element: () => popover,
505
+ focusDay: () => queueMicrotask(() => popover?.querySelector('[data-date-focus="true"]')?.focus())
506
+ };
507
+ }
508
+
509
+ /** The day an empty calendar opens on: today, moved inside min/max, so a limited field never opens on locked months only. */
510
+ const openingDay = (value, limits, context) => {
511
+ if (value) return value;
512
+ const today = dateKey(new Date(), context);
513
+ if (limits.min && today < limits.min) return limits.min;
514
+ if (limits.max && today > limits.max) return limits.max;
515
+ return today;
516
+ };
517
+ const fieldName = owner => typeof owner.label === "string" ? owner.label : owner["aria-label"];
518
+
519
+ /** Clicks on the calendar's empty space keep focus where it is, so the draft and caret stay in the text. */
520
+ const keepFocus = event => {
521
+ if (!event.target.closest("button")) event.preventDefault();
522
+ };
523
+ const caretToEnd = input => {
524
+ input?.focus();
525
+ input?.setSelectionRange(input.value.length, input.value.length);
526
+ };
527
+ function DateStatusLine(props) {
528
+ return [_$ssr(_tmpl$22, _$ssrAttribute("id", _$escape(props.id, true), false), _$ssrAttribute("data-tone", _$escape(props.tone, true), false) + _$ssrAttribute("title", typeof props.text === "string" && props.text ? _$escape(props.text, true) : _$escape(undefined, true), false), _$escape(props.text)), _$ssr(_tmpl$23, _$escape(props.announcement))];
529
+ }
530
+
531
+ /**
532
+ * The typed date field. The text and the calendar always show the same day: typing moves the calendar, picking a day
533
+ * fills the text, and the status line says which day the text means before it is committed.
534
+ */
535
+ function DateTextField(props) {
536
+ const uiMessages = useUiMessages();
537
+ const meta = createFieldMeta(props.owner.id);
538
+ const locale = () => props.dateConfig().locale;
539
+ const t = createMemo(() => resolveDateInputMessages(locale()));
540
+ const today = () => dateKey(new Date(), props.dateConfig());
541
+ const format = key => key ? formatDateInput(key, locale()) : "";
542
+ const limits = () => ({
543
+ min: props.owner.min,
544
+ max: props.owner.max
545
+ });
546
+ const interactive = () => !props.owner.disabled && !props.owner.readOnly;
547
+ const [text, setText] = createSignal(format(props.value()));
548
+ const [dirty, setDirty] = createSignal(false);
549
+ const [failure, setFailure] = createSignal();
550
+ const [announcement, setAnnouncement] = createSignal("");
551
+ const [visibleMonth, setVisibleMonth] = createSignal(parseDateValue(openingDay(props.value(), limits(), props.dateConfig()), props.dateConfig()));
552
+ let shell;
553
+ let input;
554
+ let toggle;
555
+ let statusLine;
556
+ const [sheetOpen, setSheetOpen] = createSignal(false);
557
+ let announceTimer;
558
+ const calendar = createCalendarPopover({
559
+ anchor: () => shell,
560
+ below: () => statusLine,
561
+ wide: () => Boolean(props.owner.presets?.length)
562
+ });
563
+ const popoverId = `${meta.controlId}-calendar`;
564
+ const sheetId = `${meta.controlId}-sheet`;
565
+ // The status line holds the field's message slot, so it carries the `<id>-error` every field family uses.
566
+ const statusId = meta.errorId;
567
+ const parse = value => parseDateInput(value, {
568
+ today: today(),
569
+ locale: locale()
570
+ });
571
+ const parsed = createMemo(() => dirty() ? parse(text()) : undefined);
572
+ const draftKey = () => {
573
+ const result = parsed();
574
+ if (!result) return props.value();
575
+ return result.kind === "date" ? result.date : null;
576
+ };
577
+
578
+ // A value set from outside replaces the text, unless someone is typing.
579
+ createEffect(on([props.value, locale], () => {
580
+ if (dirty()) return;
581
+ setText(format(props.value()));
582
+ setFailure(undefined);
583
+ setVisibleMonth(parseDateValue(openingDay(props.value(), limits(), props.dateConfig()), props.dateConfig()));
584
+ }));
585
+ // The calendar follows the typed day, and a refused text stops being red once it means an allowed day.
586
+ createEffect(on(parsed, result => {
587
+ if (result?.kind === "date") {
588
+ setVisibleMonth(parseDateValue(result.date, props.dateConfig()));
589
+ if (!limitErrorText(result.date, limits(), locale(), t())) setFailure(undefined);
590
+ } else if (result?.kind === "empty") setFailure(undefined);
591
+ }));
592
+ onCleanup(() => clearTimeout(announceTimer));
593
+ const externalError = () => resolveMaybeAccessor(props.owner.error);
594
+ const invalid = () => Boolean(failure() || externalError());
595
+ const status = createMemo(() => {
596
+ const failed = failure();
597
+ if (failed) return {
598
+ tone: "error",
599
+ text: failed
600
+ };
601
+ const result = parsed();
602
+ if (result?.kind === "date") {
603
+ const limit = limitErrorText(result.date, limits(), locale(), t());
604
+ return limit ? {
605
+ tone: "rest",
606
+ text: limit
607
+ } : {
608
+ tone: "draft",
609
+ text: draftStatusText(result.date, result.note, locale(), t())
610
+ };
611
+ }
612
+ if (result?.kind === "error") return {
613
+ tone: "rest",
614
+ text: typingStatusText(result.error, locale(), t(), today())
615
+ };
616
+ const external = externalError();
617
+ if (external) return {
618
+ tone: "error",
619
+ text: external
620
+ };
621
+ const value = props.value();
622
+ if (!value || result) return {
623
+ tone: "rest",
624
+ text: ""
625
+ };
626
+ return {
627
+ tone: "rest",
628
+ text: props.owner.renderValue?.(value) ?? restStatusText(value, today(), locale())
629
+ };
630
+ });
631
+ const save = next => {
632
+ if (next !== props.value()) commitFieldValue(props.owner, next);
633
+ };
634
+ const fail = message => {
635
+ setFailure(message);
636
+ setAnnouncement(message);
637
+ };
638
+ /** Enter, leaving the field and a picked day commit; a refused text stays for correction and the value is kept. */
639
+ const commit = () => {
640
+ const result = parse(text());
641
+ setDirty(false);
642
+ if (result.kind === "empty") {
643
+ if (props.owner.required) return fail(t().required);
644
+ setFailure(undefined);
645
+ return save(null);
646
+ }
647
+ if (result.kind === "error") return fail(errorText(result.error, locale(), t(), today()));
648
+ const limit = limitErrorText(result.date, limits(), locale(), t());
649
+ if (limit) return fail(limit);
650
+ setFailure(undefined);
651
+ setText(format(result.date));
652
+ save(result.date);
653
+ };
654
+ const restore = () => {
655
+ setText(format(props.value()));
656
+ setDirty(false);
657
+ setFailure(undefined);
658
+ setVisibleMonth(parseDateValue(openingDay(props.value(), limits(), props.dateConfig()), props.dateConfig()));
659
+ calendar.close();
660
+ };
661
+ const pick = key => {
662
+ setText(format(key));
663
+ setDirty(false);
664
+ setFailure(undefined);
665
+ save(key);
666
+ calendar.close();
667
+ };
668
+ const openSheet = async () => {
669
+ setSheetOpen(true);
670
+ calendar.close();
671
+ const {
672
+ openDateSheet
673
+ } = await import("./date-sheet");
674
+ await openDateSheet({
675
+ title: props.owner.label ?? props.owner["aria-label"],
676
+ subtitle: props.value() ? formatDateLong(props.value(), locale()) : t().noDate,
677
+ ariaLabel: fieldName(props.owner),
678
+ id: sheetId,
679
+ locale: locale() ?? "en",
680
+ content: close => _$createComponent(DatePickerPanel, {
681
+ visibleMonth: visibleMonth,
682
+ setVisibleMonth: setVisibleMonth,
683
+ selected: draftKey,
684
+ focusDate: draftKey,
685
+ onSelect: key => {
686
+ // Saved at once: the sheet's promise settles only after its history entry has gone.
687
+ pick(key);
688
+ close(key);
689
+ },
690
+ get dateConfig() {
691
+ return props.dateConfig();
692
+ },
693
+ get min() {
694
+ return props.owner.min;
695
+ },
696
+ get max() {
697
+ return props.owner.max;
698
+ }
699
+ })
700
+ });
701
+ setSheetOpen(false);
702
+ toggle?.focus({
703
+ preventScroll: true
704
+ });
705
+ };
706
+ /** Phones open the calendar as a sheet; there the text only brings up the keypad. */
707
+ const openCalendar = focusDay => {
708
+ if (!interactive()) return;
709
+ if (prefersMenuSheet()) return void openSheet();
710
+ if (!dirty()) setVisibleMonth(parseDateValue(openingDay(props.value(), limits(), props.dateConfig()), props.dateConfig()));
711
+ calendar.show();
712
+ if (focusDay) calendar.focusDay();
713
+ };
714
+ const onPointerDown = event => {
715
+ const target = event.target;
716
+ if (!interactive() || calendar.element()?.contains(target) || target.closest(".k2b-date-field__clear")) return;
717
+ const onIcon = target.closest(".k2b-date-field__toggle, .k2b-date-field__icon");
718
+ if (prefersMenuSheet()) {
719
+ if (onIcon) {
720
+ event.preventDefault();
721
+ void openSheet();
722
+ }
723
+ return;
724
+ }
725
+ if (onIcon) {
726
+ event.preventDefault();
727
+ input?.focus();
728
+ if (calendar.open()) calendar.close();else openCalendar(false);
729
+ return;
730
+ }
731
+ // A pointer in the text opens the calendar and keeps the caret; Tab focus never opens it.
732
+ if (event.button === 0 && !calendar.open()) setTimeout(() => openCalendar(false));
733
+ };
734
+ const onKeyDown = event => {
735
+ // A refused text keeps Enter too, so it does not submit the form around it.
736
+ if (event.key === "Enter" && (dirty() || calendar.open() || failure())) {
737
+ event.preventDefault();
738
+ commit();
739
+ calendar.close();
740
+ } else if (event.key === "Escape" && (dirty() || calendar.open() || failure())) {
741
+ event.preventDefault();
742
+ event.stopPropagation();
743
+ restore();
744
+ } else if (event.key === "ArrowDown") {
745
+ event.preventDefault();
746
+ openCalendar(true);
747
+ } else if (event.key === "ArrowUp" && event.altKey) {
748
+ event.preventDefault();
749
+ calendar.close();
750
+ }
751
+ };
752
+ const onFocusOut = event => {
753
+ const next = event.relatedTarget;
754
+ if (sheetOpen() || next === input || next && calendar.element()?.contains(next)) return;
755
+ if (dirty()) commit();
756
+ calendar.close();
757
+ };
758
+ return _$createComponent(Field, {
759
+ get ["class"]() {
760
+ return props.owner.class;
761
+ },
762
+ get label() {
763
+ return props.owner.label;
764
+ },
765
+ get description() {
766
+ return props.owner.description;
767
+ },
768
+ get required() {
769
+ return props.owner.required;
770
+ },
771
+ get disabled() {
772
+ return props.owner.disabled;
773
+ },
774
+ meta: meta,
775
+ get children() {
776
+ return _$ssr(_tmpl$27, _$ssrAttribute("data-open", calendar.open() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-readonly", props.owner.readOnly ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-invalid", invalid() ? "true" : _$escape(undefined, true), false), _$ssrElement("input", _$mergeProps(() => fieldControlAria(meta, props.owner), {
777
+ get id() {
778
+ return meta.controlId;
779
+ },
780
+ "class": "k2b-input",
781
+ type: "text",
782
+ inputMode: "numeric",
783
+ autocomplete: "off",
784
+ spellcheck: false,
785
+ role: "combobox",
786
+ "aria-haspopup": "dialog",
787
+ get ["aria-expanded"]() {
788
+ return calendar.open() || sheetOpen();
789
+ },
790
+ get ["aria-controls"]() {
791
+ return sheetOpen() ? sheetId : popoverId;
792
+ },
793
+ get ["aria-describedby"]() {
794
+ return [props.owner["aria-describedby"], props.owner.description ? meta.descriptionId : undefined, statusId].filter(Boolean).join(" ");
795
+ },
796
+ get ["aria-invalid"]() {
797
+ return invalid() ? "true" : undefined;
798
+ },
799
+ get placeholder() {
800
+ return props.owner.placeholder ?? datePattern(locale(), t());
801
+ },
802
+ get value() {
803
+ return text();
804
+ },
805
+ get disabled() {
806
+ return props.owner.disabled;
807
+ },
808
+ get readOnly() {
809
+ return props.owner.readOnly;
810
+ }
811
+ }), undefined, false), _$escape(_$createComponent(Show, {
812
+ get when() {
813
+ return _$memo(() => !!props.owner.clearable)() && interactive();
814
+ },
815
+ get children() {
816
+ return _$ssr(_tmpl$24, _$ssrAttribute("data-hidden", text() ? _$escape(undefined, true) : "true", false) + _$ssrAttribute("aria-label", _$escape(uiMessages().clearDate, true), false));
817
+ }
818
+ })), _$escape(_$createComponent(Show, {
819
+ get when() {
820
+ return !props.owner.readOnly;
821
+ },
822
+ get fallback() {
823
+ return _$ssr(_tmpl$28);
824
+ },
825
+ get children() {
826
+ return _$ssr(_tmpl$25, _$ssrAttribute("disabled", props.owner.disabled, true) + _$ssrAttribute("aria-label", calendar.open() ? _$escape(t().closeCalendar, true) : _$escape(t().openCalendar, true), false) + _$ssrAttribute("aria-expanded", _$escape(calendar.open() || sheetOpen(), true), false) + _$ssrAttribute("aria-controls", sheetOpen() ? _$escape(sheetId, true) : _$escape(popoverId, true), false));
827
+ }
828
+ })), _$ssrAttribute("id", _$escape(popoverId, true), false), _$ssrAttribute("aria-label", _$escape(fieldName(props.owner) ?? uiMessages().datePicker, true), false), _$escape(_$createComponent(Show, {
829
+ get when() {
830
+ return calendar.open();
831
+ },
832
+ get children() {
833
+ return _$ssr(_tmpl$26, _$escape(_$createComponent(PresetRail, {
834
+ get presets() {
835
+ return props.owner.presets;
836
+ },
837
+ onSelect: value => {
838
+ pick(value);
839
+ caretToEnd(input);
840
+ }
841
+ })), _$escape(_$createComponent(DatePickerPanel, {
842
+ visibleMonth: visibleMonth,
843
+ setVisibleMonth: setVisibleMonth,
844
+ selected: draftKey,
845
+ focusDate: draftKey,
846
+ onSelect: key => {
847
+ pick(key);
848
+ caretToEnd(input);
849
+ },
850
+ get dateConfig() {
851
+ return props.dateConfig();
852
+ },
853
+ get min() {
854
+ return props.owner.min;
855
+ },
856
+ get max() {
857
+ return props.owner.max;
858
+ }
859
+ })));
860
+ }
861
+ })), _$escape(_$createComponent(DateStatusLine, {
862
+ id: statusId,
863
+ get tone() {
864
+ return status().tone;
865
+ },
866
+ get text() {
867
+ return status().text;
868
+ },
869
+ get announcement() {
870
+ return announcement();
871
+ }
872
+ })));
873
+ }
874
+ });
875
+ }
876
+ const datePattern = (locale, t) => dateInputPattern(locale, {
877
+ day: t.dayLetters,
878
+ month: t.monthLetters,
879
+ year: t.yearLetters
880
+ });
881
+
882
+ /** "11.11.2026 – 14.11.2026", "11.11. bis 14.11." or "Nov 11 to Nov 14" pasted into either half fills both. */
883
+ const splitPastedRange = text => {
884
+ const parts = text.trim().split(/\s*[–—]\s*|\s+-\s+|\s+(?:bis|to|until)\s+/i);
885
+ return parts.length === 2 && parts[0] && parts[1] ? [parts[0], parts[1]] : undefined;
886
+ };
887
+
888
+ /** A date-only range as two typed halves in one field; the calendar keeps two clicks and Apply. */
889
+ function DateRangeTextField(props) {
890
+ const uiMessages = useUiMessages();
891
+ const meta = createFieldMeta(props.owner.id);
892
+ const locale = () => props.dateConfig().locale;
893
+ const t = createMemo(() => resolveDateInputMessages(locale()));
894
+ const today = () => dateKey(new Date(), props.dateConfig());
895
+ const format = key => key ? formatDateInput(key, locale()) : "";
896
+ const limits = () => ({
897
+ min: props.owner.min,
898
+ max: props.owner.max
899
+ });
900
+ const interactive = () => !props.owner.disabled && !props.owner.readOnly;
901
+ const [startText, setStartText] = createSignal(format(props.value().start));
902
+ const [endText, setEndText] = createSignal(format(props.value().end));
903
+ const [dirty, setDirty] = createSignal(false);
904
+ const [failure, setFailure] = createSignal();
905
+ const [announcement, setAnnouncement] = createSignal("");
906
+ const [visibleMonth, setVisibleMonth] = createSignal(parseDateValue(openingDay(props.value().start || props.value().end, limits(), props.dateConfig()), props.dateConfig()));
907
+ // The calendar's own draft: two clicks pick a range, Apply commits it.
908
+ const [picked, setPicked] = createSignal(props.value());
909
+ const [anchor, setAnchor] = createSignal(null);
910
+ const [previewDate, setPreviewDate] = createSignal(null);
911
+ let shell;
912
+ let startInput;
913
+ let endInput;
914
+ let toggle;
915
+ let statusLine;
916
+ const [sheetOpen, setSheetOpen] = createSignal(false);
917
+ let announceTimer;
918
+ const wide = () => Boolean(props.owner.datePresets?.length || props.owner.presets?.length);
919
+ const calendar = createCalendarPopover({
920
+ anchor: () => shell,
921
+ below: () => statusLine,
922
+ wide
923
+ });
924
+ const popoverId = `${meta.controlId}-calendar`;
925
+ const sheetId = `${meta.controlId}-sheet`;
926
+ // The status line holds the field's message slot, so it carries the `<id>-error` every field family uses.
927
+ const statusId = meta.errorId;
928
+ const name = () => fieldName(props.owner);
929
+ const parse = value => parseDateInput(value, {
930
+ today: today(),
931
+ locale: locale()
932
+ });
933
+ const halves = createMemo(() => dirty() ? [parse(startText()), parse(endText())] : undefined);
934
+ const draft = createMemo(() => {
935
+ const current = halves();
936
+ if (!current) return props.value();
937
+ const [start, end] = current;
938
+ return {
939
+ start: start.kind === "date" ? start.date : null,
940
+ end: end.kind === "date" ? end.date : null
941
+ };
942
+ });
943
+ const shownRange = createMemo(() => previewRange(picked(), anchor(), previewDate()));
944
+ const fill = range => {
945
+ setStartText(format(range.start));
946
+ setEndText(format(range.end));
947
+ };
948
+ createEffect(on([props.value, locale], () => {
949
+ if (dirty()) return;
950
+ fill(props.value());
951
+ setPicked(props.value());
952
+ setFailure(undefined);
953
+ }));
954
+ createEffect(on(draft, range => {
955
+ // A day picked in the calendar wrote this text itself; its half-picked range keeps going.
956
+ if (!dirty() || range.start === picked().start && range.end === picked().end) return;
957
+ setPicked(range);
958
+ setAnchor(null);
959
+ if (range.start || range.end) setVisibleMonth(parseDateValue(range.start || range.end, props.dateConfig()));
960
+ setFailure(undefined);
961
+ }));
962
+ onCleanup(() => clearTimeout(announceTimer));
963
+ const externalError = () => resolveMaybeAccessor(props.owner.error);
964
+ const invalid = () => Boolean(failure() || externalError());
965
+ const status = createMemo(() => {
966
+ const failed = failure();
967
+ if (failed) return {
968
+ tone: "error",
969
+ text: failed
970
+ };
971
+ const current = halves();
972
+ if (current) {
973
+ const mistake = current.find(half => half.kind === "error");
974
+ if (mistake) return {
975
+ tone: "rest",
976
+ text: typingStatusText(mistake.error, locale(), t(), today())
977
+ };
978
+ const range = draft();
979
+ const limit = [range.start, range.end].map(key => key && limitErrorText(key, limits(), locale(), t())).find(Boolean);
980
+ if (limit) return {
981
+ tone: "rest",
982
+ text: limit
983
+ };
984
+ const text = rangeStatusText(range, locale(), t());
985
+ return {
986
+ tone: text ? "draft" : "rest",
987
+ text: text ? `→ ${text}` : ""
988
+ };
989
+ }
990
+ const external = externalError();
991
+ if (external) return {
992
+ tone: "error",
993
+ text: external
994
+ };
995
+ return {
996
+ tone: "rest",
997
+ text: rangeStatusText(props.value(), locale(), t())
998
+ };
999
+ });
1000
+ const save = next => {
1001
+ if (next.start !== props.value().start || next.end !== props.value().end) commitFieldValue(props.owner, next);
1002
+ };
1003
+ const fail = message => {
1004
+ setFailure(message);
1005
+ setAnnouncement(message);
1006
+ };
1007
+ const commitRange = range => {
1008
+ setDirty(false);
1009
+ const limit = [range.start, range.end].map(key => key && limitErrorText(key, limits(), locale(), t())).find(Boolean);
1010
+ if (limit) return fail(limit);
1011
+ if (range.start && range.end && range.end < range.start) return fail(t().endBeforeStart);
1012
+ if (range.start && !range.end) return fail(t().missingEnd);
1013
+ if (range.end && !range.start) return fail(t().missingStart);
1014
+ if (!range.start && props.owner.required) return fail(t().required);
1015
+ setFailure(undefined);
1016
+ fill(range);
1017
+ setPicked(range);
1018
+ save(range);
1019
+ };
1020
+ const commit = () => {
1021
+ const [start, end] = [parse(startText()), parse(endText())];
1022
+ const mistake = [start, end].find(half => half.kind === "error");
1023
+ if (mistake) {
1024
+ setDirty(false);
1025
+ return fail(errorText(mistake.error, locale(), t(), today()));
1026
+ }
1027
+ commitRange({
1028
+ start: start.kind === "date" ? start.date : null,
1029
+ end: end.kind === "date" ? end.date : null
1030
+ });
1031
+ };
1032
+ const restore = () => {
1033
+ fill(props.value());
1034
+ setPicked(props.value());
1035
+ setAnchor(null);
1036
+ setDirty(false);
1037
+ setFailure(undefined);
1038
+ calendar.close();
1039
+ };
1040
+ /** Calendar clicks fill both halves at once, so the text, the status line and the calendar show the same range. */
1041
+ const pickDay = key => {
1042
+ const next = pickRangeDay(picked(), anchor(), key);
1043
+ // One update: the start alone would read as a typed range and reset the anchor of the next click.
1044
+ batch(() => {
1045
+ setPicked(next.range);
1046
+ setAnchor(next.anchor);
1047
+ setPreviewDate(null);
1048
+ fill(next.range);
1049
+ setDirty(true);
1050
+ });
1051
+ };
1052
+ const apply = range => {
1053
+ commitRange(range);
1054
+ calendar.close();
1055
+ };
1056
+ const canApply = () => Boolean(picked().start) === Boolean(picked().end);
1057
+ const rangeCalendar = onApply => [_$ssr(_tmpl$26, _$escape(_$createComponent(Show, {
1058
+ get when() {
1059
+ return props.owner.datePresets?.length;
1060
+ },
1061
+ get fallback() {
1062
+ return _$createComponent(PresetRail, {
1063
+ get presets() {
1064
+ return props.owner.presets;
1065
+ },
1066
+ onSelect: onApply
1067
+ });
1068
+ },
1069
+ get children() {
1070
+ return _$createComponent(PresetRail, {
1071
+ get presets() {
1072
+ return props.owner.datePresets;
1073
+ },
1074
+ onSelect: value => {
1075
+ const next = value ? {
1076
+ start: value,
1077
+ end: value
1078
+ } : {
1079
+ start: null,
1080
+ end: null
1081
+ };
1082
+ batch(() => {
1083
+ setPicked(next);
1084
+ setAnchor(null);
1085
+ fill(next);
1086
+ setDirty(true);
1087
+ });
1088
+ if (value) setVisibleMonth(parseDateValue(value, props.dateConfig()));
1089
+ }
1090
+ });
1091
+ }
1092
+ })), _$escape(_$createComponent(DatePickerPanel, {
1093
+ visibleMonth: visibleMonth,
1094
+ setVisibleMonth: setVisibleMonth,
1095
+ range: shownRange,
1096
+ focusDate: () => picked().start,
1097
+ onSelect: pickDay,
1098
+ onDayPreview: key => setPreviewDate(rangeAnchor(picked(), anchor()) ? key : null),
1099
+ get dateConfig() {
1100
+ return props.dateConfig();
1101
+ },
1102
+ get min() {
1103
+ return props.owner.min;
1104
+ },
1105
+ get max() {
1106
+ return props.owner.max;
1107
+ }
1108
+ }))), _$ssr(_tmpl$29, _$escape(_$createComponent(Show, {
1109
+ get when() {
1110
+ return formatDateOnlyRangeDuration(shownRange(), props.dateConfig());
1111
+ },
1112
+ children: duration => _$ssr(_tmpl$30, _$escape(duration()))
1113
+ })), _$ssrAttribute("disabled", !canApply(), true), _$escape(uiMessages().apply))];
1114
+ const openSheet = async () => {
1115
+ setSheetOpen(true);
1116
+ calendar.close();
1117
+ const before = {
1118
+ start: startText(),
1119
+ end: endText(),
1120
+ dirty: dirty()
1121
+ };
1122
+ // Every opening starts from the text, also after a dismissed sheet left other days picked.
1123
+ batch(() => {
1124
+ setPicked(draft());
1125
+ setAnchor(null);
1126
+ setPreviewDate(null);
1127
+ });
1128
+ const {
1129
+ openDateSheet
1130
+ } = await import("./date-sheet");
1131
+ const range = await openDateSheet({
1132
+ title: props.owner.label ?? props.owner["aria-label"],
1133
+ subtitle: rangeStatusText(props.value(), locale(), t()) || t().noDate,
1134
+ ariaLabel: name(),
1135
+ id: sheetId,
1136
+ locale: locale() ?? "en",
1137
+ content: close => rangeCalendar(next => {
1138
+ // Saved at once, as in the single date sheet.
1139
+ commitRange(next);
1140
+ close(next);
1141
+ })
1142
+ });
1143
+ setSheetOpen(false);
1144
+ // A dismissed sheet saves nothing and leaves the text as it was before the sheet opened.
1145
+ if (!range) batch(() => {
1146
+ setStartText(before.start);
1147
+ setEndText(before.end);
1148
+ setDirty(before.dirty);
1149
+ setAnchor(null);
1150
+ });
1151
+ toggle?.focus({
1152
+ preventScroll: true
1153
+ });
1154
+ };
1155
+ const openCalendar = focusDay => {
1156
+ if (!interactive()) return;
1157
+ setAnchor(null);
1158
+ setPreviewDate(null);
1159
+ setPicked(draft());
1160
+ if (prefersMenuSheet()) return void openSheet();
1161
+ const range = draft();
1162
+ setVisibleMonth(parseDateValue(openingDay(range.start || range.end, limits(), props.dateConfig()), props.dateConfig()));
1163
+ calendar.show();
1164
+ if (focusDay) calendar.focusDay();
1165
+ };
1166
+ const onPointerDown = event => {
1167
+ const target = event.target;
1168
+ if (!interactive() || calendar.element()?.contains(target) || target.closest(".k2b-date-field__clear")) return;
1169
+ const onIcon = target.closest(".k2b-date-field__toggle, .k2b-date-field__icon");
1170
+ if (prefersMenuSheet()) {
1171
+ if (onIcon) {
1172
+ event.preventDefault();
1173
+ void openSheet();
1174
+ }
1175
+ return;
1176
+ }
1177
+ if (onIcon) {
1178
+ event.preventDefault();
1179
+ if (!shell?.contains(document.activeElement)) startInput?.focus();
1180
+ if (calendar.open()) calendar.close();else openCalendar(false);
1181
+ return;
1182
+ }
1183
+ if (event.button === 0 && !calendar.open()) setTimeout(() => openCalendar(false));
1184
+ };
1185
+ const onKeyDown = event => {
1186
+ // A refused text keeps Enter too, so it does not submit the form around it.
1187
+ if (event.key === "Enter" && (dirty() || calendar.open() || failure())) {
1188
+ event.preventDefault();
1189
+ commit();
1190
+ calendar.close();
1191
+ } else if (event.key === "Escape" && (dirty() || calendar.open() || failure())) {
1192
+ event.preventDefault();
1193
+ event.stopPropagation();
1194
+ restore();
1195
+ } else if (event.key === "ArrowDown") {
1196
+ event.preventDefault();
1197
+ openCalendar(true);
1198
+ } else if (event.key === "ArrowUp" && event.altKey) {
1199
+ event.preventDefault();
1200
+ calendar.close();
1201
+ }
1202
+ };
1203
+ const onInput = setter => event => {
1204
+ setter(event.currentTarget.value);
1205
+ setDirty(true);
1206
+ clearTimeout(announceTimer);
1207
+ announceTimer = setTimeout(() => {
1208
+ const range = draft();
1209
+ setAnnouncement(range.start || range.end ? rangeStatusText(range, locale(), t()) : "");
1210
+ }, 500);
1211
+ };
1212
+ const onPaste = event => {
1213
+ const parts = splitPastedRange(event.clipboardData?.getData("text") ?? "");
1214
+ if (!parts) return;
1215
+ event.preventDefault();
1216
+ setStartText(parts[0]);
1217
+ setEndText(parts[1]);
1218
+ setDirty(true);
1219
+ };
1220
+ const onFocusOut = event => {
1221
+ const next = event.relatedTarget;
1222
+ if (sheetOpen() || next && shell?.contains(next)) return;
1223
+ if (dirty()) commit();
1224
+ calendar.close();
1225
+ };
1226
+ const inputProps = {
1227
+ class: "k2b-input",
1228
+ type: "text",
1229
+ inputMode: "numeric",
1230
+ autocomplete: "off",
1231
+ spellcheck: false,
1232
+ "aria-haspopup": "dialog",
1233
+ onKeyDown,
1234
+ onPaste
1235
+ };
1236
+ const describedBy = () => [props.owner["aria-describedby"], props.owner.description ? meta.descriptionId : undefined, statusId].filter(Boolean).join(" ");
1237
+ return _$createComponent(Field, {
1238
+ get ["class"]() {
1239
+ return props.owner.class;
1240
+ },
1241
+ get label() {
1242
+ return props.owner.label;
1243
+ },
1244
+ get description() {
1245
+ return props.owner.description;
1246
+ },
1247
+ get required() {
1248
+ return props.owner.required;
1249
+ },
1250
+ get disabled() {
1251
+ return props.owner.disabled;
1252
+ },
1253
+ meta: meta,
1254
+ get children() {
1255
+ return _$ssr(_tmpl$31, _$ssrAttribute("data-open", calendar.open() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-readonly", props.owner.readOnly ? "true" : _$escape(undefined, true), false), _$ssrAttribute("aria-labelledby", props.owner.label ? _$escape(meta.labelId, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-label", props.owner.label ? _$escape(undefined, true) : _$escape(props.owner["aria-label"], true), false) + _$ssrAttribute("data-invalid", invalid() ? "true" : _$escape(undefined, true), false), _$ssrElement("input", _$mergeProps(inputProps, {
1256
+ role: "combobox",
1257
+ get ["aria-controls"]() {
1258
+ return sheetOpen() ? sheetId : popoverId;
1259
+ },
1260
+ get id() {
1261
+ return meta.controlId;
1262
+ },
1263
+ get ["aria-label"]() {
1264
+ return name() ? t().startOf({
1265
+ label: name()
1266
+ }) : t().start;
1267
+ },
1268
+ get ["aria-expanded"]() {
1269
+ return calendar.open() || sheetOpen();
1270
+ },
1271
+ get ["aria-describedby"]() {
1272
+ return describedBy();
1273
+ },
1274
+ get ["aria-invalid"]() {
1275
+ return invalid() ? "true" : undefined;
1276
+ },
1277
+ get ["aria-required"]() {
1278
+ return props.owner.required || undefined;
1279
+ },
1280
+ get placeholder() {
1281
+ return t().start;
1282
+ },
1283
+ get value() {
1284
+ return startText();
1285
+ },
1286
+ get disabled() {
1287
+ return props.owner.disabled;
1288
+ },
1289
+ get readOnly() {
1290
+ return props.owner.readOnly;
1291
+ }
1292
+ }), undefined, false), _$ssrElement("input", _$mergeProps(inputProps, {
1293
+ role: "combobox",
1294
+ get ["aria-controls"]() {
1295
+ return sheetOpen() ? sheetId : popoverId;
1296
+ },
1297
+ get ["aria-label"]() {
1298
+ return name() ? t().endOf({
1299
+ label: name()
1300
+ }) : t().end;
1301
+ },
1302
+ get ["aria-expanded"]() {
1303
+ return calendar.open() || sheetOpen();
1304
+ },
1305
+ get ["aria-describedby"]() {
1306
+ return describedBy();
1307
+ },
1308
+ get ["aria-invalid"]() {
1309
+ return invalid() ? "true" : undefined;
1310
+ },
1311
+ get ["aria-required"]() {
1312
+ return props.owner.required || undefined;
1313
+ },
1314
+ get placeholder() {
1315
+ return t().end;
1316
+ },
1317
+ get value() {
1318
+ return endText();
1319
+ },
1320
+ get disabled() {
1321
+ return props.owner.disabled;
1322
+ },
1323
+ get readOnly() {
1324
+ return props.owner.readOnly;
1325
+ }
1326
+ }), undefined, false), _$escape(_$createComponent(Show, {
1327
+ get when() {
1328
+ return _$memo(() => !!props.owner.clearable)() && interactive();
1329
+ },
1330
+ get children() {
1331
+ return _$ssr(_tmpl$24, _$ssrAttribute("data-hidden", startText() || endText() ? _$escape(undefined, true) : "true", false) + _$ssrAttribute("aria-label", _$escape(uiMessages().clearDate, true), false));
1332
+ }
1333
+ })), _$escape(_$createComponent(Show, {
1334
+ get when() {
1335
+ return !props.owner.readOnly;
1336
+ },
1337
+ get fallback() {
1338
+ return _$ssr(_tmpl$28);
1339
+ },
1340
+ get children() {
1341
+ return _$ssr(_tmpl$25, _$ssrAttribute("disabled", props.owner.disabled, true) + _$ssrAttribute("aria-label", calendar.open() ? _$escape(t().closeCalendar, true) : _$escape(t().openCalendar, true), false) + _$ssrAttribute("aria-expanded", _$escape(calendar.open() || sheetOpen(), true), false) + _$ssrAttribute("aria-controls", sheetOpen() ? _$escape(sheetId, true) : _$escape(popoverId, true), false));
1342
+ }
1343
+ })), _$ssrAttribute("id", _$escape(popoverId, true), false), _$ssrAttribute("aria-label", _$escape(name() ?? uiMessages().datePicker, true), false), _$escape(_$createComponent(Show, {
1344
+ get when() {
1345
+ return calendar.open();
1346
+ },
1347
+ get children() {
1348
+ return rangeCalendar(range => {
1349
+ apply(range);
1350
+ caretToEnd(endInput);
1351
+ });
1352
+ }
1353
+ })), _$escape(_$createComponent(DateStatusLine, {
1354
+ id: statusId,
1355
+ get tone() {
1356
+ return status().tone;
1357
+ },
1358
+ get text() {
1359
+ return status().text;
1360
+ },
1361
+ get announcement() {
1362
+ return announcement();
1363
+ }
1364
+ })));
1365
+ }
1366
+ });
1367
+ }
406
1368
  export function DatePicker(props) {
407
1369
  const value = () => resolveMaybeAccessor(props.value);
408
1370
  const dateConfig = useDateConfigLocale(() => props.dateConfig);
409
1371
  const [visibleMonth, setVisibleMonth] = createSignal(parseDateValue(value(), dateConfig()));
410
1372
  const valueLabel = () => displayDate(value(), dateConfig());
411
- return _$createComponent(PickerShell, {
412
- owner: props,
413
- icon: "ti ti-calendar",
414
- valueLabel: valueLabel,
415
- get valueContent() {
416
- return props.renderValue ? () => props.renderValue(value()) : undefined;
1373
+ return _$createComponent(Show, {
1374
+ get when() {
1375
+ return props.appearance === "plain";
417
1376
  },
418
- clearValue: null,
419
- onOpen: () => setVisibleMonth(parseDateValue(value(), dateConfig())),
420
- get wide() {
421
- return Boolean(props.presets?.length);
1377
+ get fallback() {
1378
+ return _$createComponent(DateTextField, {
1379
+ owner: props,
1380
+ value: value,
1381
+ dateConfig: dateConfig
1382
+ });
422
1383
  },
423
- children: close => _$ssr(_tmpl$21, _$escape(_$createComponent(PresetRail, {
424
- get presets() {
425
- return props.presets;
426
- },
427
- onSelect: value => {
428
- close();
429
- commitFieldValue(props, value);
430
- }
431
- })), _$escape(_$createComponent(DatePickerPanel, {
432
- visibleMonth: visibleMonth,
433
- setVisibleMonth: setVisibleMonth,
434
- selected: value,
435
- focusDate: value,
436
- onSelect: value => {
437
- close();
438
- commitFieldValue(props, value);
439
- },
440
- get dateConfig() {
441
- return dateConfig();
442
- }
443
- })))
1384
+ get children() {
1385
+ return _$createComponent(PickerShell, {
1386
+ owner: props,
1387
+ icon: "ti ti-calendar",
1388
+ valueLabel: valueLabel,
1389
+ get valueContent() {
1390
+ return props.renderValue ? () => props.renderValue(value()) : undefined;
1391
+ },
1392
+ clearValue: null,
1393
+ onOpen: () => setVisibleMonth(parseDateValue(openingDay(value(), {
1394
+ min: props.min,
1395
+ max: props.max
1396
+ }, dateConfig()), dateConfig())),
1397
+ get wide() {
1398
+ return Boolean(props.presets?.length);
1399
+ },
1400
+ children: close => _$ssr(_tmpl$26, _$escape(_$createComponent(PresetRail, {
1401
+ get presets() {
1402
+ return props.presets;
1403
+ },
1404
+ onSelect: value => {
1405
+ close();
1406
+ commitFieldValue(props, value);
1407
+ }
1408
+ })), _$escape(_$createComponent(DatePickerPanel, {
1409
+ visibleMonth: visibleMonth,
1410
+ setVisibleMonth: setVisibleMonth,
1411
+ selected: value,
1412
+ focusDate: value,
1413
+ onSelect: value => {
1414
+ close();
1415
+ commitFieldValue(props, value);
1416
+ },
1417
+ get dateConfig() {
1418
+ return dateConfig();
1419
+ },
1420
+ get min() {
1421
+ return props.min;
1422
+ },
1423
+ get max() {
1424
+ return props.max;
1425
+ }
1426
+ })))
1427
+ });
1428
+ }
444
1429
  });
445
1430
  }
446
1431
  export function DateTimePicker(props) {
@@ -463,7 +1448,10 @@ export function DateTimePicker(props) {
463
1448
  setDraftDate(next.date);
464
1449
  setDraftTime(next.time || defaultTime());
465
1450
  setWithTime(valueHasTime());
466
- setVisibleMonth(parseDateValue(next.date, dateConfig()));
1451
+ setVisibleMonth(parseDateValue(openingDay(next.date, {
1452
+ min: props.min,
1453
+ max: props.max
1454
+ }, dateConfig()), dateConfig()));
467
1455
  };
468
1456
  const canApply = () => Boolean(draftDate()) && (!withTime() || isCompleteTime(draftTime()));
469
1457
  const draftValue = () => withTime() ? toDateTimeValue(draftDate(), draftTime(), dateConfig()) : draftDate() || null;
@@ -499,7 +1487,7 @@ export function DateTimePicker(props) {
499
1487
  close();
500
1488
  commitFieldValue(props, next);
501
1489
  };
502
- return [_$ssr(_tmpl$21, _$escape(_$createComponent(PresetRail, {
1490
+ return [_$ssr(_tmpl$26, _$escape(_$createComponent(PresetRail, {
503
1491
  get presets() {
504
1492
  return props.presets;
505
1493
  },
@@ -520,8 +1508,14 @@ export function DateTimePicker(props) {
520
1508
  },
521
1509
  get dateConfig() {
522
1510
  return dateConfig();
1511
+ },
1512
+ get min() {
1513
+ return props.min;
1514
+ },
1515
+ get max() {
1516
+ return props.max;
523
1517
  }
524
- }))), _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
1518
+ }))), _$ssr(_tmpl$29, _$escape(_$createComponent(Show, {
525
1519
  get when() {
526
1520
  return props.optionalTime;
527
1521
  },
@@ -534,12 +1528,12 @@ export function DateTimePicker(props) {
534
1528
  });
535
1529
  },
536
1530
  get children() {
537
- return _$ssr(_tmpl$23, _$ssrAttribute("data-with-time", withTime() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1531
+ return _$ssr(_tmpl$33, _$ssrAttribute("data-with-time", withTime() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
538
1532
  get when() {
539
1533
  return withTime();
540
1534
  },
541
1535
  get fallback() {
542
- return _$ssr(_tmpl$25, _$escape(messages().addTime));
1536
+ return _$ssr(_tmpl$34, _$escape(messages().addTime));
543
1537
  },
544
1538
  get children() {
545
1539
  return [_$createComponent(TimeInput, {
@@ -547,7 +1541,7 @@ export function DateTimePicker(props) {
547
1541
  return draftTime();
548
1542
  },
549
1543
  onChange: setDraftTime
550
- }), _$ssr(_tmpl$22, _$ssrAttribute("aria-label", _$escape(messages().removeTime, true), false) + _$ssrAttribute("title", _$escape(messages().removeTime, true), false))];
1544
+ }), _$ssr(_tmpl$32, _$ssrAttribute("aria-label", _$escape(messages().removeTime, true), false) + _$ssrAttribute("title", _$escape(messages().removeTime, true), false))];
551
1545
  }
552
1546
  })));
553
1547
  }
@@ -591,7 +1585,10 @@ export function DateRangePicker(props) {
591
1585
  setPreviewDate(null);
592
1586
  setStartTime(next.start.time || "09:00");
593
1587
  setEndTime(next.end.time || "10:00");
594
- setVisibleMonth(parseDateValue(next.start.date || next.end.date, dateConfig()));
1588
+ setVisibleMonth(parseDateValue(openingDay(next.start.date || next.end.date, {
1589
+ min: props.min,
1590
+ max: props.max
1591
+ }, dateConfig()), dateConfig()));
595
1592
  };
596
1593
  const valueLabel = () => {
597
1594
  if (!value().start && !value().end) return "";
@@ -600,7 +1597,7 @@ export function DateRangePicker(props) {
600
1597
  };
601
1598
  const valueContent = () => {
602
1599
  const format = withTime() ? formatDateTimeValue : displayDate;
603
- return _$ssr(_tmpl$26, value().start ? _$escape(format(value().start, dateConfig())) : _$escape(messages().start), value().end ? _$escape(format(value().end, dateConfig())) : "End");
1600
+ return _$ssr(_tmpl$35, value().start ? _$escape(format(value().start, dateConfig())) : _$escape(messages().start), value().end ? _$escape(format(value().end, dateConfig())) : "End");
604
1601
  };
605
1602
  const selectDate = value => {
606
1603
  const next = pickRangeDay(draftRange(), anchor(), value);
@@ -653,108 +1650,128 @@ export function DateRangePicker(props) {
653
1650
  close();
654
1651
  commitFieldValue(props, value);
655
1652
  };
656
- return _$createComponent(PickerShell, {
657
- owner: props,
658
- icon: "ti ti-calendar-stats",
659
- valueLabel: valueLabel,
660
- valueContent: valueContent,
661
- clearValue: {
662
- start: null,
663
- end: null
1653
+ return _$createComponent(Show, {
1654
+ get when() {
1655
+ return props.appearance === "plain" || withTime();
664
1656
  },
665
- get timezone() {
666
- return _$memo(() => !!withTime())() ? dateConfig().timeZone : undefined;
667
- },
668
- footerMeta: () => _$createComponent(Show, {
669
- get when() {
670
- return durationLabel();
671
- },
672
- get children() {
673
- return _$ssr(_tmpl$27, _$escape(durationLabel()));
674
- }
675
- }),
676
- onOpen: syncDraft,
677
- get wide() {
678
- return Boolean(props.datePresets?.length || props.presets?.length);
679
- },
680
- children: close => [_$ssr(_tmpl$21, _$escape(_$createComponent(Show, {
681
- get when() {
682
- return props.datePresets?.length;
683
- },
684
- get fallback() {
685
- return _$createComponent(PresetRail, {
686
- get presets() {
687
- return props.presets;
1657
+ get fallback() {
1658
+ return _$createComponent(DateRangeTextField, {
1659
+ owner: props,
1660
+ value: value,
1661
+ dateConfig: dateConfig
1662
+ });
1663
+ },
1664
+ get children() {
1665
+ return _$createComponent(PickerShell, {
1666
+ owner: props,
1667
+ icon: "ti ti-calendar-stats",
1668
+ valueLabel: valueLabel,
1669
+ valueContent: valueContent,
1670
+ clearValue: {
1671
+ start: null,
1672
+ end: null
1673
+ },
1674
+ get timezone() {
1675
+ return _$memo(() => !!withTime())() ? dateConfig().timeZone : undefined;
1676
+ },
1677
+ footerMeta: () => _$createComponent(Show, {
1678
+ get when() {
1679
+ return durationLabel();
688
1680
  },
689
- onSelect: value => {
690
- close();
691
- commitFieldValue(props, value);
1681
+ get children() {
1682
+ return _$ssr(_tmpl$30, _$escape(durationLabel()));
692
1683
  }
693
- });
694
- },
695
- get children() {
696
- return _$createComponent(PresetRail, {
697
- get presets() {
698
- return props.datePresets;
1684
+ }),
1685
+ onOpen: syncDraft,
1686
+ get wide() {
1687
+ return Boolean(props.datePresets?.length || props.presets?.length);
1688
+ },
1689
+ children: close => [_$ssr(_tmpl$26, _$escape(_$createComponent(Show, {
1690
+ get when() {
1691
+ return props.datePresets?.length;
699
1692
  },
700
- onSelect: value => {
701
- setDraftRange(value ? {
702
- start: value,
703
- end: value
704
- } : {
705
- start: null,
706
- end: null
1693
+ get fallback() {
1694
+ return _$createComponent(PresetRail, {
1695
+ get presets() {
1696
+ return props.presets;
1697
+ },
1698
+ onSelect: value => {
1699
+ close();
1700
+ commitFieldValue(props, value);
1701
+ }
707
1702
  });
708
- if (value) setVisibleMonth(parseDateValue(value, dateConfig()));
709
- }
710
- });
711
- }
712
- })), _$escape(_$createComponent(DatePickerPanel, {
713
- visibleMonth: visibleMonth,
714
- setVisibleMonth: setVisibleMonth,
715
- range: displayRange,
716
- focusDate: () => draftRange().start,
717
- onSelect: selectDate,
718
- onDayPreview: value => setPreviewDate(rangeAnchor(draftRange(), anchor()) ? value : null),
719
- get dateConfig() {
720
- return dateConfig();
721
- }
722
- }))), _$createComponent(Show, {
723
- get when() {
724
- return withTime();
725
- },
726
- get children() {
727
- return _$ssr(_tmpl$28, _$escape(_$createComponent(TimeInput, {
728
- get label() {
729
- return messages().start;
730
1703
  },
731
- get time() {
732
- return startTime();
1704
+ get children() {
1705
+ return _$createComponent(PresetRail, {
1706
+ get presets() {
1707
+ return props.datePresets;
1708
+ },
1709
+ onSelect: value => {
1710
+ setDraftRange(value ? {
1711
+ start: value,
1712
+ end: value
1713
+ } : {
1714
+ start: null,
1715
+ end: null
1716
+ });
1717
+ if (value) setVisibleMonth(parseDateValue(value, dateConfig()));
1718
+ }
1719
+ });
1720
+ }
1721
+ })), _$escape(_$createComponent(DatePickerPanel, {
1722
+ visibleMonth: visibleMonth,
1723
+ setVisibleMonth: setVisibleMonth,
1724
+ range: displayRange,
1725
+ focusDate: () => draftRange().start,
1726
+ onSelect: selectDate,
1727
+ onDayPreview: value => setPreviewDate(rangeAnchor(draftRange(), anchor()) ? value : null),
1728
+ get dateConfig() {
1729
+ return dateConfig();
733
1730
  },
734
- onChange: setStartTime
735
- })), _$escape(_$createComponent(TimeInput, {
736
- get label() {
737
- return messages().end;
1731
+ get min() {
1732
+ return props.min;
738
1733
  },
739
- get time() {
740
- return endTime();
1734
+ get max() {
1735
+ return props.max;
1736
+ }
1737
+ }))), _$createComponent(Show, {
1738
+ get when() {
1739
+ return withTime();
741
1740
  },
742
- onChange: setEndTime
743
- })));
744
- }
745
- }), _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
746
- get when() {
747
- return _$memo(() => !!withTime())() && props.durationPresets?.length;
748
- },
749
- get children() {
750
- return _$ssr(_tmpl$29, _$ssrAttribute("aria-label", _$escape(messages().durationPresets, true), false), _$escape(_$createComponent(For, {
751
- get each() {
752
- return props.durationPresets;
1741
+ get children() {
1742
+ return _$ssr(_tmpl$36, _$escape(_$createComponent(TimeInput, {
1743
+ get label() {
1744
+ return messages().start;
1745
+ },
1746
+ get time() {
1747
+ return startTime();
1748
+ },
1749
+ onChange: setStartTime
1750
+ })), _$escape(_$createComponent(TimeInput, {
1751
+ get label() {
1752
+ return messages().end;
1753
+ },
1754
+ get time() {
1755
+ return endTime();
1756
+ },
1757
+ onChange: setEndTime
1758
+ })));
1759
+ }
1760
+ }), _$ssr(_tmpl$29, _$escape(_$createComponent(Show, {
1761
+ get when() {
1762
+ return _$memo(() => !!withTime())() && props.durationPresets?.length;
753
1763
  },
754
- children: preset => _$ssr(_tmpl$13, _$ssrAttribute("aria-pressed", _$escape(durationMinutes(), true) === _$escape(preset.minutes, true), false), _$escape(preset.label))
755
- })));
756
- }
757
- })), _$ssrAttribute("disabled", Boolean(draftRange().start) !== Boolean(draftRange().end) || Boolean(withTime() && draftRange().start && draftRange().end && (!isCompleteTime(startTime()) || !isCompleteTime(endTime()))), true), _$escape(messages().apply))]
1764
+ get children() {
1765
+ return _$ssr(_tmpl$37, _$ssrAttribute("aria-label", _$escape(messages().durationPresets, true), false), _$escape(_$createComponent(For, {
1766
+ get each() {
1767
+ return props.durationPresets;
1768
+ },
1769
+ children: preset => _$ssr(_tmpl$14, _$ssrAttribute("aria-pressed", _$escape(durationMinutes(), true) === _$escape(preset.minutes, true), false), _$escape(preset.label))
1770
+ })));
1771
+ }
1772
+ })), _$ssrAttribute("disabled", Boolean(draftRange().start) !== Boolean(draftRange().end) || Boolean(withTime() && draftRange().start && draftRange().end && (!isCompleteTime(startTime()) || !isCompleteTime(endTime()))), true), _$escape(messages().apply))]
1773
+ });
1774
+ }
758
1775
  });
759
1776
  }
760
1777
  //# sourceMappingURL=DatePicker.js.map