@k2b/ui 0.20.0 → 0.21.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 (51) hide show
  1. package/dist/browser/actions/Button.js +20 -31
  2. package/dist/browser/actions/Button.js.map +1 -1
  3. package/dist/browser/actions/ContextMenu.js +7 -3
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/CopyButton.js +10 -8
  6. package/dist/browser/actions/CopyButton.js.map +1 -1
  7. package/dist/browser/actions/GestureMenu.js +4 -0
  8. package/dist/browser/actions/GestureMenu.js.map +1 -1
  9. package/dist/browser/content/Calendar.js +1951 -565
  10. package/dist/browser/content/Calendar.js.map +1 -1
  11. package/dist/browser/content/calendar-messages.js +83 -0
  12. package/dist/browser/content/calendar-messages.js.map +1 -0
  13. package/dist/browser/content/calendar-month-layout.js +108 -0
  14. package/dist/browser/content/calendar-month-layout.js.map +1 -0
  15. package/dist/browser/content/index.js.map +1 -1
  16. package/dist/browser/inputs/DatePicker.js +46 -10
  17. package/dist/browser/inputs/DatePicker.js.map +1 -1
  18. package/dist/browser/inputs/date-picker.js +24 -0
  19. package/dist/browser/inputs/date-picker.js.map +1 -1
  20. package/dist/browser/intl/messages.js +6 -0
  21. package/dist/browser/intl/messages.js.map +1 -1
  22. package/dist/ssr/actions/Button.js +21 -32
  23. package/dist/ssr/actions/Button.js.map +1 -1
  24. package/dist/ssr/actions/ContextMenu.js +7 -3
  25. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  26. package/dist/ssr/actions/CopyButton.js +9 -7
  27. package/dist/ssr/actions/CopyButton.js.map +1 -1
  28. package/dist/ssr/actions/GestureMenu.js +4 -0
  29. package/dist/ssr/actions/GestureMenu.js.map +1 -1
  30. package/dist/ssr/content/Calendar.js +1223 -105
  31. package/dist/ssr/content/Calendar.js.map +1 -1
  32. package/dist/ssr/content/calendar-messages.js +83 -0
  33. package/dist/ssr/content/calendar-messages.js.map +1 -0
  34. package/dist/ssr/content/calendar-month-layout.js +108 -0
  35. package/dist/ssr/content/calendar-month-layout.js.map +1 -0
  36. package/dist/ssr/content/index.js.map +1 -1
  37. package/dist/ssr/inputs/DatePicker.js +34 -4
  38. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  39. package/dist/ssr/inputs/date-picker.js +24 -0
  40. package/dist/ssr/inputs/date-picker.js.map +1 -1
  41. package/dist/ssr/intl/messages.js +6 -0
  42. package/dist/ssr/intl/messages.js.map +1 -1
  43. package/dist/styles.css +1 -1
  44. package/dist/types/actions/GestureMenu.d.ts +2 -0
  45. package/dist/types/content/Calendar.d.ts +32 -0
  46. package/dist/types/content/calendar-messages.d.ts +41 -0
  47. package/dist/types/content/calendar-month-layout.d.ts +52 -0
  48. package/dist/types/content/index.d.ts +1 -1
  49. package/dist/types/inputs/date-picker.d.ts +7 -0
  50. package/dist/types/intl/messages.d.ts +3 -0
  51. package/package.json +1 -1
@@ -4,61 +4,85 @@ import { ssrElement as _$ssrElement } from "solid-js/web";
4
4
  import { mergeProps as _$mergeProps } from "solid-js/web";
5
5
  import { ssrStyle as _$ssrStyle } from "solid-js/web";
6
6
  import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
7
- import { escape as _$escape } from "solid-js/web";
8
7
  import { createComponent as _$createComponent } from "solid-js/web";
9
8
  import { ssr as _$ssr } from "solid-js/web";
10
- var _tmpl$ = "<span class=\"k2b-calendar-event__marker\" aria-hidden=\"true\"></span>",
11
- _tmpl$2 = ["<span class=\"k2b-calendar-event__title\">", "", "</span>"],
12
- _tmpl$3 = ["<span class=\"k2b-calendar-event__meta\">", "</span>"],
13
- _tmpl$4 = ["<span class=\"k2b-calendar-event__description\">", "</span>"],
14
- _tmpl$5 = ["<a", " class=\"k2b-calendar-event\" data-calendar-event", " style=\"", "\"", ">", "</a>"],
15
- _tmpl$6 = ["<button type=\"button\" class=\"k2b-calendar-event\" data-calendar-event", " data-interactive=\"true\"", " style=\"", "\"", ">", "</button>"],
16
- _tmpl$7 = ["<div class=\"k2b-calendar-event\" role=\"group\" data-calendar-event", " style=\"", "\"", ">", "</div>"],
17
- _tmpl$8 = ["<div role=\"radiogroup\"", " aria-orientation=\"horizontal\" class=\"k2b-segmented-control k2b-calendar-view-switcher\">", "</div>"],
18
- _tmpl$9 = ["<i class=\"", "\"></i>"],
19
- _tmpl$0 = ["<button type=\"button\"", " class=\"k2b-calendar-header__nav-button\"><i class=\"", "\"></i></button>"],
20
- _tmpl$1 = ["<span class=\"k2b-button__label\">", "</span>"],
21
- _tmpl$10 = "<span class=\"k2b-calendar-header__progress\" aria-hidden=\"true\"></span>",
22
- _tmpl$11 = ["<header class=\"k2b-calendar-header\"><div class=\"k2b-calendar-header__navigation\">", "", "<div class=\"k2b-calendar-header__title\">", "</div></div><div class=\"k2b-calendar-header__actions\">", "", "", "</div>", "</header>"],
23
- _tmpl$12 = ["<div class=\"k2b-calendar-month__weekday\">", "</div>"],
24
- _tmpl$13 = ["<div class=\"k2b-calendar-month\" style=\"", "\"><div class=\"k2b-calendar-month__weekdays\"", ">", "", "</div>", "</div>"],
25
- _tmpl$14 = ["<div class=\"k2b-calendar-month__week-number\">", "</div>"],
26
- _tmpl$15 = ["<div class=\"k2b-calendar-month__week\"", ">", "", "</div>"],
27
- _tmpl$16 = ["<div class=\"k2b-calendar-preview\">", "</div>"],
28
- _tmpl$17 = ["<div class=\"k2b-calendar-month__day\"", ">", "<div class=\"k2b-calendar-month__day-header\">", "", "</div><div class=\"k2b-calendar-month__events\">", "", "", "</div><div class=\"k2b-calendar-month__dots\">", "</div></div>"],
29
- _tmpl$18 = ["<span class=\"k2b-calendar-month__day-number\"", ">", "</span>"],
30
- _tmpl$19 = ["<span class=\"k2b-calendar-month__badge\">", "", "</span>"],
31
- _tmpl$20 = ["<span class=\"k2b-calendar-dot\"", " style=\"", "\"></span>"],
32
- _tmpl$21 = ["<div class=\"k2b-calendar-time-grid__all-day\" style=\"", "\"><div class=\"k2b-calendar-time-grid__all-day-label\">", "</div>", "</div>"],
33
- _tmpl$22 = ["<div class=\"k2b-calendar-time-grid\"><div class=\"k2b-calendar-time-grid__days\" style=\"", "\"><div></div>", "</div>", "<div class=\"k2b-calendar-time-grid__scroll\"><div class=\"k2b-calendar-time-grid__grid\" style=\"", "\"><div class=\"k2b-calendar-time-grid__gutter\" style=\"", "\">", "</div>", "</div></div></div>"],
34
- _tmpl$23 = ["<span class=\"k2b-calendar-time-grid__day-label\"", ">", "</span>"],
35
- _tmpl$24 = ["<span class=\"k2b-calendar-time-grid__badge\">", "", "</span>"],
36
- _tmpl$25 = ["<div class=\"k2b-calendar-time-grid__all-day-events\">", "", "</div>"],
37
- _tmpl$26 = ["<div class=\"k2b-calendar-time-grid__hour\"", ">", ":00</div>"],
38
- _tmpl$27 = ["<div class=\"k2b-calendar-time-grid__now\" style=\"", "\"></div>"],
39
- _tmpl$28 = ["<div class=\"k2b-calendar-time-grid__column\" style=\"", "\">", "", "", "", "", "</div>"],
40
- _tmpl$29 = ["<div class=\"k2b-calendar-preview__title\">", "</div>"],
41
- _tmpl$30 = ["<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>"],
42
- _tmpl$31 = ["<button type=\"button\"", " draggable=\"false\" class=\"k2b-calendar-time-grid__resize\"><span class=\"k2b-calendar-time-grid__resize-icon\" aria-hidden=\"true\"></span></button>"],
43
- _tmpl$32 = ["<div class=\"k2b-calendar-time-grid__event-position\" style=\"", "\"><div class=\"k2b-calendar-time-grid__event-frame\">", "", "</div></div>"],
44
- _tmpl$33 = ["<button type=\"button\" class=\"k2b-calendar-time-grid__overflow\" style=\"", "\"", "><span class=\"k2b-calendar-time-grid__overflow-count\">+", "</span></button>"],
45
- _tmpl$34 = ["<div class=\"k2b-calendar-time-grid__overflow-menu\" style=\"", "\"><div class=\"k2b-calendar-time-grid__overflow-title\">", " - ", "</div>", "</div>"],
46
- _tmpl$35 = ["<div class=\"k2b-calendar-year\">", "</div>"],
47
- _tmpl$36 = ["<div class=\"k2b-calendar-year__month\"><div class=\"k2b-calendar-year__title\">", "</div><div class=\"k2b-calendar-year__grid\">", "</div></div>"],
48
- _tmpl$37 = ["<span class=\"k2b-calendar-year__indicator\"", " style=\"", "\"></span>"],
49
- _tmpl$38 = ["<div class=\"k2b-calendar-mobile-month__events\">", "</div>"],
50
- _tmpl$39 = ["<div class=\"k2b-calendar-mobile-month\">", "<div class=\"k2b-calendar-mobile-month__agenda\"><div class=\"k2b-calendar-mobile-month__title\">", "</div>", "</div></div>"],
51
- _tmpl$40 = ["<div class=\"k2b-calendar-mobile-month__empty\">", "</div>"],
52
- _tmpl$41 = ["<div class=\"k2b-calendar-body\">", "</div>"],
53
- _tmpl$42 = ["<section class=\"", "\"", ">", "", "", "</section>"];
9
+ import { escape as _$escape } from "solid-js/web";
10
+ var _tmpl$ = ["<span class=\"k2b-calendar-event__time\">", "</span>"],
11
+ _tmpl$2 = "<span class=\"k2b-calendar-event__marker\" aria-hidden=\"true\"></span>",
12
+ _tmpl$3 = ["<span class=\"k2b-calendar-event__title\">", "", "</span>"],
13
+ _tmpl$4 = ["<span class=\"k2b-calendar-event__meta\">", "</span>"],
14
+ _tmpl$5 = ["<span class=\"k2b-calendar-event__description\">", "</span>"],
15
+ _tmpl$6 = ["<span class=\"k2b-calendar-event__hint\" data-edge=\"start\" aria-hidden=\"true\">", "</span>"],
16
+ _tmpl$7 = ["<span class=\"k2b-calendar-event__hint\" data-edge=\"end\" aria-hidden=\"true\">", "</span>"],
17
+ _tmpl$8 = ["<a", " class=\"k2b-calendar-event\" data-calendar-event", " style=\"", "\"", ">", "</a>"],
18
+ _tmpl$9 = ["<button type=\"button\" class=\"k2b-calendar-event\" data-calendar-event", " data-interactive=\"true\"", " style=\"", "\"", ">", "</button>"],
19
+ _tmpl$0 = ["<div class=\"k2b-calendar-event\" role=\"group\" data-calendar-event", " style=\"", "\"", ">", "</div>"],
20
+ _tmpl$1 = ["<div role=\"radiogroup\"", " aria-orientation=\"horizontal\" class=\"k2b-segmented-control k2b-calendar-view-switcher\">", "</div>"],
21
+ _tmpl$10 = ["<i class=\"", "\"></i>"],
22
+ _tmpl$11 = ["<button type=\"button\"", " class=\"k2b-calendar-header__nav-button\"><i class=\"", "\"></i></button>"],
23
+ _tmpl$12 = ["<span class=\"k2b-button__label\">", "</span>"],
24
+ _tmpl$13 = ["<div class=\"k2b-calendar-header__content\">", "</div>"],
25
+ _tmpl$14 = "<span class=\"k2b-calendar-header__progress\" aria-hidden=\"true\"></span>",
26
+ _tmpl$15 = ["<header class=\"k2b-calendar-header\"><div class=\"k2b-calendar-header__navigation\">", "", "<div class=\"k2b-calendar-header__title\"><span>", "</span>", "</div></div>", "<div class=\"k2b-calendar-header__actions\">", "", "", "</div>", "</header>"],
27
+ _tmpl$16 = ["<span class=\"k2b-calendar-header__title-size\" aria-hidden=\"true\">", "</span>"],
28
+ _tmpl$17 = ["<div class=\"k2b-calendar-month__weekday\">", "</div>"],
29
+ _tmpl$18 = ["<div class=\"k2b-calendar-month\" style=\"", "\"><div class=\"k2b-calendar-month__weekdays\"", ">", "", "</div>", "</div>"],
30
+ _tmpl$19 = ["<div class=\"k2b-calendar-month__week-number\">", "</div>"],
31
+ _tmpl$20 = ["<div class=\"k2b-calendar-month__week\"", ">", "", "</div>"],
32
+ _tmpl$21 = ["<div class=\"k2b-calendar-preview\">", "</div>"],
33
+ _tmpl$22 = ["<div class=\"k2b-calendar-month__day\"", ">", "<div class=\"k2b-calendar-month__day-header\">", "", "</div><div class=\"k2b-calendar-month__events\">", "", "", "</div><div class=\"k2b-calendar-month__dots\">", "</div></div>"],
34
+ _tmpl$23 = ["<span class=\"k2b-calendar-month__day-number\"", ">", "</span>"],
35
+ _tmpl$24 = ["<span class=\"k2b-calendar-month__badge\">", "", "</span>"],
36
+ _tmpl$25 = ["<span class=\"k2b-calendar-dot\"", " style=\"", "\"></span>"],
37
+ _tmpl$26 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\">", "</div>"],
38
+ _tmpl$27 = ["<div class=\"k2b-calendar-month\" role=\"grid\"", " aria-multiselectable=\"true\" data-lanes=\"true\" style=\"", "\"><div class=\"k2b-calendar-month__weekdays\" role=\"row\"", ">", "", "</div>", "</div>"],
39
+ _tmpl$28 = ["<div class=\"k2b-calendar-month__weekday\" role=\"columnheader\"", ">", "</div>"],
40
+ _tmpl$29 = ["<div class=\"k2b-calendar-month__week-number\" role=\"rowheader\">", "</div>"],
41
+ _tmpl$30 = ["<div class=\"k2b-calendar-month__week\" role=\"row\"", ">", "", "</div>"],
42
+ _tmpl$31 = ["<button type=\"button\" class=\"k2b-calendar-month__week-link\" tabindex=\"-1\"", ">", "</button>"],
43
+ _tmpl$32 = ["<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>"],
44
+ _tmpl$33 = ["<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>"],
45
+ _tmpl$34 = ["<div class=\"k2b-calendar-month__segment\" style=\"", "\">", "</div>"],
46
+ _tmpl$35 = ["<ul class=\"k2b-calendar-day-list__events\">", "</ul>"],
47
+ _tmpl$36 = "<i class=\"ti ti-plus\" aria-hidden=\"true\"></i>",
48
+ _tmpl$37 = ["<div class=\"k2b-calendar-day-list\"><div class=\"k2b-calendar-day-list__title\">", "</div>", "<div class=\"k2b-calendar-day-list__actions\">", "", "</div></div>"],
49
+ _tmpl$38 = ["<p class=\"k2b-calendar-day-list__empty\">", "</p>"],
50
+ _tmpl$39 = ["<li>", "</li>"],
51
+ _tmpl$40 = "<i class=\"ti ti-zoom-in\" aria-hidden=\"true\"></i>",
52
+ _tmpl$41 = ["<div class=\"k2b-calendar-popover\" popover=\"auto\" role=\"dialog\"", ">", "</div>"],
53
+ _tmpl$42 = ["<div class=\"k2b-calendar-time-grid__all-day\" style=\"", "\"><div class=\"k2b-calendar-time-grid__all-day-label\">", "</div>", "</div>"],
54
+ _tmpl$43 = ["<div class=\"k2b-calendar-time-grid\"><div class=\"k2b-calendar-time-grid__days\" style=\"", "\"><div></div>", "</div>", "<div class=\"k2b-calendar-time-grid__scroll\"><div class=\"k2b-calendar-time-grid__grid\" style=\"", "\"><div class=\"k2b-calendar-time-grid__gutter\" style=\"", "\">", "</div>", "</div></div></div>"],
55
+ _tmpl$44 = ["<span class=\"k2b-calendar-time-grid__day-label\"", ">", "</span>"],
56
+ _tmpl$45 = ["<span class=\"k2b-calendar-time-grid__badge\">", "", "</span>"],
57
+ _tmpl$46 = ["<div class=\"k2b-calendar-time-grid__all-day-events\">", "", "</div>"],
58
+ _tmpl$47 = ["<div class=\"k2b-calendar-time-grid__hour\"", ">", ":00</div>"],
59
+ _tmpl$48 = ["<div class=\"k2b-calendar-time-grid__now\" style=\"", "\"></div>"],
60
+ _tmpl$49 = ["<div class=\"k2b-calendar-time-grid__column\" style=\"", "\">", "", "", "", "", "</div>"],
61
+ _tmpl$50 = ["<div class=\"k2b-calendar-preview__title\">", "</div>"],
62
+ _tmpl$51 = ["<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>"],
63
+ _tmpl$52 = ["<button type=\"button\"", " draggable=\"false\" class=\"k2b-calendar-time-grid__resize\"><span class=\"k2b-calendar-time-grid__resize-icon\" aria-hidden=\"true\"></span></button>"],
64
+ _tmpl$53 = ["<div class=\"k2b-calendar-time-grid__event-position\" style=\"", "\"><div class=\"k2b-calendar-time-grid__event-frame\">", "", "</div></div>"],
65
+ _tmpl$54 = ["<button type=\"button\" class=\"k2b-calendar-time-grid__overflow\" style=\"", "\"", "><span class=\"k2b-calendar-time-grid__overflow-count\">+", "</span></button>"],
66
+ _tmpl$55 = ["<div class=\"k2b-calendar-time-grid__overflow-menu\" style=\"", "\"><div class=\"k2b-calendar-time-grid__overflow-title\">", " - ", "</div>", "</div>"],
67
+ _tmpl$56 = ["<div class=\"k2b-calendar-year\">", "</div>"],
68
+ _tmpl$57 = ["<div class=\"k2b-calendar-year__month\"><div class=\"k2b-calendar-year__title\">", "</div><div class=\"k2b-calendar-year__grid\">", "</div></div>"],
69
+ _tmpl$58 = ["<span class=\"k2b-calendar-year__indicator\"", " style=\"", "\"></span>"],
70
+ _tmpl$59 = ["<div class=\"k2b-calendar-mobile-month__events\">", "</div>"],
71
+ _tmpl$60 = ["<div class=\"k2b-calendar-mobile-month\">", "<div class=\"k2b-calendar-mobile-month__agenda\"><div class=\"k2b-calendar-mobile-month__title\">", "</div>", "</div></div>"],
72
+ _tmpl$61 = ["<div class=\"k2b-calendar-mobile-month__empty\">", "</div>"],
73
+ _tmpl$62 = ["<div class=\"k2b-calendar-body\">", "</div>"],
74
+ _tmpl$63 = ["<section class=\"", "\"", ">", "", "</section>"];
54
75
  import { Link } from "@k2b/ssr/nav";
