@nomideusz/svelte-calendar 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.
package/README.md CHANGED
@@ -34,7 +34,9 @@ That's it — 6 views (Day/Week × Planner, Agenda, Mobile), auto-coloring, drag
34
34
  ## Views
35
35
 
36
36
  Switch between **Planner** (time grid), **Scroll** (weeks stacked in one
37
- vertical scroller — drag to move, drop onto a day, scrolls under a drag),
37
+ vertical scroller, Hey-Calendar style — each class at the height of its
38
+ start time, month names down the side; drag to move, drop onto a day,
39
+ scrolls under a drag),
38
40
  **Agenda** (list) and the **Month** grid:
39
41
 
40
42
  ```svelte
@@ -1,4 +1,4 @@
1
- import { startOfWeek, DAY_MS } from '../core/time.js';
1
+ import { startOfWeek, addDaysMs, diffDays, DAY_MS } from '../core/time.js';
2
2
  import { VIVID_PALETTE } from '../core/palette.js';
3
3
  // ── Helpers ─────────────────────────────────────────────
4
4
  /** Parse "HH:MM" into [hours, minutes] */
@@ -100,14 +100,14 @@ function computeUntilFromCount(rec, startDateObj, mondayStart) {
100
100
  const maxCycles = Math.ceil(count / days.length) + 2;
101
101
  for (let c = 0; c < maxCycles; c++) {
102
102
  for (const day of days) {
103
- const dayMs = weekMs + isoWeekdayToOffset(day) * DAY_MS;
103
+ const dayMs = addDaysMs(weekMs, isoWeekdayToOffset(day));
104
104
  if (dayMs >= startMs) {
105
105
  remaining--;
106
106
  if (remaining === 0)
107
107
  return new Date(dayMs);
108
108
  }
109
109
  }
110
- weekMs += interval * 7 * DAY_MS;
110
+ weekMs = addDaysMs(weekMs, interval * 7);
111
111
  }
112
112
  return undefined;
113
113
  }
@@ -165,16 +165,16 @@ function projectWeekly(rec, range, startDate, effectiveUntil, mondayStart, out)
165
165
  weekMs = anchorWeekMs;
166
166
  }
167
167
  else {
168
- const weeksBetween = Math.floor((rangeStartMs - anchorWeekMs) / (7 * DAY_MS));
168
+ const weeksBetween = Math.floor(diffDays(rangeStartMs, anchorWeekMs) / 7);
169
169
  const skip = Math.floor(weeksBetween / interval);
170
- weekMs = anchorWeekMs + skip * interval * 7 * DAY_MS;
171
- if (weekMs + 7 * DAY_MS <= rangeStartMs) {
172
- weekMs += interval * 7 * DAY_MS;
170
+ weekMs = addDaysMs(anchorWeekMs, skip * interval * 7);
171
+ if (addDaysMs(weekMs, 7) <= rangeStartMs) {
172
+ weekMs = addDaysMs(weekMs, interval * 7);
173
173
  }
174
174
  }
175
175
  while (weekMs < rangeEndMs) {
176
176
  for (const day of days) {
177
- const dayMs = weekMs + isoWeekdayToOffset(day) * DAY_MS;
177
+ const dayMs = addDaysMs(weekMs, isoWeekdayToOffset(day));
178
178
  if (startDateMs != null && dayMs < startDateMs)
179
179
  continue;
180
180
  if (dayMs >= rangeEndMs)
@@ -187,7 +187,7 @@ function projectWeekly(rec, range, startDate, effectiveUntil, mondayStart, out)
187
187
  out.push(ev);
188
188
  }
189
189
  }
190
- weekMs += interval * 7 * DAY_MS;
190
+ weekMs = addDaysMs(weekMs, interval * 7);
191
191
  }
192
192
  }
193
193
  function projectMonthly(rec, range, startDate, effectiveUntil, out) {
@@ -7,7 +7,7 @@
7
7
  * For date-fns locale integration later, this module can import from
8
8
  * `date-fns/locale/*` and pass to `format()`.
9
9
  */
10
- import { DAY_MS } from './time.js';
10
+ import { addDaysMs } from './time.js';
11
11
  /** English defaults — used unless overridden via `setLabels()`. */
