@k2b/ui 0.24.0 → 0.25.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 (128) hide show
  1. package/README.md +2 -2
  2. package/dist/browser/actions/Dropdown.js +67 -52
  3. package/dist/browser/actions/Dropdown.js.map +1 -1
  4. package/dist/browser/actions/GestureMenu.js +3 -3
  5. package/dist/browser/actions/GestureMenu.js.map +1 -1
  6. package/dist/browser/actions/SpotlightSearch.js +13 -6
  7. package/dist/browser/actions/SpotlightSearch.js.map +1 -1
  8. package/dist/browser/actions/Toolbar.js +75 -1
  9. package/dist/browser/actions/Toolbar.js.map +1 -1
  10. package/dist/browser/content/Calendar.js +1538 -911
  11. package/dist/browser/content/Calendar.js.map +1 -1
  12. package/dist/browser/content/CalendarDayView.js +1 -1
  13. package/dist/browser/content/CalendarDayView.js.map +1 -1
  14. package/dist/browser/content/CalendarWeekView.js +1 -1
  15. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  16. package/dist/browser/content/Kbd.js +51 -0
  17. package/dist/browser/content/Kbd.js.map +1 -0
  18. package/dist/browser/content/LabelChip.js +38 -0
  19. package/dist/browser/content/LabelChip.js.map +1 -0
  20. package/dist/browser/content/MarkdownView.js +2 -1
  21. package/dist/browser/content/MarkdownView.js.map +1 -1
  22. package/dist/browser/content/Steps.js +34 -0
  23. package/dist/browser/content/Steps.js.map +1 -0
  24. package/dist/browser/content/VirtualFeed.js +33 -1
  25. package/dist/browser/content/VirtualFeed.js.map +1 -1
  26. package/dist/browser/content/calendar-dialogs.js +32 -60
  27. package/dist/browser/content/calendar-dialogs.js.map +1 -1
  28. package/dist/browser/content/calendar-month-scroll.js +80 -0
  29. package/dist/browser/content/calendar-month-scroll.js.map +1 -0
  30. package/dist/browser/content/index.js +4 -1
  31. package/dist/browser/content/index.js.map +1 -1
  32. package/dist/browser/content/markdown-info-blocks.js +33 -5
  33. package/dist/browser/content/markdown-info-blocks.js.map +1 -1
  34. package/dist/browser/feedback/prompts.js +17 -3
  35. package/dist/browser/feedback/prompts.js.map +1 -1
  36. package/dist/browser/intl/index.js +1 -0
  37. package/dist/browser/intl/index.js.map +1 -1
  38. package/dist/browser/intl/shortcut-messages.js +77 -0
  39. package/dist/browser/intl/shortcut-messages.js.map +1 -0
  40. package/dist/browser/intl/shortcut.js +123 -0
  41. package/dist/browser/intl/shortcut.js.map +1 -0
  42. package/dist/browser/layout/AppWorkspace.js +343 -325
  43. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  44. package/dist/browser/layout/TableOfContents.js +115 -0
  45. package/dist/browser/layout/TableOfContents.js.map +1 -0
  46. package/dist/browser/layout/app-workspace-controller.js +3 -46
  47. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  48. package/dist/browser/layout/index.js +1 -0
  49. package/dist/browser/layout/index.js.map +1 -1
  50. package/dist/browser/layout/table-of-contents-messages.js +19 -0
  51. package/dist/browser/layout/table-of-contents-messages.js.map +1 -0
  52. package/dist/browser/surfaces/LinkCard.js +40 -24
  53. package/dist/browser/surfaces/LinkCard.js.map +1 -1
  54. package/dist/ssr/actions/Dropdown.js +11 -5
  55. package/dist/ssr/actions/Dropdown.js.map +1 -1
  56. package/dist/ssr/actions/GestureMenu.js +3 -3
  57. package/dist/ssr/actions/GestureMenu.js.map +1 -1
  58. package/dist/ssr/actions/SpotlightSearch.js +9 -3
  59. package/dist/ssr/actions/SpotlightSearch.js.map +1 -1
  60. package/dist/ssr/actions/Toolbar.js +67 -1
  61. package/dist/ssr/actions/Toolbar.js.map +1 -1
  62. package/dist/ssr/content/Calendar.js +863 -312
  63. package/dist/ssr/content/Calendar.js.map +1 -1
  64. package/dist/ssr/content/CalendarDayView.js +1 -1
  65. package/dist/ssr/content/CalendarDayView.js.map +1 -1
  66. package/dist/ssr/content/CalendarWeekView.js +1 -1
  67. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  68. package/dist/ssr/content/Kbd.js +38 -0
  69. package/dist/ssr/content/Kbd.js.map +1 -0
  70. package/dist/ssr/content/LabelChip.js +21 -0
  71. package/dist/ssr/content/LabelChip.js.map +1 -0
  72. package/dist/ssr/content/MarkdownView.js +2 -1
  73. package/dist/ssr/content/MarkdownView.js.map +1 -1
  74. package/dist/ssr/content/Steps.js +14 -0
  75. package/dist/ssr/content/Steps.js.map +1 -0
  76. package/dist/ssr/content/VirtualFeed.js +33 -1
  77. package/dist/ssr/content/VirtualFeed.js.map +1 -1
  78. package/dist/ssr/content/calendar-dialogs.js +12 -32
  79. package/dist/ssr/content/calendar-dialogs.js.map +1 -1
  80. package/dist/ssr/content/calendar-month-scroll.js +80 -0
  81. package/dist/ssr/content/calendar-month-scroll.js.map +1 -0
  82. package/dist/ssr/content/index.js +4 -1
  83. package/dist/ssr/content/index.js.map +1 -1
  84. package/dist/ssr/content/markdown-info-blocks.js +33 -5
  85. package/dist/ssr/content/markdown-info-blocks.js.map +1 -1
  86. package/dist/ssr/feedback/prompts.js +17 -3
  87. package/dist/ssr/feedback/prompts.js.map +1 -1
  88. package/dist/ssr/intl/index.js +1 -0
  89. package/dist/ssr/intl/index.js.map +1 -1
  90. package/dist/ssr/intl/shortcut-messages.js +77 -0
  91. package/dist/ssr/intl/shortcut-messages.js.map +1 -0
  92. package/dist/ssr/intl/shortcut.js +123 -0
  93. package/dist/ssr/intl/shortcut.js.map +1 -0
  94. package/dist/ssr/layout/AppWorkspace.js +65 -59
  95. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  96. package/dist/ssr/layout/TableOfContents.js +79 -0
  97. package/dist/ssr/layout/TableOfContents.js.map +1 -0
  98. package/dist/ssr/layout/app-workspace-controller.js +3 -46
  99. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  100. package/dist/ssr/layout/index.js +1 -0
  101. package/dist/ssr/layout/index.js.map +1 -1
  102. package/dist/ssr/layout/table-of-contents-messages.js +19 -0
  103. package/dist/ssr/layout/table-of-contents-messages.js.map +1 -0
  104. package/dist/ssr/surfaces/LinkCard.js +26 -10
  105. package/dist/ssr/surfaces/LinkCard.js.map +1 -1
  106. package/dist/styles.css +1 -1
  107. package/dist/types/actions/Dropdown.d.ts +12 -0
  108. package/dist/types/actions/SpotlightSearch.d.ts +2 -0
  109. package/dist/types/content/Calendar.d.ts +16 -6
  110. package/dist/types/content/Kbd.d.ts +16 -0
  111. package/dist/types/content/LabelChip.d.ts +12 -0
  112. package/dist/types/content/MarkdownView.d.ts +2 -1
  113. package/dist/types/content/Steps.d.ts +12 -0
  114. package/dist/types/content/VirtualFeed.d.ts +8 -0
  115. package/dist/types/content/calendar-dialogs.d.ts +0 -2
  116. package/dist/types/content/calendar-month-scroll.d.ts +25 -0
  117. package/dist/types/content/index.d.ts +7 -1
  118. package/dist/types/content/markdown-info-blocks.d.ts +21 -4
  119. package/dist/types/feedback/prompts.d.ts +8 -2
  120. package/dist/types/intl/index.d.ts +2 -0
  121. package/dist/types/intl/shortcut-messages.d.ts +35 -0
  122. package/dist/types/intl/shortcut.d.ts +29 -0
  123. package/dist/types/layout/AppWorkspace.d.ts +3 -1
  124. package/dist/types/layout/TableOfContents.d.ts +24 -0
  125. package/dist/types/layout/index.d.ts +2 -0
  126. package/dist/types/layout/table-of-contents-messages.d.ts +4 -0
  127. package/dist/types/surfaces/LinkCard.d.ts +3 -0
  128. package/package.json +3 -3