55
76
  import { dates as calendar } from "@k2b/stdlib";
56
- import { createMemo, createSignal, For, mergeProps, onCleanup, onMount, Show } from "solid-js";
77
+ import { createEffect, createMemo, createSignal, For, mergeProps, on, onCleanup, onMount, Show, untrack } from "solid-js";
57
78
  import { Button } from "../actions/Button";
79
+ import { GestureMenu } from "../actions/GestureMenu";
58
80
  import SegmentedControl from "../actions/SegmentedControl";
59
81
  import { useDateConfigLocale } from "../intl/locale";
60
82
  import { useUiMessages } from "../intl/messages";
61
83
  import { layoutCalendarIntervals } from "./calendar-event-layout";
84
+ import { useCalendarMessages } from "./calendar-messages";
85
+ import { fitMonthWeek, layoutMonthWeek, monthLaneCapacity } from "./calendar-month-layout";
62
86
  import { calendarDayIndexAtPoint, calendarMinuteAtPoint, startCalendarPointerSession } from "./calendar-pointer";
63
87
 
64
88
  /** A view the application adds to the switcher; the calendar shows its header and toolbar, the application the body. */
@@ -235,7 +259,7 @@ const EventChip = props => {
235
259
  const showDescription = () => Boolean(props.event.description?.trim() && props.fill && !props.event.allDay && !props.compact && durationHours() >= 1.5);
236
260
  const timeLabel = () => `${formatTime(props.event.startDate, dateConfig())} - ${formatTime(props.event.endDate, dateConfig())}`;
237
261
  const ariaLabel = () => {
238
- const label = props.event.allDay ? props.event.title : messages().calendarEventTime({
262
+ const label = props.label ? props.label : props.event.allDay ? props.event.title : messages().calendarEventTime({
239
263
  title: props.event.title,
240
264
  start: formatTime(props.event.startDate, dateConfig()),
241
265
  end: formatTime(props.event.endDate, dateConfig())
@@ -249,38 +273,60 @@ const EventChip = props => {
249
273
  end: props.event.endDate,
250
274
  allDay: props.event.allDay ?? false,
251
275
  durationHours: durationHours(),
252
- timeLabel: timeLabel()
276
+ timeLabel: timeLabel(),
277
+ leadingTime: props.leadingTime
253
278
  });
254
- const defaultContent = [_$ssr(_tmpl$2, _$escape(_$createComponent(Show, {
279
+ const defaultContent = [_$createComponent(Show, {
280
+ get when() {
281
+ return props.leadingTime;
282
+ },
283
+ get children() {
284
+ return _$ssr(_tmpl$, _$escape(props.leadingTime));
285
+ }
286
+ }), _$ssr(_tmpl$3, _$escape(_$createComponent(Show, {
255
287
  get when() {
256
288
  return props.event.display === "marker";
257
289
  },
258
290
  get children() {
259
- return _$ssr(_tmpl$);
291
+ return _$ssr(_tmpl$2);
260
292
  }
261
293
  })), _$escape(props.event.title)), _$createComponent(Show, {
262
294
  get when() {
263
295
  return showTime();
264
296
  },
265
297
  get children() {
266
- return _$ssr(_tmpl$3, _$escape(timeLabel()));
298
+ return _$ssr(_tmpl$4, _$escape(timeLabel()));
267
299
  }
268
300
  }), _$createComponent(Show, {
269
301
  get when() {
270
302
  return showLocation();
271
303
  },
272
304
  get children() {
273
- return _$ssr(_tmpl$3, _$escape(props.event.location));
305
+ return _$ssr(_tmpl$4, _$escape(props.event.location));
274
306
  }
275
307
  }), _$createComponent(Show, {
276
308
  get when() {
277
309
  return showDescription();
278
310
  },
279
311
  get children() {
280
- return _$ssr(_tmpl$4, _$escape(props.event.description));
312
+ return _$ssr(_tmpl$5, _$escape(props.event.description));
313
+ }
314
+ })];
315
+ const content = () => [_$createComponent(Show, {
316
+ get when() {
317
+ return props.hintStart;
318
+ },
319
+ get children() {
320
+ return _$ssr(_tmpl$6, _$escape(props.hintStart));
321
+ }
322
+ }), renderedEvent() ?? defaultContent, _$createComponent(Show, {
323
+ get when() {
324
+ return props.hintEnd;
325
+ },
326
+ get children() {
327
+ return _$ssr(_tmpl$7, _$escape(props.hintEnd));
281
328
  }
282
329
  })];
283
- const content = () => renderedEvent() ?? defaultContent;
284
330
  const isActionable = () => isInteractive() || Boolean(props.onMovePointerDown);
285
331
  let suppressClickUntil = 0;
286
332
  const onClick = event => {
@@ -312,7 +358,7 @@ const EventChip = props => {
312
358
  suppressClickUntil = performance.now() + 400;
313
359
  });
314
360
  };
315
- return props.href ? _$ssr(_tmpl$5, _$ssrAttribute("href", _$escape(props.href, true), false), _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-interactive", isInteractive() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-display", _$escape(props.event.display, true), false) + _$ssrAttribute("data-space-item-id", _$escape(props.event.dataSpaceItemId, true), false), _$ssrStyle(style()), _$ssrAttribute("draggable", props.onMovePointerDown ? _$escape(false, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content())) : isActionable() ? _$ssr(_tmpl$6, _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-display", _$escape(props.event.display, true), false), _$ssrStyle(style()), _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content())) : _$ssr(_tmpl$7, _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-interactive", _$escape(undefined, true), false) + _$ssrAttribute("data-display", _$escape(props.event.display, true), false), _$ssrStyle(style()), _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content()));
361
+ return props.href ? _$ssr(_tmpl$8, _$ssrAttribute("href", _$escape(props.href, true), false), _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-before", props.continuesBefore ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-after", props.continuesAfter ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-bar", props.bar ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-interactive", isInteractive() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-display", _$escape(props.event.display, true), false) + _$ssrAttribute("data-space-item-id", _$escape(props.event.dataSpaceItemId, true), false), _$ssrStyle(style()), _$ssrAttribute("draggable", props.onMovePointerDown ? _$escape(false, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content())) : isActionable() ? _$ssr(_tmpl$9, _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-before", props.continuesBefore ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-after", props.continuesAfter ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-bar", props.bar ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-display", _$escape(props.event.display, true), false), _$ssrStyle(style()), _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content())) : _$ssr(_tmpl$0, _$ssrAttribute("data-color", props.event.colorHex ? _$escape(undefined, true) : _$escape(color(), true), false) + _$ssrAttribute("data-selected", selected() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact", props.compact ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-fill", props.fill ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-short", short() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-moving", props.moving ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-before", props.continuesBefore ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-continues-after", props.continuesAfter ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-bar", props.bar ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-interactive", _$escape(undefined, true), false) + _$ssrAttribute("data-display", _$escape(props.event.display, true), false), _$ssrStyle(style()), _$ssrAttribute("aria-label", _$escape(ariaLabel(), true), false), _$escape(content()));
316
362
  };
317
363
  const slotInteractionProps = (owner, slot, suppressed, nativeControl = false) => {
318
364
  const isSlotChild = event => {
@@ -395,13 +441,17 @@ const CalendarViewLinks = props => {
395
441
  refs[event.key === "Home" ? 0 : props.options.length - 1]?.click();
396
442
  }
397
443
  };
398
- return _$ssr(_tmpl$8, _$ssrAttribute("aria-label", _$escape(messages().calendarView, true), false), _$escape(_$createComponent(For, {
444
+ return _$ssr(_tmpl$1, _$ssrAttribute("aria-label", _$escape(messages().calendarView, true), false), _$escape(_$createComponent(For, {
399
445
  get each() {
400
446
  return props.options;
401
447
  },
402
448
  children: (option, index) => {
403
449
  const active = () => (props.view === "mobile-month" ? "month" : props.view) === option.value;
404
- const href = () => props.owner.getViewHref?.(option.value) ?? "#";
450
+ const href = () => {
451
+ const day = props.focusDay;
452
+ const atDay = day && !active() ? props.owner.getDateHref?.(day, option.value) : undefined;
453
+ return atDay ?? props.owner.getViewHref?.(option.value) ?? "#";
454
+ };
405
455
  return _$createComponent(CalendarNavigationLink, {
406
456
  get owner() {
407
457
  return props.owner;
@@ -430,12 +480,28 @@ const CalendarViewLinks = props => {
430
480
  })));
431
481
  };
432
482
 
483
+ /**
484
+ * The same day some months later, or the last day of that month when it is shorter: a step from January 31 lands on
485
+ * February 28, never in March. stdlib's `addMonths` runs over into the next month.
486
+ */
487
+ const addMonthsClamped = (date, months, dateConfig) => {
488
+ const moved = calendar.addMonths(date, months, dateConfig);
489
+ if (calendar.formatDateKey(moved, dateConfig).slice(8) === calendar.formatDateKey(date, dateConfig).slice(8)) return moved;
490
+ return calendar.addDays(calendar.startOfMonth(moved, dateConfig), -1, dateConfig);
491
+ };
492
+
433
493
  /** Custom views page by day, like the day view. */
434
494
  const adjacentCalendarDate = (date, view, direction, dateConfig) => {
435
- if (view === "year") return calendar.addMonths(date, direction * 12, dateConfig);
436
- if (view === "month" || view === "mobile-month") return calendar.addMonths(date, direction, dateConfig);
495
+ if (view === "year") return addMonthsClamped(date, direction * 12, dateConfig);
496
+ if (view === "month" || view === "mobile-month") return addMonthsClamped(date, direction, dateConfig);
437
497
  return calendar.addDays(date, direction * (view === "week" ? 7 : 1), dateConfig);
438
498
  };
499
+
500
+ /** Opens a view at a day through the host's callbacks. Hosts may keep the view and the date apart, so both hear it. */
501
+ const openViewAt = (owner, date, view) => {
502
+ owner.onViewChange?.(view);
503
+ owner.onDateChange?.(date, view);
504
+ };
439
505
  const CalendarHeader = props => {
440
506
  const messages = useUiMessages();
441
507
  const dateConfig = () => ownerDateConfig(props.owner);
@@ -456,8 +522,51 @@ const CalendarHeader = props => {
456
522
  if (props.view === "week") return `${formatDay(calendar.startOfWeek(props.date, dateConfig()), dateConfig())} - ${formatDay(calendar.addDays(calendar.startOfWeek(props.date, dateConfig()), 6, dateConfig()), dateConfig())}`;
457
523
  return formatMonth(props.date, dateConfig());
458
524
  };
525
+ /**
526
+ * The titles this view can show around the current date: the months of the year, the weeks of the month, or a day
527
+ * of each weekday in every month. Their widest one sets the title's width.
528
+ */
529
+ const titleWidths = createMemo(() => {
530
+ const context = dateConfig();
531
+ if (props.view === "year") return [];
532
+ const {
533
+ year
534
+ } = zonedYearMonth(props.date, context);
535
+ if (props.view === "month" || props.view === "mobile-month") return Array.from({
536
+ length: 12
537
+ }, (_, month) => formatMonth(zonedMonthDate(year, month, context), context));
538
+ if (props.view === "week") {
539
+ const first = calendar.startOfWeek(calendar.startOfMonth(props.date, context), context);
540
+ return Array.from({
541
+ length: 6
542
+ }, (_, week) => {
543
+ const start = calendar.addDays(first, week * 7, context);
544
+ return `${formatDay(start, context)} - ${formatDay(calendar.addDays(start, 6, context), context)}`;
545
+ });
546
+ }
547
+ return Array.from({
548
+ length: 12
549
+ }, (_, month) => month).flatMap(month => {
550
+ const first = zonedMonthDate(year, month, context);
551
+ return Array.from({
552
+ length: 7
553
+ }, (_, offset) => calendar.addDays(first, 21 + offset, context).toLocaleDateString(context.locale ?? "en", {
554
+ weekday: "long",
555
+ month: "long",
556
+ day: "numeric",
557
+ year: "numeric",
558
+ timeZone: context.timeZone
559
+ }));
560
+ });
561
+ });
459
562
  const goDate = date => props.owner.onDateChange?.(date, props.view);
460
563
  const goView = view => {
564
+ // Like the switcher's links, the other views open at the day selected in the month view.
565
+ const day = props.focusDay;
566
+ if (day && props.owner.onDateChange && view !== props.view) {
567
+ openViewAt(props.owner, day, view);
568
+ return;
569
+ }
461
570
  if (props.owner.onViewChange) {
462
571
  props.owner.onViewChange(view);
463
572
  return;
@@ -478,9 +587,9 @@ const CalendarHeader = props => {
478
587
  class: "k2b-calendar-header__nav-button"
479
588
  },
480
589
  get children() {
481
- return _$ssr(_tmpl$9, `ti ${_$escape(icon, true)}`);
590
+ return _$ssr(_tmpl$10, `ti ${_$escape(icon, true)}`);
482
591
  }
483
- }) : props.owner.onDateChange ? _$ssr(_tmpl$0, _$ssrAttribute("aria-label", _$escape(label, true), false), `ti ${_$escape(icon, true)}`) : _$createComponent(CalendarNavigationLink, {
592
+ }) : props.owner.onDateChange ? _$ssr(_tmpl$11, _$ssrAttribute("aria-label", _$escape(label, true), false), `ti ${_$escape(icon, true)}`) : _$createComponent(CalendarNavigationLink, {
484
593
  get owner() {
485
594
  return props.owner;
486
595
  },
@@ -491,14 +600,14 @@ const CalendarHeader = props => {
491
600
  class: "k2b-calendar-header__nav-button"
492
601
  },
493
602
  get children() {
494
- return _$ssr(_tmpl$9, `ti ${_$escape(icon, true)}`);
603
+ return _$ssr(_tmpl$10, `ti ${_$escape(icon, true)}`);
495
604
  }
496
605
  });
497
606
  };
498
607
  const todayButton = () => {
499
608
  const today = calendar.today(dateConfig());
500
609
  const href = props.owner.getDateHref?.(today, props.view);
501
- const label = _$ssr(_tmpl$1, _$escape(props.labels.today));
610
+ const label = _$ssr(_tmpl$12, _$escape(props.labels.today));
502
611
  return (props.owner.onNavigateHref || props.owner.onNavigate) && href ? _$createComponent(CalendarNavigationLink, {
503
612
  get owner() {
504
613
  return props.owner;
@@ -545,7 +654,19 @@ const CalendarHeader = props => {
545
654
  value: "year",
546
655
  label: props.labels.year
547
656
  }].filter(option => !props.owner.views || props.owner.views.includes(option.value)), ...(props.owner.customViews ?? [])]);
548
- return _$ssr(_tmpl$11, _$escape(navButton(previous(), "ti-chevron-left", props.labels.previous)), _$escape(navButton(next(), "ti-chevron-right", props.labels.next)), _$escape(title()), _$escape(todayButton()), _$escape(_$createComponent(Show, {
657
+ return _$ssr(_tmpl$15, _$escape(navButton(previous(), "ti-chevron-left", props.labels.previous)), _$escape(navButton(next(), "ti-chevron-right", props.labels.next)), _$escape(title()), _$escape(_$createComponent(For, {
658
+ get each() {
659
+ return titleWidths();
660
+ },
661
+ children: candidate => _$ssr(_tmpl$16, _$escape(candidate))
662
+ })), _$escape(_$createComponent(Show, {
663
+ get when() {
664
+ return props.owner.toolbarContent;
665
+ },
666
+ get children() {
667
+ return _$ssr(_tmpl$13, _$escape(props.owner.toolbarContent));
668
+ }
669
+ })), _$escape(todayButton()), _$escape(_$createComponent(Show, {
549
670
  get when() {
550
671
  return _$memo(() => !!!props.owner.onViewChange)() && props.owner.getViewHref;
551
672
  },
@@ -571,6 +692,9 @@ const CalendarHeader = props => {
571
692
  },
572
693
  get options() {
573
694
  return viewOptions();
695
+ },
696
+ get focusDay() {
697
+ return props.focusDay;
574
698
  }
575
699
  });
576
700
  }
@@ -579,12 +703,15 @@ const CalendarHeader = props => {
579
703
  return props.owner.navigationPending;
580
704
  },
581
705
  get children() {
582
- return _$ssr(_tmpl$10);
706
+ return _$ssr(_tmpl$14);
583
707
  }
584
708
  })));
585
709
  };
586
- const MonthView = props => {
710
+
711
+ /** The compact month of `mobile-month`: a picker whose days link to their agenda. */
712
+ const MonthPickerView = props => {
587
713
  const messages = useUiMessages();
714
+ const calendarMessages = useCalendarMessages();
588
715
  const dateConfig = createMemo(() => ownerDateConfig(props.owner));
589
716
  const [movePreview, setMovePreview] = createSignal(null);
590
717
  const [movingEventId, setMovingEventId] = createSignal("");
@@ -637,28 +764,28 @@ const MonthView = props => {
637
764
  });
638
765
  };
639
766
  onCleanup(() => cancelInteraction?.());
640
- return _$ssr(_tmpl$13, _$ssrStyleProperty("grid-template-rows:", `auto repeat(${_$escape(weeks().length, true)}, ${props.compact ? "minmax(2.75rem, auto)" : "minmax(5rem, 1fr)"})`), _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
767
+ return _$ssr(_tmpl$18, _$ssrStyleProperty("grid-template-rows:", `auto repeat(${_$escape(weeks().length, true)}, ${props.compact ? "minmax(2.75rem, auto)" : "minmax(5rem, 1fr)"})`), _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
641
768
  get when() {
642
769
  return props.owner.withWeekNumbers;
643
770
  },
644
771
  get children() {
645
- return _$ssr(_tmpl$12, _$escape(messages().weekShort));
772
+ return _$ssr(_tmpl$17, _$escape(messages().weekShort));
646
773
  }
647
774
  })), _$escape(_$createComponent(For, {
648
775
  get each() {
649
776
  return weekdays();
650
777
  },
651
- children: day => _$ssr(_tmpl$12, _$escape(day))
778
+ children: day => _$ssr(_tmpl$17, _$escape(day))
652
779
  })), _$escape(_$createComponent(For, {
653
780
  get each() {
654
781
  return weeks();
655
782
  },
656
- children: week => _$ssr(_tmpl$15, _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
783
+ children: week => _$ssr(_tmpl$20, _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
657
784
  get when() {
658
785
  return props.owner.withWeekNumbers;
659
786
  },
660
787
  get children() {
661
- return _$ssr(_tmpl$14, _$escape(zonedWeekNumber(week[0], dateConfig())));
788
+ return _$ssr(_tmpl$19, _$escape(zonedWeekNumber(week[0], dateConfig())));
662
789
  }
663
790
  })), _$escape(_$createComponent(For, {
664
791
  each: week,
@@ -687,7 +814,7 @@ const MonthView = props => {
687
814
  hiddenCount: Math.max(0, candidates.length - visibleLimit)
688
815
  };
689
816
  });
690
- return _$ssr(_tmpl$17, _$ssrAttribute("data-calendar-day-key", _$escape(dayKey, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-drop-preview", movePreview()?.start && calendar.formatDateKey(movePreview().start, dateConfig()) === dayKey ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
817
+ return _$ssr(_tmpl$22, _$ssrAttribute("data-calendar-day-key", _$escape(dayKey, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-drop-preview", movePreview()?.start && calendar.formatDateKey(movePreview().start, dateConfig()) === dayKey ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
691
818
  get when() {
692
819
  return props.owner.onSlotActivate;
693
820
  },
@@ -736,7 +863,7 @@ const MonthView = props => {
736
863
  return props.owner.onSlotActivate && href;
737
864
  },
738
865
  get fallback() {
739
- return _$ssr(_tmpl$18, _$ssrAttribute("data-today", isToday ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false), _$escape(calendar.formatDayNumber(day, dateConfig())));
866
+ return _$ssr(_tmpl$23, _$ssrAttribute("data-today", isToday ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-outside", !sameMonth ? "true" : _$escape(undefined, true), false), _$escape(calendar.formatDayNumber(day, dateConfig())));
740
867
  },
741
868
  children: dayHref => _$createComponent(CalendarNavigationLink, {
742
869
  get owner() {
@@ -761,18 +888,18 @@ const MonthView = props => {
761
888
  })
762
889
  })), _$escape(_$createComponent(Show, {
763
890
  when: dayBadge,
764
- children: badge => _$ssr(_tmpl$19, _$escape(_$createComponent(Show, {
891
+ children: badge => _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
765
892
  get when() {
766
893
  return badge().icon;
767
894
  },
768
- children: icon => _$ssr(_tmpl$9, `k2b-calendar-month__badge-icon ti ti-${_$escape(icon(), true)}`)
895
+ children: icon => _$ssr(_tmpl$10, `k2b-calendar-month__badge-icon ti ti-${_$escape(icon(), true)}`)
769
896
  })), _$escape(badge().label))
770
897
  })), _$escape(_$createComponent(Show, {
771
898
  get when() {
772
899
  return eventStack().preview;
773
900
  },
774
901
  get children() {
775
- return _$ssr(_tmpl$16, _$escape(props.events.find(event => event.id === eventStack().preview.id)?.title ?? messages().moveEvent));
902
+ return _$ssr(_tmpl$21, _$escape(props.events.find(event => event.id === eventStack().preview.id)?.title ?? messages().moveEvent));
776
903
  }
777
904
  })), _$escape(_$createComponent(For, {
778
905
  get each() {
@@ -808,7 +935,9 @@ const MonthView = props => {
808
935
  class: "k2b-calendar-month__more"
809
936
  },
810
937
  get children() {
811
- return ["+", eventStack().hiddenCount, " more"];
938
+ return calendarMessages().calendarMoreEvents({
939
+ count: eventStack().hiddenCount
940
+ });
812
941
  }
813
942
  });
814
943
  }
@@ -816,7 +945,7 @@ const MonthView = props => {
816
945
  get each() {
817
946
  return events.slice(0, 4);
818
947
  },
819
- children: event => _$ssr(_tmpl$20, _$ssrAttribute("data-color", event.colorHex ? _$escape(undefined, true) : _$escape(event.color ?? "blue", true), false), _$ssrStyle(event.colorHex ? {
948
+ children: event => _$ssr(_tmpl$25, _$ssrAttribute("data-color", event.colorHex ? _$escape(undefined, true) : _$escape(event.color ?? "blue", true), false), _$ssrStyle(event.colorHex ? {
820
949
  "background-color": event.colorHex
821
950
  } : undefined))
822
951
  })));
@@ -824,6 +953,989 @@ const MonthView = props => {
824
953
  })))
825
954
  })));
826
955
  };
956
+
957
+ /** One entry of the month grid: its whole range, and the days it covers as `YYYY-MM-DD` keys. */
958
+
959
+ /** Lane height and gap of the month entries, in rem; the stylesheet reads both from the grid's custom properties. */
960
+ const MONTH_LANE_REM = 1.25;
961
+ const MONTH_LANE_GAP_REM = 0.125;
962
+ /** Rows drawn before the browser has measured the cells, as in the server response. */
963
+ const MONTH_DEFAULT_LANES = 3;
964
+ /** Room a bar keeps beside its title and date hint: padding, torn edge, gap, and an icon such as a task box or a flag. */
965
+ const MONTH_HINT_SLACK_REM = 2.25;
966
+ const dayKeyNumber = key => {
967
+ const [year = "1970", month = "1", day = "1"] = key.split("-");
968
+ return Date.UTC(Number(year), Number(month) - 1, Number(day)) / 86_400_000;
969
+ };
970
+ const orderedSelection = (anchor, focus) => anchor <= focus ? {
971
+ first: anchor,
972
+ last: focus
973
+ } : {
974
+ first: focus,
975
+ last: anchor
976
+ };
977
+
978
+ /** Groups the per-day pieces of the events back into one entry each: bars first, then the others by time. */
979
+ const monthItems = events => {
980
+ const items = new Map();
981
+ for (const piece of events) {
982
+ const key = `${piece.id}\u0000${piece.sourceStartDate.getTime()}`;
983
+ const item = items.get(key);
984
+ if (item) {
985
+ if (piece.dayKey < item.firstKey) item.firstKey = piece.dayKey;
986
+ if (piece.dayKey > item.lastKey) item.lastKey = piece.dayKey;
987
+ item.days.set(piece.dayKey, piece);
988
+ continue;
989
+ }
990
+ items.set(key, {
991
+ key,
992
+ event: {
993
+ ...piece,
994
+ startDate: piece.sourceStartDate,
995
+ endDate: piece.sourceEndDate
996
+ },
997
+ firstKey: piece.dayKey,
998
+ lastKey: piece.dayKey,
999
+ bar: false,
1000
+ days: new Map([[piece.dayKey, piece]])
1001
+ });
1002
+ }
1003
+ for (const item of items.values()) item.bar = item.firstKey !== item.lastKey || Boolean(item.event.allDay && item.event.display !== "marker");
1004
+ return [...items.values()].sort((left, right) => Number(right.bar) - Number(left.bar) || left.event.sourceStartDate.getTime() - right.event.sourceStartDate.getTime() || left.event.title.localeCompare(right.event.title));
1005
+ };
1006
+
1007
+ /** Places a calendar popover below its anchor when there is room and above it otherwise, inside the viewport. */
1008
+ const placeCalendarPopover = (anchor, popover) => {
1009
+ const margin = 8;
1010
+ const gap = 4;
1011
+ const rect = anchor.getBoundingClientRect();
1012
+ const width = Math.min(popover.offsetWidth || 288, window.innerWidth - margin * 2);
1013
+ const height = popover.offsetHeight;
1014
+ const left = Math.max(margin, Math.min(rect.left, window.innerWidth - width - margin));
1015
+ const roomBelow = window.innerHeight - rect.bottom - gap - margin;
1016
+ const above = height > roomBelow && rect.top - gap - margin > roomBelow;
1017
+ const top = above ? Math.max(margin, rect.top - height - gap) : Math.min(rect.bottom + gap, window.innerHeight - height - margin);
1018
+ popover.style.left = `${left}px`;
1019
+ popover.style.top = `${Math.max(margin, top)}px`;
1020
+ };
1021
+ let measureContext;
1022
+ /** The width of a text in a font, from a canvas, so a bar can tell whether its title leaves room for a date hint. */
1023
+ const textWidth = (text, font) => {
1024
+ measureContext ??= typeof document === "undefined" ? null : document.createElement("canvas").getContext("2d");
1025
+ if (!measureContext) return Number.POSITIVE_INFINITY;
1026
+ measureContext.font = font;
1027
+ return measureContext.measureText(text).width;
1028
+ };
1029
+
1030
+ /** Follows a calendar link the way the calendar's own links do: through the host's navigation when it has one. */
1031
+ const followCalendarHref = (owner, href) => {
1032
+ if (owner.onNavigateHref) owner.onNavigateHref(href);else window.location.assign(href);
1033
+ };
1034
+ const PRINTABLE_KEY = /^.$/u;
1035
+
1036
+ /**
1037
+ * The month grid. A click selects a day and never navigates, a drag or Shift selects a range, and the arrow keys move
1038
+ * the selection. The quick create opens at the selection: quietly after a click, with the focus after a drag, a
1039
+ * double-click, Enter, or N. Every event is one bar per week row in a lane it keeps on each of its days, and each
1040
+ * cell shows as many entries as its height holds.
1041
+ */
1042
+ const MonthView = props => {
1043
+ const messages = useUiMessages();
1044
+ const calendarMessages = useCalendarMessages();
1045
+ const dateConfig = createMemo(() => ownerDateConfig(props.owner));
1046
+ // The shown month and its grid change only with the month, so a refresh of the same month keeps every cell and its
1047
+ // focus.
1048
+ const month = createMemo(() => zonedYearMonth(props.date, dateConfig()), undefined, {
1049
+ equals: (previous, next) => previous.year === next.year && previous.month === next.month
1050
+ });
1051
+ const weeks = createMemo(() => calendar.getMonthGrid(month().year, month().month, dateConfig()).map(week => week.map(day => ({
1052
+ day,
1053
+ key: calendar.formatDateKey(day, dateConfig())
1054
+ }))), undefined, {
1055
+ equals: (previous, next) => previous.map(week => week[0]?.key).join() === next.map(week => week[0]?.key).join()
1056
+ });
1057
+ const weekdays = createMemo(() => calendar.weekdays(dateConfig()));
1058
+ const todayKey = createMemo(() => calendar.formatDateKey(props.now, dateConfig()));
1059
+ const items = createMemo(() => monthItems(props.events));
1060
+ const weekLayouts = createMemo(() => weeks().map(week => layoutMonthWeek(week.map(entry => entry.key), items().map(item => ({
1061
+ item,
1062
+ firstKey: item.firstKey,
1063
+ lastKey: item.lastKey,
1064
+ bar: item.bar
1065
+ })))));
1066
+ const [capacities, setCapacities] = createSignal([]);
1067
+ const [cellWidth, setCellWidth] = createSignal(0);
1068
+ const [barFont, setBarFont] = createSignal("");
1069
+ const [anchorKey, setAnchorKey] = createSignal(null);
1070
+ const [focusKey, setFocusKey] = createSignal(null);
1071
+ const [popover, setPopover] = createSignal(null);
1072
+ /** Whether the quick create holds the focus; until then the grid keeps it. */
1073
+ const [createActive, setCreateActive] = createSignal(false);
1074
+ const [linkedItem, setLinkedItem] = createSignal("");
1075
+ const [movePreview, setMovePreview] = createSignal(null);
1076
+ const [movingEventId, setMovingEventId] = createSignal("");
1077
+ const cells = new Map();
1078
+ let popoverElement;
1079
+ let cancelInteraction;
1080
+ let suppressClickUntil = 0;
1081
+ let lastPointer = "mouse";
1082
+ let menuKey = null;
1083
+ /** The day to select once paging with Page Up or Page Down has shown its month. */
1084
+ let pendingKey = null;
1085
+ const selection = createMemo(() => {
1086
+ const anchor = anchorKey();
1087
+ const focus = focusKey();
1088
+ return anchor && focus ? orderedSelection(anchor, focus) : null;
1089
+ });
1090
+ const parseKey = key => calendar.parseCalendarDate(key, dateConfig());
1091
+ const rangeOf = range => ({
1092
+ start: parseKey(range.first),
1093
+ end: calendar.addDays(parseKey(range.last), 1, dateConfig()),
1094
+ allDay: true
1095
+ });
1096
+ const visibleKeys = createMemo(() => weeks().flatMap(week => week.map(entry => entry.key)));
1097
+ const rovingKey = createMemo(() => {
1098
+ const keys = visibleKeys();
1099
+ const focus = focusKey();
1100
+ if (focus && keys.includes(focus)) return focus;
1101
+ if (keys.includes(todayKey())) return todayKey();
1102
+ return calendar.formatDateKey(zonedMonthDate(month().year, month().month, dateConfig()), dateConfig());
1103
+ });
1104
+ const dropRange = createMemo(() => {
1105
+ const preview = movePreview();
1106
+ if (!preview) return null;
1107
+ const keys = previewSegments(preview, visibleKeys().map(parseKey), dateConfig()).map(piece => piece.dayKey);
1108
+ if (keys.length === 0) return null;
1109
+ keys.sort();
1110
+ return {
1111
+ first: keys[0],
1112
+ last: keys[keys.length - 1]
1113
+ };
1114
+ });
1115
+ const within = (range, key) => Boolean(range && key >= range.first && key <= range.last);
1116
+ /** Where a day sits in the selection: alone, at its start or end, or in between. */
1117
+ const selectionPart = key => {
1118
+ const range = selection();
1119
+ if (!range || !within(range, key)) return undefined;
1120
+ if (range.first === range.last) return "single";
1121
+ return key === range.first ? "start" : key === range.last ? "end" : "middle";
1122
+ };
1123
+ const select = (anchor, focus = anchor) => {
1124
+ setAnchorKey(anchor);
1125
+ setFocusKey(focus);
1126
+ };
1127
+ // The host learns every change of the selected days, and a new month starts without a selection. The selection
1128
+ // goes with the view, so switching to another view reports that nothing is selected.
1129
+ createEffect(on(selection, range => props.owner.onSelectionChange?.(range ? rangeOf(range) : null), {
1130
+ defer: true
1131
+ }));
1132
+ onCleanup(() => {
1133
+ if (untrack(selection)) props.owner.onSelectionChange?.(null);
1134
+ });
1135
+ createEffect(on(selection, range => props.onFocusDay?.(range ? parseKey(range.first) : null)));
1136
+ createEffect(on(month, () => {
1137
+ setPopover(null);
1138
+ const key = pendingKey;
1139
+ pendingKey = null;
1140
+ if (key && visibleKeys().includes(key)) {
1141
+ select(key);
1142
+ queueMicrotask(() => focusCell(key));
1143
+ } else select(null);
1144
+ }, {
1145
+ defer: true
1146
+ }));
1147
+ const dayLabel = day => day.toLocaleDateString(dateConfig().locale ?? "en", {
1148
+ weekday: "long",
1149
+ year: "numeric",
1150
+ month: "long",
1151
+ day: "numeric",
1152
+ timeZone: dateConfig().timeZone
1153
+ });
1154
+ const shortDayLabel = (key, withMonth = true) => parseKey(key).toLocaleDateString(dateConfig().locale ?? "en", {
1155
+ day: "numeric",
1156
+ ...(withMonth ? {
1157
+ month: "short"
1158
+ } : {}),
1159
+ timeZone: dateConfig().timeZone
1160
+ });
1161
+ const monthDayLabel = (date, withTime) => date.toLocaleString(dateConfig().locale ?? "en", {
1162
+ month: "long",
1163
+ day: "numeric",
1164
+ ...(withTime ? {
1165
+ hour: "2-digit",
1166
+ minute: "2-digit"
1167
+ } : {}),
1168
+ timeZone: dateConfig().timeZone
1169
+ });
1170
+ /** A long event names the days it spans; a one-day event keeps the chip's own title and time. */
1171
+ const itemLabel = item => {
1172
+ if (item.firstKey === item.lastKey) return undefined;
1173
+ const allDay = Boolean(item.event.allDay);
1174
+ return messages().calendarEventTime({
1175
+ title: item.event.title,
1176
+ start: monthDayLabel(allDay ? parseKey(item.firstKey) : item.event.sourceStartDate, !allDay),
1177
+ end: monthDayLabel(allDay ? parseKey(item.lastKey) : item.event.sourceEndDate, !allDay)
1178
+ });
1179
+ };
1180
+ /** The start time before the title of a one-day entry; bars and all-day entries show none. */
1181
+ const leadingTime = item => item.bar || item.event.allDay ? undefined : formatTime(item.event.sourceStartDate, dateConfig());
1182
+ /** The day at a torn end of a bar, with its month when that day lies outside the shown month. */
1183
+ const hintDate = target => ({
1184
+ day: Number(target.slice(8, 10)),
1185
+ month: Number(target.slice(0, 4)) === month().year && Number(target.slice(5, 7)) === month().month + 1 ? undefined : parseKey(target).toLocaleDateString(dateConfig().locale ?? "en", {
1186
+ month: "short",
1187
+ timeZone: dateConfig().timeZone
1188
+ }).replace(/\.$/, "")
1189
+ });
1190
+ const selectionHeading = range => {
1191
+ if (range.first === range.last) return parseKey(range.first).toLocaleDateString(dateConfig().locale ?? "en", {
1192
+ weekday: "long",
1193
+ month: "long",
1194
+ day: "numeric",
1195
+ timeZone: dateConfig().timeZone
1196
+ });
1197
+ const days = dayKeyNumber(range.last) - dayKeyNumber(range.first) + 1;
1198
+ return calendarMessages().calendarSelectedDays({
1199
+ start: shortDayLabel(range.first),
1200
+ end: shortDayLabel(range.last),
1201
+ count: days
1202
+ });
1203
+ };
1204
+ const viewAvailable = view => !props.owner.views || props.owner.views.includes(view);
1205
+ const viewHref = (key, view) => viewAvailable(view) ? props.owner.getDateHref?.(parseKey(key), view) : undefined;
1206
+ /** Whether the calendar can open another view at a day: through a link, or through the host's date change. */
1207
+ const canOpenView = view => viewAvailable(view) && Boolean(props.owner.getDateHref || props.owner.onDateChange);
1208
+ const openView = (key, view) => {
1209
+ const href = viewHref(key, view);
1210
+ if (href) followCalendarHref(props.owner, href);else openViewAt(props.owner, parseKey(key), view);
1211
+ };
1212
+ /** The calendar's own entries of the day menu, Open day and Open week, need no application entries. */
1213
+ const hasMenu = () => Boolean(props.owner.selectionMenu) || canOpenView("day") || canOpenView("week");
1214
+
1215
+ /** The day cell under a point, from the cells' boxes: a bar lies over days that are not its parent. */
1216
+ const dayKeyAtPoint = (clientX, clientY) => {
1217
+ for (const [key, cell] of cells) {
1218
+ if (!cell.isConnected) continue;
1219
+ const rect = cell.getBoundingClientRect();
1220
+ if (clientX >= rect.left && clientX < rect.right && clientY >= rect.top && clientY < rect.bottom) return key;
1221
+ }
1222
+ return null;
1223
+ };
1224
+ const focusCell = key => cells.get(key)?.focus({
1225
+ preventScroll: true
1226
+ });
1227
+ const closePopover = (restoreFocus = true) => {
1228
+ if (!popover()) return;
1229
+ setPopover(null);
1230
+ setCreateActive(false);
1231
+ if (restoreFocus) focusCell(rovingKey());
1232
+ };
1233
+ const popoverFocusable = () => popoverElement?.querySelector("input,textarea,select,button,a[href],[tabindex]:not([tabindex='-1'])") ?? null;
1234
+
1235
+ /**
1236
+ * Opens the quick create at the selection. Quietly, it only shows beside the grid, which keeps the focus until the
1237
+ * person types or tabs into it; otherwise it takes the focus at once.
1238
+ */
1239
+ const openCreate = (range, options) => {
1240
+ if (!props.owner.renderQuickCreate) return false;
1241
+ const anchor = cells.get(focusKey() && within(range, focusKey()) ? focusKey() : range.last);
1242
+ if (!anchor) return false;
1243
+ setCreateActive(options.active);
1244
+ setPopover({
1245
+ kind: "create",
1246
+ range,
1247
+ anchor,
1248
+ create: options.create
1249
+ });
1250
+ return true;
1251
+ };
1252
+ const activateCreate = () => {
1253
+ if (popover()?.kind !== "create") return false;
1254
+ setCreateActive(true);
1255
+ popoverFocusable()?.focus();
1256
+ return true;
1257
+ };
1258
+ /** Enter, a double-click, and N create on the selection, with the quick create or the host's own create action. */
1259
+ const createOnSelection = () => {
1260
+ const range = selection();
1261
+ if (!range) return;
1262
+ if (openCreate(range, {
1263
+ active: true
1264
+ })) return;
1265
+ if (props.owner.onSlotActivate) props.owner.onSlotActivate(rangeOf(range));
1266
+ };
1267
+ const openDayList = key => {
1268
+ const anchor = cells.get(key);
1269
+ if (!anchor) return;
1270
+ setCreateActive(true);
1271
+ setPopover({
1272
+ kind: "day",
1273
+ dayKey: key,
1274
+ anchor
1275
+ });
1276
+ };
1277
+ /** A quiet quick create follows the selection; any other popover closes when the selection changes. */
1278
+ const followSelection = () => {
1279
+ const current = popover();
1280
+ const range = selection();
1281
+ if (!current) return;
1282
+ if (current.kind === "create" && !createActive() && range) openCreate(range, {
1283
+ active: false
1284
+ });else closePopover(false);
1285
+ };
1286
+ const pageTo = target => {
1287
+ const date = parseKey(target);
1288
+ const href = props.owner.getDateHref?.(date, "month");
1289
+ // Only a month that is on its way may select the day, so a host that cannot page never leaves one behind.
1290
+ if (props.owner.onDateChange) {
1291
+ pendingKey = target;
1292
+ props.owner.onDateChange(date, "month");
1293
+ } else if (href) {
1294
+ pendingKey = target;
1295
+ followCalendarHref(props.owner, href);
1296
+ }
1297
+ };
1298
+ const moveFocus = (key, extend) => {
1299
+ if (!visibleKeys().includes(key)) {
1300
+ pageTo(key);
1301
+ return;
1302
+ }
1303
+ if (extend) select(anchorKey() ?? focusKey() ?? key, key);else select(key);
1304
+ focusCell(key);
1305
+ followSelection();
1306
+ };
1307
+ const onCellKeyDown = (event, key) => {
1308
+ if (event.target !== event.currentTarget || event.ctrlKey || event.metaKey || event.altKey) return;
1309
+ const current = popover();
1310
+ const quiet = current?.kind === "create" && !createActive();
1311
+ // The quiet quick create takes over once the person types or tabs into it. Space stays the grid's key for the day
1312
+ // list; N is a title's first letter like any other, as the quick create it would open is already there.
1313
+ if (quiet && (event.key === "Tab" && !event.shiftKey || PRINTABLE_KEY.test(event.key) && event.key !== " ")) {
1314
+ const field = popoverFocusable();
1315
+ if (field) {
1316
+ if (event.key === "Tab") event.preventDefault();
1317
+ setCreateActive(true);
1318
+ field.focus();
1319
+ return;
1320
+ }
1321
+ }
1322
+ const day = parseKey(key);
1323
+ const shift = days => calendar.formatDateKey(calendar.addDays(day, days, dateConfig()), dateConfig());
1324
+ const weekday = (dayKeyNumber(key) - dayKeyNumber(calendar.formatDateKey(calendar.startOfWeek(day, dateConfig()), dateConfig()))) % 7;
1325
+ const target = event.key === "ArrowLeft" ? shift(-1) : event.key === "ArrowRight" ? shift(1) : event.key === "ArrowUp" ? shift(-7) : event.key === "ArrowDown" ? shift(7) : event.key === "Home" ? shift(-weekday) : event.key === "End" ? shift(6 - weekday) : null;
1326
+ if (target) {
1327
+ event.preventDefault();
1328
+ moveFocus(target, event.shiftKey);
1329
+ return;
1330
+ }
1331
+ if (event.key === "PageUp" || event.key === "PageDown") {
1332
+ event.preventDefault();
1333
+ pageTo(calendar.formatDateKey(addMonthsClamped(day, event.key === "PageUp" ? -1 : 1, dateConfig()), dateConfig()));
1334
+ return;
1335
+ }
1336
+ if (event.key === "Enter" || (event.key === "n" || event.key === "N") && !event.shiftKey) {
1337
+ event.preventDefault();
1338
+ if (!within(selection(), key)) select(key);
1339
+ createOnSelection();
1340
+ return;
1341
+ }
1342
+ if (event.key === " ") {
1343
+ event.preventDefault();
1344
+ if (!within(selection(), key)) select(key);
1345
+ openDayList(key);
1346
+ return;
1347
+ }
1348
+ if (event.key === "Escape") {
1349
+ if (current) closePopover(false);else if (selection()) select(null);else return;
1350
+ event.preventDefault();
1351
+ return;
1352
+ }
1353
+ // The keyboard opens the day's menu where the day is, as a right-click there would.
1354
+ if (hasMenu() && (event.key === "ContextMenu" || event.shiftKey && event.key === "F10")) {
1355
+ event.preventDefault();
1356
+ event.stopPropagation();
1357
+ menuKey = key;
1358
+ const rect = event.currentTarget.getBoundingClientRect();
1359
+ event.currentTarget?.dispatchEvent(new MouseEvent("contextmenu", {
1360
+ bubbles: true,
1361
+ cancelable: true,
1362
+ clientX: rect.left + 12,
1363
+ clientY: rect.top + 12
1364
+ }));
1365
+ }
1366
+ };
1367
+ const fromControl = event => event.target instanceof Element && Boolean(event.target.closest("a,button,[data-calendar-event],.k2b-calendar-month__lanes > *"));
1368
+ const onCellClick = (event, key) => {
1369
+ if (fromControl(event) || performance.now() < suppressClickUntil) return;
1370
+ // A second tap on the one selected day opens the quick create, as a double-click does with a mouse.
1371
+ if (lastPointer === "touch") {
1372
+ const range = selection();
1373
+ if (range && range.first === key && range.last === key && openCreate(range, {
1374
+ active: true
1375
+ })) return;
1376
+ select(key);
1377
+ closePopover(false);
1378
+ return;
1379
+ }
1380
+ if (event.shiftKey && anchorKey()) select(anchorKey(), key);else select(key);
1381
+ focusCell(key);
1382
+ const range = selection();
1383
+ if (range) openCreate(range, {
1384
+ active: false
1385
+ });
1386
+ };
1387
+ const onCellDoubleClick = (event, key) => {
1388
+ if (fromControl(event) || lastPointer === "touch") return;
1389
+ event.preventDefault();
1390
+ if (!within(selection(), key)) select(key);
1391
+ if (popover()?.kind === "create") activateCreate();else createOnSelection();
1392
+ };
1393
+ /** The day under the pointer: a bar that spans several days sits in the cell of its first day. */
1394
+ const pressedKey = (event, key) => dayKeyAtPoint(event.clientX, event.clientY) ?? key;
1395
+ const onCellPointerDown = (event, key) => {
1396
+ lastPointer = event.pointerType;
1397
+ // The menu acts on the pressed day, or on the selection when the press is inside it.
1398
+ menuKey = pressedKey(event, key);
1399
+ if (event.pointerType !== "mouse" || event.button !== 0 || event.shiftKey || fromControl(event)) return;
1400
+ cancelInteraction?.();
1401
+ cancelInteraction = startCalendarPointerSession({
1402
+ event,
1403
+ resolve: (clientX, clientY) => dayKeyAtPoint(clientX, clientY),
1404
+ onActivate: () => {
1405
+ suppressClickUntil = performance.now() + 400;
1406
+ closePopover(false);
1407
+ select(key);
1408
+ },
1409
+ onPreview: current => select(key, current),
1410
+ onCommit: current => {
1411
+ select(key, current);
1412
+ focusCell(current);
1413
+ const range = orderedSelection(key, current);
1414
+ openCreate(range, {
1415
+ active: range.first !== range.last
1416
+ });
1417
+ },
1418
+ onCancel: () => select(key)
1419
+ });
1420
+ };
1421
+ const onMenuOpen = () => {
1422
+ closePopover(false);
1423
+ const key = menuKey;
1424
+ if (key && !within(selection(), key)) select(key);
1425
+ };
1426
+ // Until a day is selected, the menu acts on the day that takes focus, so it is never empty.
1427
+ const menuItems = createMemo(() => {
1428
+ if (!hasMenu()) return [];
1429
+ const range = selection() ?? {
1430
+ first: rovingKey(),
1431
+ last: rovingKey()
1432
+ };
1433
+ const own = props.owner.selectionMenu?.(rangeOf(range), {
1434
+ quickCreate: create => {
1435
+ select(range.first, range.last);
1436
+ if (!openCreate(range, {
1437
+ active: true,
1438
+ create
1439
+ })) props.owner.onSlotActivate?.(rangeOf(range));
1440
+ }
1441
+ }) ?? [];
1442
+ const actions = own.filter(item => !("items" in item));
1443
+ const sections = own.filter(item => "items" in item);
1444
+ const navigation = [...(canOpenView("day") ? [{
1445
+ label: calendarMessages().calendarOpenDay,
1446
+ icon: "ti ti-zoom-in",
1447
+ action: () => openView(range.first, "day")
1448
+ }] : []), ...(canOpenView("week") ? [{
1449
+ label: calendarMessages().calendarOpenWeek,
1450
+ icon: "ti ti-calendar-week",
1451
+ action: () => openView(range.first, "week")
1452
+ }] : []), ...(range.first !== range.last ? [{
1453
+ label: calendarMessages().calendarClearSelection,
1454
+ icon: "ti ti-x",
1455
+ action: () => select(null)
1456
+ }] : [])];
1457
+ const groups = [{
1458
+ items: actions
1459
+ }, ...sections, {
1460
+ items: navigation
1461
+ }].filter(section => section.items.length > 0);
1462
+ // The first group names the days the menu acts on, also when the calendar's own entries are all it has.
1463
+ const [first, ...rest] = groups;
1464
+ return first && !first.sectionLabel ? [{
1465
+ ...first,
1466
+ sectionLabel: selectionHeading(range)
1467
+ }, ...rest] : groups;
1468
+ });
1469
+ const clearMove = () => {
1470
+ setMovePreview(null);
1471
+ setMovingEventId("");
1472
+ };
1473
+ const startMove = (pointerEvent, item, markDragged) => {
1474
+ if (!props.owner.onEventDrop) return;
1475
+ const event = item.event;
1476
+ const grabbed = dayKeyAtPoint(pointerEvent.clientX, pointerEvent.clientY) ?? item.firstKey;
1477
+ const firstDay = startOfDay(event.sourceStartDate, dateConfig());
1478
+ cancelInteraction?.();
1479
+ cancelInteraction = startCalendarPointerSession({
1480
+ event: pointerEvent,
1481
+ resolve: (clientX, clientY) => {
1482
+ const key = dayKeyAtPoint(clientX, clientY);
1483
+ if (!key) return null;
1484
+ // The event moves by as many days as the pointer, wherever on the bar it was grabbed.
1485
+ const day = calendar.addDays(firstDay, dayKeyNumber(key) - dayKeyNumber(grabbed), dateConfig());
1486
+ return {
1487
+ id: event.id,
1488
+ title: event.title,
1489
+ ...moveEventToDay(event, day, dateConfig())
1490
+ };
1491
+ },
1492
+ onActivate: () => {
1493
+ markDragged();
1494
+ suppressClickUntil = performance.now() + 400;
1495
+ closePopover(false);
1496
+ setMovingEventId(event.id);
1497
+ },
1498
+ onPreview: setMovePreview,
1499
+ onCommit: next => {
1500
+ clearMove();
1501
+ if (eventTimeChanged(event, next)) props.owner.onEventDrop?.(event, next);
1502
+ },
1503
+ onCancel: clearMove
1504
+ });
1505
+ };
1506
+
1507
+ // Each week row measures the room below its day numbers; the rows only resize with the grid, never with content.
1508
+ let observer;
1509
+ const lanesOf = new WeakMap();
1510
+ const measure = (element, week) => {
1511
+ const style = getComputedStyle(element);
1512
+ const rootSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
1513
+ const capacity = monthLaneCapacity(element.clientHeight, MONTH_LANE_REM * rootSize, MONTH_LANE_GAP_REM * rootSize);
1514
+ setCapacities(current => {
1515
+ if (current[week] === capacity) return current;
1516
+ const next = [...current];
1517
+ next[week] = capacity;
1518
+ return next;
1519
+ });
1520
+ if (week === 0) {
1521
+ setCellWidth(element.clientWidth);
1522
+ setBarFont(`600 ${0.6875 * rootSize}px ${style.fontFamily}`);
1523
+ }
1524
+ };
1525
+ const observeLanes = (element, week) => {
1526
+ lanesOf.set(element, week);
1527
+ if (typeof ResizeObserver === "undefined") return;
1528
+ observer ??= new ResizeObserver(entries => {
1529
+ for (const entry of entries) {
1530
+ const index = lanesOf.get(entry.target);
1531
+ if (index !== undefined) measure(entry.target, index);
1532
+ }
1533
+ });
1534
+ observer.observe(element);
1535
+ onCleanup(() => observer?.unobserve(element));
1536
+ };
1537
+ /** A date hint shows only when the bar holds its title and the hint whole; screen readers get the range anyway. */
1538
+ const hintFits = (title, hint, span) => {
1539
+ const width = cellWidth();
1540
+ const font = barFont();
1541
+ if (width <= 0 || !font) return false;
1542
+ const rootSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
1543
+ return textWidth(title, font) + textWidth(hint, font) + MONTH_HINT_SLACK_REM * rootSize <= span * width;
1544
+ };
1545
+ createEffect(() => {
1546
+ const current = popover();
1547
+ const element = popoverElement;
1548
+ if (!element) return;
1549
+ if (!current) {
1550
+ if (element.matches(":popover-open")) element.hidePopover();
1551
+ return;
1552
+ }
1553
+ if (!element.matches(":popover-open")) element.showPopover();
1554
+ const focus = untrack(createActive);
1555
+ queueMicrotask(() => {
1556
+ if (popover() !== current || !element.isConnected) return;
1557
+ placeCalendarPopover(current.anchor, element);
1558
+ if (focus) popoverFocusable()?.focus();
1559
+ });
1560
+ });
1561
+ onMount(() => {
1562
+ const reposition = () => {
1563
+ const current = popover();
1564
+ if (current && popoverElement) placeCalendarPopover(current.anchor, popoverElement);
1565
+ };
1566
+ window.addEventListener("resize", reposition);
1567
+ window.addEventListener("scroll", reposition, true);
1568
+ onCleanup(() => {
1569
+ window.removeEventListener("resize", reposition);
1570
+ window.removeEventListener("scroll", reposition, true);
1571
+ });
1572
+ });
1573
+ onCleanup(() => {
1574
+ cancelInteraction?.();
1575
+ observer?.disconnect();
1576
+ });
1577
+ const dayEntries = dayKey => items().filter(item => item.days.has(dayKey));
1578
+ /** Every bar of a long entry lights up with the one under the pointer. */
1579
+ const linkProps = item => ({
1580
+ onPointerEnter: event => {
1581
+ if (event.pointerType === "mouse") setLinkedItem(item.key);
1582
+ },
1583
+ onPointerLeave: () => {
1584
+ if (linkedItem() === item.key) setLinkedItem("");
1585
+ }
1586
+ });
1587
+ const grid = () => _$ssr(_tmpl$27, _$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, {
1588
+ get when() {
1589
+ return props.owner.withWeekNumbers;
1590
+ },
1591
+ get children() {
1592
+ return _$ssr(_tmpl$26, _$escape(messages().weekShort));
1593
+ }
1594
+ })), _$escape(_$createComponent(For, {
1595
+ get each() {
1596
+ return weekdays();
1597
+ },
1598
+ children: (day, index) => _$ssr(_tmpl$28, _$ssrAttribute("data-weekend", index() > 4 ? "true" : _$escape(undefined, true), false), _$escape(day))
1599
+ })), _$escape(_$createComponent(For, {
1600
+ get each() {
1601
+ return weeks();
1602
+ },
1603
+ children: (week, weekIndex) => {
1604
+ const fit = createMemo(() => fitMonthWeek(weekLayouts()[weekIndex()] ?? {
1605
+ bars: [],
1606
+ singles: []
1607
+ }, capacities()[weekIndex()] ?? MONTH_DEFAULT_LANES));
1608
+ const weekHref = () => viewHref(week[0].key, "week");
1609
+ const weekNumber = () => zonedWeekNumber(week[0].day, dateConfig());
1610
+ return _$ssr(_tmpl$30, _$ssrAttribute("data-week-numbers", props.owner.withWeekNumbers ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1611
+ get when() {
1612
+ return props.owner.withWeekNumbers;
1613
+ },
1614
+ get children() {
1615
+ return _$ssr(_tmpl$29, _$escape(_$createComponent(Show, {
1616
+ get when() {
1617
+ return weekHref();
1618
+ },
1619
+ get fallback() {
1620
+ return _$createComponent(Show, {
1621
+ get when() {
1622
+ return canOpenView("week");
1623
+ },
1624
+ get fallback() {
1625
+ return weekNumber();
1626
+ },
1627
+ get children() {
1628
+ return _$ssr(_tmpl$31, _$ssrAttribute("title", _$escape(calendarMessages().calendarOpenWeekNumber({
1629
+ week: weekNumber()
1630
+ }), true), false) + _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarOpenWeekNumber({
1631
+ week: weekNumber()
1632
+ }), true), false), _$escape(weekNumber()));
1633
+ }
1634
+ });
1635
+ },
1636
+ children: href => _$createComponent(CalendarNavigationLink, {
1637
+ get owner() {
1638
+ return props.owner;
1639
+ },
1640
+ get href() {
1641
+ return href();
1642
+ },
1643
+ get anchorProps() {
1644
+ return {
1645
+ class: "k2b-calendar-month__week-link",
1646
+ tabIndex: -1,
1647
+ title: calendarMessages().calendarOpenWeekNumber({
1648
+ week: weekNumber()
1649
+ }),
1650
+ "aria-label": calendarMessages().calendarOpenWeekNumber({
1651
+ week: weekNumber()
1652
+ })
1653
+ };
1654
+ },
1655
+ get children() {
1656
+ return weekNumber();
1657
+ }
1658
+ })
1659
+ })));
1660
+ }
1661
+ })), _$escape(_$createComponent(For, {
1662
+ each: week,
1663
+ children: ({
1664
+ day,
1665
+ key
1666
+ }, column) => {
1667
+ const dayBadge = props.owner.dayBadges?.[key];
1668
+ const sameMonth = calendar.isSameMonth(day, props.date, dateConfig());
1669
+ const label = dayLabel(day);
1670
+ const bars = createMemo(() => fit().bars.filter(bar => bar.startColumn === column()));
1671
+ const cell = () => fit().cells[column()] ?? {
1672
+ top: 0,
1673
+ shown: [],
1674
+ hidden: 0
1675
+ };
1676
+ return _$ssr(_tmpl$33, _$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, {
1677
+ when: dayBadge,
1678
+ children: badge => _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
1679
+ get when() {
1680
+ return badge().icon;
1681
+ },
1682
+ children: icon => _$ssr(_tmpl$10, `k2b-calendar-month__badge-icon ti ti-${_$escape(icon(), true)}`)
1683
+ })), _$escape(badge().label))
1684
+ })), _$escape(_$createComponent(For, {
1685
+ get each() {
1686
+ return bars();
1687
+ },
1688
+ children: bar => {
1689
+ const span = bar.endColumn - bar.startColumn + 1;
1690
+ const startHint = () => {
1691
+ if (!bar.continuesBefore) return undefined;
1692
+ const hint = calendarMessages().calendarContinuesFrom(hintDate(bar.item.firstKey));
1693
+ return hintFits(bar.item.event.title, hint, span) ? hint : undefined;
1694
+ };
1695
+ const endHint = () => {
1696
+ if (!bar.continuesAfter) return undefined;
1697
+ const hint = calendarMessages().calendarContinuesUntil(hintDate(bar.item.lastKey));
1698
+ return hintFits(`${bar.item.event.title}${startHint() ?? ""}`, hint, span) ? hint : undefined;
1699
+ };
1700
+ return _$ssrElement("div", _$mergeProps({
1701
+ "class": "k2b-calendar-month__segment",
1702
+ get style() {
1703
+ return {
1704
+ "--k2b-calendar-span": String(span),
1705
+ "--k2b-calendar-row": String(bar.lane)
1706
+ };
1707
+ },
1708
+ "data-bar": "true",
1709
+ get ["data-linked"]() {
1710
+ return linkedItem() === bar.item.key && bar.item.firstKey !== bar.item.lastKey ? "true" : undefined;
1711
+ },
1712
+ get ["data-continues-before"]() {
1713
+ return bar.continuesBefore ? "true" : undefined;
1714
+ },
1715
+ get ["data-continues-after"]() {
1716
+ return bar.continuesAfter ? "true" : undefined;
1717
+ }
1718
+ }, () => linkProps(bar.item)), _$escape(_$createComponent(EventChip, {
1719
+ get event() {
1720
+ return bar.item.event;
1721
+ },
1722
+ get owner() {
1723
+ return props.owner;
1724
+ },
1725
+ get href() {
1726
+ return eventHref(props.owner, bar.item.event);
1727
+ },
1728
+ get label() {
1729
+ return itemLabel(bar.item);
1730
+ },
1731
+ compact: true,
1732
+ bar: true,
1733
+ get continuesBefore() {
1734
+ return bar.continuesBefore;
1735
+ },
1736
+ get continuesAfter() {
1737
+ return bar.continuesAfter;
1738
+ },
1739
+ get hintStart() {
1740
+ return startHint();
1741
+ },
1742
+ get hintEnd() {
1743
+ return endHint();
1744
+ },
1745
+ get moving() {
1746
+ return movingEventId() === bar.item.event.id;
1747
+ },
1748
+ get onMovePointerDown() {
1749
+ return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, bar.item, markDragged) : undefined;
1750
+ }
1751
+ })), false);
1752
+ }
1753
+ })), _$escape(_$createComponent(For, {
1754
+ get each() {
1755
+ return cell().shown;
1756
+ },
1757
+ children: (item, index) => _$ssr(_tmpl$34, _$ssrStyleProperty("--k2b-calendar-span:", "1") + _$ssrStyleProperty(";--k2b-calendar-row:", _$escape(String(cell().top + index()), true)), _$escape(_$createComponent(EventChip, {
1758
+ get event() {
1759
+ return item.event;
1760
+ },
1761
+ get owner() {
1762
+ return props.owner;
1763
+ },
1764
+ get href() {
1765
+ return eventHref(props.owner, item.event);
1766
+ },
1767
+ compact: true,
1768
+ get leadingTime() {
1769
+ return leadingTime(item);
1770
+ },
1771
+ get moving() {
1772
+ return movingEventId() === item.event.id;
1773
+ },
1774
+ get onMovePointerDown() {
1775
+ return props.owner.onEventDrop ? (pointerEvent, markDragged) => startMove(pointerEvent, item, markDragged) : undefined;
1776
+ }
1777
+ })))
1778
+ })), _$escape(_$createComponent(Show, {
1779
+ get when() {
1780
+ return cell().hidden > 0;
1781
+ },
1782
+ get children() {
1783
+ return _$ssr(_tmpl$32, _$ssrStyleProperty("--k2b-calendar-row:", _$escape(String(cell().top + cell().shown.length), true)), _$ssrAttribute("aria-label", _$escape(calendarMessages().calendarMoreEventsOn({
1784
+ count: cell().hidden,
1785
+ label
1786
+ }), true), false), _$ssrAttribute("aria-expanded", (() => {
1787
+ const current = popover();
1788
+ return current?.kind === "day" && _$escape(current.dayKey, true) === _$escape(key, true);
1789
+ })(), false), _$escape(calendarMessages().calendarMoreEvents({
1790
+ count: cell().hidden
1791
+ })), _$escape(cell().hidden));
1792
+ }
1793
+ })));
1794
+ }
1795
+ })));
1796
+ }
1797
+ })));
1798
+ const dayList = dayKey => {
1799
+ const day = parseKey(dayKey);
1800
+ const href = viewHref(dayKey, "day");
1801
+ // Read where it renders, so a refresh while the list is open updates it as it does the grid.
1802
+ const entries = () => dayEntries(dayKey);
1803
+ return _$ssr(_tmpl$37, _$escape(dayLabel(day)), _$escape(_$createComponent(Show, {
1804
+ get when() {
1805
+ return entries().length > 0;
1806
+ },
1807
+ get fallback() {
1808
+ return _$ssr(_tmpl$38, _$escape(calendarMessages().calendarNothingPlanned));
1809
+ },
1810
+ get children() {
1811
+ return _$ssr(_tmpl$35, _$escape(_$createComponent(For, {
1812
+ get each() {
1813
+ return entries();
1814
+ },
1815
+ children: item => _$ssr(_tmpl$39, _$escape(_$createComponent(EventChip, {
1816
+ get event() {
1817
+ return item.event;
1818
+ },
1819
+ get owner() {
1820
+ return props.owner;
1821
+ },
1822
+ get href() {
1823
+ return eventHref(props.owner, item.event);
1824
+ },
1825
+ get label() {
1826
+ return itemLabel(item);
1827
+ },
1828
+ compact: true,
1829
+ get bar() {
1830
+ return item.bar;
1831
+ },
1832
+ get leadingTime() {
1833
+ return leadingTime(item);
1834
+ }
1835
+ })))
1836
+ })));
1837
+ }
1838
+ })), _$escape(_$createComponent(Show, {
1839
+ when: href,
1840
+ get fallback() {
1841
+ return _$createComponent(Show, {
1842
+ get when() {
1843
+ return canOpenView("day");
1844
+ },
1845
+ get children() {
1846
+ return _$createComponent(Button, {
1847
+ type: "button",
1848
+ variant: "ghost",
1849
+ size: "sm",
1850
+ onClick: () => openView(dayKey, "day"),
1851
+ get children() {
1852
+ return [_$ssr(_tmpl$40), calendarMessages().calendarOpenDay];
1853
+ }
1854
+ });
1855
+ }
1856
+ });
1857
+ },
1858
+ children: dayHref => _$createComponent(CalendarNavigationLink, {
1859
+ get owner() {
1860
+ return props.owner;
1861
+ },
1862
+ get href() {
1863
+ return dayHref();
1864
+ },
1865
+ anchorProps: {
1866
+ class: "k2b-button",
1867
+ "data-variant": "ghost",
1868
+ "data-size": "sm"
1869
+ },
1870
+ get children() {
1871
+ return [_$ssr(_tmpl$40), calendarMessages().calendarOpenDay];
1872
+ }
1873
+ })
1874
+ })), _$escape(_$createComponent(Show, {
1875
+ get when() {
1876
+ return props.owner.renderQuickCreate;
1877
+ },
1878
+ get children() {
1879
+ return _$createComponent(Button, {
1880
+ type: "button",
1881
+ variant: "ghost",
1882
+ size: "sm",
1883
+ onClick: () => {
1884
+ select(dayKey);
1885
+ openCreate({
1886
+ first: dayKey,
1887
+ last: dayKey
1888
+ }, {
1889
+ active: true
1890
+ });
1891
+ },
1892
+ get children() {
1893
+ return [_$ssr(_tmpl$36), calendarMessages().calendarNewEvent];
1894
+ }
1895
+ });
1896
+ }
1897
+ })));
1898
+ };
1899
+ return [_$createComponent(Show, {
1900
+ get when() {
1901
+ return hasMenu();
1902
+ },
1903
+ get fallback() {
1904
+ return grid();
1905
+ },
1906
+ get children() {
1907
+ return _$createComponent(GestureMenu, {
1908
+ get items() {
1909
+ return menuItems();
1910
+ },
1911
+ get label() {
1912
+ return calendarMessages().calendarSelectionActions;
1913
+ },
1914
+ tabIndex: -1,
1915
+ onOpen: onMenuOpen,
1916
+ "class": "k2b-calendar-month-menu",
1917
+ get children() {
1918
+ return grid();
1919
+ }
1920
+ });
1921
+ }
1922
+ }), _$ssr(_tmpl$41, _$ssrAttribute("aria-label", (() => {
1923
+ const current = popover();
1924
+ if (current?.kind === "day") return dayLabel(parseKey(current.dayKey));
1925
+ return _$escape(calendarMessages().calendarQuickCreate, true);
1926
+ })(), false) + _$ssrAttribute("data-kind", _$escape(popover()?.kind, true), false) + _$ssrAttribute("data-quiet", popover()?.kind === "create" && !createActive() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1927
+ get when() {
1928
+ return popover();
1929
+ },
1930
+ keyed: true,
1931
+ children: value => value.kind === "create" ? props.owner.renderQuickCreate?.(rangeOf(value.range), {
1932
+ create: value.create,
1933
+ close: () => {
1934
+ if (popover() === value) closePopover();
1935
+ }
1936
+ }) : dayList(value.dayKey)
1937
+ })))];
1938
+ };
827
1939
  const TimeGridView = props => {
828
1940
  const messages = useUiMessages();
829
1941
  const dateConfig = createMemo(() => ownerDateConfig(props.owner));
@@ -1035,7 +2147,7 @@ const TimeGridView = props => {
1035
2147
  });
1036
2148
  });
1037
2149
  });
