@nomideusz/svelte-calendar 0.4.0 → 0.5.2

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 (73) hide show
  1. package/README.md +331 -568
  2. package/dist/adapters/recurring.d.ts +45 -8
  3. package/dist/adapters/recurring.js +227 -49
  4. package/dist/adapters/rest.js +6 -1
  5. package/dist/calendar/Calendar.svelte +118 -30
  6. package/dist/calendar/Calendar.svelte.d.ts +9 -8
  7. package/dist/calendar/index.d.ts +0 -1
  8. package/dist/calendar/index.js +0 -1
  9. package/dist/core/index.d.ts +5 -4
  10. package/dist/core/index.js +2 -3
  11. package/dist/core/locale.d.ts +74 -0
  12. package/dist/core/locale.js +96 -4
  13. package/dist/core/time.d.ts +30 -0
  14. package/dist/core/time.js +42 -0
  15. package/dist/core/types.d.ts +1 -72
  16. package/dist/core/types.js +1 -8
  17. package/dist/engine/event-store.svelte.js +2 -2
  18. package/dist/engine/index.d.ts +1 -1
  19. package/dist/engine/view-state.svelte.d.ts +11 -6
  20. package/dist/engine/view-state.svelte.js +5 -4
  21. package/dist/index.d.ts +6 -7
  22. package/dist/index.js +4 -4
  23. package/dist/primitives/EmptySlot.svelte +6 -16
  24. package/dist/primitives/EventBlock.svelte +6 -19
  25. package/dist/primitives/NowIndicator.svelte +7 -4
  26. package/dist/primitives/TimeGutter.svelte +1 -1
  27. package/dist/theme/index.d.ts +1 -1
  28. package/dist/theme/index.js +1 -1
  29. package/dist/theme/presets.d.ts +11 -19
  30. package/dist/theme/presets.js +25 -111
  31. package/dist/views/agenda/Agenda.svelte +17 -1516
  32. package/dist/views/agenda/Agenda.svelte.d.ts +0 -14
  33. package/dist/views/agenda/AgendaDay.svelte +976 -0
  34. package/dist/views/agenda/AgendaDay.svelte.d.ts +14 -0
  35. package/dist/views/agenda/AgendaWeek.svelte +775 -0
  36. package/dist/views/agenda/AgendaWeek.svelte.d.ts +15 -0
  37. package/dist/views/agenda/index.d.ts +2 -0
  38. package/dist/views/agenda/index.js +2 -0
  39. package/dist/views/index.d.ts +2 -6
  40. package/dist/views/index.js +2 -6
  41. package/dist/views/planner/Planner.svelte +17 -0
  42. package/dist/views/planner/Planner.svelte.d.ts +7 -0
  43. package/dist/views/planner/PlannerDay.svelte +957 -0
  44. package/dist/views/{day/DayGrid.svelte.d.ts → planner/PlannerDay.svelte.d.ts} +6 -16
  45. package/dist/views/planner/PlannerWeek.svelte +840 -0
  46. package/dist/views/{week/WeekGrid.svelte.d.ts → planner/PlannerWeek.svelte.d.ts} +4 -9
  47. package/dist/views/planner/index.d.ts +1 -0
  48. package/dist/views/planner/index.js +1 -0
  49. package/dist/widget/CalendarWidget.svelte +54 -33
  50. package/dist/widget/CalendarWidget.svelte.d.ts +1 -1
  51. package/package.json +70 -65
  52. package/widget/svelte-calendar.css +1 -0
  53. package/widget/widget.js +122 -260
  54. package/dist/calendar/Toolbar.svelte +0 -244
  55. package/dist/calendar/Toolbar.svelte.d.ts +0 -21
  56. package/dist/views/day/DayGrid.svelte +0 -1104
  57. package/dist/views/day/DayTimeline.svelte +0 -533
  58. package/dist/views/day/DayTimeline.svelte.d.ts +0 -8
  59. package/dist/views/day/index.d.ts +0 -2
  60. package/dist/views/day/index.js +0 -2
  61. package/dist/views/schedule/WeekSchedule.svelte +0 -137
  62. package/dist/views/schedule/WeekSchedule.svelte.d.ts +0 -40
  63. package/dist/views/schedule/index.d.ts +0 -1
  64. package/dist/views/schedule/index.js +0 -2
  65. package/dist/views/settings/Settings.svelte +0 -585
  66. package/dist/views/settings/Settings.svelte.d.ts +0 -39
  67. package/dist/views/settings/index.d.ts +0 -2
  68. package/dist/views/settings/index.js +0 -1
  69. package/dist/views/week/WeekGrid.svelte +0 -632
  70. package/dist/views/week/WeekHeatmap.svelte +0 -461
  71. package/dist/views/week/WeekHeatmap.svelte.d.ts +0 -9
  72. package/dist/views/week/index.d.ts +0 -2
  73. package/dist/views/week/index.js +0 -2