12
12
  export const defaultLabels = {
13
13
  today: 'Today',
@@ -155,9 +155,9 @@ export function fmtDay(ms, todayMs, opts, locale) {
155
155
  const L = _labels;
156
156
  if (ms === todayMs)
157
157
  return opts?.short ? L.today : `${L.today} · ${short}`;
158
- if (ms === todayMs - DAY_MS)
158
+ if (ms === addDaysMs(todayMs, -1))
159
159
  return opts?.short ? L.yesterday : `${L.yesterday} · ${short}`;
160
- if (ms === todayMs + DAY_MS)
160
+ if (ms === addDaysMs(todayMs, 1))
161
161
  return opts?.short ? L.tomorrow : `${L.tomorrow} · ${short}`;
162
162
  if (opts?.short) {
163
163
  return new Date(ms).toLocaleDateString(loc, { weekday: 'short', day: 'numeric' });
@@ -170,7 +170,7 @@ export function fmtDay(ms, todayMs, opts, locale) {
170
170
  export function fmtWeekRange(weekStartMs, locale, weekEndMs) {
171
171
  const loc = locale ?? defaultLocale;
172
172
  const s = new Date(weekStartMs);
173
- const e = new Date(weekEndMs ?? weekStartMs + 6 * DAY_MS);
173
+ const e = new Date(weekEndMs ?? addDaysMs(weekStartMs, 6));
174
174
  const sm = s.toLocaleDateString(loc, { month: 'short' });
175
175
  const em = e.toLocaleDateString(loc, { month: 'short' });
176
176
  const sy = s.getFullYear();
package/dist/core/time.js CHANGED
@@ -74,8 +74,7 @@ export function isMultiDay(ev) {
74
74
  export function isAllDay(ev) {
75
75
  if (ev.allDay)
76
76
  return true;
77
- const duration = ev.end.getTime() - ev.start.getTime();
78
- if (duration < DAY_MS)
77
+ if (ev.end.getTime() < addDaysMs(ev.start.getTime(), 1))
79
78
  return false;
80
79
  const s = ev.start;
81
80
  return s.getHours() === 0 && s.getMinutes() === 0 && s.getSeconds() === 0;
@@ -86,7 +85,7 @@ export function isAllDay(ev) {
86
85
  */
87
86
  export function segmentForDay(ev, dayMs) {
88
87
  const dayStart = sod(dayMs);
89
- const dayEnd = dayStart + DAY_MS;
88
+ const dayEnd = addDaysMs(dayStart, 1);
90
89
  const evStart = ev.start.getTime();
91
90
  const evEnd = ev.end.getTime();
92
91
  // No overlap
@@ -95,8 +94,8 @@ export function segmentForDay(ev, dayMs) {
95
94
  const firstDayMs = sod(evStart);
96
95
  // For end time: if event ends exactly at midnight, last day is the day before
97
96
  const lastDayMs = sod(evEnd - 1);
98
- const totalDays = Math.floor((lastDayMs - firstDayMs) / DAY_MS) + 1;
99
- const dayIndex = Math.floor((dayStart - firstDayMs) / DAY_MS) + 1;
97
+ const totalDays = diffDays(lastDayMs, firstDayMs) + 1;
98
+ const dayIndex = diffDays(dayStart, firstDayMs) + 1;
100
99
  return {
101
100
  ev,
102
101
  start: new Date(Math.max(evStart, dayStart)),
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { SvelteMap } from 'svelte/reactivity';
19
19
  import { untrack } from 'svelte';
20
- import { sod, DAY_MS } from '../core/time.js';
20
+ import { sod, addDaysMs } from '../core/time.js';
21
21
  /**
22
22
  * Create a reactive event store backed by a CalendarAdapter.
23
23
  *
@@ -103,7 +103,7 @@ export function createEventStore(adapter) {
103
103
  },
104
104
  forDay(date) {
105
105
  const dayStart = new Date(sod(date.getTime()));
106
- const dayEnd = new Date(dayStart.getTime() + DAY_MS);
106
+ const dayEnd = new Date(addDaysMs(dayStart.getTime(), 1));
107
107
  return eventArray.filter((ev) => overlaps(ev, dayStart, dayEnd));
108
108
  },
109
109
  byId(id) {
@@ -11,7 +11,7 @@
11
11
  * vs.prev() — go back one period
12
12
  * vs.goToday() — jump to today
13
13
  */
14
- import { startOfWeek as calcStartOfWeek, addDaysMs, DAY_MS } from '../core/time.js';
14
+ import { startOfWeek as calcStartOfWeek, addDaysMs } from '../core/time.js';
15
15
  function inferMode(view) {
16
16
  if (view.startsWith('day'))
17
17
  return 'day';
@@ -23,7 +23,7 @@ function computeRange(focus, mode, mondayStart, dayCount = 7) {
23
23
  if (mode === 'day') {
24
24
  const start = new Date(focus);
25
25
  start.setHours(0, 0, 0, 0);
26
- const end = new Date(start.getTime() + DAY_MS);
26
+ const end = new Date(addDaysMs(start.getTime(), 1));
27
27
  return { start, end };
28
28
  }
29
29
  if (mode === 'month') {
@@ -48,7 +48,7 @@ function computeRange(focus, mode, mondayStart, dayCount = 7) {
48
48
  start.setHours(0, 0, 0, 0);
49
49
  return {
50
50
  start,
51
- end: new Date(start.getTime() + dayCount * DAY_MS),
51
+ end: new Date(addDaysMs(start.getTime(), dayCount)),
52
52
  };
53
53
  }
54
54
  export function createViewState(options = {}) {
@@ -33,7 +33,7 @@
33
33
  import { untrack } from 'svelte';
34
34
  import { createEventStore } from '../engine/event-store.svelte.js';
35
35
  import { createClock } from '../core/clock.svelte.js';
36
- import { sod, DAY_MS, isAllDay, isMultiDay } from '../core/time.js';
36
+ import { sod, addDaysMs, isAllDay, isMultiDay } from '../core/time.js';
37
37
  import { fmtTime as _fmtTime, fmtDuration } from '../core/locale.js';
38
38
  import { timeUntilMs, progress as _progress, groupIntoSlots } from '../views/shared/format.js';
39
39
  // ─── Implementation ─────────────────────────────────────
@@ -49,17 +49,17 @@ export function createAgenda(options) {
49
49
  // ── Load events for focus date range ──
50
50
  $effect(() => {
51
51
  const start = new Date(focusDayMs);
52
- const end = new Date(focusDayMs + lookahead * DAY_MS);
52
+ const end = new Date(addDaysMs(focusDayMs, lookahead));
53
53
  store.load({ start, end });
54
54
  });
55
55
  // Eager initial load
56
56
  untrack(() => {
57
57
  const start = new Date(focusDayMs);
58
- const end = new Date(focusDayMs + lookahead * DAY_MS);
58
+ const end = new Date(addDaysMs(focusDayMs, lookahead));
59
59
  store.load({ start, end });
60
60
  });
61
61
  // ── Day derivations ──
62
- const dayEnd = $derived(focusDayMs + DAY_MS);
62
+ const dayEnd = $derived(addDaysMs(focusDayMs, 1));
63
63
  const isToday = $derived(focusDayMs === clock.today);
64
64
  const isPast = $derived(focusDayMs < clock.today);
65
65
  const isFuture = $derived(focusDayMs > clock.today);
@@ -93,8 +93,8 @@ export function createAgenda(options) {
93
93
  return { past, current, upcoming, upcomingSlots: groupIntoSlots(upcoming) };
94
94
  });
95
95
  // ── Navigation ──
96
- function prev() { focusDayMs = focusDayMs - DAY_MS; }
97
- function next() { focusDayMs = focusDayMs + DAY_MS; }
96
+ function prev() { focusDayMs = addDaysMs(focusDayMs, -1); }
97
+ function next() { focusDayMs = addDaysMs(focusDayMs, 1); }
98
98
  function goToday() { focusDayMs = clock.today; }
99
99
  function setDate(date) { focusDayMs = sod(date.getTime()); }
100
100
  // ── Format helpers ──
@@ -46,7 +46,7 @@ import { toZonedTime, fromZonedTime, wrapAdapterWithTimezone } from '../core/tim
46
46
  import { createSelection } from '../engine/selection.svelte.js';
47
47
  import { createDragState } from '../engine/drag.svelte.js';
48
48
  import { createClock } from '../core/clock.svelte.js';
49
- import { sod, DAY_MS, HOUR_MS, startOfWeek as sowFn, isAllDay, isMultiDay, segmentForDay, } from '../core/time.js';
49
+ import { sod, addDaysMs, HOUR_MS, startOfWeek as sowFn, isAllDay, isMultiDay, segmentForDay, } from '../core/time.js';
50
50
  import { monthLong, weekdayLong, weekdayShort } from '../core/locale.js';
51
51
  export function createCalendar(options) {
52
52
  const { adapter, mondayStart: initialMondayStart = true, initialDate: rawInitialDate, locale, visibleHours, snapInterval = 15, equalDays = false, hideDays, blockedSlots, disabledDates, days: initialDayCount = 7, readOnly = false, minDuration, maxDuration, oneventclick, oneventcreate, oneventmove, } = options;
@@ -85,14 +85,14 @@ export function createCalendar(options) {
85
85
  const events = store.events;
86
86
  const todayMs = clock.today;
87
87
  const result = [];
88
- for (let ms = startMs; ms < endMs; ms += DAY_MS) {
88
+ for (let ms = startMs; ms < endMs; ms = addDaysMs(ms, 1)) {
89
89
  const date = new Date(ms);
90
90
  const jsDay = date.getDay();
91
91
  const isoDay = jsDay === 0 ? 7 : jsDay;
92
92
  // Skip hidden days
93
93
  if (hideDays?.includes(isoDay))
94
94
  continue;
95
- const dayEnd = ms + DAY_MS;
95
+ const dayEnd = addDaysMs(ms, 1);
96
96
  const dayEventsAll = events
97
97
  .filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > ms)
98
98
  .sort((a, b) => a.start.getTime() - b.start.getTime());
@@ -134,7 +134,7 @@ export function createCalendar(options) {
134
134
  if (chunk.length === 0)
135
135
  continue;
136
136
  const periodStart = chunk[0].ms;
137
- const periodEnd = periodStart + dayCount * DAY_MS;
137
+ const periodEnd = addDaysMs(periodStart, dayCount);
138
138
  result.push({
139
139
  periodStart,
140
140
  isCurrent: todayMs >= periodStart && todayMs < periodEnd,
@@ -32,7 +32,7 @@
32
32
  */
33
33
  import { untrack } from 'svelte';
34
34
  import { createEventStore } from '../engine/event-store.svelte.js';
35
- import { sod, DAY_MS } from '../core/time.js';
35
+ import { sod, addDaysMs } from '../core/time.js';
36
36
  import { fmtTime as _fmtTime, fmtDuration } from '../core/locale.js';
37
37
  // ─── Implementation ─────────────────────────────────────
38
38
  export function createRangeAgenda(options) {
@@ -43,7 +43,7 @@ export function createRangeAgenda(options) {
43
43
  const store = $derived(createEventStore(resolveAdapter()));
44
44
  // ── Window start (reactive, writable) ──
45
45
  let startMs = $state(sod(initialDate?.getTime() ?? Date.now()));
46
- const endMs = $derived(startMs + dayCount * DAY_MS);
46
+ const endMs = $derived(addDaysMs(startMs, dayCount));
47
47
  // ── Load events for the window (re-runs on paging / adapter swap) ──
48
48
  $effect(() => {
49
49
  store.load({ start: new Date(startMs), end: new Date(endMs) });
@@ -57,8 +57,8 @@ export function createRangeAgenda(options) {
57
57
  const todayMs = sod(Date.now());
58
58
  const events = store.events;
59
59
  return Array.from({ length: dayCount }, (_, i) => {
60
- const ms = startMs + i * DAY_MS;
61
- const dayEnd = ms + DAY_MS;
60
+ const ms = addDaysMs(startMs, i);
61
+ const dayEnd = addDaysMs(ms, 1);
62
62
  const date = new Date(ms);
63
63
  // JS getDay(): 0=Sun … 6=Sat → ISO 1=Mon … 7=Sun
64
64
  const weekday = ((date.getDay() + 6) % 7) + 1;
@@ -92,10 +92,10 @@ export function createRangeAgenda(options) {
92
92
  return store.error;
93
93
  },
94
94
  prev() {
95
- startMs -= dayCount * DAY_MS;
95
+ startMs = addDaysMs(startMs, -dayCount);
96
96
  },
97
97
  next() {
98
- startMs += dayCount * DAY_MS;
98
+ startMs = addDaysMs(startMs, dayCount);
99
99
  },
100
100
  goToday() {
101
101
  startMs = sod(Date.now());
@@ -12,7 +12,7 @@
12
12
  * Clean numbered schedule list.
13
13
  */
14
14
  import { createClock } from "../../core/clock.svelte.js";
15
- import { sod, DAY_MS, dayNum, isAllDay, isMultiDay } from "../../core/time.js";
15
+ import { sod, addDaysMs, dayNum, isAllDay, isMultiDay } from "../../core/time.js";
16
16
  import { weekdayLong, monthLong } from "../../core/locale.js";
17
17
  import { useCalendarContext } from "../shared/context.svelte.js";
18
18
  import EventContent from "../shared/EventContent.svelte";
@@ -69,9 +69,9 @@ function handleClick(ev) {
69
69
  }
70
70
  // ── Day derivations ─────────────────────────────────
71
71
  const dayMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
72
- const dayEnd = $derived(dayMs + DAY_MS);
72
+ const dayEnd = $derived(addDaysMs(dayMs, 1));
73
73
  const isToday = $derived(dayMs === clock.today);
74
- const isTomorrow = $derived(dayMs === clock.today + DAY_MS);
74
+ const isTomorrow = $derived(dayMs === addDaysMs(clock.today, 1));
75
75
  const isPastDay = $derived(equalDays ? false : dayMs < clock.today);
76
76
  /** All events for this day, sorted chronologically */
77
77
  const dayEvents = $derived.by(() => {
@@ -9,7 +9,7 @@
9
9
  * Answers: "What's coming up and when do I need to be ready?"
10
10
  */
11
11
  import { createClock } from "../../core/clock.svelte.js";
12
- import { sod, DAY_MS, startOfWeek, dayNum, isAllDay, isMultiDay, segmentForDay } from "../../core/time.js";
12
+ import { sod, addDaysMs, startOfWeek, dayNum, isAllDay, isMultiDay, segmentForDay } from "../../core/time.js";
13
13
  import { weekdayLong, monthLong } from "../../core/locale.js";
14
14
  import { useCalendarContext } from "../shared/context.svelte.js";
15
15
  import EventContent from "../shared/EventContent.svelte";
@@ -115,11 +115,11 @@ const customDays = $derived(viewState?.dayCount ?? 7);
115
115
  const weekDays = $derived.by(() => {
116
116
  const now = clock.tick;
117
117
  const todayMs = clock.today;
118
- const tomorrowMs = todayMs + DAY_MS;
118
+ const tomorrowMs = addDaysMs(todayMs, 1);
119
119
  const out = [];
120
120
  for (let i = 0; i < customDays; i++) {
121
- const ms = weekStartMs + i * DAY_MS;
122
- const dEnd = ms + DAY_MS;
121
+ const ms = addDaysMs(weekStartMs, i);
122
+ const dEnd = addDaysMs(ms, 1);
123
123
  const dayEvts = events.filter((ev) => ev.start.getTime() < dEnd && ev.end.getTime() > ms).sort((a, b) => a.start.getTime() - b.start.getTime());
124
124
  const allDayEvts = dayEvts.filter((ev) => isAllDay(ev) || isMultiDay(ev));
125
125
  const timedEvts = dayEvts.filter((ev) => !isAllDay(ev) && !isMultiDay(ev));
@@ -9,7 +9,7 @@
9
9
  import { useCalendarContext } from "../shared/context.svelte.js";
10
10
  import EventContent from "../shared/EventContent.svelte";
11
11
  import { createClock } from "../../core/clock.svelte.js";
12
- import { DAY_MS, HOUR_MS, sod, isAllDay, isMultiDay, segmentForDay } from "../../core/time.js";
12
+ import { DAY_MS, HOUR_MS, sod, addDaysMs, isAllDay, isMultiDay, segmentForDay } from "../../core/time.js";
13
13
  import { fmtH, fmtTime } from "../../core/locale.js";
14
14
  import { createSwipe } from "./swipe.js";
15
15
  let { height = null, events = [], style = "", locale, focusDate, oneventclick, oneventcreate, selectedEventId = null, readOnly = false, visibleHours } = $props();
@@ -36,7 +36,7 @@ const hourCount = $derived(Math.max(1, endHour - startHour));
36
36
  const gridHeight = $derived(hourCount * HOUR_HEIGHT);
37
37
  // ── Day state ──────────────────────────────────────
38
38
  const dayMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
39
- const dayEnd = $derived(dayMs + DAY_MS);
39
+ const dayEnd = $derived(addDaysMs(dayMs, 1));
40
40
  const isToday = $derived(dayMs === clock.today);
41
41
  const isDisabled = $derived(disabledSet.has(dayMs));
42
42
  // ── Load range ─────────────────────────────────────
@@ -8,7 +8,7 @@
8
8
  <script lang="ts">import { useCalendarContext } from "../shared/context.svelte.js";
9
9
  import EventContent from "../shared/EventContent.svelte";
10
10
  import { createClock } from "../../core/clock.svelte.js";
11
- import { DAY_MS, sod, isAllDay, isMultiDay } from "../../core/time.js";
11
+ import { DAY_MS, sod, addDaysMs, isAllDay, isMultiDay } from "../../core/time.js";
12
12
  import { startOfWeek as sowFn } from "../../core/time.js";
13
13
  import { fmtTime as _fmtTime, weekdayShort } from "../../core/locale.js";
14
14
  import { createSwipe } from "./swipe.js";
@@ -47,7 +47,7 @@ const dayCells = $derived.by(() => {
47
47
  const result = [];
48
48
  const hideSet = new Set(hideDays ?? []);
49
49
  for (let i = 0; i < customDays; i++) {
50
- const ms = weekStart + i * DAY_MS;
50
+ const ms = addDaysMs(weekStart, i);
51
51
  const d = new Date(ms);
52
52
  const jsDay = d.getDay();
53
53
  const isoDay = jsDay === 0 ? 7 : jsDay;
@@ -56,7 +56,7 @@ const dayCells = $derived.by(() => {
56
56
  const isPast = equalDays ? false : ms < todayMs;
57
57
  const isWeekend = jsDay === 0 || jsDay === 6;
58
58
  const isDisabled = disabledSet.has(ms);
59
- const dayEnd = ms + DAY_MS;
59
+ const dayEnd = addDaysMs(ms, 1);
60
60
  const dayEvents = events.filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > ms).sort((a, b) => a.start.getTime() - b.start.getTime());
61
61
  const allDayCount = dayEvents.filter((ev) => isAllDay(ev) || isMultiDay(ev)).length;
62
62
  result.push({
@@ -8,7 +8,7 @@
8
8
  -->
9
9
  <script lang="ts">import { useCalendarContext } from "../shared/context.svelte.js";
10
10
  import { createClock } from "../../core/clock.svelte.js";
11
- import { DAY_MS, sod, isAllDay } from "../../core/time.js";
11
+ import { DAY_MS, sod, addDaysMs, isAllDay } from "../../core/time.js";
12
12
  import { fmtTime, weekdayShort } from "../../core/locale.js";
13
13
  let { events = [], style = "", height = null, locale, focusDate, oneventclick, selectedEventId = null } = $props();
14
14
  const ctx = useCalendarContext();
@@ -54,7 +54,7 @@ const rovingMs = $derived.by(() => {
54
54
  });
55
55
  function moveFocus(fromMs, deltaDays) {
56
56
  if (!range) return;
57
- const target = fromMs + deltaDays * DAY_MS;
57
+ const target = addDaysMs(fromMs, deltaDays);
58
58
  if (target < sod(range.start.getTime()) || target >= range.end.getTime()) return;
59
59
  focusMs = target;
60
60
  const el = bodyEl?.querySelector(`[data-ms="${target}"]`);
@@ -108,7 +108,7 @@ function cellLabel(cell) {
108
108
  }
109
109
  function eventsForDay(ms) {
110
110
  const dayStart = ms;
111
- const dayEnd = ms + DAY_MS;
111
+ const dayEnd = addDaysMs(ms, 1);
112
112
  return events.filter((e) => e.start.getTime() < dayEnd && e.end.getTime() > dayStart).sort((a, b) => {
113
113
  const aAll = isAllDay(a) ? 0 : 1;
114
114
  const bAll = isAllDay(b) ? 0 : 1;
@@ -118,10 +118,10 @@ function eventsForDay(ms) {
118
118
  const weeks = $derived.by(() => {
119
119
  if (!range) return [];
120
120
  const rows = [];
121
- for (let ms = sod(range.start.getTime()); ms < range.end.getTime(); ms += 7 * DAY_MS) {
121
+ for (let ms = sod(range.start.getTime()); ms < range.end.getTime(); ms = addDaysMs(ms, 7)) {
122
122
  const row = [];
123
123
  for (let i = 0; i < 7; i++) {
124
- const cellMs = ms + i * DAY_MS;
124
+ const cellMs = addDaysMs(ms, i);
125
125
  const date = new Date(cellMs);
126
126
  const jsDay = date.getDay();
127
127
  const dayEvents = eventsForDay(cellMs);