@@ -27,47 +27,49 @@ var _tmpl$ = ["<div role=\"radiogroup\"", " aria-orientation=\"horizontal\" clas
27
27
  _tmpl$16 = ["<span class=\"k2b-calendar-month__day-number\"", ">", "</span>"],
28
28
  _tmpl$17 = ["<span class=\"k2b-calendar-month__badge\">", "", "</span>"],
29
29
  _tmpl$18 = ["<span class=\"k2b-calendar-dot\"", " style=\"", "\"></span>"],
30
- _tmpl$19 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\">", "</div>"],
31
- _tmpl$20 = ["<div class=\"k2b-calendar-month\" role=\"grid\"", " aria-multiselectable=\"true\" data-lanes=\"true\" style=\"", "\"><div class=\"k2b-calendar-month__weekdays\" role=\"row\"", ">", "", "</div>", "</div>"],
32
- _tmpl$21 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\"", ">", "</div>"],
33
- _tmpl$22 = ["<div class=\"k2b-calendar-month__week-number\" role=\"rowheader\">", "</div>"],
34
- _tmpl$23 = ["<div class=\"k2b-calendar-month__week\" role=\"row\"", ">", "", "</div>"],
35
- _tmpl$24 = ["<button type=\"button\" class=\"k2b-calendar-month__week-link\" tabindex=\"-1\"", ">", "</button>"],
36
- _tmpl$25 = ["<button type=\"button\" class=\"k2b-calendar-month__more\" tabindex=\"-1\" style=\"", "\"", " aria-haspopup=\"dialog\"", "><span class=\"k2b-calendar-month__more-long\">", "</span><span class=\"k2b-calendar-month__more-short\">+", "</span></button>"],
37
- _tmpl$26 = ["<div class=\"k2b-calendar-month__day\" role=\"gridcell\"", "><div class=\"k2b-calendar-month__day-header\"><span class=\"k2b-calendar-month__day-number\"", ">", "</span>", "</div><div class=\"k2b-calendar-month__lanes\">", "", "", "</div></div>"],
38
- _tmpl$27 = ["<div class=\"k2b-calendar-month__segment\" style=\"", "\">", "</div>"],
39
- _tmpl$28 = ["<ul class=\"k2b-calendar-day-list__events\">", "</ul>"],
40
- _tmpl$29 = "<i class=\"ti ti-plus\" aria-hidden=\"true\"></i>",
41
- _tmpl$30 = ["<div class=\"k2b-calendar-day-list\"><div class=\"k2b-calendar-day-list__title\">", "</div>", "<div class=\"k2b-calendar-day-list__actions\">", "", "</div></div>"],
42
- _tmpl$31 = ["<p class=\"k2b-calendar-day-list__empty\">", "</p>"],
43
- _tmpl$32 = ["<li>", "</li>"],
44
- _tmpl$33 = "<i class=\"ti ti-zoom-in\" aria-hidden=\"true\"></i>",
45
- _tmpl$34 = ["<div class=\"k2b-calendar-popover\" popover=\"auto\" role=\"dialog\"", ">", "</div>"],
46
- _tmpl$35 = ["<div class=\"k2b-calendar-time-grid__all-day\" style=\"", "\"><div class=\"k2b-calendar-time-grid__all-day-label\">", "</div>", "</div>"],
47
- _tmpl$36 = ["<div class=\"k2b-calendar-time-grid\"", "><div class=\"k2b-calendar-time-grid__days\" style=\"", "\"><div class=\"k2b-calendar-time-grid__corner\"></div>", "</div>", "<div class=\"k2b-calendar-time-grid__scroll k2b-detail-trail\"><div class=\"k2b-calendar-time-grid__grid\" style=\"", "\"><div class=\"k2b-calendar-time-grid__gutter\" style=\"", "\">", "</div>", "</div></div></div>"],
48
- _tmpl$37 = ["<span class=\"k2b-calendar-time-grid__day-label\"", ">", "</span>"],
49
- _tmpl$38 = ["<span class=\"k2b-calendar-time-grid__badge\">", "", "</span>"],
50
- _tmpl$39 = ["<div class=\"k2b-calendar-time-grid__all-day-events\">", "", "</div>"],
51
- _tmpl$40 = ["<div class=\"k2b-calendar-time-grid__hour\"", ">", ":00</div>"],
52
- _tmpl$41 = ["<div class=\"k2b-calendar-time-grid__now\" style=\"", "\"></div>"],
53
- _tmpl$42 = ["<div class=\"k2b-calendar-time-grid__column\" style=\"", "\">", "", "", "", "", "</div>"],
54
- _tmpl$43 = ["<div class=\"k2b-calendar-preview__title\">", "</div>"],
55
- _tmpl$44 = ["<div class=\"k2b-calendar-preview k2b-calendar-preview--timed\"", " style=\"", "\"><div class=\"k2b-calendar-preview__content\">", "<div class=\"k2b-calendar-preview__label\">", " - ", "</div></div></div>"],
56
- _tmpl$45 = ["<button type=\"button\"", " draggable=\"false\" class=\"k2b-calendar-time-grid__resize\"><span class=\"k2b-calendar-time-grid__resize-icon\" aria-hidden=\"true\"></span></button>"],
57
- _tmpl$46 = ["<div class=\"k2b-calendar-time-grid__event-position\" style=\"", "\"><div class=\"k2b-calendar-time-grid__event-frame\">", "", "</div></div>"],
58
- _tmpl$47 = ["<button type=\"button\" class=\"k2b-calendar-time-grid__overflow\" style=\"", "\"", "><span class=\"k2b-calendar-time-grid__overflow-count\">+", "</span></button>"],
59
- _tmpl$48 = ["<div class=\"k2b-calendar-time-grid__overflow-menu\" style=\"", "\"><div class=\"k2b-calendar-time-grid__overflow-title\">", " - ", "</div>", "</div>"],
60
- _tmpl$49 = ["<div class=\"k2b-calendar-year\">", "</div>"],
61
- _tmpl$50 = ["<div class=\"k2b-calendar-year__month\"><div class=\"k2b-calendar-year__title\">", "</div><div class=\"k2b-calendar-year__grid\">", "</div></div>"],
62
- _tmpl$51 = ["<span class=\"k2b-calendar-year__indicator\"", " style=\"", "\"></span>"],
63
- _tmpl$52 = ["<div class=\"k2b-calendar-mobile-month__events\">", "</div>"],
64
- _tmpl$53 = ["<div class=\"k2b-calendar-mobile-month\">", "<div class=\"k2b-calendar-mobile-month__agenda\"><div class=\"k2b-calendar-mobile-month__title\">", "</div>", "</div></div>"],
65
- _tmpl$54 = ["<div class=\"k2b-calendar-mobile-month__empty\">", "</div>"],
66
- _tmpl$55 = ["<div class=\"k2b-calendar-body\">", "</div>"],
67
- _tmpl$56 = ["<section class=\"", "\"", ">", "", "</section>"];
30
+ _tmpl$19 = ["<div class=\"k2b-calendar-month__week-number\" role=\"rowheader\">", "</div>"],
31
+ _tmpl$20 = ["<div class=\"k2b-calendar-month__week\" role=\"row\"", " style=\"", "\"", ">", "", "</div>"],
32
+ _tmpl$21 = ["<button type=\"button\" class=\"k2b-calendar-month__week-link\" tabindex=\"-1\"", ">", "</button>"],
33
+ _tmpl$22 = ["<button type=\"button\" class=\"k2b-calendar-month__more\" tabindex=\"-1\" style=\"", "\"", " aria-haspopup=\"dialog\"", "><span class=\"k2b-calendar-month__more-long\">", "</span><span class=\"k2b-calendar-month__more-short\">+", "</span></button>"],
34
+ _tmpl$23 = ["<div class=\"k2b-calendar-month__day\" role=\"gridcell\"", "><div class=\"k2b-calendar-month__day-header\"><span class=\"k2b-calendar-month__day-number\"", ">", "</span>", "</div><div class=\"k2b-calendar-month__lanes\">", "", "", "</div></div>"],
35
+ _tmpl$24 = ["<div class=\"k2b-calendar-month__segment\" style=\"", "\">", "</div>"],
36
+ _tmpl$25 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\">", "</div>"],
37
+ _tmpl$26 = ["<div class=\"k2b-calendar-month__weekdays\" role=\"row\"", ">", "", "</div>"],
38
+ _tmpl$27 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\"", ">", "</div>"],
39
+ _tmpl$28 = ["<div class=\"k2b-calendar-month\" role=\"grid\"", " aria-multiselectable=\"true\" data-lanes=\"true\" style=\"", "\">", "", "</div>"],
40
+ _tmpl$29 = ["<div class=\"k2b-calendar-month-viewport\"><div class=\"k2b-calendar-month\" role=\"grid\"", " aria-multiselectable=\"true\" data-lanes=\"true\" data-continuous=\"true\"", " style=\"", "\">", "<div class=\"k2b-calendar-month__track\" role=\"rowgroup\" style=\"", "\">", "</div></div><div class=\"k2b-calendar-month__scroll-label\" aria-hidden=\"true\"", ">", "</div></div>"],
41
+ _tmpl$30 = ["<ul class=\"k2b-calendar-day-list__events\">", "</ul>"],
42
+ _tmpl$31 = "<i class=\"ti ti-plus\" aria-hidden=\"true\"></i>",
43
+ _tmpl$32 = ["<div class=\"k2b-calendar-day-list\"><div class=\"k2b-calendar-day-list__title\">", "</div>", "<div class=\"k2b-calendar-day-list__actions\">", "", "</div></div>"],
44
+ _tmpl$33 = ["<p class=\"k2b-calendar-day-list__empty\">", "</p>"],
45
+ _tmpl$34 = ["<li>", "</li>"],
46
+ _tmpl$35 = "<i class=\"ti ti-zoom-in\" aria-hidden=\"true\"></i>",
47
+ _tmpl$36 = ["<div class=\"k2b-calendar-popover\" popover=\"auto\" role=\"dialog\"", ">", "</div>"],
48
+ _tmpl$37 = ["<div class=\"k2b-calendar-time-grid__all-day\" style=\"", "\"><div class=\"k2b-calendar-time-grid__all-day-label\">", "</div>", "</div>"],
49
+ _tmpl$38 = ["<div class=\"k2b-calendar-time-grid\"", "><div class=\"k2b-calendar-time-grid__days\" style=\"", "\"><div class=\"k2b-calendar-time-grid__corner\"></div>", "</div>", "<div class=\"k2b-calendar-time-grid__scroll k2b-detail-trail\"><div class=\"k2b-calendar-time-grid__grid\" style=\"", "\"><div class=\"k2b-calendar-time-grid__gutter\" style=\"", "\">", "</div>", "</div></div></div>"],
50
+ _tmpl$39 = ["<span class=\"k2b-calendar-time-grid__day-label\"", ">", "</span>"],
51
+ _tmpl$40 = ["<span class=\"k2b-calendar-time-grid__badge\">", "", "</span>"],
52
+ _tmpl$41 = ["<div class=\"k2b-calendar-time-grid__all-day-events\">", "", "</div>"],
53
+ _tmpl$42 = ["<div class=\"k2b-calendar-time-grid__hour\"", ">", ":00</div>"],
54
+ _tmpl$43 = ["<div class=\"k2b-calendar-time-grid__now\" style=\"", "\"></div>"],
55
+ _tmpl$44 = ["<div class=\"k2b-calendar-time-grid__column\" style=\"", "\">", "", "", "", "", "</div>"],
56
+ _tmpl$45 = ["<div class=\"k2b-calendar-preview__title\">", "</div>"],
57
+ _tmpl$46 = ["<div class=\"k2b-calendar-preview k2b-calendar-preview--timed\"", " style=\"", "\"><div class=\"k2b-calendar-preview__content\">", "<div class=\"k2b-calendar-preview__label\">", " - ", "</div></div></div>"],
58
+ _tmpl$47 = ["<button type=\"button\"", " draggable=\"false\" class=\"k2b-calendar-time-grid__resize\"><span class=\"k2b-calendar-time-grid__resize-icon\" aria-hidden=\"true\"></span></button>"],
59
+ _tmpl$48 = ["<div class=\"k2b-calendar-time-grid__event-position\" style=\"", "\"><div class=\"k2b-calendar-time-grid__event-frame\">", "", "</div></div>"],
60
+ _tmpl$49 = ["<button type=\"button\" class=\"k2b-calendar-time-grid__overflow\" style=\"", "\"", "><span class=\"k2b-calendar-time-grid__overflow-count\">+", "</span></button>"],
61
+ _tmpl$50 = ["<div class=\"k2b-calendar-time-grid__overflow-menu\" style=\"", "\"><div class=\"k2b-calendar-time-grid__overflow-title\">", " - ", "</div>", "</div>"],
62
+ _tmpl$51 = ["<div class=\"k2b-calendar-year\">", "</div>"],
63
+ _tmpl$52 = ["<div class=\"k2b-calendar-year__month\"><div class=\"k2b-calendar-year__title\">", "</div><div class=\"k2b-calendar-year__grid\">", "</div></div>"],
64
+ _tmpl$53 = ["<span class=\"k2b-calendar-year__indicator\"", " style=\"", "\"></span>"],
65
+ _tmpl$54 = ["<div class=\"k2b-calendar-mobile-month__events\">", "</div>"],
66
+ _tmpl$55 = ["<div class=\"k2b-calendar-mobile-month\">", "<div class=\"k2b-calendar-mobile-month__agenda\"><div class=\"k2b-calendar-mobile-month__title\">", "</div>", "</div></div>"],
67
+ _tmpl$56 = ["<div class=\"k2b-calendar-mobile-month__empty\">", "</div>"],
68
+ _tmpl$57 = ["<div class=\"k2b-calendar-body\">", "</div>"],
69
+ _tmpl$58 = ["<section class=\"", "\"", ">", "", "</section>"];
68
70
  import { Link } from "@k2b/ssr/nav";
69
71
  import { dates as calendar } from "@k2b/stdlib";
70
- import { createEffect, createMemo, createSignal, For, mergeProps, on, onCleanup, onMount, Show, untrack } from "solid-js";
72
+ import { createComputed, createEffect, createMemo, createSignal, For, mergeProps, on, onCleanup, onMount, Show, untrack } from "solid-js";
71
73
  import { Button } from "../actions/Button";
72
74
  import { GestureMenu } from "../actions/GestureMenu";
73
75
  import SegmentedControl from "../actions/SegmentedControl";
@@ -75,13 +77,15 @@ import { useDateConfigLocale } from "../intl/locale";
75
77
  import { useUiMessages } from "../intl/messages";
76
78
  import { CalendarDayView } from "./CalendarDayView";
77
79
  import { CalendarWeekView } from "./CalendarWeekView";
80
+ import { addDayKeys } from "./calendar-days";
78
81
  import { openCalendarGoTo, openCalendarShortcuts } from "./calendar-dialogs";
79
82
  import { layoutCalendarIntervals } from "./calendar-event-layout";
80
83
  import { useCalendarMessages } from "./calendar-messages";
81
84
  import { fitMonthWeek, layoutMonthWeek, monthLaneCapacity } from "./calendar-month-layout";
85
+ import { MONTH_ROW_BLOCK, MONTH_ROWS_IN_VIEW, monthFocusRow, monthOfWeek, monthRenderWindow, monthRevealTop, monthRowOf, monthTodayDirection, monthVisibleRows, monthWeekKeys } from "./calendar-month-scroll";
82
86
  import { calendarDayIndexAtPoint, calendarMinuteAtPoint, startCalendarPointerSession } from "./calendar-pointer";
83
87
  import { EventChip, eventHref, followCalendarHref, formatTime, ownerDateConfig, parseDate, placeCalendarPopover, textWidth, validDate } from "./calendar-shared";
84
- import { compactGermanDate } from "./calendar-time-view";
88
+ import { compactGermanDate, reducedMotion } from "./calendar-time-view";
85
89
 
86
90
  /** The days a host loaded events for, from the first day `from` up to `to`, both `YYYY-MM-DD` or dates. */
87
91
 
@@ -413,11 +417,13 @@ const CalendarHeader = props => {
413
417
  const calendarMessages = useCalendarMessages();
414
418
  const dateConfig = () => ownerDateConfig(props.owner);
415
419
  const timeView = () => props.nav.timeView();
420
+ const monthView = () => props.nav.monthView();
421
+ const scrollingView = () => timeView() ?? monthView();
416
422
  const parseKey = key => calendar.parseCalendarDate(key, dateConfig());
417
423
  /** The date the header pages from: the first day a day or week view shows, else the date. */
418
424
  const base = () => {
419
425
  const shown = timeView()?.visibleKeys().first;
420
- return shown ? parseKey(shown) : props.date;
426
+ return shown ? parseKey(shown) : monthView()?.stepFrom() ?? props.date;
421
427
  };
422
428
  const previous = () => adjacentCalendarDate(base(), props.view, -1, dateConfig());
423
429
  const next = () => adjacentCalendarDate(base(), props.view, 1, dateConfig());
@@ -451,7 +457,7 @@ const CalendarHeader = props => {
451
457
  timeZone: dateConfig().timeZone
452
458
  });
453
459
  if (props.view === "week") return `${formatDay(calendar.startOfWeek(props.date, dateConfig()), dateConfig())} - ${formatDay(calendar.addDays(calendar.startOfWeek(props.date, dateConfig()), 6, dateConfig()), dateConfig())}`;
454
- return formatMonth(props.date, dateConfig());
460
+ return formatMonth(monthView()?.focusMonth() ?? props.date, dateConfig());
455
461
  };