@@ -1,533 +0,0 @@
1
- <script lang="ts">
2
- import { getContext, onMount } from 'svelte';
3
- import { fly } from 'svelte/transition';
4
- import { createClock } from '../../core/clock.svelte.js';
5
- import type { DayTimelineProps } from '../../core/types.js';
6
- import type { DragState } from '../../engine/drag.svelte.js';
7
- import { DAY_MS, HOUR_MS, HOURS, sod } from '../../core/time.js';
8
- import { fmtH, fmtDay } from '../../core/locale.js';
9
-
10
- let {
11
- pastDays = 1,
12
- futureDays = 30,
13
- hourWidth = 120,
14
- nowPosition = 0.25,
15
- events = [],
16
- style = '',
17
- locale,
18
- focusDate,
19
- oneventclick,
20
- selectedEventId = null,
21
- }: DayTimelineProps & { locale?: string; [key: string]: unknown } = $props();
22
-
23
- // ── Drag support (available when inside Calendar) ──
24
- const drag = getContext<DragState>('calendar:drag') as DragState | undefined;
25
- const commitDragCtx = getContext<() => void>('calendar:commitDrag') as (() => void) | undefined;
26
-
27
- const clock = createClock();
28
-
29
- let following = $state(true);
30
- let dragging = $state(false);
31
- let rafId = 0;
32
- let dragStartX = 0;
33
- let dragScrollStart = 0;
34
- let el: HTMLDivElement;
35
-
36
- // ─── Derived ────────────────────────────────────────────
37
- const dayW = $derived(hourWidth * 24);
38
- const count = $derived(pastDays + 1 + futureDays);
39
- const totalW = $derived(count * dayW);
40
- /** When focusDate is set, centre the timeline on that date */
41
- const centerMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
42
- const origin = $derived(centerMs - pastDays * DAY_MS);
43
- const nowPx = $derived(((clock.tick - origin) / HOUR_MS) * hourWidth);
44
-
45
- const days = $derived.by(() => {
46
- const out: { ms: number; x: number; name: string; today: boolean }[] = [];
47
- for (let i = 0; i < count; i++) {
48
- const ms = origin + i * DAY_MS;
49
- out.push({
50
- ms,
51
- x: i * dayW,
52
- name: fmtDay(ms, clock.today, undefined, locale),
53
- today: ms === clock.today
54
- });
55
- }
56
- return out;
57
- });
58
-
59
- // ─── Lifecycle ──────────────────────────────────────────
60
- onMount(() => {
61
- function frame() {
62
- if (following && el && !dragging) {
63
- const now = Date.now();
64
- const px = ((now - origin) / HOUR_MS) * hourWidth;
65
- el.scrollLeft = px - el.clientWidth * nowPosition;
66
- }
67
- rafId = requestAnimationFrame(frame);
68
- }
69
- rafId = requestAnimationFrame(frame);
70
- return () => cancelAnimationFrame(rafId);
71
- });
72
-
73
- // ─── Drag-to-scroll ────────────────────────────────────
74
- const SCROLL_THRESHOLD = 3;
75
-
76
- function onPointerDown(e: PointerEvent) {
77
- if (e.button !== 0) return;
78
- const target = e.target as HTMLElement;
79
- if (target.closest('.dt-event')) return;
80
- dragStartX = e.clientX;
81
- dragScrollStart = el.scrollLeft;
82
-
83
- window.addEventListener('pointermove', onScrollPointerMove);
84
- window.addEventListener('pointerup', onScrollPointerUp, { once: true });
85
- window.addEventListener('pointercancel', onScrollPointerUp, { once: true });
86
- }
87
-
88
- function onScrollPointerMove(e: PointerEvent) {
89
- const dx = e.clientX - dragStartX;
90
- if (!dragging && Math.abs(dx) >= SCROLL_THRESHOLD) {
91
- dragging = true;
92
- following = false;
93
- }
94
- if (dragging) {
95
- el.scrollLeft = dragScrollStart - dx;
96
- }
97
- }
98
-
99
- function onScrollPointerUp() {
100
- window.removeEventListener('pointermove', onScrollPointerMove);
101
- dragging = false;
102
- }
103
-
104
- function userInput() {
105
- if (!dragging) following = false;
106
- }
107
-
108
- function jumpNow() {
109
- following = true;
110
- }
111
-
112
- // ─── Event positioning (horizontal) ─────────────────────
113
- import type { TimelineEvent } from '../../core/types.js';
114
-
115
- interface PositionedEvent {
116
- ev: TimelineEvent;
117
- x: number;
118
- width: number;
119
- row: number;
120
- isDragged: boolean;
121
- }
122
-
123
- const positionedEvents = $derived.by(() => {
124
- const result: PositionedEvent[] = [];
125
- const dragP = drag?.active && drag.mode === 'move' ? drag.payload : null;
126
-
127
- const raw = events.map((ev) => {
128
- const isDragged = dragP?.eventId === ev.id;
129
- const startMs = isDragged ? dragP!.start.getTime() : ev.start.getTime();
130
- const endMs = isDragged ? dragP!.end.getTime() : ev.end.getTime();
131
- return { ev, startMs, endMs, isDragged };
132
- });
133
- raw.sort((a, b) => a.startMs - b.startMs);
134
-
135
- // Simple row-packing for overlap
136
- const rows: number[] = [];
137
-
138
- for (const { ev, startMs, endMs, isDragged } of raw) {
139
- const x = ((startMs - origin) / HOUR_MS) * hourWidth;
140
- const w = Math.max(((endMs - startMs) / HOUR_MS) * hourWidth, 20);
141
-
142
- let row = 0;
143
- for (let r = 0; r < rows.length; r++) {
144
- if (rows[r] <= startMs) {
145
- row = r;
146
- rows[r] = endMs;
147
- break;
148
- }
149
- row = r + 1;
150
- }
151
- if (row >= rows.length) rows.push(endMs);
152
-
153
- result.push({ ev, x, width: w, row, isDragged });
154
- }
155
- return result;
156
- });
157
-
158
- // ─── Event drag-to-move ───────────────────────────────────────
159
- const DRAG_THRESHOLD = 5;
160
- const SNAP_MS = 15 * 60_000;
161
- let evDragStartX = 0;
162
- let evDragStarted = false;
163
- let evDragging = $state(false);
164
- let evDragId = $state<string | null>(null);
165
- let evDragEvent: TimelineEvent | null = null;
166
-
167
- function onEventPointerDown(e: PointerEvent, ev: TimelineEvent) {
168
- if (e.button !== 0 || !drag) return;
169
- e.stopPropagation();
170
- evDragStartX = e.clientX;
171
- evDragStarted = false;
172
- evDragId = ev.id;
173
- evDragEvent = ev;
174
-
175
- window.addEventListener('pointermove', onEvWindowPointerMove);
176
- window.addEventListener('pointerup', onEvWindowPointerUp, { once: true });
177
- window.addEventListener('pointercancel', onEvWindowPointerCancel, { once: true });
178
- }
179
-
180
- function onEvWindowPointerMove(e: PointerEvent) {
181
- const ev = evDragEvent;
182
- if (!drag || !ev || evDragId !== ev.id) return;
183
- const dx = e.clientX - evDragStartX;
184
- if (!evDragStarted && Math.abs(dx) < DRAG_THRESHOLD) return;
185
-
186
- if (!evDragStarted) {
187
- evDragStarted = true;
188
- evDragging = true;
189
- drag.beginMove(ev.id, ev.start, ev.end);
190
- }
191
-
192
- const rawDeltaMs = (dx / hourWidth) * HOUR_MS;
193
- const snappedDelta = Math.round(rawDeltaMs / SNAP_MS) * SNAP_MS;
194
- drag.updatePointer(
195
- new Date(ev.start.getTime() + snappedDelta),
196
- new Date(ev.end.getTime() + snappedDelta),
197
- );
198
- }
199
-
200
- function cleanupEvDrag() {
201
- window.removeEventListener('pointermove', onEvWindowPointerMove);
202
- window.removeEventListener('pointerup', onEvWindowPointerUp);
203
- window.removeEventListener('pointercancel', onEvWindowPointerCancel);
204
- evDragStarted = false;
205
- evDragging = false;
206
- evDragId = null;
207
- evDragEvent = null;
208
- }
209
-
210
- function onEvWindowPointerUp() {
211
- if (!drag) { cleanupEvDrag(); return; }
212
- if (!evDragStarted) {
213
- if (evDragEvent) oneventclick?.(evDragEvent);
214
- } else {
215
- commitDragCtx?.();
216
- }
217
- cleanupEvDrag();
218
- }
219
-
220
- function onEvWindowPointerCancel() {
221
- if (drag && evDragStarted) drag.cancel();
222
- cleanupEvDrag();
223
- }
224
- </script>
225
-
226
- <div class="dt" style={style || undefined}>
227
- <!-- svelte-ignore a11y_no_static_element_interactions -->
228
- <div
229
- class="dt-scroll"
230
- class:dt-grabbing={dragging}
231
- bind:this={el}
232
- onwheel={userInput}
233
- onpointerdown={onPointerDown}
234
- >
235
- <div class="dt-track" style:width="{totalW}px">
236
- {#each days as d (d.ms)}
237
- <div class="dt-day" class:dt-today={d.today} style:left="{d.x}px" style:width="{dayW}px">
238
- <div class="dt-day-hd">
239
- <span class="dt-day-nm">{d.name}</span>
240
- </div>
241
- </div>
242
-
243
- {#each HOURS as h}
244
- {@const x = d.x + h * hourWidth}
245
- <div class="dt-tick" style:left="{x}px">
246
- <span class="dt-tick-lb">{fmtH(h, locale)}</span>
247
- </div>
248
- <div class="dt-tick dt-tick--h" style:left="{x + hourWidth * 0.5}px"></div>
249
- {/each}
250
- {/each}
251
-
252
- <div class="dt-now" style:left="{nowPx}px">
253
- <span class="dt-now-tag">{clock.hm}<span class="dt-now-sec">{clock.s}</span></span>
254
- <div class="dt-now-line"></div>
255
- </div>
256
-
257
- <!-- Events -->
258
- {#each positionedEvents as p (p.ev.id)}
259
- <div
260
- class="dt-event"
261
- class:dt-event--selected={selectedEventId === p.ev.id}
262
- class:dt-event--dragging={p.isDragged}
263
- style:left="{p.x}px"
264
- style:width="{p.width}px"
265
- style:top="calc(38px + {p.row * 34}px)"
266
- style:--ev-color={p.ev.color ?? 'var(--dt-accent)'}
267
- role="button"
268
- tabindex="0"
269
- onpointerdown={(e) => onEventPointerDown(e, p.ev)}
270
- onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); oneventclick?.(p.ev); } }}
271
- >
272
- <span class="dt-ev-title">{p.ev.title}</span>
273
- {#if p.ev.subtitle}
274
- <span class="dt-ev-sub">{p.ev.subtitle}</span>
275
- {/if}
276
- {#if p.ev.tags?.length}
277
- {#each p.ev.tags as tag}
278
- <span class="dt-ev-tag">{tag}</span>
279
- {/each}
280
- {/if}
281
- </div>
282
- {/each}
283
- </div>
284
- </div>
285
-
286
- <div class="dt-fog dt-fog--l"></div>
287
- <div class="dt-fog dt-fog--r"></div>
288
-
289
- {#if !following}
290
- <button class="dt-btn" onclick={jumpNow} transition:fly={{ y: 6, duration: 180 }}>
291
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
292
- <circle cx="12" cy="12" r="10" />
293
- <circle cx="12" cy="12" r="3" fill="currentColor" />
294
- </svg>
295
- Now
296
- </button>
297
- {/if}
298
- </div>
299
-
300
- <style>
301
- .dt {
302
- --dt-bg: #0b0e14;
303
- --dt-surface: #10141c;
304
- --dt-border: rgba(148, 163, 184, 0.07);
305
- --dt-border-day: rgba(148, 163, 184, 0.14);
306
- --dt-text: rgba(226, 232, 240, 0.85);
307
- --dt-text-2: rgba(148, 163, 184, 0.55);
308
- --dt-text-3: rgba(100, 116, 139, 0.55);
309
- --dt-accent: #ef4444;
310
- --dt-accent-dim: rgba(239, 68, 68, 0.18);
311
- --dt-glow: rgba(239, 68, 68, 0.35);
312
- --dt-today-bg: rgba(239, 68, 68, 0.02);
313
- --dt-btn-text: #fff;
314
- --dt-scrollbar: rgba(148, 163, 184, 0.12);
315
- --dt-header: 34px;
316
- --dt-ruler: 16px;
317
- --dt-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;
318
- --dt-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
319
-
320
- position: relative;
321
- height: 160px;
322
- background: var(--dt-bg);
323
- border: 1px solid var(--dt-border);
324
- border-radius: 10px;
325
- overflow: hidden;
326
- user-select: none;
327
- font-variant-numeric: tabular-nums;
328
- }
329
-
330
- .dt-scroll {
331
- width: 100%;
332
- height: 100%;
333
- overflow-x: auto;
334
- overflow-y: hidden;
335
- touch-action: pan-x;
336
- cursor: grab;
337
- scrollbar-width: thin;
338
- scrollbar-color: var(--dt-scrollbar) transparent;
339
- }
340
- .dt-scroll::-webkit-scrollbar { height: 5px; }
341
- .dt-scroll::-webkit-scrollbar-thumb { background: var(--dt-scrollbar); border-radius: 4px; }
342
- .dt-scroll::-webkit-scrollbar-track { background: transparent; }
343
- .dt-grabbing { cursor: grabbing; }
344
-
345
- .dt-track { position: relative; height: 100%; }
346
-
347
- .dt-day {
348
- position: absolute;
349
- top: 0;
350
- height: 100%;
351
- border-left: 1px solid var(--dt-border-day);
352
- box-sizing: border-box;
353
- }
354
- .dt-day:first-of-type { border-left: none; }
355
- .dt-today { background: var(--dt-today-bg); }
356
-
357
- .dt-day-hd {
358
- display: flex;
359
- align-items: baseline;
360
- gap: 8px;
361
- padding: 9px 14px;
362
- height: var(--dt-header);
363
- box-sizing: border-box;
364
- border-bottom: 1px solid var(--dt-border);
365
- }
366
- .dt-day-nm {
367
- font: 600 11px / 1 var(--dt-sans);
368
- letter-spacing: 0.04em;
369
- text-transform: uppercase;
370
- color: var(--dt-text-2);
371
- white-space: nowrap;
372
- }
373
- .dt-today .dt-day-nm { color: var(--dt-accent); }
374
-
375
- .dt-tick {
376
- position: absolute;
377
- top: var(--dt-header);
378
- bottom: 0;
379
- width: 0;
380
- }
381
- .dt-tick::before {
382
- content: '';
383
- position: absolute;
384
- top: var(--dt-ruler);
385
- bottom: 0;
386
- width: 1px;
387
- background: var(--dt-border);
388
- }
389
- .dt-tick-lb {
390
- position: absolute;
391
- top: 1px;
392
- left: 5px;
393
- font: 500 9px / 1 var(--dt-mono);
394
- color: var(--dt-text-3);
395
- white-space: nowrap;
396
- pointer-events: none;
397
- }
398
- .dt-tick--h {
399
- bottom: auto;
400
- height: calc(var(--dt-ruler) + 8px);
401
- }
402
- .dt-tick--h::before {
403
- top: var(--dt-ruler);
404
- height: 6px;
405
- bottom: auto;
406
- opacity: 0.45;
407
- }
408
-
409
- .dt-now {
410
- position: absolute;
411
- top: 0;
412
- bottom: 0;
413
- z-index: 10;
414
- pointer-events: none;
415
- transform: translateX(-1px);
416
- }
417
- .dt-now-line {
418
- position: absolute;
419
- top: 0;
420
- bottom: 0;
421
- left: 0;
422
- width: 2px;
423
- background: var(--dt-accent);
424
- box-shadow: 0 0 10px var(--dt-glow), 0 0 24px var(--dt-accent-dim);
425
- animation: now-pulse 3s ease-in-out infinite;
426
- }
427
- @keyframes now-pulse {
428
- 0%, 100% { box-shadow: 0 0 10px var(--dt-glow), 0 0 24px var(--dt-accent-dim); }
429
- 50% { box-shadow: 0 0 16px var(--dt-glow), 0 0 36px var(--dt-glow); }
430
- }
431
- .dt-now-tag {
432
- position: absolute;
433
- top: 7px;
434
- left: 8px;
435
- font: 700 10px / 1 var(--dt-mono);
436
- color: var(--dt-accent);
437
- background: var(--dt-bg);
438
- border: 1px solid var(--dt-accent-dim);
439
- padding: 3px 6px;
440
- border-radius: 4px;
441
- white-space: nowrap;
442
- z-index: 1;
443
- }
444
- .dt-now-sec { font-weight: 400; opacity: 0.5; font-size: 9px; }
445
-
446
- .dt-fog {
447
- position: absolute;
448
- top: 0;
449
- bottom: 0;
450
- width: 48px;
451
- pointer-events: none;
452
- z-index: 5;
453
- }
454
- .dt-fog--l { left: 0; background: linear-gradient(to right, var(--dt-bg), transparent); }
455
- .dt-fog--r { right: 0; background: linear-gradient(to left, var(--dt-bg), transparent); }
456
-
457
- .dt-btn {
458
- position: absolute;
459
- bottom: 14px;
460
- right: 14px;
461
- z-index: 20;
462
- display: inline-flex;
463
- align-items: center;
464
- gap: 5px;
465
- padding: 5px 13px 5px 9px;
466
- font: 600 11px / 1 var(--dt-sans);
467
- color: var(--dt-btn-text);
468
- background: var(--dt-accent);
469
- border: none;
470
- border-radius: 100px;
471
- cursor: pointer;
472
- box-shadow: 0 2px 16px var(--dt-glow);
473
- transition: transform 120ms ease, box-shadow 120ms ease;
474
- }
475
- .dt-btn svg { width: 13px; height: 13px; }
476
- .dt-btn:hover { transform: scale(1.06); box-shadow: 0 4px 24px var(--dt-glow); }
477
- .dt-btn:active { transform: scale(0.96); }
478
-
479
- /* ─── Events ─────────────────────────────────────── */
480
- .dt-event {
481
- position: absolute;
482
- height: 28px;
483
- z-index: 6;
484
- border-radius: 5px;
485
- padding: 0 8px;
486
- display: flex;
487
- align-items: center;
488
- gap: 4px;
489
- cursor: pointer;
490
- background: color-mix(in srgb, var(--ev-color) 22%, transparent);
491
- border-left: 3px solid var(--ev-color);
492
- transition: box-shadow 120ms, transform 80ms;
493
- }
494
- .dt-event:hover {
495
- box-shadow: 0 2px 10px color-mix(in srgb, var(--ev-color) 30%, transparent);
496
- transform: translateY(-1px);
497
- }
498
- .dt-event--selected {
499
- box-shadow: 0 0 0 2px var(--ev-color), 0 2px 12px color-mix(in srgb, var(--ev-color) 40%, transparent);
500
- }
501
- .dt-event--dragging {
502
- opacity: 0.85;
503
- z-index: 50;
504
- box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);
505
- cursor: grabbing;
506
- transition: none;
507
- }
508
- .dt-ev-title {
509
- font: 600 10px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
510
- color: var(--dt-text, #e2e8f0);
511
- white-space: nowrap;
512
- overflow: hidden;
513
- text-overflow: ellipsis;
514
- }
515
-
516
- .dt-ev-sub {
517
- font: 400 9px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
518
- color: var(--dt-text-2, rgba(148, 163, 184, 0.6));
519
- white-space: nowrap;
520
- overflow: hidden;
521
- text-overflow: ellipsis;
522
- }
523
-
524
- .dt-ev-tag {
525
- font: 500 7px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
526
- color: var(--ev-color, var(--dt-accent));
527
- background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 20%, transparent);
528
- padding: 1px 3px;
529
- border-radius: 2px;
530
- white-space: nowrap;
531
- flex-shrink: 0;
532
- }
533
- </style>
@@ -1,8 +0,0 @@
1
- import type { DayTimelineProps } from '../../core/types.js';
2
- type $$ComponentProps = DayTimelineProps & {
3
- locale?: string;
4
- [key: string]: unknown;
5
- };
6
- declare const DayTimeline: import("svelte").Component<$$ComponentProps, {}, "">;
7
- type DayTimeline = ReturnType<typeof DayTimeline>;
8
- export default DayTimeline;
@@ -1,2 +0,0 @@
1
- export { default as DayGrid } from './DayGrid.svelte';
2
- export { default as DayTimeline } from './DayTimeline.svelte';
@@ -1,2 +0,0 @@
1
- export { default as DayGrid } from './DayGrid.svelte';
2
- export { default as DayTimeline } from './DayTimeline.svelte';
@@ -1,137 +0,0 @@
1
- <!--
2
- WeekSchedule — zero-config weekly schedule display.
3
-
4
- A convenience wrapper that pre-wires Calendar + WeekGrid + Agenda
5
- so consumers can render a read-only weekly schedule with a single import.
6
-
7
- Usage:
8
- <WeekSchedule
9
- events={events}
10
- theme={neutral}
11
- locale="pl-PL"
12
- height={560}
13
- readOnly
14
- />
15
-
16
- Or with a recurring adapter:
17
- <WeekSchedule
18
- schedule={weeklyClasses}
19
- theme={neutral}
20
- readOnly
21
- />
22
- -->
23
- <script lang="ts">
24
- import type { TimelineEvent } from '../../core/types.js';
25
- import type { RecurringEvent } from '../../adapters/recurring.js';
26
- import type { MemoryAdapterOptions } from '../../adapters/memory.js';
27
- import { createMemoryAdapter } from '../../adapters/memory.js';
28
- import { createRecurringAdapter, type RecurringAdapterOptions } from '../../adapters/recurring.js';
29
- import Calendar from '../../calendar/Calendar.svelte';
30
- import type { CalendarView } from '../../calendar/Calendar.svelte';
31
- import { WeekGrid } from '../week/index.js';
32
- import { Agenda } from '../agenda/index.js';
33
-
34
- interface Props {
35
- /** Concrete events (mutually exclusive with `schedule`) */
36
- events?: TimelineEvent[];
37
- /** Recurring weekly schedule (mutually exclusive with `events`) */
38
- schedule?: RecurringEvent[];
39
- /** CSS theme string */
40
- theme?: string;
41
- /** BCP 47 locale */
42
- locale?: string;
43
- /** Text direction */
44
- dir?: 'ltr' | 'rtl' | 'auto';
45
- /** Total height */
46
- height?: number;
47
- /** Start week on Monday */
48
- mondayStart?: boolean;
49
- /** Read-only mode (default: true for schedule display) */
50
- readOnly?: boolean;
51
- /** Visible hour range [startHour, endHour) */
52
- visibleHours?: [number, number];
53
- /** Show toolbar */
54
- showToolbar?: boolean;
55
- /** Show agenda view toggle */
56
- showAgenda?: boolean;
57
- /** Map of category/title to color */
58
- colorMap?: Record<string, string>;
59
- /** Auto-assign colors */
60
- autoColor?: boolean;
61
- /** Event click handler */
62
- oneventclick?: (event: TimelineEvent) => void;
63
- /** Event create handler (only works when readOnly is false) */
64
- oneventcreate?: (range: { start: Date; end: Date }) => void;
65
- }
66
-
67
- let {
68
- events,
69
- schedule,
70
- theme = '',
71
- locale,
72
- dir,
73
- height = 560,
74
- mondayStart = true,
75
- readOnly = true,
76
- visibleHours,
77
- showToolbar = true,
78
- showAgenda = true,
79
- colorMap,
80
- autoColor,
81
- oneventclick,
82
- oneventcreate,
83
- }: Props = $props();
84
-
85
- // Auto-create the adapter from events or schedule
86
- const adapter = $derived.by(() => {
87
- if (schedule) {
88
- return createRecurringAdapter(schedule, {
89
- mondayStart,
90
- colorMap,
91
- autoColor,
92
- } satisfies RecurringAdapterOptions);
93
- }
94
- return createMemoryAdapter(events ?? [], {
95
- colorMap,
96
- autoColor,
97
- } satisfies MemoryAdapterOptions);
98
- });
99
-
100
- // Pre-wired views
101
- const views = $derived.by((): CalendarView[] => {
102
- const v: CalendarView[] = [
103
- {
104
- id: 'week-grid',
105
- label: 'Week',
106
- granularity: 'week',
107
- component: WeekGrid,
108
- },
109
- ];
110
- if (showAgenda) {
111
- v.push({
112
- id: 'agenda',
113
- label: 'Agenda',
114
- granularity: 'week',
115
- component: Agenda,
116
- props: { mode: 'week' },
117
- });
118
- }
119
- return v;
120
- });
121
- </script>
122
-
123
- <Calendar
124
- {adapter}
125
- {views}
126
- defaultView="week-grid"
127
- {theme}
128
- {locale}
129
- {dir}
130
- {height}
131
- {mondayStart}
132
- {readOnly}
133
- {visibleHours}
134
- {showToolbar}
135
- {oneventclick}
136
- oneventcreate={readOnly ? undefined : oneventcreate}
137
- />