1038
- return _$ssr(_tmpl$22, _$ssrStyleProperty("grid-template-columns:", `4rem repeat(${_$escape(props.days.length, true)}, minmax(0, 1fr))`), _$escape(_$createComponent(For, {
2150
+ return _$ssr(_tmpl$43, _$ssrStyleProperty("grid-template-columns:", `4rem repeat(${_$escape(props.days.length, true)}, minmax(0, 1fr))`), _$escape(_$createComponent(For, {
1039
2151
  get each() {
1040
2152
  return props.days;
1041
2153
  },
@@ -1053,13 +2165,13 @@ const TimeGridView = props => {
1053
2165
  class: "k2b-calendar-time-grid__day"
1054
2166
  },
1055
2167
  get children() {
1056
- return [_$ssr(_tmpl$23, _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false), _$escape(formatDay(day, dateConfig()))), _$createComponent(Show, {
2168
+ return [_$ssr(_tmpl$44, _$ssrAttribute("data-today", today() ? "true" : _$escape(undefined, true), false), _$escape(formatDay(day, dateConfig()))), _$createComponent(Show, {
1057
2169
  when: dayBadge,
1058
- children: badge => _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
2170
+ children: badge => _$ssr(_tmpl$45, _$escape(_$createComponent(Show, {
1059
2171
  get when() {
1060
2172
  return badge().icon;
1061
2173
  },
1062
- children: icon => _$ssr(_tmpl$9, `k2b-calendar-time-grid__badge-icon ti ti-${_$escape(icon(), true)}`)
2174
+ children: icon => _$ssr(_tmpl$10, `k2b-calendar-time-grid__badge-icon ti ti-${_$escape(icon(), true)}`)
1063
2175
  })), _$escape(badge().label))
1064
2176
  })];
1065
2177
  }
@@ -1070,7 +2182,7 @@ const TimeGridView = props => {
1070
2182
  return !props.owner.hideAllDay;
1071
2183
  },
1072
2184
  get children() {
1073
- return _$ssr(_tmpl$21, _$ssrStyleProperty("grid-template-columns:", `4rem repeat(${_$escape(props.days.length, true)}, minmax(0, 1fr))`) + _$ssrStyleProperty(";max-height:", `${_$escape(props.owner.allDayMaxHeightRem ?? 7, true)}rem`), _$escape(props.labels.allDay), _$escape(_$createComponent(For, {
2185
+ return _$ssr(_tmpl$42, _$ssrStyleProperty("grid-template-columns:", `4rem repeat(${_$escape(props.days.length, true)}, minmax(0, 1fr))`) + _$ssrStyleProperty(";max-height:", `${_$escape(props.owner.allDayMaxHeightRem ?? 7, true)}rem`), _$escape(props.labels.allDay), _$escape(_$createComponent(For, {
1074
2186
  get each() {
1075
2187
  return props.days;
1076
2188
  },
@@ -1094,9 +2206,9 @@ const TimeGridView = props => {
1094
2206
  end: calendar.addDays(start, 1, dateConfig()),
1095
2207
  allDay: true
1096
2208
  };
1097
- }, () => performance.now() < suppressSlotClickUntil)), _$ssr(_tmpl$25, _$escape(_$createComponent(For, {
2209
+ }, () => performance.now() < suppressSlotClickUntil)), _$ssr(_tmpl$46, _$escape(_$createComponent(For, {
1098
2210
  each: previewAllDay,
1099
- children: event => _$ssr(_tmpl$16, _$escape(event.title))
2211
+ children: event => _$ssr(_tmpl$21, _$escape(event.title))
1100
2212
  })), _$escape(_$createComponent(For, {
1101
2213
  get each() {
1102
2214
  return allDay();
@@ -1125,7 +2237,7 @@ const TimeGridView = props => {
1125
2237
  get each() {
1126
2238
  return hours();
1127
2239
  },
1128
- children: hour => _$ssr(_tmpl$26, _$ssrAttribute("data-outside-business", hour < businessStartHour() || hour > businessEndHour() ? "true" : _$escape(undefined, true), false), _$escape(`${hour}`.padStart(2, "0")))
2240
+ children: hour => _$ssr(_tmpl$47, _$ssrAttribute("data-outside-business", hour < businessStartHour() || hour > businessEndHour() ? "true" : _$escape(undefined, true), false), _$escape(`${hour}`.padStart(2, "0")))
1129
2241
  })), _$escape(_$createComponent(For, {
1130
2242
  get each() {
1131
2243
  return props.days;
@@ -1133,12 +2245,12 @@ const TimeGridView = props => {
1133
2245
  children: day => {
1134
2246
  const dayKey = calendar.formatDateKey(day, dateConfig());
1135
2247
  const layouts = createMemo(() => timedEventLayouts(eventsByDay().get(dayKey)?.timed ?? []));
1136
- return _$ssr(_tmpl$28, _$ssrStyleProperty("min-width:", _$escape(dayColumnMinWidth(layouts()), true)) + _$ssrStyleProperty(";grid-template-rows:", `repeat(${_$escape(hours().length, true)}, minmax(4rem, 1fr))`), _$escape(_$createComponent(Show, {
2248
+ return _$ssr(_tmpl$49, _$ssrStyleProperty("min-width:", _$escape(dayColumnMinWidth(layouts()), true)) + _$ssrStyleProperty(";grid-template-rows:", `repeat(${_$escape(hours().length, true)}, minmax(4rem, 1fr))`), _$escape(_$createComponent(Show, {
1137
2249
  get when() {
1138
2250
  return currentTimeLine(day) !== null;
1139
2251
  },
1140
2252
  get children() {
1141
- return _$ssr(_tmpl$27, _$ssrStyleProperty("top:", `${_$escape(currentTimeLine(day) ?? 0, true)}%`));
2253
+ return _$ssr(_tmpl$48, _$ssrStyleProperty("top:", `${_$escape(currentTimeLine(day) ?? 0, true)}%`));
1142
2254
  }
1143
2255
  })), _$escape(_$createComponent(For, {
1144
2256
  get each() {
@@ -1167,12 +2279,12 @@ const TimeGridView = props => {
1167
2279
  children: event => {
1168
2280
  const layout = eventLayout(event);
1169
2281
  const short = event.endDate.getTime() - event.startDate.getTime() < 60 * 60_000;
1170
- return _$ssr(_tmpl$30, _$ssrAttribute("data-short", short ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$escape(_$createComponent(Show, {
2282
+ return _$ssr(_tmpl$51, _$ssrAttribute("data-short", short ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$escape(_$createComponent(Show, {
1171
2283
  get when() {
1172
2284
  return event.title;
1173
2285
  },
1174
2286
  get children() {
1175
- return _$ssr(_tmpl$29, _$escape(event.title));
2287
+ return _$ssr(_tmpl$50, _$escape(event.title));
1176
2288
  }
1177
2289
  })), _$escape(formatTime(event.startDate, dateConfig())), _$escape(formatTime(event.endDate, dateConfig())));
1178
2290
  }
@@ -1218,7 +2330,7 @@ const TimeGridView = props => {
1218
2330
  onCancel: clearInteraction
1219
2331
  });
1220
2332
  };
1221
- return _$ssr(_tmpl$32, _$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, {
2333
+ return _$ssr(_tmpl$53, _$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, {
1222
2334
  event: event,
1223
2335
  get owner() {
1224
2336
  return props.owner;
@@ -1238,7 +2350,7 @@ const TimeGridView = props => {
1238
2350
  return props.owner.onEventResize;
1239
2351
  },
1240
2352
  get children() {
1241
- return _$ssr(_tmpl$31, _$ssrAttribute("aria-label", _$escape(messages().resizeEvent, true), false));
2353
+ return _$ssr(_tmpl$52, _$ssrAttribute("aria-label", _$escape(messages().resizeEvent, true), false));
1242
2354
  }
1243
2355
  })));
1244
2356
  }
@@ -1250,14 +2362,14 @@ const TimeGridView = props => {
1250
2362
  const layout = timeRangeLayout(overflow.groupStartDate, overflow.groupEndDate);
1251
2363
  const key = `${dayKey}-${overflow.groupId}`;
1252
2364
  const hiddenTitle = () => overflow.hiddenEvents.map(event => `${formatTime(event.startDate, dateConfig())} ${event.title}`).join("\n");
1253
- return [_$ssr(_tmpl$33, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$ssrAttribute("title", _$escape(hiddenTitle(), true), false) + _$ssrAttribute("aria-label", _$escape(messages().hiddenOverlappingEvents({
2365
+ return [_$ssr(_tmpl$54, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`) + _$ssrStyleProperty(";height:", `${_$escape(layout.height, true)}%`), _$ssrAttribute("title", _$escape(hiddenTitle(), true), false) + _$ssrAttribute("aria-label", _$escape(messages().hiddenOverlappingEvents({
1254
2366
  count: overflow.hiddenEvents.length
1255
2367
  }), true), false), _$escape(overflow.hiddenEvents.length)), _$createComponent(Show, {
1256
2368
  get when() {
1257
2369
  return expandedOverflow() === key;
1258
2370
  },
1259
2371
  get children() {
1260
- return _$ssr(_tmpl$34, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`), _$escape(formatTime(overflow.groupStartDate, dateConfig())), _$escape(formatTime(overflow.groupEndDate, dateConfig())), _$escape(_$createComponent(For, {
2372
+ return _$ssr(_tmpl$55, _$ssrStyleProperty("top:", `${_$escape(layout.top, true)}%`), _$escape(formatTime(overflow.groupStartDate, dateConfig())), _$escape(formatTime(overflow.groupEndDate, dateConfig())), _$escape(_$createComponent(For, {
1261
2373
  get each() {
1262
2374
  return overflow.hiddenEvents;
1263
2375
  },
@@ -1316,11 +2428,11 @@ const YearView = props => {
1316
2428
  };
1317
2429
  });
1318
2430
  });
1319
- return _$ssr(_tmpl$35, _$escape(_$createComponent(For, {
2431
+ return _$ssr(_tmpl$56, _$escape(_$createComponent(For, {
1320
2432
  get each() {
1321
2433
  return months();
1322
2434
  },
1323
- children: month => _$ssr(_tmpl$36, _$escape(month.label), _$escape(_$createComponent(For, {
2435
+ children: month => _$ssr(_tmpl$57, _$escape(month.label), _$escape(_$createComponent(For, {
1324
2436
  get each() {
1325
2437
  return month.days;
1326
2438
  },
@@ -1343,7 +2455,7 @@ const YearView = props => {
1343
2455
  get when() {
1344
2456
  return day.events[0];
1345
2457
  },
1346
- children: event => _$ssr(_tmpl$37, _$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 ? {
2458
+ children: event => _$ssr(_tmpl$58, _$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 ? {
1347
2459
  "background-color": day.isToday ? "white" : event().colorHex
1348
2460
  } : undefined))
1349
2461
  })];
@@ -1356,7 +2468,7 @@ const MobileMonthView = props => {
1356
2468
  const dateConfig = createMemo(() => ownerDateConfig(props.owner));
1357
2469
  const selectedDateKey = createMemo(() => calendar.formatDateKey(props.selectedDate, dateConfig()));
1358
2470
  const selectedEvents = createMemo(() => props.events.filter(event => event.dayKey === selectedDateKey()));
1359
- return _$ssr(_tmpl$39, _$escape(_$createComponent(MonthView, {
2471
+ return _$ssr(_tmpl$60, _$escape(_$createComponent(MonthPickerView, {
1360
2472
  get owner() {
1361
2473
  return {
1362
2474
  ...props.owner,
@@ -1386,10 +2498,10 @@ const MobileMonthView = props => {
1386
2498
  return selectedEvents().length > 0;
1387
2499
  },
1388
2500
  get fallback() {
1389
- return _$ssr(_tmpl$40, _$escape(props.labels.noEvents));
2501
+ return _$ssr(_tmpl$61, _$escape(props.labels.noEvents));
1390
2502
  },
1391
2503
  get children() {
1392
- return _$ssr(_tmpl$38, _$escape(_$createComponent(For, {
2504
+ return _$ssr(_tmpl$59, _$escape(_$createComponent(For, {
1393
2505
  get each() {
1394
2506
  return selectedEvents();
1395
2507
  },
@@ -1406,7 +2518,7 @@ const MobileMonthView = props => {
1406
2518
  }
1407
2519
  })));
1408
2520
  };
1409
- const CalendarBody = props => _$ssr(_tmpl$41, _$escape(props.children));
2521
+ const CalendarBody = props => _$ssr(_tmpl$62, _$escape(props.children));
1410
2522
  const CalendarRoot = props => {
1411
2523
  const messages = useUiMessages();
1412
2524
  // Subviews derive their date config from the owner props, so the inherited
@@ -1427,6 +2539,8 @@ const CalendarRoot = props => {
1427
2539
  const date = createMemo(() => safeDate(props.date));
1428
2540
  const selectedDate = createMemo(() => safeDate(props.selectedDate ?? props.date));
1429
2541
  const [now, setNow] = createSignal(new Date());
2542
+ /** The first day selected in the month view, so the view switcher opens the other views at it. */
2543
+ const [focusDay, setFocusDay] = createSignal(null);
1430
2544
  const normalizedEvents = createMemo(() => normalizeEvents(props.events, dateConfig()));
1431
2545
  const mergedLabels = createMemo(() => ({
1432
2546
  today: messages().today,
@@ -1453,7 +2567,7 @@ const CalendarRoot = props => {
1453
2567
  document.removeEventListener("visibilitychange", updateNow);
1454
2568
  });
1455
2569
  });
1456
- return _$ssr(_tmpl$42, `k2b-content-calendar ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-busy", props.navigationPending ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(CalendarHeader, {
2570
+ return _$ssr(_tmpl$63, `k2b-content-calendar ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-busy", props.navigationPending ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(CalendarHeader, {
1457
2571
  get date() {
1458
2572
  return date();
1459
2573
  },
@@ -1463,8 +2577,11 @@ const CalendarRoot = props => {
1463
2577
  get labels() {
1464
2578
  return mergedLabels();
1465
2579
  },
1466
- owner: owner
1467
- })), _$escape(props.toolbarContent), _$escape(_$createComponent(Show, {
2580
+ owner: owner,
2581
+ get focusDay() {
2582
+ return _$memo(() => view() === "month")() ? focusDay() : null;
2583
+ }
2584
+ })), _$escape(_$createComponent(Show, {
1468
2585
  get when() {
1469
2586
  return !custom();
1470
2587
  },
@@ -1496,7 +2613,8 @@ const CalendarRoot = props => {
1496
2613
  },
1497
2614
  get labels() {
1498
2615
  return mergedLabels();
1499
- }
2616
+ },
2617
+ onFocusDay: setFocusDay
1500
2618
  });
1501
2619
  }
1502
2620
  });