@nomideusz/svelte-calendar 0.13.0 → 0.14.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
@@ -1,5 +1,7 @@
1
1
  # @nomideusz/svelte-calendar
2
2
 
3
+ [![npm](https://badgen.net/npm/v/@nomideusz/svelte-calendar)](https://www.npmjs.com/package/@nomideusz/svelte-calendar) [![license](https://badgen.net/badge/license/MIT/blue)](https://github.com/nomideusz/svelte-calendar/blob/main/LICENSE)
4
+
3
5
  A themeable **Svelte 5** calendar with **Day/Week Planner & Agenda** views, touch-first mobile views, and a smart auto-theme that adapts to any page.
4
6
 
5
7
  **[Live demo → svelte-calendar.xyz](https://svelte-calendar.xyz/)**
@@ -57,6 +57,7 @@ let {
57
57
  visibleHours,
58
58
  initialDate,
59
59
  snapInterval = 15,
60
+ minColumnWidth = 110,
60
61
  showModePills = true,
61
62
  showNavigation = true,
62
63
  equalDays = false,
@@ -132,7 +133,7 @@ const effectiveTheme = $derived(theme === auto && autoTheme !== false ? probedTh
132
133
  const effectiveAdapter = $derived(
133
134
  timezone ? wrapAdapterWithTimezone(adapter, timezone) : adapter
134
135
  );
135
- const store = $derived(createEventStore(effectiveAdapter));
136
+ const store = createEventStore(() => effectiveAdapter);
136
137
  const viewState = createViewState(untrack(() => ({
137
138
  view: activeViewId ?? views[0]?.id,
138
139
  mondayStart,
@@ -256,6 +257,9 @@ setContext("calendar", {
256
257
  get snapInterval() {
257
258
  return snapInterval;
258
259
  },
260
+ get minColumnWidth() {
261
+ return minColumnWidth;
262
+ },
259
263
  get eventSnippet() {
260
264
  return eventSnippet;
261
265
  },
@@ -57,6 +57,13 @@ interface Props {
57
57
  initialDate?: Date;
58
58
  /** Drag snap interval in minutes (default: 15) */
59
59
  snapInterval?: number;
60
+ /**
61
+ * Minimum width (px) of a day column in planner views (default: 110).
62
+ * Below the resulting total the grid scrolls horizontally. Lower it when
63
+ * the calendar shares its row with a sidebar and the whole week must stay
64
+ * visible without scrolling.
65
+ */
66
+ minColumnWidth?: number;
60
67
  /** Show the Day/Week mode pills (default: true) */
61
68
  showModePills?: boolean;
62
69
  /** Show prev/next/today navigation controls (default: true) */
@@ -26,5 +26,11 @@ export interface EventStore {
26
26
  }
27
27
  /**
28
28
  * Create a reactive event store backed by a CalendarAdapter.
29
+ *
30
+ * Pass a getter instead of an adapter when the adapter identity can change
31
+ * (a `$derived` adapter, a host that rebuilds it to force a refetch): the
32
+ * store then stays alive across the swap and reloads, instead of being torn
33
+ * down and rebuilt with an empty event map — which reads as every event
34
+ * blinking out and reappearing.
29
35
  */
30
- export declare function createEventStore(adapter: CalendarAdapter): EventStore;
36
+ export declare function createEventStore(adapter: CalendarAdapter | (() => CalendarAdapter)): EventStore;
@@ -19,11 +19,20 @@ import { SvelteMap } from 'svelte/reactivity';
19
19
  import { sod, DAY_MS } from '../core/time.js';
20
20
  /**
21
21
  * Create a reactive event store backed by a CalendarAdapter.
22
+ *
23
+ * Pass a getter instead of an adapter when the adapter identity can change
24
+ * (a `$derived` adapter, a host that rebuilds it to force a refetch): the
25
+ * store then stays alive across the swap and reloads, instead of being torn
26
+ * down and rebuilt with an empty event map — which reads as every event
27
+ * blinking out and reappearing.
22
28
  */
23
29
  export function createEventStore(adapter) {
30
+ const getAdapter = typeof adapter === 'function' ? adapter : () => adapter;
24
31
  let eventMap = new SvelteMap();
25
32
  let loading = $state(false);
26
33
  let error = $state(null);
34
+ /** Guards against an older in-flight load pruning a newer one's result */
35
+ let loadSeq = 0;
27
36
  // Derived array view of the map — consumers read this.
28
37
  const eventArray = $derived([...eventMap.values()]);
29
38
  // ── Internal helpers ──
@@ -48,11 +57,21 @@ export function createEventStore(adapter) {
48
57
  return error;
49
58
  },
50
59
  async load(range) {
60
+ const seq = ++loadSeq;
51
61
  loading = true;
52
62
  error = null;
53
63
  try {
54
- const fetched = await adapter.fetchEvents(range);
55
- // Merge: upsert fetched, don't blow away events outside this range
64
+ const fetched = await getAdapter().fetchEvents(range);
65
+ if (seq !== loadSeq)
66
+ return; // superseded by a newer load
67
+ // Merge: upsert fetched, don't blow away events outside this range.
68
+ // Inside the range the adapter is authoritative — drop what it no
69
+ // longer returns, or deleted/moved events linger until remount.
70
+ const keep = new Set(fetched.map((ev) => ev.id));
71
+ for (const ev of [...eventMap.values()]) {
72
+ if (!keep.has(ev.id) && overlaps(ev, range.start, range.end))
73
+ removeEvent(ev.id);
74
+ }
56
75
  for (const ev of fetched) {
57
76
  upsertEvent(ev);
58
77
  }
@@ -61,7 +80,8 @@ export function createEventStore(adapter) {
61
80
  error = e instanceof Error ? e.message : String(e);
62
81
  }
63
82
  finally {
64
- loading = false;
83
+ if (seq === loadSeq)
84
+ loading = false;
65
85
  }
66
86
  },
67
87
  forRange(start, end) {
@@ -76,12 +96,12 @@ export function createEventStore(adapter) {
76
96
  return eventMap.get(id);
77
97
  },
78
98
  async add(eventData) {
79
- if (!adapter.createEvent)
99
+ if (!getAdapter().createEvent)
80
100
  throw new Error('Adapter is read-only: createEvent not implemented');
81
101
  loading = true;
82
102
  error = null;
83
103
  try {
84
- const created = await adapter.createEvent(eventData);
104
+ const created = await getAdapter().createEvent(eventData);
85
105
  upsertEvent(created);
86
106
  return created;
87
107
  }
@@ -94,12 +114,12 @@ export function createEventStore(adapter) {
94
114
  }
95
115
  },
96
116
  async update(id, patch) {
97
- if (!adapter.updateEvent)
117
+ if (!getAdapter().updateEvent)
98
118
  throw new Error('Adapter is read-only: updateEvent not implemented');
99
119
  loading = true;
100
120
  error = null;
101
121
  try {
102
- const updated = await adapter.updateEvent(id, patch);
122
+ const updated = await getAdapter().updateEvent(id, patch);
103
123
  upsertEvent(updated);
104
124
  }
105
125
  catch (e) {
@@ -111,12 +131,12 @@ export function createEventStore(adapter) {
111
131
  }
112
132
  },
113
133
  async remove(id) {
114
- if (!adapter.deleteEvent)
134
+ if (!getAdapter().deleteEvent)
115
135
  throw new Error('Adapter is read-only: deleteEvent not implemented');
116
136
  loading = true;
117
137
  error = null;
118
138
  try {
119
- await adapter.deleteEvent(id);
139
+ await getAdapter().deleteEvent(id);
120
140
  removeEvent(id);
121
141
  }
122
142
  catch (e) {
@@ -138,8 +158,12 @@ export function createEventStore(adapter) {
138
158
  await this.update(id, { start: newStart, end: newEnd });
139
159
  }
140
160
  catch (e) {
141
- // Revert optimistic update on failure
142
- if (existing)
161
+ // Revert optimistic update on failure — except for a read-only
162
+ // adapter, where the HOST owns persistence (Calendar forwards to
163
+ // oneventmove). Reverting there snaps the block back to its old
164
+ // slot for the length of the host's round-trip.
165
+ const msg = e instanceof Error ? e.message : '';
166
+ if (existing && !msg.includes('read-only'))
143
167
  upsertEvent(existing);
144
168
  throw e;
145
169
  }
@@ -53,7 +53,7 @@ const disabledSet = $derived(ctx.disabledSet);
53
53
  const SNAP_MS = $derived(ctx.snapInterval * 6e4);
54
54
  const HOUR_H = 48;
55
55
  const GUTTER_W = 48;
56
- const MIN_COL_W = 110;
56
+ const MIN_COL_W = $derived(ctx.minColumnWidth);
57
57
  const ALLDAY_MAX = 3;
58
58
  const startHour = $derived(visibleHours?.[0] ?? 0);
59
59
  const endHour = $derived(visibleHours?.[1] ?? 24);
@@ -135,7 +135,6 @@ const layoutByDay = $derived.by(() => {
135
135
  const infos = [];
136
136
  for (const ev of events) {
137
137
  if (isAllDay(ev) || isMultiDay(ev)) continue;
138
- if (ev.id === movingId) continue;
139
138
  const isResizing = rsP?.eventId === ev.id;
140
139
  const s0 = isResizing ? rsP.start.getTime() : ev.start.getTime();
141
140
  const e0 = isResizing ? rsP.end.getTime() : ev.end.getTime();
@@ -143,7 +142,7 @@ const layoutByDay = $derived.by(() => {
143
142
  const sMs = Math.max(s0, bandStart);
144
143
  const eMs = Math.min(e0, bandEnd);
145
144
  if (eMs <= sMs) continue;
146
- infos.push({ ev, startMs: sMs, endMs: eMs, isResizing, col: 0, totalCols: 1 });
145
+ infos.push({ ev, startMs: sMs, endMs: eMs, isResizing, isMoving: ev.id === movingId, col: 0, totalCols: 1 });
147
146
  }
148
147
  infos.sort((a, b) => a.startMs - b.startMs || b.endMs - a.endMs);
149
148
  const par = infos.map((_, i) => i);
@@ -185,7 +184,8 @@ const layoutByDay = $derived.by(() => {
185
184
  height: Math.max(24, (inf.endMs - inf.startMs) / HOUR_MS * HOUR_H),
186
185
  col: inf.col,
187
186
  totalCols: inf.totalCols,
188
- isResizing: inf.isResizing
187
+ isResizing: inf.isResizing,
188
+ isMoving: inf.isMoving
189
189
  }))
190
190
  );
191
191
  }
@@ -222,8 +222,41 @@ $effect(() => {
222
222
  el.scrollTop = (targetHour - startHour) * HOUR_H;
223
223
  });
224
224
  });
225
+ let rafId = 0;
226
+ let rafRun = null;
227
+ let rectCache = null;
228
+ function perFrame(handler) {
229
+ return (e) => {
230
+ rafRun = () => {
231
+ rectCache = colsEl?.getBoundingClientRect() ?? null;
232
+ try {
233
+ handler(e);
234
+ } finally {
235
+ rectCache = null;
236
+ }
237
+ };
238
+ if (rafId) return;
239
+ rafId = requestAnimationFrame(flushFrame);
240
+ };
241
+ }
242
+ function flushFrame() {
243
+ if (rafId) {
244
+ cancelAnimationFrame(rafId);
245
+ rafId = 0;
246
+ }
247
+ const run = rafRun;
248
+ rafRun = null;
249
+ run?.();
250
+ }
251
+ function cancelFrame() {
252
+ if (rafId) {
253
+ cancelAnimationFrame(rafId);
254
+ rafId = 0;
255
+ }
256
+ rafRun = null;
257
+ }
225
258
  function colsRect() {
226
- return colsEl.getBoundingClientRect();
259
+ return rectCache ?? colsEl.getBoundingClientRect();
227
260
  }
228
261
  function pointerDayIndex(clientX) {
229
262
  const r = colsRect();
@@ -339,7 +372,7 @@ function onColsPointerDown(e) {
339
372
  window.addEventListener("pointerup", onColsCreateUp, { once: true });
340
373
  window.addEventListener("pointercancel", onColsCreateCancel, { once: true });
341
374
  }
342
- function onColsCreateMove(e) {
375
+ const onColsCreateMove = perFrame((e) => {
343
376
  if (!drag) return;
344
377
  if (!crStarted) {
345
378
  if (longPressTimer !== null) {
@@ -357,8 +390,9 @@ function onColsCreateMove(e) {
357
390
  new Date(Math.min(crAnchorMs, snapped)),
358
391
  new Date(Math.max(crAnchorMs + SNAP_MS, snapped))
359
392
  );
360
- }
393
+ });
361
394
  function cleanupColsCreate() {
395
+ cancelFrame();
362
396
  clearLongPress();
363
397
  removeTouchScrollBlock();
364
398
  window.removeEventListener("pointermove", onColsCreateMove);
@@ -367,6 +401,7 @@ function cleanupColsCreate() {
367
401
  crStarted = false;
368
402
  }
369
403
  function onColsCreateUp() {
404
+ if (crStarted) flushFrame();
370
405
  if (drag && crStarted) {
371
406
  suppressColsClick = true;
372
407
  commitDragCtx?.();
@@ -421,7 +456,7 @@ function onEventPointerDown(e, ev) {
421
456
  window.addEventListener("pointerup", onEvUp, { once: true });
422
457
  window.addEventListener("pointercancel", onEvCancel, { once: true });
423
458
  }
424
- function onEvMove(e) {
459
+ const onEvMove = perFrame((e) => {
425
460
  const ev = evDragEvent;
426
461
  if (!drag || !ev || !evDragMovable) return;
427
462
  if (!evDragStarted) {
@@ -434,8 +469,9 @@ function onEvMove(e) {
434
469
  const raw = pointerTimeMs(e.clientX, e.clientY) - evGrabOffsetMs;
435
470
  const snapped = Math.round(raw / SNAP_MS) * SNAP_MS;
436
471
  drag.updatePointer(new Date(snapped), new Date(snapped + duration));
437
- }
472
+ });
438
473
  function cleanupEvDrag() {
474
+ cancelFrame();
439
475
  window.removeEventListener("pointermove", onEvMove);
440
476
  window.removeEventListener("pointerup", onEvUp);
441
477
  window.removeEventListener("pointercancel", onEvCancel);
@@ -444,6 +480,7 @@ function cleanupEvDrag() {
444
480
  evDragEvent = null;
445
481
  }
446
482
  function onEvUp() {
483
+ if (evDragStarted) flushFrame();
447
484
  if (!evDragStarted && evDragEvent) {
448
485
  oneventclick?.(evDragEvent);
449
486
  } else if (evDragStarted && drag) {
@@ -474,7 +511,7 @@ function onResizePointerDown(e, ev, edge) {
474
511
  window.addEventListener("pointerup", onResizeUp, { once: true });
475
512
  window.addEventListener("pointercancel", onResizeCancel, { once: true });
476
513
  }
477
- function onResizeMove(e) {
514
+ const onResizeMove = perFrame((e) => {
478
515
  const ev = rsEvent;
479
516
  if (!drag || !ev) return;
480
517
  if (!rsStarted) {
@@ -493,8 +530,9 @@ function onResizeMove(e) {
493
530
  const start = Math.min(snapped, ev.end.getTime() - SNAP_MS);
494
531
  drag.updatePointer(new Date(start), ev.end);
495
532
  }
496
- }
533
+ });
497
534
  function cleanupResize() {
535
+ cancelFrame();
498
536
  removeTouchScrollBlock();
499
537
  window.removeEventListener("pointermove", onResizeMove);
500
538
  window.removeEventListener("pointerup", onResizeUp);
@@ -503,6 +541,7 @@ function cleanupResize() {
503
541
  rsEvent = null;
504
542
  }
505
543
  function onResizeUp() {
544
+ if (rsStarted) flushFrame();
506
545
  if (drag && rsStarted) {
507
546
  suppressColsClick = true;
508
547
  commitDragCtx?.();
@@ -568,6 +607,7 @@ function onWindowKeydown(e) {
568
607
  class="tw"
569
608
  class:tw--auto={autoHeight}
570
609
  style={style || undefined}
610
+ style:--tw-col-min="{MIN_COL_W}px"
571
611
  style:height={autoHeight ? undefined : (height ? `${height}px` : '100%')}
572
612
  role="region"
573
613
  aria-label={L.weekAhead}
@@ -703,6 +743,7 @@ function onWindowKeydown(e) {
703
743
  class:tw-ev--selected={selectedEventId === p.ev.id}
704
744
  class:tw-ev--current={isCurrent}
705
745
  class:tw-ev--resizing={p.isResizing}
746
+ class:tw-ev--moving={p.isMoving}
706
747
  class:tw-ev--readonly={p.ev.data?.readOnly}
707
748
  class:tw-ev--cancelled={p.ev.status === 'cancelled'}
708
749
  class:tw-ev--tentative={p.ev.status === 'tentative'}
@@ -857,7 +898,7 @@ function onWindowKeydown(e) {
857
898
 
858
899
  .tw-hd {
859
900
  flex: 1 1 0;
860
- min-width: 110px;
901
+ min-width: var(--tw-col-min, 110px);
861
902
  display: flex;
862
903
  flex-direction: column;
863
904
  align-items: center;
@@ -923,7 +964,7 @@ function onWindowKeydown(e) {
923
964
 
924
965
  .tw-ad-cell {
925
966
  flex: 1 1 0;
926
- min-width: 110px;
967
+ min-width: var(--tw-col-min, 110px);
927
968
  display: flex;
928
969
  flex-direction: column;
929
970
  gap: 2px;
@@ -1075,7 +1116,7 @@ function onWindowKeydown(e) {
1075
1116
  /* ─── Day column ─────────────────────────────────── */
1076
1117
  .tw-col {
1077
1118
  flex: 1 1 0;
1078
- min-width: 110px;
1119
+ min-width: var(--tw-col-min, 110px);
1079
1120
  position: relative;
1080
1121
  border-left: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
1081
1122
  box-sizing: border-box;
@@ -1197,6 +1238,12 @@ function onWindowKeydown(e) {
1197
1238
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
1198
1239
  cursor: ns-resize;
1199
1240
  }
1241
+ /* Origin of an in-flight move: still there, clearly no longer the subject. */
1242
+ .tw-ev--moving {
1243
+ opacity: 0.3;
1244
+ pointer-events: none;
1245
+ box-shadow: none;
1246
+ }
1200
1247
  /* Status treatments: token-level dims + a non-opacity signal
1201
1248
  (strikethrough / border style) — consistent with the other views. */
1202
1249
  .tw-ev--cancelled {
@@ -8,6 +8,7 @@ export interface CalendarContext {
8
8
  readonly drag: DragState | undefined;
9
9
  readonly commitDrag: (() => void) | undefined;
10
10
  readonly snapInterval: number;
11
+ readonly minColumnWidth: number;
11
12
  readonly equalDays: boolean;
12
13
  readonly showDates: boolean;
13
14
  readonly hideDays: number[] | undefined;
@@ -28,6 +28,7 @@ export function useCalendarContext() {
28
28
  get drag() { return raw?.drag; },
29
29
  get commitDrag() { return raw?.commitDrag; },
30
30
  get snapInterval() { return raw?.snapInterval ?? 15; },
31
+ get minColumnWidth() { return raw?.minColumnWidth ?? 110; },
31
32
  get equalDays() { return raw?.equalDays ?? false; },
32
33
  get showDates() { return raw?.showDates ?? true; },
33
34
  get hideDays() { return raw?.hideDays; },
package/package.json CHANGED
@@ -1,74 +1,75 @@
1
1
  {
2
- "name": "@nomideusz/svelte-calendar",
3
- "version": "0.13.0",
4
- "description": "A themeable Svelte 5 calendar with Day and Week views — Planner and Agenda.",
5
- "type": "module",
6
- "license": "MIT",
7
- "svelte": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "svelte": "./dist/index.js",
13
- "default": "./dist/index.js"
14
- },
15
- "./widget": "./widget/widget.js"
16
- },
17
- "files": [
18
- "dist",
19
- "widget",
20
- "!dist/**/*.test.*",
21
- "!dist/**/*.spec.*"
22
- ],
23
- "scripts": {
24
- "dev": "vite dev",
25
- "build": "vite build",
26
- "package": "svelte-kit sync && svelte-package",
27
- "package:watch": "svelte-kit sync && svelte-package --watch",
28
- "build:widget": "vite build --config vite.config.widget.ts",
29
- "prepublishOnly": "npm run package",
30
- "preview": "vite preview",
31
- "prepare": "svelte-kit sync && svelte-package",
32
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
33
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
34
- "test": "vitest run",
35
- "test:watch": "vitest"
36
- },
37
- "peerDependencies": {
38
- "svelte": "^5.0.0"
39
- },
40
- "devDependencies": {
41
- "@sveltejs/adapter-auto": "^7.0.1",
42
- "@sveltejs/kit": "^2.69.1",
43
- "@sveltejs/package": "^2.5.8",
44
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
45
- "@types/node": "^22.20.0",
46
- "@vercel/analytics": "^1.6.1",
47
- "jsdom": "^29.1.1",
48
- "marked": "^17.0.6",
49
- "svelte": "^5.56.4",
50
- "svelte-check": "^4.7.1",
51
- "typescript": "^5.9.3",
52
- "vite": "^7.3.6",
53
- "vitest": "^4.1.9"
54
- },
55
- "dependencies": {
56
- "date-fns": "^4.4.0",
57
- "date-fns-tz": "^3.2.0"
58
- },
59
- "keywords": [
60
- "svelte",
61
- "calendar",
62
- "scheduler",
63
- "timeline",
64
- "week",
65
- "day",
66
- "booking",
67
- "svelte5"
68
- ],
69
- "repository": {
70
- "type": "git",
71
- "url": "https://github.com/nomideusz/svelte-calendar"
72
- },
73
- "homepage": "https://svelte-calendar.xyz/"
2
+ "name": "@nomideusz/svelte-calendar",
3
+ "version": "0.14.0",
4
+ "description": "A themeable Svelte 5 calendar with Day and Week views — Planner and Agenda.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "license": "MIT",
8
+ "svelte": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "svelte": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./widget": "./widget/widget.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "widget",
21
+ "!dist/**/*.test.*",
22
+ "!dist/**/*.spec.*"
23
+ ],
24
+ "scripts": {
25
+ "dev": "vite dev",
26
+ "build": "vite build",
27
+ "package": "svelte-kit sync && svelte-package",
28
+ "package:watch": "svelte-kit sync && svelte-package --watch",
29
+ "build:widget": "vite build --config vite.config.widget.ts",
30
+ "prepublishOnly": "npm run package",
31
+ "preview": "vite preview",
32
+ "prepare": "svelte-kit sync && svelte-package",
33
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
34
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
35
+ "test": "vitest run",
36
+ "test:watch": "vitest"
37
+ },
38
+ "peerDependencies": {
39
+ "svelte": "^5.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@sveltejs/adapter-auto": "^7.0.1",
43
+ "@sveltejs/kit": "^2.69.1",
44
+ "@sveltejs/package": "^2.5.8",
45
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
46
+ "@types/node": "^22.20.0",
47
+ "@vercel/analytics": "^1.6.1",
48
+ "jsdom": "^29.1.1",
49
+ "marked": "^17.0.6",
50
+ "svelte": "^5.56.4",
51
+ "svelte-check": "^4.7.1",
52
+ "typescript": "^5.9.3",
53
+ "vite": "^7.3.6",
54
+ "vitest": "^4.1.9"
55
+ },
56
+ "dependencies": {
57
+ "date-fns": "^4.4.0",
58
+ "date-fns-tz": "^3.2.0"
59
+ },
60
+ "keywords": [
61
+ "svelte",
62
+ "calendar",
63
+ "scheduler",
64
+ "timeline",
65
+ "week",
66
+ "day",
67
+ "booking",
68
+ "svelte5"
69
+ ],
70
+ "repository": {
71
+ "type": "git",
72
+ "url": "https://github.com/nomideusz/svelte-calendar"
73
+ },
74
+ "homepage": "https://svelte-calendar.xyz/"
74
75
  }