456
462
  /**
457
463
  * The titles this view can show around the current date: the months of the year, the weeks of the month and across
@@ -515,37 +521,52 @@ const CalendarHeader = props => {
515
521
  const href = props.owner.getViewHref?.(view);
516
522
  if (href) window.location.href = href;
517
523
  };
524
+ /**
525
+ * A previous or next button. Its element stays while its target changes, such as with each step through the months,
526
+ * so a repeated click or Enter lands on the same button and keeps its focus.
527
+ */
518
528
  const navButton = (date, icon, label, direction) => {
519
- const href = props.owner.getDateHref?.(date, props.view);
520
- const step = () => timeView()?.step(direction) ?? false;
521
- const title = props.nav.commands().length > 0 ? `${label} (${direction > 0 ? "J" : "K"})` : label;
522
- return (props.owner.onNavigateHref || props.owner.onNavigate) && href ? _$createComponent(CalendarNavigationLink, {
529
+ const href = () => props.owner.getDateHref?.(date(), props.view);
530
+ const step = () => scrollingView()?.step(direction) ?? false;
531
+ const title = () => props.nav.commands().length > 0 ? `${label} (${direction > 0 ? "J" : "K"})` : label;
532
+ const link = target => _$createComponent(CalendarNavigationLink, {
523
533
  get owner() {
524
534
  return props.owner;
525
535
  },
526
- href: href,
536
+ get href() {
537
+ return target();
538
+ },
527
539
  intercept: step,
528
- anchorProps: {
529
- "aria-label": label,
530
- title,
531
- class: "k2b-calendar-header__nav-button"
540
+ get anchorProps() {
541
+ return {
542
+ "aria-label": label,
543
+ title: title(),
544
+ class: "k2b-calendar-header__nav-button"
545
+ };
532
546
  },
533
547
  get children() {
534
548
  return _$ssr(_tmpl$2, `ti ${_$escape(icon, true)}`);
535
549
  }
536
- }) : props.owner.onDateChange ? _$ssr(_tmpl$3, _$ssrAttribute("aria-label", _$escape(label, true), false) + _$ssrAttribute("title", _$escape(title, true), false), `ti ${_$escape(icon, true)}`) : _$createComponent(CalendarNavigationLink, {
537
- get owner() {
538
- return props.owner;
550
+ });
551
+ return _$createComponent(Show, {
552
+ get when() {
553
+ return Boolean((props.owner.onNavigateHref || props.owner.onNavigate) && href());
539
554
  },
540
- href: href ?? "#",
541
- intercept: step,
542
- anchorProps: {
543
- "aria-label": label,
544
- title,
545
- class: "k2b-calendar-header__nav-button"
555
+ get fallback() {
556
+ return _$createComponent(Show, {
557
+ get when() {
558
+ return props.owner.onDateChange;
559
+ },
560
+ get fallback() {
561
+ return link(() => href() ?? "#");
562
+ },
563
+ get children() {
564
+ return _$ssr(_tmpl$3, _$ssrAttribute("aria-label", _$escape(label, true), false) + _$ssrAttribute("title", _$escape(title(), true), false), `ti ${_$escape(icon, true)}`);
565
+ }
566
+ });
546
567
  },
547
568
  get children() {
548
- return _$ssr(_tmpl$2, `ti ${_$escape(icon, true)}`);
569
+ return link(() => href() ?? "#");
549
570
  }
550
571
  });
551
572
  };
@@ -553,7 +574,7 @@ const CalendarHeader = props => {
553
574
  * In the day and week views that scroll through their days, Today points where today lies, or shows a calm dot while
554
575
  * it is on screen; the icon keeps its place either way. The other views name their period in the title.
555
576
  */
556
- const direction = () => timeMode() ? (timeView()?.todayDirection() ?? todayDirectionOf(String(props.view), props.date, dateConfig())) || "here" : undefined;
577
+ const direction = () => timeMode() || monthView() ? (scrollingView()?.todayDirection() ?? todayDirectionOf(String(props.view), props.date, dateConfig())) || "here" : undefined;
557
578
  /** One arrow that the stylesheet turns toward today, or the dot while today shows. */
558
579
  const todayIcon = () => _$createComponent(Show, {
559
580
  get when() {
@@ -574,7 +595,7 @@ const CalendarHeader = props => {
574
595
  const todayButton = () => {
575
596
  const today = calendar.today(dateConfig());
576
597
  const href = props.owner.getDateHref?.(today, props.view);
577
- const intercept = () => timeView()?.today() ?? false;
598
+ const intercept = () => scrollingView()?.today() ?? false;
578
599
  const title = () => props.nav.commands().length > 0 ? `${props.labels.today} (H)` : undefined;
579
600
  const label = [todayIcon(), _$ssr(_tmpl$6, _$escape(props.labels.today))];
580
601
  return (props.owner.onNavigateHref || props.owner.onNavigate) && href ? _$createComponent(CalendarNavigationLink, {
@@ -641,7 +662,7 @@ const CalendarHeader = props => {
641
662
  value: "year",
642
663
  label: props.labels.year
643
664
  }].filter(option => !props.owner.views || props.owner.views.includes(option.value)), ...(props.owner.customViews ?? [])]);
644
- return _$ssr(_tmpl$0, _$escape(navButton(previous(), "ti-chevron-left", props.labels.previous, -1)), _$escape(navButton(next(), "ti-chevron-right", props.labels.next, 1)), _$escape(title()), _$escape(_$createComponent(For, {
665
+ return _$ssr(_tmpl$0, _$escape(navButton(previous, "ti-chevron-left", props.labels.previous, -1)), _$escape(navButton(next, "ti-chevron-right", props.labels.next, 1)), _$escape(title()), _$escape(_$createComponent(For, {
645
666
  get each() {
646
667
  return titleWidths();
647
668
  },
@@ -950,7 +971,7 @@ const MonthPickerView = props => {
950
971
 
951
972
  /** One entry of the month grid: its whole range, and the days it covers as `YYYY-MM-DD` keys. */
952
973
 
953
- /** What the calendar's controller asks of the month view. */
974
+ /** What the calendar's controller and header ask of the month view; the rest serves the month that scrolls. */
954
975
 
955
976
  /** Lane height and gap of the month entries, in rem; the stylesheet reads both from the grid's custom properties. */
956
977
  const MONTH_LANE_REM = 1.25;
@@ -1010,9 +1031,13 @@ const PRINTABLE_KEY = /^.$/u;
1010
1031
  const MonthView = props => {
1011
1032
  const calendarMessages = useCalendarMessages();
1012
1033
  const dateConfig = createMemo(() => ownerDateConfig(props.owner));
1034
+ const continuous = createMemo(() => Boolean(props.owner.range));
1035
+ const [mounted, setMounted] = createSignal(false);
1036
+ const hostMonthKey = () => calendar.formatDateKey(calendar.startOfMonth(props.date, dateConfig()), dateConfig());
1037
+ const [focusMonthKey, setFocusMonthKey] = createSignal(untrack(hostMonthKey));
1013
1038
  // The shown month and its grid change only with the month, so a refresh of the same month keeps every cell and its
1014
1039
  // focus.
1015
- const month = createMemo(() => zonedYearMonth(props.date, dateConfig()), undefined, {
1040
+ const month = createMemo(() => zonedYearMonth(continuous() ? calendar.parseCalendarDate(focusMonthKey(), dateConfig()) : props.date, dateConfig()), undefined, {
1016
1041
  equals: (previous, next) => previous.year === next.year && previous.month === next.month
1017
1042
  });
1018
1043
  const weeks = createMemo(() => calendar.getMonthGrid(month().year, month().month, dateConfig()).map(week => week.map(day => ({
@@ -1024,17 +1049,12 @@ const MonthView = props => {
1024
1049
  const weekdays = createMemo(() => calendar.weekdays(dateConfig()));
1025
1050
  const todayKey = createMemo(() => calendar.formatDateKey(props.now, dateConfig()));
1026
1051
  const items = createMemo(() => monthItems(props.events));
1027
- const weekLayouts = createMemo(() => weeks().map(week => layoutMonthWeek(week.map(entry => entry.key), items().map(item => ({
1028
- item,
1029
- firstKey: item.firstKey,
1030
- lastKey: item.lastKey,
1031
- bar: item.bar
1032
- })))));
1033
1052
  const [capacities, setCapacities] = createSignal([]);
1034
1053
  const [cellWidth, setCellWidth] = createSignal(0);
1035
1054
  const [barFont, setBarFont] = createSignal("");
1036
1055
  const [anchorKey, setAnchorKey] = createSignal(null);
1037
1056
  const [focusKey, setFocusKey] = createSignal(null);
1057
+ const [focusedCellKey, setFocusedCellKey] = createSignal(null);
1038
1058
  const [popover, setPopover] = createSignal(null);
1039
1059
  /** Whether the quick create holds the focus; until then the grid keeps it. */
1040
1060
  const [createActive, setCreateActive] = createSignal(false);
@@ -1060,13 +1080,479 @@ const MonthView = props => {
1060
1080
  end: calendar.addDays(parseKey(range.last), 1, dateConfig()),
1061
1081
  allDay: true
1062
1082
  });
1063
- const visibleKeys = createMemo(() => weeks().flatMap(week => week.map(entry => entry.key)));
1083
+ const hostKey = () => calendar.formatDateKey(props.date, dateConfig());
1084
+ const loadedRange = createMemo(() => {
1085
+ const range = props.owner.range;
1086
+ const keyOf = value => calendar.formatDateKey(parseDate(value, dateConfig()), dateConfig());
1087
+ return range ? {
1088
+ first: keyOf(range.from),
1089
+ to: keyOf(range.to)
1090
+ } : {
1091
+ first: hostMonthKey(),
1092
+ to: hostMonthKey()
1093
+ };
1094
+ });
1095
+ const loadedWeeks = createMemo(() => monthWeekKeys(loadedRange().first, loadedRange().to, dateConfig().firstDayOfWeek ?? 1));
1096
+ const loaded = key => key >= loadedRange().first && key < loadedRange().to;
1097
+ // The first paint starts at the month's first week. Mount adds the earlier weeks and positions synchronously.
1098
+ const trackWeeks = createMemo(() => {
1099
+ const all = loadedWeeks();
1100
+ const anchor = monthRowOf(all, hostMonthKey());
1101
+ return mounted() || anchor === undefined ? all : all.slice(anchor);
1102
+ });
1103
+ const [scrollTop, setScrollTop] = createSignal(0);
1104
+ const [rowHeight, setRowHeight] = createSignal(80);
1105
+ const [viewHeight, setViewHeight] = createSignal(400);
1106
+ const [labelShown, setLabelShown] = createSignal(false);
1107
+ const [pulseKey, setPulseKey] = createSignal("");
1108
+ let scroller;
1109
+ let weekdayElement;
1110
+ let ready = false;
1111
+ let positioned = false;
1112
+ let silentTop = null;
1113
+ let frame = 0;
1114
+ let labelTimer = 0;
1115
+ let reportTimer = 0;
1116
+ let queueTimer = 0;
1117
+ let glideTimer = 0;
1118
+ let arrivalTimer = 0;
1119
+ let pulseTimer = 0;
1120
+ /** The month that quick repeated steps head for; the next step and the arrows' links go on from it. */
1121
+ const [queuedMonth, setQueuedMonth] = createSignal(null);
1122
+ let pendingTarget = null;
1123
+ let pendingOrigin = null;
1124
+ let glideTarget = null;
1125
+ let pulseOnArrival = false;
1126
+ let reported = "";
1127
+ let reportedFocus = "";
1128
+ let askedEarlier = "";
1129
+ let askedLater = "";
1130
+ let exhaustedEarlier = "";
1131
+ let exhaustedLater = "";
1132
+ const scrollFocusDay = createMemo(() => {
1133
+ const first = focusMonthKey();
1134
+ const range = selection();
1135
+ const selected = range && range.last >= first ? range.first > first ? range.first : first : null;
1136
+ if (selected?.slice(0, 7) === first.slice(0, 7)) return selected;
1137
+ if (hostKey().slice(0, 7) === first.slice(0, 7)) return hostKey();
1138
+ return todayKey().slice(0, 7) === first.slice(0, 7) ? todayKey() : first;
1139
+ });
1140
+ const visibleRange = createMemo(() => {
1141
+ const weeks = trackWeeks();
1142
+ const rows = monthVisibleRows(scrollTop(), viewHeight(), rowHeight(), weeks.length);
1143
+ const first = weeks[rows.first] ?? focusMonthKey();
1144
+ return {
1145
+ first,
1146
+ last: addDayKeys(weeks[Math.max(rows.first, rows.end - 1)] ?? first, 6)
1147
+ };
1148
+ });
1149
+ const renderWindow = createMemo(() => mounted() ? monthRenderWindow(scrollTop(), viewHeight(), rowHeight(), trackWeeks().length) : {
1150
+ first: 0,
1151
+ end: Math.min(trackWeeks().length, MONTH_ROW_BLOCK * 2)
1152
+ }, undefined, {
1153
+ equals: (left, right) => left.first === right.first && left.end === right.end
1154
+ });
1155
+ /** The window's weeks, and those of the focused day, the selection's moving end, and an open popover's day. */
1156
+ const renderedWeeks = createMemo(() => {
1157
+ const weeks = trackWeeks();
1158
+ const window = renderWindow();
1159
+ const keys = new Set(weeks.slice(window.first, window.end));
1160
+ for (const key of [focusKey(), focusedCellKey(), popover()?.anchor.dataset.calendarDayKey]) {
1161
+ const row = key ? monthRowOf(weeks, key) : undefined;
1162
+ if (row !== undefined) keys.add(weeks[row]);
1163
+ }
1164
+ return [...keys].sort();
1165
+ });
1166
+ // Rows keep their elements, but the list moves a kept row when the window passes it, and a browser drops the focus
1167
+ // from an element that moves. The focused element takes it back once the rows are in place.
1168
+ let refocus = null;
1169
+ createComputed(on(renderedWeeks, () => {
1170
+ const active = typeof document === "undefined" ? null : document.activeElement;
1171
+ refocus = active && scroller?.contains(active) ? active : null;
1172
+ }, {
1173
+ defer: true
1174
+ }));
1175
+ createEffect(on(renderedWeeks, () => {
1176
+ const element = refocus;
1177
+ refocus = null;
1178
+ if (element instanceof HTMLElement && element.isConnected && document.activeElement !== element) element.focus({
1179
+ preventScroll: true
1180
+ });
1181
+ }, {
1182
+ defer: true
1183
+ }));
1184
+ const placeOfToday = () => {
1185
+ const weeks = trackWeeks();
1186
+ const row = monthRowOf(weeks, todayKey());
1187
+ return row === undefined ? todayKey() < (weeks[0] ?? focusMonthKey()) ? "up" : "down" : monthTodayDirection(row, scrollTop(), viewHeight(), rowHeight());
1188
+ };
1189
+ const measureScroll = () => {
1190
+ if (!scroller) return;
1191
+ const row = scroller.querySelector(".k2b-calendar-month__week");
1192
+ const height = row?.getBoundingClientRect().height;
1193
+ if (height && height > 0) setRowHeight(height);
1194
+ setViewHeight(Math.max(0, scroller.clientHeight - (weekdayElement?.getBoundingClientRect().height ?? 0)));
1195
+ };
1196
+ const loadMore = edge => {
1197
+ if (!ready) return;
1198
+ const range = loadedRange();
1199
+ const asked = edge === "earlier" ? range.first : range.to;
1200
+ const loader = edge === "earlier" ? props.owner.onLoadEarlier : props.owner.onLoadLater;
1201
+ if (!loader || (edge === "earlier" ? askedEarlier === asked || exhaustedEarlier === asked : askedLater === asked || exhaustedLater === asked)) return;
1202
+ if (edge === "earlier") askedEarlier = asked;else askedLater = asked;
1203
+ const result = loader();
1204
+ if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") {
1205
+ const settled = () => {
1206
+ if (!ready || (edge === "earlier" ? loadedRange().first : loadedRange().to) !== asked) return;
1207
+ if (edge === "earlier") exhaustedEarlier = asked;else exhaustedLater = asked;
1208
+ const target = pendingOrigin === (edge === "earlier" ? -1 : 1) ? pendingTarget : null;
1209
+ if (target) {
1210
+ pendingTarget = null;
1211
+ pendingOrigin = null;
1212
+ setQueuedMonth(null);
1213
+ window.clearTimeout(queueTimer);
1214
+ }
1215
+ if (edge === "later" && glideTarget?.expires === Number.POSITIVE_INFINITY) {
1216
+ glideTarget = null;
1217
+ pulseOnArrival = false;
1218
+ setQueuedMonth(null);
1219
+ for (const timer of [queueTimer, glideTimer, arrivalTimer]) window.clearTimeout(timer);
1220
+ readScroll();
1221
+ }
1222
+ if (target) props.goToDate?.(parseKey(target));
1223
+ };
1224
+ void Promise.resolve(result).then(settled, settled);
1225
+ }
1226
+ };
1227
+ /** Asks for more weeks near either end; weeks inserted above a running glide would move its way, so it waits. */
1228
+ const loadNearEdges = () => {
1229
+ if (!scroller) return;
1230
+ if (!glideTarget && scroller.scrollTop < MONTH_ROWS_IN_VIEW * rowHeight()) loadMore("earlier");
1231
+ if (scroller.scrollTop + viewHeight() > (trackWeeks().length - MONTH_ROWS_IN_VIEW * 2) * rowHeight()) loadMore("later");
1232
+ };
1233
+ const reportVisible = () => {
1234
+ if (!ready) return;
1235
+ const {
1236
+ first,
1237
+ last
1238
+ } = untrack(visibleRange);
1239
+ const focus = untrack(scrollFocusDay);
1240
+ const value = `${first}/${last}/${focus}`;
1241
+ if (reported === value) return;
1242
+ reported = value;
1243
+ window.clearTimeout(reportTimer);
1244
+ reportTimer = window.setTimeout(() => {
1245
+ reportedFocus = focus;
1246
+ props.owner.onVisibleDateChange?.(parseKey(first), parseKey(last), parseKey(focus));
1247
+ }, 250);
1248
+ };
1249
+ const keepLabel = () => {
1250
+ window.clearTimeout(labelTimer);
1251
+ labelTimer = window.setTimeout(() => setLabelShown(false), 650);
1252
+ };
1253
+ const readScroll = (showLabel = true) => {
1254
+ if (!scroller) return;
1255
+ const top = scroller.scrollTop;
1256
+ setScrollTop(top);
1257
+ const weeks = trackWeeks();
1258
+ const week = weeks[monthFocusRow(top, viewHeight(), rowHeight(), weeks.length)];
1259
+ if (week) {
1260
+ const next = monthOfWeek(week);
1261
+ if (next !== untrack(focusMonthKey)) {
1262
+ setFocusMonthKey(next);
1263
+ if (showLabel && ready) {
1264
+ setLabelShown(true);
1265
+ keepLabel();
1266
+ }
1267
+ }
1268
+ }
1269
+ reportVisible();
1270
+ };
1271
+ const pulseToday = () => {
1272
+ if (reducedMotion()) return;
1273
+ setPulseKey(todayKey());
1274
+ window.clearTimeout(pulseTimer);
1275
+ pulseTimer = window.setTimeout(() => setPulseKey(""), 1000);
1276
+ };
1277
+ const finishGlide = () => {
1278
+ if (!scroller) return;
1279
+ const target = glideTarget;
1280
+ const row = target ? monthRowOf(trackWeeks(), target.week) : undefined;
1281
+ // A clamped glide may still be waiting for more weeks below; scrollend is not arrival then.
1282
+ if (target && row !== undefined && Math.abs(scroller.scrollTop - (row * rowHeight() + target.offset)) > 1) return;
1283
+ glideTarget = null;
1284
+ if (pendingOrigin !== -1 && pendingOrigin !== 1) {
1285
+ setQueuedMonth(null);
1286
+ window.clearTimeout(queueTimer);
1287
+ }
1288
+ window.clearTimeout(glideTimer);
1289
+ window.clearTimeout(arrivalTimer);
1290
+ readScroll();
1291
+ // A glide that lands near an end asks for the next weeks there, as the reader's own scrolling does.
1292
+ if (target) loadNearEdges();
1293
+ if (pulseOnArrival) {
1294
+ pulseOnArrival = false;
1295
+ pulseToday();
1296
+ }
1297
+ };
1298
+ const cancelGlide = () => {
1299
+ if (!scroller) return;
1300
+ if (pendingOrigin === -1 || pendingOrigin === 1) {
1301
+ pendingTarget = null;
1302
+ pendingOrigin = null;
1303
+ setQueuedMonth(null);
1304
+ window.clearTimeout(queueTimer);
1305
+ }
1306
+ if (!glideTarget) return;
1307
+ // A reader's wheel or touch takes over from navigation, including any target waiting for more weeks.
1308
+ glideTarget = null;
1309
+ pulseOnArrival = false;
1310
+ setQueuedMonth(null);
1311
+ for (const timer of [queueTimer, glideTimer, arrivalTimer]) window.clearTimeout(timer);
1312
+ scroller.scrollTo({
1313
+ top: scroller.scrollTop,
1314
+ behavior: "instant"
1315
+ });
1316
+ };
1317
+ const issueGlide = (top, smooth) => {
1318
+ if (!scroller) return;
1319
+ const maximum = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
1320
+ const target = Math.max(0, Math.min(top, maximum));
1321
+ if (smooth && !reducedMotion() && Math.abs(target - scroller.scrollTop) > viewHeight() * 3) {
1322
+ scroller.scrollTop = target - Math.sign(target - scroller.scrollTop) * viewHeight();
1323
+ readScroll();
1324
+ }
1325
+ scroller.scrollTo({
1326
+ top: target,
1327
+ behavior: smooth && !reducedMotion() ? "smooth" : "instant"
1328
+ });
1329
+ if (top > maximum + 1) loadMore("later");
1330
+ if (!smooth || reducedMotion()) {
1331
+ readScroll();
1332
+ finishGlide();
1333
+ }
1334
+ };
1335
+ const glide = (top, smooth = true) => {
1336
+ if (!scroller) return;
1337
+ const row = Math.max(0, Math.min(trackWeeks().length - 1, Math.floor(top / rowHeight())));
1338
+ const week = trackWeeks()[row];
1339
+ if (!week) return;
1340
+ // A target past the loaded weeks waits for them however long they take; the reader's own scrolling ends the wait.
1341
+ const waiting = top > scroller.scrollHeight - scroller.clientHeight + 1;
1342
+ glideTarget = {
1343
+ week,
1344
+ offset: top - row * rowHeight(),
1345
+ expires: waiting ? Number.POSITIVE_INFINITY : performance.now() + 1600
1346
+ };
1347
+ window.clearTimeout(glideTimer);
1348
+ window.clearTimeout(arrivalTimer);
1349
+ if (!waiting) glideTimer = window.setTimeout(() => {
1350
+ glideTarget = null;
1351
+ pulseOnArrival = false;
1352
+ }, 1600);
1353
+ arrivalTimer = window.setTimeout(finishGlide, 900);
1354
+ issueGlide(top, smooth);
1355
+ };
1356
+ const positionMonth = (key, smooth = true, reveal = false) => {
1357
+ if (!continuous() || !loaded(key) || !scroller) return false;
1358
+ const weeks = trackWeeks();
1359
+ const first = monthRowOf(weeks, `${key.slice(0, 7)}-01`);
1360
+ const row = monthRowOf(weeks, key);
1361
+ if (first === undefined || row === undefined) return false;
1362
+ const top = first * rowHeight();
1363
+ glide(reveal ? monthRevealTop(row, top, viewHeight(), rowHeight()) ?? top : top, smooth);
1364
+ return true;
1365
+ };
1366
+ const goToMonth = key => positionMonth(key, true, true);
1367
+ const stepMonth = direction => {
1368
+ if (!continuous() || !scroller) return false;
1369
+ const target = calendar.formatDateKey(calendar.addMonths(parseKey(queuedMonth() ?? focusMonthKey()), direction, dateConfig()), dateConfig());
1370
+ if (loaded(target)) {
1371
+ pendingTarget = null;
1372
+ pendingOrigin = null;
1373
+ if (!positionMonth(target)) return false;
1374
+ } else {
1375
+ // A month past the loaded weeks asks for them and waits; an end the host has reached goes through its navigation.
1376
+ const range = loadedRange();
1377
+ const later = direction === 1;
1378
+ const edge = later ? range.to : range.first;
1379
+ if (!(later ? props.owner.onLoadLater : props.owner.onLoadEarlier) || (later ? exhaustedLater : exhaustedEarlier) === edge) return false;
1380
+ pendingTarget = target;
1381
+ pendingOrigin = direction;
1382
+ loadMore(later ? "later" : "earlier");
1383
+ }
1384
+ // Instant positioning may finish during positionMonth; queue afterwards so rapid clicks still accumulate.
1385
+ setQueuedMonth(target);
1386
+ window.clearTimeout(queueTimer);
1387
+ // A glide that waits for more weeks keeps its month queued until it arrives or the reader scrolls.
1388
+ queueTimer = window.setTimeout(() => {
1389
+ if (pendingOrigin !== -1 && pendingOrigin !== 1 && glideTarget?.expires !== Number.POSITIVE_INFINITY) setQueuedMonth(null);
1390
+ }, 1200);
1391
+ return true;
1392
+ };
1393
+ const goToday = () => {
1394
+ if (!continuous() || !loaded(todayKey()) || !scroller) return false;
1395
+ pulseOnArrival = !reducedMotion();
1396
+ if (positionMonth(todayKey(), true, true)) return true;
1397
+ pulseOnArrival = false;
1398
+ return false;
1399
+ };
1400
+ const revealDay = key => {
1401
+ const row = monthRowOf(trackWeeks(), key);
1402
+ if (row === undefined || !scroller) return;
1403
+ const top = monthRevealTop(row, scroller.scrollTop, viewHeight(), rowHeight());
1404
+ if (top !== undefined) glide(top);
1405
+ };
1406
+ const onScroll = () => {
1407
+ if (!ready || !scroller) return;
1408
+ // Initial positioning and range rebasing are not the reader asking for more weeks.
1409
+ if (silentTop !== null && Math.abs(scroller.scrollTop - silentTop) < 1) return;
1410
+ silentTop = null;
1411
+ if (untrack(labelShown)) keepLabel();
1412
+ if (!frame) frame = requestAnimationFrame(() => {
1413
+ frame = 0;
1414
+ readScroll();
1415
+ if (!scroller) return;
1416
+ loadNearEdges();
1417
+ });
1418
+ };
1419
+ // Capture the old track before render effects change its height, then restore the same week and pixel below.
1420
+ let previousWeeks = [];
1421
+ let savedAnchor = null;
1422
+ createComputed(on(loadedWeeks, weeks => {
1423
+ if (!untrack(mounted) || !scroller) return;
1424
+ const top = scroller.scrollTop;
1425
+ const row = Math.floor(top / untrack(rowHeight));
1426
+ const week = previousWeeks[row];
1427
+ savedAnchor = week ? {
1428
+ week,
1429
+ delta: top - row * untrack(rowHeight)
1430
+ } : null;
1431
+ const next = week ? monthRowOf(weeks, week) : undefined;
1432
+ // Move the render window before DOM updates, so even a larger rebase retains the visible rows' elements.
1433
+ if (next !== undefined && savedAnchor) setScrollTop(next * untrack(rowHeight) + savedAnchor.delta);
1434
+ }, {
1435
+ defer: true
1436
+ }));
1437
+ createEffect(on(loadedWeeks, () => {
1438
+ if (!untrack(mounted) || !scroller) return;
1439
+ measureScroll();
1440
+ previousWeeks = untrack(trackWeeks);
1441
+ const row = savedAnchor ? monthRowOf(previousWeeks, savedAnchor.week) : undefined;
1442
+ const top = row !== undefined && savedAnchor ? row * untrack(rowHeight) + savedAnchor.delta : scroller.scrollTop;
1443
+ // Weeks added below move nothing, and writing the same position would stop a glide on its way.
1444
+ const moved = Math.abs(scroller.scrollTop - top) >= 1;
1445
+ if (moved) scroller.scrollTop = top;
1446
+ if (pendingTarget && loaded(pendingTarget)) {
1447
+ const key = pendingTarget;
1448
+ pendingTarget = null;
1449
+ pendingOrigin = null;
1450
+ positioned = positionMonth(key, positioned) || positioned;
1451
+ if (pendingKey === key) {
1452
+ pendingKey = null;
1453
+ select(key);
1454
+ queueMicrotask(() => focusCell(key));
1455
+ }
1456
+ } else {
1457
+ if (pendingTarget && (pendingOrigin === -1 || pendingOrigin === 1)) loadMore(pendingOrigin === -1 ? "earlier" : "later");
1458
+ // A glide goes on after the weeks above moved it, and one that waited at the end goes on into the new weeks.
1459
+ if (glideTarget && performance.now() < glideTarget.expires && (moved || glideTarget.expires === Number.POSITIVE_INFINITY)) {
1460
+ const target = monthRowOf(previousWeeks, glideTarget.week);
1461
+ if (target !== undefined) issueGlide(target * untrack(rowHeight) + glideTarget.offset, true);
1462
+ }
1463
+ }
1464
+ savedAnchor = null;
1465
+ silentTop = scroller.scrollTop;
1466
+ readScroll(false);
1467
+ }, {
1468
+ defer: true
1469
+ }));
1470
+ createEffect(on(hostKey, key => {
1471
+ if (!continuous() || !untrack(mounted) || !scroller) return;
1472
+ if (key === reportedFocus) {
1473
+ if (pendingOrigin === "host") {
1474
+ pendingTarget = null;
1475
+ pendingOrigin = null;
1476
+ }
1477
+ return;
1478
+ }
1479
+ pendingTarget = loaded(key) ? null : key;
1480
+ pendingOrigin = pendingTarget ? "host" : null;
1481
+ if (pendingTarget) return;
1482
+ const visible = untrack(visibleRange);
1483
+ if (key.slice(0, 7) !== untrack(focusMonthKey).slice(0, 7) || key < visible.first || key > visible.last) positionMonth(key);
1484
+ if (pendingKey === key) {
1485
+ pendingKey = null;
1486
+ select(key);
1487
+ queueMicrotask(() => focusCell(key));
1488
+ }
1489
+ }, {
1490
+ defer: true
1491
+ }));
1492
+ createEffect(on(scrollFocusDay, () => {
1493
+ if (continuous()) reportVisible();
1494
+ }, {
1495
+ defer: true
1496
+ }));
1497
+ // The scrolling starts once the grid is on the page, also when a host passes `range` only later.
1498
+ createEffect(on(continuous, active => {
1499
+ if (!active || !scroller) return;
1500
+ setMounted(true);
1501
+ measureScroll();
1502
+ previousWeeks = trackWeeks();
1503
+ const row = monthRowOf(previousWeeks, hostMonthKey());
1504
+ if (row !== undefined && loaded(hostKey())) scroller.scrollTop = row * rowHeight();
1505
+ // Too few weeks below the month, such as the days of another view while the month's own load, cannot put its
1506
+ // first week on top yet; the month goes there once its weeks arrive.
1507
+ positioned = row !== undefined && Math.abs(scroller.scrollTop - row * rowHeight()) < 1;
1508
+ if (!positioned) {
1509
+ pendingTarget = hostKey();
1510
+ pendingOrigin = "mount";
1511
+ }
1512
+ readScroll(false);
1513
+ silentTop = scroller.scrollTop;
1514
+ ready = true;
1515
+ reportVisible();
1516
+ scroller.addEventListener("scrollend", finishGlide);
1517
+ const resize = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(() => {
1518
+ if (!scroller) return;
1519
+ const height = untrack(rowHeight);
1520
+ const position = scroller.scrollTop / height;
1521
+ measureScroll();
1522
+ if (height !== untrack(rowHeight)) {
1523
+ scroller.scrollTop = position * untrack(rowHeight);
1524
+ if (glideTarget) {
1525
+ const target = monthRowOf(untrack(trackWeeks), glideTarget.week);
1526
+ if (target !== undefined) issueGlide(target * untrack(rowHeight) + glideTarget.offset, true);
1527
+ }
1528
+ silentTop = scroller.scrollTop;
1529
+ }
1530
+ readScroll(false);
1531
+ });
1532
+ resize?.observe(scroller);
1533
+ const element = scroller;
1534
+ onCleanup(() => {
1535
+ resize?.disconnect();
1536
+ element.removeEventListener("scrollend", finishGlide);
1537
+ for (const timer of [labelTimer, reportTimer, queueTimer, glideTimer, arrivalTimer, pulseTimer]) window.clearTimeout(timer);
1538
+ if (frame) cancelAnimationFrame(frame);
1539
+ frame = 0;
1540
+ ready = false;
1541
+ setMounted(false);
1542
+ });
1543
+ }));
1544
+ const visibleKeys = createMemo(() => continuous() ? renderedWeeks().flatMap(key => Array.from({
1545
+ length: 7
1546
+ }, (_, day) => addDayKeys(key, day))) : weeks().flatMap(week => week.map(entry => entry.key)));
1064
1547
  const rovingKey = createMemo(() => {
1065
1548
  const keys = visibleKeys();
1066
- const focus = focusKey();
1549
+ const focus = focusKey() ?? (continuous() ? focusedCellKey() : null);
1067
1550
  if (focus && keys.includes(focus)) return focus;
1068
- if (keys.includes(todayKey())) return todayKey();
1069
- return calendar.formatDateKey(zonedMonthDate(month().year, month().month, dateConfig()), dateConfig());
1551
+ if (keys.includes(todayKey()) && (!continuous() || todayKey() >= visibleRange().first && todayKey() <= visibleRange().last)) return todayKey();
1552
+ const first = calendar.formatDateKey(zonedMonthDate(month().year, month().month, dateConfig()), dateConfig());
1553
+ if (!continuous() || keys.includes(first)) return first;
1554
+ const visible = visibleRange();
1555
+ return keys.find(key => key.slice(0, 7) === first.slice(0, 7) && key >= visible.first && key <= visible.last) ?? keys.find(key => key >= visible.first && key <= visible.last) ?? keys[0] ?? first;
1070
1556
  });
1071
1557
  const dropRange = createMemo(() => {
1072
1558
  const preview = movePreview();
@@ -1091,8 +1577,7 @@ const MonthView = props => {
1091
1577
  setAnchorKey(anchor);
1092
1578
  setFocusKey(focus);
1093
1579
  };
1094
- // The host learns every change of the selected days, and a new month starts without a selection. The selection
1095
- // goes with the view, so switching to another view reports that nothing is selected.
1580
+ // Selection survives scrolling; only paging starts a month without it. Switching views clears the host's selection.
1096
1581
  createEffect(on(selection, range => props.owner.onSelectionChange?.(range ? rangeOf(range) : null), {
1097
1582
  defer: true
1098
1583
  }));
@@ -1101,6 +1586,7 @@ const MonthView = props => {
1101
1586
  });
1102
1587
  createEffect(on(selection, range => props.onFocusDay?.(range ? parseKey(range.first) : null)));
1103
1588
  createEffect(on(month, () => {
1589
+ if (continuous()) return;
1104
1590
  setPopover(null);
1105
1591
  const key = pendingKey;
1106
1592
  pendingKey = null;
@@ -1224,6 +1710,11 @@ const MonthView = props => {
1224
1710
  popoverFocusable()?.focus();
1225
1711
  return true;
1226
1712
  };
1713
+ /**
1714
+ * The day to create on without a selection. The month that scrolls takes the day it stands for, today or the 1st of
1715
+ * the month in focus, never a day the keyboard last visited in another month.
1716
+ */
1717
+ const unselectedKey = () => continuous() ? scrollFocusDay() : rovingKey();
1227
1718
  /** Enter, a double-click, and N create on the selection, with the quick create or the host's own create action. */
1228
1719
  const createOnSelection = () => {
1229
1720
  const range = selection();
@@ -1236,8 +1727,8 @@ const MonthView = props => {
1236
1727
  onMount(() => props.register?.({
1237
1728
  quickCreate: create => {
1238
1729
  const range = selection() ?? {
1239
- first: rovingKey(),
1240
- last: rovingKey()
1730
+ first: unselectedKey(),
1731
+ last: unselectedKey()
1241
1732
  };
1242
1733
  select(range.first, range.last);
1243
1734
  if (!openCreate(range, {
@@ -1246,10 +1737,18 @@ const MonthView = props => {
1246
1737
  })) props.owner.onSlotActivate?.(rangeOf(range));
1247
1738
  },
1248
1739
  createTarget: () => rangeOf(selection() ?? {
1249
- first: rovingKey(),
1250
- last: rovingKey()
1740
+ first: unselectedKey(),
1741
+ last: unselectedKey()
1251
1742
  }),
1252
- typing: () => popover()?.kind === "create" && !createActive()
1743
+ typing: () => popover()?.kind === "create" && !createActive(),
1744
+ continuous,
1745
+ focusMonth: () => parseKey(focusMonthKey()),
1746
+ stepFrom: () => parseKey(queuedMonth() ?? focusMonthKey()),
1747
+ focusDay: () => parseKey(scrollFocusDay()),
1748
+ step: stepMonth,
1749
+ goTo: goToMonth,
1750
+ today: goToday,
1751
+ todayDirection: placeOfToday
1253
1752
  }));
1254
1753
  onCleanup(() => props.register?.(null));
1255
1754
  const openDayList = key => {
@@ -1284,12 +1783,15 @@ const MonthView = props => {
1284
1783
  }
1285
1784
  };
1286
1785
  const moveFocus = (key, extend) => {
1287
- if (!visibleKeys().includes(key)) {
1786
+ if (continuous() ? !loaded(key) : !visibleKeys().includes(key)) {
1288
1787
  pageTo(key);
1289
1788
  return;
1290
1789
  }
1291
1790
  if (extend) select(anchorKey() ?? focusKey() ?? key, key);else select(key);
1292
- focusCell(key);
1791
+ if (continuous()) {
1792
+ revealDay(key);
1793
+ queueMicrotask(() => focusCell(key));
1794
+ } else focusCell(key);
1293
1795
  followSelection();
1294
1796
  };
1295
1797
  const onCellKeyDown = (event, key) => {
@@ -1318,7 +1820,12 @@ const MonthView = props => {
1318
1820
  }
1319
1821
  if (event.key === "PageUp" || event.key === "PageDown") {
1320
1822
  event.preventDefault();
1321
- pageTo(calendar.formatDateKey(addMonthsClamped(day, event.key === "PageUp" ? -1 : 1, dateConfig()), dateConfig()));
1823
+ const target = calendar.formatDateKey(addMonthsClamped(day, event.key === "PageUp" ? -1 : 1, dateConfig()), dateConfig());
1824
+ if (continuous() && loaded(target)) {
1825
+ moveFocus(target, event.shiftKey);
1826
+ // The month's first week goes on top, unless the day lies in a sixth week that would then stay out of view.
1827
+ goToMonth(target);
1828
+ } else pageTo(target);
1322
1829
  return;
1323
1830
  }
1324
1831
  if (event.key === "Enter" || (event.key === "n" || event.key === "N") && !event.shiftKey) {
@@ -1388,6 +1895,9 @@ const MonthView = props => {
1388
1895
  cancelInteraction?.();
1389
1896
  cancelInteraction = startCalendarPointerSession({
1390
1897
  event,
1898
+ scrollContainer: continuous() ? scroller : undefined,
1899
+ // A drag along the last row on screen selects its days; the view scrolls once the pointer came from inside.
1900
+ armAutoScroll: true,
1391
1901
  resolve: (clientX, clientY) => dayKeyAtPoint(clientX, clientY),
1392
1902
  onActivate: () => {
1393
1903
  suppressClickUntil = performance.now() + 400;
@@ -1396,6 +1906,8 @@ const MonthView = props => {
1396
1906
  },
1397
1907
  onPreview: current => select(key, current),
1398
1908
  onCommit: current => {
1909
+ // The click that ends a long drag, such as one that scrolled the view, must not select the pressed day again.
1910
+ suppressClickUntil = performance.now() + 400;
1399
1911
  select(key, current);
1400
1912
  focusCell(current);
1401
1913
  const range = orderedSelection(key, current);
@@ -1415,8 +1927,8 @@ const MonthView = props => {
1415
1927
  const menuItems = createMemo(() => {
1416
1928
  if (!hasMenu()) return [];
1417
1929
  const range = selection() ?? {
1418
- first: rovingKey(),
1419
- last: rovingKey()
1930
+ first: unselectedKey(),
1931
+ last: unselectedKey()
1420
1932
  };
1421
1933
  const own = props.owner.selectionMenu?.(rangeOf(range), {
1422
1934
  quickCreate: create => {
@@ -1466,6 +1978,8 @@ const MonthView = props => {
1466
1978
  cancelInteraction?.();
1467
1979
  cancelInteraction = startCalendarPointerSession({
1468
1980
  event: pointerEvent,
1981
+ scrollContainer: continuous() ? scroller : undefined,
1982
+ armAutoScroll: true,
1469
1983
  resolve: (clientX, clientY) => {
1470
1984
  const key = dayKeyAtPoint(clientX, clientY);
1471
1985
  if (!key) return null;
@@ -1485,6 +1999,9 @@ const MonthView = props => {
1485
1999
  },
1486
2000
  onPreview: setMovePreview,
1487
2001
  onCommit: next => {
2002
+ // The click that ends a long move, such as one that scrolled the view, must not open the entry.
2003
+ markDragged();
2004
+ suppressClickUntil = performance.now() + 400;
1488
2005
  clearMove();
1489
2006
  if (eventTimeChanged(event, next)) props.owner.onEventDrop?.(event, next);
1490
2007
  },
@@ -1572,235 +2089,264 @@ const MonthView = props => {
1572
2089
  if (linkedItem() === item.key) setLinkedItem("");
1573
2090
  }
1574
2091
  });
1575
- const grid = () => _$ssr(_tmpl$20, _$ssrAttribute("aria-label", _$escape(formatMonth(props.date, dateConfig()), true), false), _$ssrStyleProperty("grid-template-rows:", `auto repeat(${_$escape(weeks().length, true)}, minmax(5rem, 1fr))`) + _$ssrStyleProperty(";--k2b-calendar-lane:", "1.25rem") + _$ssrStyleProperty(";--k2b-calendar-lane-gap:", "0.125rem"), _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1576
- get when() {
1577
- return props.owner.withWeekNumbers;
1578
- },
1579
- get children() {
1580
- return _$ssr(_tmpl$19, _$escape(calendarMessages().weekShort));
1581
- }
1582
- })), _$escape(_$createComponent(For, {
1583
- get each() {
1584
- return weekdays();
1585
- },
1586
- children: (day, index) => _$ssr(_tmpl$21, _$ssrAttribute("data-weekend", index() > 4 ? "true" : _$escape(undefined, true), false), _$escape(day))
1587
- })), _$escape(_$createComponent(For, {
1588
- get each() {
1589
- return weeks();
1590
- },
1591
- children: (week, weekIndex) => {
1592
- const fit = createMemo(() => fitMonthWeek(weekLayouts()[weekIndex()] ?? {
1593
- bars: [],
1594
- singles: []
1595
- }, capacities()[weekIndex()] ?? MONTH_DEFAULT_LANES));
1596
- const weekHref = () => viewHref(week[0].key, "week");
1597
- const weekNumber = () => zonedWeekNumber(week[0].day, dateConfig());
1598
- return _$ssr(_tmpl$23, _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1599
- get when() {
1600
- return props.owner.withWeekNumbers;
1601
- },
1602
- get children() {
1603
- return _$ssr(_tmpl$22, _$escape(_$createComponent(Show, {
1604
- get when() {
1605
- return weekHref();
1606
- },
1607
- get fallback() {
1608
- return _$createComponent(Show, {
1609
- get when() {
1610
- return canOpenView("week");
1611
- },
1612
- get fallback() {
1613
- return weekNumber();
1614
- },
1615
- get children() {
1616
- return _$ssr(_tmpl$24, _$ssrAttribute("title", _$escape(calendarMessages().calendarOpenWeekNumber({
1617
- week: weekNumber()
1618
- }), true), false) + _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarOpenWeekNumber({
1619
- week: weekNumber()
1620
- }), true), false), _$escape(weekNumber()));
1621
- }
1622
- });
1623
- },
1624
- children: href => _$createComponent(CalendarNavigationLink, {
1625
- get owner() {
1626
- return props.owner;
1627
- },
1628
- get href() {
1629
- return href();
2092
+ const weekRow = (week, weekIndex) => {
2093
+ const layout = createMemo(() => layoutMonthWeek(week().map(entry => entry.key), items().map(item => ({
2094
+ item,
2095
+ firstKey: item.firstKey,
2096
+ lastKey: item.lastKey,
2097
+ bar: item.bar
2098
+ }))));
2099
+ // The continuous rows are all as high as the first, so they share its measured capacity.
2100
+ const capacity = () => capacities()[continuous() ? 0 : weekIndex()] ?? MONTH_DEFAULT_LANES;
2101
+ const fit = createMemo(() => fitMonthWeek(layout(), capacity()));
2102
+ const weekHref = () => viewHref(week()[0].key, "week");
2103
+ const weekNumber = () => zonedWeekNumber(week()[0].day, dateConfig());
2104
+ return _$ssr(_tmpl$20, _$ssrAttribute("data-week-key", continuous() ? _$escape(week()[0]?.key, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-rowindex", continuous() ? _$escape(weekIndex(), true) + 2 : _$escape(undefined, true), false), _$ssrStyle(continuous() ? {
2105
+ "--k2b-calendar-week-index": String(weekIndex())
2106
+ } : undefined), _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
2107
+ get when() {
2108
+ return props.owner.withWeekNumbers;
2109
+ },
2110
+ get children() {
2111
+ return _$ssr(_tmpl$19, _$escape(_$createComponent(Show, {
2112
+ get when() {
2113
+ return weekHref();
2114
+ },
2115
+ get fallback() {
2116
+ return _$createComponent(Show, {
2117
+ get when() {
2118
+ return canOpenView("week");
1630
2119
  },
1631
- get anchorProps() {
1632
- return {
1633
- class: "k2b-calendar-month__week-link",
1634
- tabIndex: -1,
1635
- title: calendarMessages().calendarOpenWeekNumber({
1636
- week: weekNumber()
1637
- }),
1638
- "aria-label": calendarMessages().calendarOpenWeekNumber({
1639
- week: weekNumber()
1640
- })
1641
- };
2120
+ get fallback() {
2121
+ return weekNumber();
1642
2122
  },
1643
2123
  get children() {
1644
- return weekNumber();
2124
+ return _$ssr(_tmpl$21, _$ssrAttribute("title", _$escape(calendarMessages().calendarOpenWeekNumber({
2125
+ week: weekNumber()
2126
+ }), true), false) + _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarOpenWeekNumber({
2127
+ week: weekNumber()
2128
+ }), true), false), _$escape(weekNumber()));
1645
2129
  }
1646
- })
1647
- })));
1648
- }
1649
- })), _$escape(_$createComponent(For, {
1650
- each: week,
1651
- children: ({
1652
- day,
1653
- key
1654
- }, column) => {
1655
- const dayBadge = props.owner.dayBadges?.[key];
1656
- const sameMonth = calendar.isSameMonth(day, props.date, dateConfig());
1657
- const label = dayLabel(day);
1658
- const bars = createMemo(() => fit().bars.filter(bar => bar.startColumn === column()));
1659
- const cell = () => fit().cells[column()] ?? {
1660
- top: 0,
1661
- shown: [],
1662
- hidden: 0
1663
- };
1664
- return _$ssr(_tmpl$26, _$ssrAttribute("tabindex", rovingKey() === key ? 0 : -1, false) + _$ssrAttribute("aria-label", _$escape(label, true), false) + _$ssrAttribute("aria-selected", within(selection(), key) ? "true" : "false", false) + _$ssrAttribute("aria-current", key === todayKey() ? "date" : _$escape(undefined, true), false) + _$ssrAttribute("data-calendar-day-key", _$escape(key, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-weekend", column() > 4 ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-past", key < todayKey() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-selected", _$escape(selectionPart(key), true), false) + _$ssrAttribute("data-drop-preview", within(dropRange(), key) ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-today", key === todayKey() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false), _$escape(calendar.formatDayNumber(day, dateConfig())), _$escape(_$createComponent(Show, {
1665
- when: dayBadge,
1666
- children: badge => _$ssr(_tmpl$17, _$escape(_$createComponent(Show, {
1667
- get when() {
1668
- return badge().icon;
1669
- },
1670
- children: icon => _$ssr(_tmpl$2, `k2b-calendar-month__badge-icon ti ti-${_$escape(icon(), true)}`)
1671
- })), _$escape(badge().label))
1672
- })), _$escape(_$createComponent(For, {
1673
- get each() {
1674
- return bars();
2130
+ });
2131
+ },
2132
+ children: href => _$createComponent(CalendarNavigationLink, {
2133
+ get owner() {
2134
+ return props.owner;
1675
2135
  },
1676
- children: bar => {
1677
- const span = bar.endColumn - bar.startColumn + 1;
1678
- const startHint = () => {
1679
- if (!bar.continuesBefore) return undefined;
1680
- const hint = calendarMessages().calendarContinuesFrom(hintDate(bar.item.firstKey));
1681
- return hintFits(bar.item.event.title, hint, span) ? hint : undefined;
1682
- };
1683
- const endHint = () => {
1684
- if (!bar.continuesAfter) return undefined;
1685
- const hint = calendarMessages().calendarContinuesUntil(hintDate(bar.item.lastKey));
1686
- return hintFits(`${bar.item.event.title}${startHint() ?? ""}`, hint, span) ? hint : undefined;
2136
+ get href() {
2137
+ return href();
2138
+ },
2139
+ get anchorProps() {
2140
+ return {
2141
+ class: "k2b-calendar-month__week-link",
2142
+ tabIndex: -1,
2143
+ title: calendarMessages().calendarOpenWeekNumber({
2144
+ week: weekNumber()
2145
+ }),
2146
+ "aria-label": calendarMessages().calendarOpenWeekNumber({
2147
+ week: weekNumber()
2148
+ })
1687
2149
  };
1688
- return _$ssrElement("div", _$mergeProps({
1689
- "class": "k2b-calendar-month__segment",
1690
- get style() {
1691
- return {
1692
- "--k2b-calendar-span": String(span),
1693
- "--k2b-calendar-row": String(bar.lane)
1694
- };
1695
- },
1696
- "data-bar": "true",
1697
- get ["data-linked"]() {
1698
- return linkedItem() === bar.item.key && bar.item.firstKey !== bar.item.lastKey ? "true" : undefined;
1699
- },
1700
- get ["data-continues-before"]() {
1701
- return bar.continuesBefore ? "true" : undefined;
1702
- },
1703
- get ["data-continues-after"]() {
1704
- return bar.continuesAfter ? "true" : undefined;
1705
- }
1706
- }, () => linkProps(bar.item)), _$escape(_$createComponent(EventChip, {
1707
- get event() {
1708
- return bar.item.event;
1709
- },
1710
- get owner() {
1711
- return props.owner;
1712
- },
1713
- get href() {
1714
- return eventHref(props.owner, bar.item.event);
1715
- },
1716
- get label() {
1717
- return itemLabel(bar.item);
1718
- },
1719
- compact: true,
1720
- bar: true,
1721
- get continuesBefore() {
1722
- return bar.continuesBefore;
1723
- },
1724
- get continuesAfter() {
1725
- return bar.continuesAfter;
1726
- },
1727
- get hintStart() {
1728
- return startHint();
1729
- },
1730
- get hintEnd() {
1731
- return endHint();
1732
- },
1733
- get moving() {
1734
- return movingEventId() === bar.item.event.id;
1735
- },
1736
- get onMovePointerDown() {
1737
- return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, bar.item, markDragged) : undefined;
1738
- }
1739
- })), false);
2150
+ },
2151
+ get children() {
2152
+ return weekNumber();
1740
2153
  }
1741
- })), _$escape(_$createComponent(For, {
1742
- get each() {
1743
- return cell().shown;
2154
+ })
2155
+ })));
2156
+ }
2157
+ })), _$escape(_$createComponent(For, {
2158
+ get each() {
2159
+ return week();
2160
+ },
2161
+ children: ({
2162
+ day,
2163
+ key
2164
+ }, column) => {
2165
+ const dayBadge = props.owner.dayBadges?.[key];
2166
+ const sameMonth = () => calendar.isSameMonth(day, continuous() ? parseKey(focusMonthKey()) : props.date, dateConfig());
2167
+ const label = dayLabel(day);
2168
+ const bars = createMemo(() => fit().bars.filter(bar => bar.startColumn === column()));
2169
+ const cell = () => fit().cells[column()] ?? {
2170
+ top: 0,
2171
+ shown: [],
2172
+ hidden: 0
2173
+ };
2174
+ return _$ssr(_tmpl$23, _$ssrAttribute("tabindex", rovingKey() === key ? 0 : -1, false) + _$ssrAttribute("aria-label", _$escape(label, true), false) + _$ssrAttribute("aria-selected", within(selection(), key) ? "true" : "false", false) + _$ssrAttribute("aria-current", key === todayKey() ? "date" : _$escape(undefined, true), false) + _$ssrAttribute("data-calendar-day-key", _$escape(key, true), false) + _$ssrAttribute("data-outside", !sameMonth() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-month-first", continuous() && key.endsWith("-01") ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-month-top", continuous() && Number(key.slice(8)) <= 7 ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-weekend", column() > 4 ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-past", key < todayKey() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-selected", _$escape(selectionPart(key), true), false) + _$ssrAttribute("data-drop-preview", within(dropRange(), key) ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-pulse", continuous() && pulseKey() === key ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-today", key === todayKey() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", !sameMonth() ? "true" : _$escape(undefined, true), false), continuous() && key.endsWith("-01") ? _$escape(shortDayLabel(key).replace(/\.$/, "")) : _$escape(calendar.formatDayNumber(day, dateConfig())), _$escape(_$createComponent(Show, {
2175
+ when: dayBadge,
2176
+ children: badge => _$ssr(_tmpl$17, _$escape(_$createComponent(Show, {
2177
+ get when() {
2178
+ return badge().icon;
1744
2179
  },
1745
- children: (item, index) => _$ssr(_tmpl$27, _$ssrStyleProperty("--k2b-calendar-span:", "1") + _$ssrStyleProperty(";--k2b-calendar-row:", _$escape(String(cell().top + index()), true)), _$escape(_$createComponent(EventChip, {
2180
+ children: icon => _$ssr(_tmpl$2, `k2b-calendar-month__badge-icon ti ti-${_$escape(icon(), true)}`)
2181
+ })), _$escape(badge().label))
2182
+ })), _$escape(_$createComponent(For, {
2183
+ get each() {
2184
+ return bars();
2185
+ },
2186
+ children: bar => {
2187
+ const span = bar.endColumn - bar.startColumn + 1;
2188
+ const startHint = () => {
2189
+ if (!bar.continuesBefore) return undefined;
2190
+ const hint = calendarMessages().calendarContinuesFrom(hintDate(bar.item.firstKey));
2191
+ return hintFits(bar.item.event.title, hint, span) ? hint : undefined;
2192
+ };
2193
+ const endHint = () => {
2194
+ if (!bar.continuesAfter) return undefined;
2195
+ const hint = calendarMessages().calendarContinuesUntil(hintDate(bar.item.lastKey));
2196
+ return hintFits(`${bar.item.event.title}${startHint() ?? ""}`, hint, span) ? hint : undefined;
2197
+ };
2198
+ return _$ssrElement("div", _$mergeProps({
2199
+ "class": "k2b-calendar-month__segment",
2200
+ get style() {
2201
+ return {
2202
+ "--k2b-calendar-span": String(span),
2203
+ "--k2b-calendar-row": String(bar.lane)
2204
+ };
2205
+ },
2206
+ "data-bar": "true",
2207
+ get ["data-linked"]() {
2208
+ return linkedItem() === bar.item.key && bar.item.firstKey !== bar.item.lastKey ? "true" : undefined;
2209
+ },
2210
+ get ["data-continues-before"]() {
2211
+ return bar.continuesBefore ? "true" : undefined;
2212
+ },
2213
+ get ["data-continues-after"]() {
2214
+ return bar.continuesAfter ? "true" : undefined;
2215
+ }
2216
+ }, () => linkProps(bar.item)), _$escape(_$createComponent(EventChip, {
1746
2217
  get event() {
1747
- return item.event;
2218
+ return bar.item.event;
1748
2219
  },
1749
2220
  get owner() {
1750
2221
  return props.owner;
1751
2222
  },
1752
2223
  get href() {
1753
- return eventHref(props.owner, item.event);
2224
+ return eventHref(props.owner, bar.item.event);
2225
+ },
2226
+ get label() {
2227
+ return itemLabel(bar.item);
1754
2228
  },
1755
2229
  compact: true,
1756
- get leadingTime() {
1757
- return leadingTime(item);
2230
+ bar: true,
2231
+ get continuesBefore() {
2232
+ return bar.continuesBefore;
2233
+ },
2234
+ get continuesAfter() {
2235
+ return bar.continuesAfter;
2236
+ },
2237
+ get hintStart() {
2238
+ return startHint();
2239
+ },
2240
+ get hintEnd() {
2241
+ return endHint();
1758
2242
  },
1759
2243
  get moving() {
1760
- return movingEventId() === item.event.id;
2244
+ return movingEventId() === bar.item.event.id;
1761
2245
  },
1762
2246
  get onMovePointerDown() {
1763
- return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, item, markDragged) : undefined;
2247
+ return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, bar.item, markDragged) : undefined;
1764
2248
  }
1765
- })))
1766
- })), _$escape(_$createComponent(Show, {
1767
- get when() {
1768
- return cell().hidden > 0;
2249
+ })), false);
2250
+ }
2251
+ })), _$escape(_$createComponent(For, {
2252
+ get each() {
2253
+ return cell().shown;
2254
+ },
2255
+ children: (item, index) => _$ssr(_tmpl$24, _$ssrStyleProperty("--k2b-calendar-span:", "1") + _$ssrStyleProperty(";--k2b-calendar-row:", _$escape(String(cell().top + index()), true)), _$escape(_$createComponent(EventChip, {
2256
+ get event() {
2257
+ return item.event;
1769
2258
  },
1770
- get children() {
1771
- return _$ssr(_tmpl$25, _$ssrStyleProperty("--k2b-calendar-row:", _$escape(String(cell().top + cell().shown.length), true)), _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarMoreEventsOn({
1772
- count: cell().hidden,
1773
- label
1774
- }), true), false), _$ssrAttribute("aria-expanded", (() => {
1775
- const current = popover();
1776
- return current?.kind === "day" && _$escape(current.dayKey, true) === _$escape(key, true);
1777
- })(), false), _$escape(calendarMessages().calendarMoreEvents({
1778
- count: cell().hidden
1779
- })), _$escape(cell().hidden));
2259
+ get owner() {
2260
+ return props.owner;
2261
+ },
2262
+ get href() {
2263
+ return eventHref(props.owner, item.event);
2264
+ },
2265
+ compact: true,
2266
+ get leadingTime() {
2267
+ return leadingTime(item);
2268
+ },
2269
+ get moving() {
2270
+ return movingEventId() === item.event.id;
2271
+ },
2272
+ get onMovePointerDown() {
2273
+ return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, item, markDragged) : undefined;
1780
2274
  }
1781
- })));
1782
- }
1783
- })));
2275
+ })))
2276
+ })), _$escape(_$createComponent(Show, {
2277
+ get when() {
2278
+ return cell().hidden > 0;
2279
+ },
2280
+ get children() {
2281
+ return _$ssr(_tmpl$22, _$ssrStyleProperty("--k2b-calendar-row:", _$escape(String(cell().top + cell().shown.length), true)), _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarMoreEventsOn({
2282
+ count: cell().hidden,
2283
+ label
2284
+ }), true), false), _$ssrAttribute("aria-expanded", (() => {
2285
+ const current = popover();
2286
+ return current?.kind === "day" && _$escape(current.dayKey, true) === _$escape(key, true);
2287
+ })(), false), _$escape(calendarMessages().calendarMoreEvents({
2288
+ count: cell().hidden
2289
+ })), _$escape(cell().hidden));
2290
+ }
2291
+ })));
2292
+ }
2293
+ })));
2294
+ };
2295
+ const weekdayRow = () => _$ssr(_tmpl$26, _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
2296
+ get when() {
2297
+ return props.owner.withWeekNumbers;
2298
+ },
2299
+ get children() {
2300
+ return _$ssr(_tmpl$25, _$escape(calendarMessages().weekShort));
1784
2301
  }
2302
+ })), _$escape(_$createComponent(For, {
2303
+ get each() {
2304
+ return weekdays();
2305
+ },
2306
+ children: (day, index) => _$ssr(_tmpl$27, _$ssrAttribute("data-weekend", index() > 4 ? "true" : _$escape(undefined, true), false), _$escape(day))
1785
2307
  })));
2308
+ const grid = () => _$ssr(_tmpl$28, _$ssrAttribute("aria-label", _$escape(formatMonth(props.date, dateConfig()), true), false), _$ssrStyleProperty("grid-template-rows:", `auto repeat(${_$escape(weeks().length, true)}, minmax(5rem, 1fr))`) + _$ssrStyleProperty(";--k2b-calendar-lane:", "1.25rem") + _$ssrStyleProperty(";--k2b-calendar-lane-gap:", "0.125rem"), _$escape(weekdayRow()), _$escape(_$createComponent(For, {
2309
+ get each() {
2310
+ return weeks();
2311
+ },
2312
+ children: (week, index) => weekRow(() => week, index)
2313
+ })));
2314
+ const continuousRow = key => {
2315
+ const days = createMemo(() => Array.from({
2316
+ length: 7
2317
+ }, (_, offset) => {
2318
+ const dayKey = addDayKeys(key, offset);
2319
+ return {
2320
+ day: parseKey(dayKey),
2321
+ key: dayKey
2322
+ };
2323
+ }));
2324
+ return weekRow(days, () => monthRowOf(trackWeeks(), key) ?? 0);
2325
+ };
2326
+ const continuousGrid = () => _$ssr(_tmpl$29, _$ssrAttribute("aria-label", _$escape(formatMonth(parseKey(focusMonthKey()), dateConfig()), true), false), _$ssrAttribute("aria-rowcount", _$escape(trackWeeks().length, true) + 1, false), _$ssrStyleProperty("--k2b-calendar-lane:", "1.25rem") + _$ssrStyleProperty(";--k2b-calendar-lane-gap:", "0.125rem"), _$escape(weekdayRow()), _$ssrStyleProperty("--k2b-calendar-weeks:", _$escape(String(trackWeeks().length), true)), _$escape(_$createComponent(For, {
2327
+ get each() {
2328
+ return renderedWeeks();
2329
+ },
2330
+ children: continuousRow
2331
+ })), _$ssrAttribute("data-show", labelShown() ? "true" : "false", false), _$escape(formatMonth(parseKey(focusMonthKey()), dateConfig())));
1786
2332
  const dayList = dayKey => {
1787
2333
  const day = parseKey(dayKey);
1788
2334
  const href = viewHref(dayKey, "day");
1789
2335
  // Read where it renders, so a refresh while the list is open updates it as it does the grid.
1790
2336
  const entries = () => dayEntries(dayKey);
1791
- return _$ssr(_tmpl$30, _$escape(dayLabel(day)), _$escape(_$createComponent(Show, {
2337
+ return _$ssr(_tmpl$32, _$escape(dayLabel(day)), _$escape(_$createComponent(Show, {
1792
2338
  get when() {
1793
2339
  return entries().length > 0;
1794
2340
  },
1795
2341
  get fallback() {
1796
- return _$ssr(_tmpl$31, _$escape(calendarMessages().calendarNothingPlanned));
2342
+ return _$ssr(_tmpl$33, _$escape(calendarMessages().calendarNothingPlanned));
1797
2343
  },
1798
2344
  get children() {
1799
- return _$ssr(_tmpl$28, _$escape(_$createComponent(For, {
2345
+ return _$ssr(_tmpl$30, _$escape(_$createComponent(For, {
1800
2346
  get each() {
1801
2347
  return entries();
1802
2348
  },
1803
- children: item => _$ssr(_tmpl$32, _$escape(_$createComponent(EventChip, {
2349
+ children: item => _$ssr(_tmpl$34, _$escape(_$createComponent(EventChip, {
1804
2350
  get event() {
1805
2351
  return item.event;
1806
2352
  },
@@ -1837,7 +2383,7 @@ const MonthView = props => {
1837
2383
  size: "sm",
1838
2384
  onClick: () => openView(dayKey, "day"),
1839
2385
  get children() {
1840
- return [_$ssr(_tmpl$33), calendarMessages().calendarOpenDay];
2386
+ return [_$ssr(_tmpl$35), calendarMessages().calendarOpenDay];
1841
2387
  }
1842
2388
  });
1843
2389
  }
@@ -1856,7 +2402,7 @@ const MonthView = props => {
1856
2402
  "data-size": "sm"
1857
2403
  },
1858
2404
  get children() {
1859
- return [_$ssr(_tmpl$33), calendarMessages().calendarOpenDay];
2405
+ return [_$ssr(_tmpl$35), calendarMessages().calendarOpenDay];
1860
2406
  }
1861
2407
  })
1862
2408
  })), _$escape(_$createComponent(Show, {
@@ -1878,18 +2424,19 @@ const MonthView = props => {
1878
2424
  });
1879
2425
  },
1880
2426
  get children() {
1881
- return [_$ssr(_tmpl$29), calendarMessages().calendarNewEvent];
2427
+ return [_$ssr(_tmpl$31), calendarMessages().calendarNewEvent];
1882
2428
  }
1883
2429
  });
1884
2430
  }
1885
2431
  })));
1886
2432
  };
2433
+ const viewGrid = () => continuous() ? continuousGrid() : grid();
1887
2434
  return [_$createComponent(Show, {
1888
2435
  get when() {
1889
2436
  return hasMenu();
1890
2437
  },
1891
2438
  get fallback() {
1892
- return grid();
2439
+ return viewGrid();
1893
2440
  },
1894
2441
  get children() {
1895
2442
  return _$createComponent(GestureMenu, {
@@ -1903,11 +2450,11 @@ const MonthView = props => {
1903
2450
  onOpen: onMenuOpen,
1904
2451
  "class": "k2b-calendar-month-menu",
1905
2452
  get children() {
1906
- return grid();
2453
+ return viewGrid();
1907
2454
  }
1908
2455
  });
1909
2456
  }
1910
- }), _$ssr(_tmpl$34, _$ssrAttribute("aria-label", (() => {
2457
+ }), _$ssr(_tmpl$36, _$ssrAttribute("aria-label", (() => {
1911
2458
  const current = popover();
1912
2459
  if (current?.kind === "day") return dayLabel(parseKey(current.dayKey));
1913
2460
  return _$escape(calendarMessages().calendarQuickCreate, true);
@@ -2158,7 +2705,7 @@ const TimeGridView = props => {
2158
2705
  });
2159
2706
  });
2160
2707
  });
2161
- return _$ssr(_tmpl$36, _$ssrAttribute("data-view", isDayView() ? "day" : "week", false), _$ssrStyleProperty("grid-template-columns:", _$escape(columnTemplate(), true)), _$escape(_$createComponent(For, {
2708
+ return _$ssr(_tmpl$38, _$ssrAttribute("data-view", isDayView() ? "day" : "week", false), _$ssrStyleProperty("grid-template-columns:", _$escape(columnTemplate(), true)), _$escape(_$createComponent(For, {
2162
2709
  get each() {
2163
2710
  return props.days;
2164
2711
  },
@@ -2176,9 +2723,9 @@ const TimeGridView = props => {
2176
2723
  class: "k2b-calendar-time-grid__day"
2177
2724
  },
2178
2725
  get children() {
2179
- return [_$ssr(_tmpl$37, _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false), _$escape(formatDay(day, dateConfig()))), _$createComponent(Show, {
2726
+ return [_$ssr(_tmpl$39, _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false), _$escape(formatDay(day, dateConfig()))), _$createComponent(Show, {
2180
2727
  when: dayBadge,
2181
- children: badge => _$ssr(_tmpl$38, _$escape(_$createComponent(Show, {
2728
+ children: badge => _$ssr(_tmpl$40, _$escape(_$createComponent(Show, {
2182
2729
  get when() {
2183
2730
  return badge().icon;
2184
2731
  },
@@ -2193,7 +2740,7 @@ const TimeGridView = props => {
2193
2740
  return !props.owner.hideAllDay;
2194
2741
  },
2195
2742
  get children() {
2196
- return _$ssr(_tmpl$35, _$ssrStyleProperty("grid-template-columns:", _$escape(columnTemplate(), true)) + _$ssrStyleProperty(";max-height:", `${_$escape(props.owner.allDayMaxHeightRem ?? 7, true)}rem`), _$escape(props.labels.allDay), _$escape(_$createComponent(For, {
2743
+ return _$ssr(_tmpl$37, _$ssrStyleProperty("grid-template-columns:", _$escape(columnTemplate(), true)) + _$ssrStyleProperty(";max-height:", `${_$escape(props.owner.allDayMaxHeightRem ?? 7, true)}rem`), _$escape(props.labels.allDay), _$escape(_$createComponent(For, {
2197
2744
  get each() {
2198
2745
  return props.days;
2199
2746
  },
@@ -2217,7 +2764,7 @@ const TimeGridView = props => {
2217
2764
  end: calendar.addDays(start, 1, dateConfig()),
2218
2765
  allDay: true
2219
2766
  };
2220
- }, () => performance.now() < suppressSlotClickUntil)), _$ssr(_tmpl$39, _$escape(_$createComponent(For, {
2767
+ }, () => performance.now() < suppressSlotClickUntil)), _$ssr(_tmpl$41, _$escape(_$createComponent(For, {
2221
2768
  each: previewAllDay,
2222
2769
  children: event => _$ssr(_tmpl$14, _$escape(event.title))
2223
2770
  })), _$escape(_$createComponent(For, {
@@ -2248,7 +2795,7 @@ const TimeGridView = props => {
2248
2795
  get each() {
2249
2796
  return hours();
2250
2797
  },
2251
- children: hour => _$ssr(_tmpl$40, _$ssrAttribute("data-outside-business", hour < businessStartHour() || hour > businessEndHour() ? "true" : _$escape(undefined, true), false), _$escape(`${hour}`.padStart(2, "0")))
2798
+ children: hour => _$ssr(_tmpl$42, _$ssrAttribute("data-outside-business", hour < businessStartHour() || hour > businessEndHour() ? "true" : _$escape(undefined, true), false), _$escape(`${hour}`.padStart(2, "0")))
2252
2799
  })), _$escape(_$createComponent(For, {
2253
2800
  get each() {
2254
2801
  return props.days;
@@ -2256,12 +2803,12 @@ const TimeGridView = props => {
2256
2803
  children: day => {
2257
2804
  const dayKey = calendar.formatDateKey(day, dateConfig());
2258
2805
  const layouts = createMemo(() => timedEventLayouts(eventsByDay().get(dayKey)?.timed ?? []));
2259
- return _$ssr(_tmpl$42, _$ssrStyleProperty("grid-template-rows:", `repeat(${_$escape(hours().length, true)}, minmax(4rem, 1fr))`), _$escape(_$createComponent(Show, {
2806
+ return _$ssr(_tmpl$44, _$ssrStyleProperty("grid-template-rows:", `repeat(${_$escape(hours().length, true)}, minmax(4rem, 1fr))`), _$escape(_$createComponent(Show, {
2260
2807
  get when() {
2261
2808
  return currentTimeLine(day) !== null;
2262
2809
  },
2263
2810
  get children() {
2264
- return _$ssr(_tmpl$41, _$ssrStyleProperty("top:", `${_$escape(currentTimeLine(day) ?? 0, true)}%`));
2811
+ return _$ssr(_tmpl$43, _$ssrStyleProperty("top:", `${_$escape(currentTimeLine(day) ?? 0, true)}%`));
2265
2812
  }
2266
2813
  })), _$escape(_$createComponent(For, {
2267
2814
  get each() {
@@ -2290,12 +2837,12 @@ const TimeGridView = props => {
2290
2837
  children: event => {
2291
2838
  const layout = eventLayout(event);
2292
2839
  const short = event.endDate.getTime() - event.startDate.getTime() < 60 * 60_000;
2293
- return _$ssr(_tmpl$44, _$ssrAttribute("data-short", short ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$escape(_$createComponent(Show, {
2840
+ return _$ssr(_tmpl$46, _$ssrAttribute("data-short", short ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$escape(_$createComponent(Show, {
2294
2841
  get when() {
2295
2842
  return event.title;
2296
2843
  },
2297
2844
  get children() {
2298
- return _$ssr(_tmpl$43, _$escape(event.title));
2845
+ return _$ssr(_tmpl$45, _$escape(event.title));
2299
2846
  }
2300
2847
  })), _$escape(formatTime(event.startDate, dateConfig())), _$escape(formatTime(event.endDate, dateConfig())));
2301
2848
  }
@@ -2341,7 +2888,7 @@ const TimeGridView = props => {
2341
2888
  onCancel: clearInteraction
2342
2889
  });
2343
2890
  };
2344
- return _$ssr(_tmpl$46, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`) + _$ssrStyleProperty(";left:", _$escape(position.left, true)) + _$ssrStyleProperty(";width:", _$escape(position.width, true)) + _$ssrStyleProperty(";z-index:", _$escape(String(20 + layoutItem.lane), true)), _$escape(_$createComponent(EventChip, {
2891
+ return _$ssr(_tmpl$48, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`) + _$ssrStyleProperty(";left:", _$escape(position.left, true)) + _$ssrStyleProperty(";width:", _$escape(position.width, true)) + _$ssrStyleProperty(";z-index:", _$escape(String(20 + layoutItem.lane), true)), _$escape(_$createComponent(EventChip, {
2345
2892
  event: event,
2346
2893
  get owner() {
2347
2894
  return props.owner;
@@ -2361,7 +2908,7 @@ const TimeGridView = props => {
2361
2908
  return props.owner.onEventResize;
2362
2909
  },
2363
2910
  get children() {
2364
- return _$ssr(_tmpl$45, _$ssrAttribute("aria-label", _$escape(calendarMessages().resizeEvent, true), false));
2911
+ return _$ssr(_tmpl$47, _$ssrAttribute("aria-label", _$escape(calendarMessages().resizeEvent, true), false));
2365
2912
  }
2366
2913
  })));
2367
2914
  }
@@ -2373,14 +2920,14 @@ const TimeGridView = props => {
2373
2920
  const layout = timeRangeLayout(overflow.groupStartDate, overflow.groupEndDate);
2374
2921
  const key = `${dayKey}-${overflow.groupId}`;
2375
2922
  const hiddenTitle = () => overflow.hiddenEvents.map(event => `${formatTime(event.startDate, dateConfig())} ${event.title}`).join("\n");
2376
- return [_$ssr(_tmpl$47, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$ssrAttribute("title", _$escape(hiddenTitle(), true), false) + _$ssrAttribute("aria-label", _$escape(calendarMessages().hiddenOverlappingEvents({
2923
+ return [_$ssr(_tmpl$49, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$ssrAttribute("title", _$escape(hiddenTitle(), true), false) + _$ssrAttribute("aria-label", _$escape(calendarMessages().hiddenOverlappingEvents({
2377
2924
  count: overflow.hiddenEvents.length
2378
2925
  }), true), false), _$escape(overflow.hiddenEvents.length)), _$createComponent(Show, {
2379
2926
  get when() {
2380
2927
  return expandedOverflow() === key;
2381
2928
  },
2382
2929
  get children() {
2383
- return _$ssr(_tmpl$48, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`), _$escape(formatTime(overflow.groupStartDate, dateConfig())), _$escape(formatTime(overflow.groupEndDate, dateConfig())), _$escape(_$createComponent(For, {
2930
+ return _$ssr(_tmpl$50, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`), _$escape(formatTime(overflow.groupStartDate, dateConfig())), _$escape(formatTime(overflow.groupEndDate, dateConfig())), _$escape(_$createComponent(For, {
2384
2931
  get each() {
2385
2932
  return overflow.hiddenEvents;
2386
2933
  },
@@ -2439,11 +2986,11 @@ const YearView = props => {
2439
2986
  };
2440
2987
  });
2441
2988
  });
2442
- return _$ssr(_tmpl$49, _$escape(_$createComponent(For, {
2989
+ return _$ssr(_tmpl$51, _$escape(_$createComponent(For, {
2443
2990
  get each() {
2444
2991
  return months();
2445
2992
  },
2446
- children: month => _$ssr(_tmpl$50, _$escape(month.label), _$escape(_$createComponent(For, {
2993
+ children: month => _$ssr(_tmpl$52, _$escape(month.label), _$escape(_$createComponent(For, {
2447
2994
  get each() {
2448
2995
  return month.days;
2449
2996
  },
@@ -2466,7 +3013,7 @@ const YearView = props => {
2466
3013
  get when() {
2467
3014
  return day.events[0];
2468
3015
  },
2469
- children: event => _$ssr(_tmpl$51, _$ssrAttribute("data-color", event().colorHex ? _$escape(undefined, true) : _$escape(event().color ?? "blue", true), false) + _$ssrAttribute("data-today", day.isToday ? "true" : _$escape(undefined, true), false), _$ssrStyle(event().colorHex ? {
3016
+ children: event => _$ssr(_tmpl$53, _$ssrAttribute("data-color", event().colorHex ? _$escape(undefined, true) : _$escape(event().color ?? "blue", true), false) + _$ssrAttribute("data-today", day.isToday ? "true" : _$escape(undefined, true), false), _$ssrStyle(event().colorHex ? {
2470
3017
  "background-color": day.isToday ? "white" : event().colorHex
2471
3018
  } : undefined))
2472
3019
  })];
@@ -2479,7 +3026,7 @@ const MobileMonthView = props => {
2479
3026
  const dateConfig = createMemo(() => ownerDateConfig(props.owner));
2480
3027
  const selectedDateKey = createMemo(() => calendar.formatDateKey(props.selectedDate, dateConfig()));
2481
3028
  const selectedEvents = createMemo(() => props.events.filter(event => event.dayKey === selectedDateKey()));
2482
- return _$ssr(_tmpl$53, _$escape(_$createComponent(MonthPickerView, {
3029
+ return _$ssr(_tmpl$55, _$escape(_$createComponent(MonthPickerView, {
2483
3030
  get owner() {
2484
3031
  return {
2485
3032
  ...props.owner,
@@ -2509,10 +3056,10 @@ const MobileMonthView = props => {
2509
3056
  return selectedEvents().length > 0;
2510
3057
  },
2511
3058
  get fallback() {
2512
- return _$ssr(_tmpl$54, _$escape(props.labels.noEvents));
3059
+ return _$ssr(_tmpl$56, _$escape(props.labels.noEvents));
2513
3060
  },
2514
3061
  get children() {
2515
- return _$ssr(_tmpl$52, _$escape(_$createComponent(For, {
3062
+ return _$ssr(_tmpl$54, _$escape(_$createComponent(For, {
2516
3063
  get each() {
2517
3064
  return selectedEvents();
2518
3065
  },
@@ -2529,7 +3076,7 @@ const MobileMonthView = props => {
2529
3076
  }
2530
3077
  })));
2531
3078
  };
2532
- const CalendarBody = props => _$ssr(_tmpl$55, _$escape(props.children));
3079
+ const CalendarBody = props => _$ssr(_tmpl$57, _$escape(props.children));
2533
3080
  const CalendarRoot = props => {
2534
3081
  const messages = useUiMessages();
2535
3082
  const calendarMessages = useCalendarMessages();
@@ -2583,24 +3130,26 @@ const CalendarRoot = props => {
2583
3130
  // The day and week views with `dayHours` scroll through their days; the header and the commands ask them first.
2584
3131
  const [timeView, setTimeView] = createSignal(null);
2585
3132
  const [monthCreate, setMonthCreate] = createSignal(null);
3133
+ const monthView = () => monthCreate()?.continuous() ? monthCreate() : null;
3134
+ const scrollingView = () => timeView() ?? monthView();
2586
3135
  const timeMode = () => Boolean(props.dayHours) && (view() === "day" || view() === "week");
2587
3136
  const parseKey = key => calendar.parseCalendarDate(key, dateConfig());
2588
3137
  /** The first shown day of a day or week view, or the selected day of the month view, for the other views. */
2589
3138
  const shownDay = () => {
2590
3139
  const first = timeView()?.visibleKeys().first;
2591
3140
  if (first) return parseKey(first);
2592
- return view() === "month" ? focusDay() : null;
3141
+ return view() === "month" ? focusDay() ?? monthView()?.focusDay() ?? null : null;
2593
3142
  };
2594
3143
  const go = (target, nextView) => {
2595
3144
  const href = props.getDateHref?.(target, nextView);
2596
3145
  if (href) followCalendarHref(owner, href);else openViewAt(props, target, nextView);
2597
3146
  };
2598
3147
  const step = direction => {
2599
- if (timeView()?.step(direction)) return;
2600
- go(adjacentCalendarDate(shownDay() ?? date(), view(), direction, dateConfig()), view());
3148
+ if (scrollingView()?.step(direction)) return;
3149
+ go(adjacentCalendarDate(monthView()?.stepFrom() ?? shownDay() ?? date(), view(), direction, dateConfig()), view());
2601
3150
  };
2602
3151
  const goToday = () => {
2603
- if (timeView()?.today()) return;
3152
+ if (scrollingView()?.today()) return;
2604
3153
  go(calendar.today(dateConfig()), view());
2605
3154
  };
2606
3155
  const goView = nextView => {
@@ -2613,7 +3162,7 @@ const CalendarRoot = props => {
2613
3162
  if (href) followCalendarHref(owner, href);else props.onViewChange?.(nextView);
2614
3163
  };
2615
3164
  const goToKey = key => {
2616
- if (timeView()?.goTo(key)) return;
3165
+ if (scrollingView()?.goTo(key)) return;
2617
3166
  go(parseKey(key), view());
2618
3167
  };
2619
3168
  const openGoTo = () => void openCalendarGoTo({
@@ -2708,6 +3257,7 @@ const CalendarRoot = props => {
2708
3257
  });
2709
3258
  const headerNav = {
2710
3259
  timeView,
3260
+ monthView,
2711
3261
  commands: () => props.onCommandsChange ? allCommands() : [],
2712
3262
  // The arrow keys page like J and K, so the list names them beside those keys.
2713
3263
  openShortcuts: () => void openCalendarShortcuts(allCommands(), calendarMessages(), custom() ? {} : {
@@ -2753,7 +3303,7 @@ const CalendarRoot = props => {
2753
3303
  },
2754
3304
  register: setTimeView
2755
3305
  });
2756
- return _$ssr(_tmpl$56, `k2b-content-calendar ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-busy", props.navigationPending ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(CalendarHeader, {
3306
+ return _$ssr(_tmpl$58, `k2b-content-calendar ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-busy", props.navigationPending ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(CalendarHeader, {
2757
3307
  get date() {
2758
3308
  return date();
2759
3309
  },
@@ -2765,7 +3315,7 @@ const CalendarRoot = props => {
2765
3315
  },
2766
3316
  owner: owner,
2767
3317
  get focusDay() {
2768
- return _$memo(() => view() === "month")() ? focusDay() : _$memo(() => !!timeView())() ? shownDay() : null;
3318
+ return _$memo(() => !!(view() === "month" || timeView()))() ? shownDay() : null;
2769
3319
  },
2770
3320
  nav: headerNav
2771
3321
  })), _$escape(_$createComponent(Show, {
@@ -2802,6 +3352,7 @@ const CalendarRoot = props => {
2802
3352
  return mergedLabels();
2803
3353
  },
2804
3354
  onFocusDay: setFocusDay,
3355
+ goToDate: date => go(date, view()),
2805
3356
  register: setMonthCreate
2806
3357
  });
2807
3358
  }