@nomideusz/svelte-calendar 0.5.2 → 0.6.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 (44) hide show
  1. package/README.md +408 -178
  2. package/dist/adapters/memory.d.ts +7 -9
  3. package/dist/adapters/memory.js +8 -23
  4. package/dist/adapters/recurring.d.ts +3 -8
  5. package/dist/adapters/recurring.js +27 -30
  6. package/dist/calendar/Calendar.svelte +457 -46
  7. package/dist/calendar/Calendar.svelte.d.ts +55 -4
  8. package/dist/core/index.d.ts +2 -2
  9. package/dist/core/index.js +1 -1
  10. package/dist/core/locale.js +2 -2
  11. package/dist/core/palette.d.ts +5 -0
  12. package/dist/core/palette.js +8 -0
  13. package/dist/core/types.d.ts +14 -0
  14. package/dist/engine/event-store.svelte.d.ts +0 -17
  15. package/dist/engine/event-store.svelte.js +30 -17
  16. package/dist/engine/index.d.ts +1 -1
  17. package/dist/engine/view-state.svelte.d.ts +10 -5
  18. package/dist/engine/view-state.svelte.js +32 -16
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.js +3 -4
  21. package/dist/theme/auto.d.ts +53 -0
  22. package/dist/theme/auto.js +534 -0
  23. package/dist/theme/index.d.ts +3 -1
  24. package/dist/theme/index.js +3 -1
  25. package/dist/theme/presets.d.ts +20 -6
  26. package/dist/theme/presets.js +19 -6
  27. package/dist/views/agenda/AgendaDay.svelte +116 -37
  28. package/dist/views/agenda/AgendaWeek.svelte +219 -70
  29. package/dist/views/agenda/index.d.ts +0 -2
  30. package/dist/views/agenda/index.js +0 -2
  31. package/dist/views/index.d.ts +3 -2
  32. package/dist/views/index.js +3 -2
  33. package/dist/views/mobile/Mobile.svelte +17 -0
  34. package/dist/views/mobile/Mobile.svelte.d.ts +7 -0
  35. package/dist/views/mobile/MobileDay.svelte +665 -0
  36. package/dist/views/mobile/MobileDay.svelte.d.ts +20 -0
  37. package/dist/views/mobile/MobileWeek.svelte +456 -0
  38. package/dist/views/mobile/MobileWeek.svelte.d.ts +20 -0
  39. package/dist/views/mobile/index.d.ts +1 -0
  40. package/dist/views/mobile/index.js +1 -0
  41. package/dist/views/planner/PlannerDay.svelte +144 -10
  42. package/dist/views/planner/PlannerWeek.svelte +145 -40
  43. package/dist/widget/CalendarWidget.svelte +2 -17
  44. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  /**
3
- * AgendaWeek — rolling 7-day agenda view.
3
+ * AgendaWeek — rolling N-day agenda view.
4
4
  *
5
5
  * "The Week Ahead":
6
6
  * Today + tomorrow expanded with time slots/countdowns.
@@ -9,11 +9,11 @@
9
9
  *
10
10
  * Answers: "What's coming up and when do I need to be ready?"
11
11
  */
12
- import { getContext } from 'svelte';
12
+ import { getContext, type Snippet } from 'svelte';
13
13
  import { createClock } from '../../core/clock.svelte.js';
14
14
  import type { TimelineEvent } from '../../core/types.js';
15
15
  import { sod, DAY_MS, startOfWeek, dayNum, isAllDay, isMultiDay } from '../../core/time.js';
16
- import { weekdayLong, weekdayShort, monthShort, fmtTime as _fmtTime, fmtDuration, getLabels } from '../../core/locale.js';
16
+ import { weekdayLong, monthLong, fmtTime as _fmtTime, fmtDuration, getLabels } from '../../core/locale.js';
17
17
  import type { ViewState } from '../../engine/view-state.svelte.js';
18
18
 
19
19
  const L = $derived(getLabels());
@@ -43,6 +43,47 @@
43
43
 
44
44
  const clock = createClock();
45
45
  const viewState = getContext<ViewState>('calendar:viewState') as ViewState | undefined;
46
+ const showNavCtx = getContext<{ current: boolean }>('calendar:showNavigation') as { current: boolean } | undefined;
47
+ const equalDaysCtx = getContext<{ current: boolean }>('calendar:equalDays') as { current: boolean } | undefined;
48
+ const showDatesCtx = getContext<{ current: boolean }>('calendar:showDates') as { current: boolean } | undefined;
49
+ const hideDaysCtx = getContext<{ current: number[] | undefined }>('calendar:hideDays') as { current: number[] | undefined } | undefined;
50
+ const mobileCtx = getContext<{ current: boolean }>('calendar:mobile') as { current: boolean } | undefined;
51
+ const showNav = $derived(showNavCtx?.current ?? true);
52
+ const equalDays = $derived(equalDaysCtx?.current ?? false);
53
+ const showDates = $derived(showDatesCtx?.current ?? true);
54
+ const hideDays = $derived(hideDaysCtx?.current);
55
+ const isMobile = $derived(mobileCtx?.current ?? false);
56
+
57
+ // ── Swipe navigation (mobile) ──────────────────────
58
+ let swipeStartX = 0;
59
+ let swipeStartY = 0;
60
+ const SWIPE_THRESHOLD = 50;
61
+
62
+ function onPointerDown(e: PointerEvent) {
63
+ if (!isMobile) return;
64
+ swipeStartX = e.clientX;
65
+ swipeStartY = e.clientY;
66
+ }
67
+
68
+ function onPointerUp(e: PointerEvent) {
69
+ if (!isMobile) return;
70
+ const dx = e.clientX - swipeStartX;
71
+ const dy = e.clientY - swipeStartY;
72
+ if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
73
+ if (dx > 0) viewState?.prev();
74
+ else viewState?.next();
75
+ }
76
+ }
77
+
78
+ // ── New feature contexts ──
79
+ const dayHeaderSnippetCtx = getContext<{ current: Snippet<[{ date: Date; isToday: boolean; dayName: string }]> | undefined }>('calendar:dayHeaderSnippet') as { current: Snippet<[{ date: Date; isToday: boolean; dayName: string }]> | undefined } | undefined;
80
+ const callbacksCtx = getContext<{ oneventhover?: (event: TimelineEvent) => void }>('calendar:callbacks') as { oneventhover?: (event: TimelineEvent) => void } | undefined;
81
+ const disabledDatesCtx = getContext<{ current: Date[] | undefined }>('calendar:disabledDates') as { current: Date[] | undefined } | undefined;
82
+
83
+ const dayHeaderSnippet = $derived(dayHeaderSnippetCtx?.current);
84
+ const oneventhover = $derived(callbacksCtx?.oneventhover);
85
+ const disabledDates = $derived(disabledDatesCtx?.current);
86
+ const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
46
87
 
47
88
  // ── Format helpers ──────────────────────────────────
48
89
  function fmtTime(d: Date): string {
@@ -128,12 +169,20 @@
128
169
 
129
170
  const weekStartMs = $derived(
130
171
  focusDate
131
- ? startOfWeek(sod(focusDate.getTime()), mondayStart)
132
- : startOfWeek(clock.today, mondayStart),
172
+ ? (viewState?.dayCount === 7
173
+ ? startOfWeek(sod(focusDate.getTime()), mondayStart)
174
+ : sod(focusDate.getTime()))
175
+ : (viewState?.dayCount === 7
176
+ ? startOfWeek(clock.today, mondayStart)
177
+ : clock.today),
133
178
  );
134
179
 
180
+ const customDays = $derived(viewState?.dayCount ?? 7);
181
+
135
182
  const isThisWeek = $derived(
136
- weekStartMs === startOfWeek(clock.today, mondayStart),
183
+ customDays === 7
184
+ ? weekStartMs === startOfWeek(clock.today, mondayStart)
185
+ : clock.today >= weekStartMs && clock.today < weekStartMs + customDays * DAY_MS,
137
186
  );
138
187
 
139
188
  const weekDays = $derived.by((): DayGroup[] => {
@@ -141,7 +190,7 @@
141
190
  const todayMs = clock.today;
142
191
  const tomorrowMs = todayMs + DAY_MS;
143
192
  const out: DayGroup[] = [];
144
- for (let i = 0; i < 7; i++) {
193
+ for (let i = 0; i < customDays; i++) {
145
194
  const ms = weekStartMs + i * DAY_MS;
146
195
  const dEnd = ms + DAY_MS;
147
196
  const dayEvts = events
@@ -163,15 +212,22 @@
163
212
  else upcomingEvents.push(ev);
164
213
  }
165
214
  let tier: DayTier;
166
- if (ms === todayMs) tier = 'today';
167
- else if (ms === tomorrowMs) tier = 'tomorrow';
168
- else if (ms < todayMs) tier = 'past';
169
- else tier = 'upcoming';
215
+ if (equalDays) {
216
+ tier = 'upcoming';
217
+ } else if (ms === todayMs) {
218
+ tier = 'today';
219
+ } else if (ms === tomorrowMs) {
220
+ tier = 'tomorrow';
221
+ } else if (ms < todayMs) {
222
+ tier = 'past';
223
+ } else {
224
+ tier = 'upcoming';
225
+ }
170
226
 
171
227
  out.push({
172
228
  ms,
173
- dayName: tier === 'today' || tier === 'tomorrow' ? weekdayLong(ms, locale) : weekdayShort(ms, locale),
174
- dateLabel: `${monthShort(ms, locale)} ${dayNum(ms)}`,
229
+ dayName: weekdayLong(ms, locale),
230
+ dateLabel: `${monthLong(ms, locale)} ${dayNum(ms)}`,
175
231
  tier,
176
232
  events: dayEvts,
177
233
  allDayEvents: allDayEvts,
@@ -182,12 +238,22 @@
182
238
  totalHours: Math.round((totalMinutes / 60) * 10) / 10,
183
239
  });
184
240
  }
241
+
242
+ // Filter hidden days if hideDays is set
243
+ if (hideDays?.length) {
244
+ return out.filter((d) => {
245
+ const jsDay = new Date(d.ms).getDay();
246
+ const iso = jsDay === 0 ? 7 : jsDay;
247
+ return !hideDays.includes(iso);
248
+ });
249
+ }
250
+
185
251
  return out;
186
252
  });
187
253
  </script>
188
254
 
189
255
  <!-- ═══ Shared event card snippet ═══ -->
190
- {#snippet eventCard(ev: TimelineEvent, isNow: boolean)}
256
+ {#snippet eventCard(ev: TimelineEvent, isNow: boolean, eta?: string)}
191
257
  <div
192
258
  class="ag-card"
193
259
  class:ag-card--selected={selectedEventId === ev.id}
@@ -196,9 +262,9 @@
196
262
  tabindex="0"
197
263
  aria-label="{ev.title}, {fmtTime(ev.start)} to {fmtTime(ev.end)}, {duration(ev)}"
198
264
  onclick={() => handleClick(ev)}
265
+ onpointerenter={() => oneventhover?.(ev)}
199
266
  onkeydown={(e) => handleKeydown(e, ev)}
200
267
  >
201
- <div class="ag-card-stripe"></div>
202
268
  <div class="ag-card-body">
203
269
  <span class="ag-card-title">{ev.title}</span>
204
270
  {#if ev.subtitle}
@@ -211,6 +277,9 @@
211
277
  {fmtTime(ev.start)} – {fmtTime(ev.end)}
212
278
  {/if}
213
279
  <span class="ag-card-dur">{duration(ev)}</span>
280
+ {#if eta}
281
+ <span class="ag-card-eta">{eta}</span>
282
+ {/if}
214
283
  </span>
215
284
  {#if ev.tags?.length}
216
285
  <div class="ag-card-tags">
@@ -228,24 +297,30 @@
228
297
  </div>
229
298
  {/snippet}
230
299
 
300
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
231
301
  <div
232
302
  class="ag ag--week"
303
+ class:ag--mobile={isMobile}
233
304
  style={style || undefined}
305
+ onpointerdown={onPointerDown}
306
+ onpointerup={onPointerUp}
234
307
  >
235
308
  <div class="ag-body" role="list" aria-label={L.weekAhead}>
236
309
  {#each weekDays as day (day.ms)}
237
310
  {@const expanded = day.tier === 'today' || day.tier === 'tomorrow'}
238
311
  {#if day.tier === 'past'}
239
312
  <!-- Past day: single collapsed line -->
240
- <div class="ag-wday ag-wday--past" role="listitem">
313
+ <div class="ag-wday ag-wday--past" class:ag-wday--disabled={disabledSet.has(day.ms)} role="listitem">
241
314
  <div class="ag-wday-head">
242
315
  <div class="ag-wday-head-left">
243
316
  <span class="ag-wday-name">{day.dayName}</span>
244
- <span class="ag-wday-date">{day.dateLabel}</span>
245
- {#if day.events.length > 0}
246
- <span class="ag-wday-done">✓ {day.events.length}</span>
247
- {/if}
317
+ {#if showDates}<span class="ag-wday-date">{day.dateLabel}</span>{/if}
248
318
  </div>
319
+ {#if dayHeaderSnippet}
320
+ <div class="ag-wday-custom-header">
321
+ {@render dayHeaderSnippet({ date: new Date(day.ms), isToday: false, dayName: day.dayName })}
322
+ </div>
323
+ {/if}
249
324
  </div>
250
325
  </div>
251
326
  {:else}
@@ -253,6 +328,8 @@
253
328
  class="ag-wday"
254
329
  class:ag-wday--today={day.tier === 'today'}
255
330
  class:ag-wday--tomorrow={day.tier === 'tomorrow'}
331
+ class:ag-wday--equal={equalDays}
332
+ class:ag-wday--disabled={disabledSet.has(day.ms)}
256
333
  role="listitem"
257
334
  >
258
335
  <!-- Day header -->
@@ -264,8 +341,13 @@
264
341
  <span class="ag-wday-badge ag-wday-badge--muted">{L.tomorrow}</span>
265
342
  {/if}
266
343
  <span class="ag-wday-name">{day.dayName}</span>
267
- <span class="ag-wday-date">{day.dateLabel}</span>
344
+ {#if showDates}<span class="ag-wday-date">{day.dateLabel}</span>{/if}
268
345
  </div>
346
+ {#if dayHeaderSnippet}
347
+ <div class="ag-wday-custom-header">
348
+ {@render dayHeaderSnippet({ date: new Date(day.ms), isToday: day.tier === 'today', dayName: day.dayName })}
349
+ </div>
350
+ {/if}
269
351
  </div>
270
352
 
271
353
  {#if day.allDayEvents.length > 0}
@@ -279,6 +361,7 @@
279
361
  tabindex="0"
280
362
  aria-label="{ev.title}, {L.allDay}"
281
363
  onclick={() => handleClick(ev)}
364
+ onpointerenter={() => oneventhover?.(ev)}
282
365
  onkeydown={(e) => handleKeydown(e, ev)}
283
366
  >
284
367
  <span class="ag-allday-dot"></span>
@@ -290,6 +373,20 @@
290
373
 
291
374
  {#if day.events.length === 0}
292
375
  <div class="ag-wday-empty">{L.noEvents}</div>
376
+ {:else if equalDays}
377
+ <!-- Equal days: card layout for all days, no time-relative badges -->
378
+ <div class="ag-wday-expanded">
379
+ {#each groupIntoSlots(day.timedEvents) as slot (slot.startMs)}
380
+ <div class="ag-wslot">
381
+
382
+ <div class="ag-wslot-cards" class:ag-wslot-cards--multi={slot.events.length > 1}>
383
+ {#each slot.events as ev (ev.id)}
384
+ {@render eventCard(ev, false)}
385
+ {/each}
386
+ </div>
387
+ </div>
388
+ {/each}
389
+ </div>
293
390
  {:else if expanded}
294
391
  <!-- Expanded: today/tomorrow get full slot treatment -->
295
392
  <div class="ag-wday-expanded">
@@ -305,15 +402,10 @@
305
402
  {/if}
306
403
  {#each groupIntoSlots(day.upcomingEvents) as slot (slot.startMs)}
307
404
  <div class="ag-wslot">
308
- <div class="ag-wslot-header">
309
- <span class="ag-wslot-time">{fmtTime(new Date(slot.startMs))}</span>
310
- {#if day.tier === 'today'}
311
- <span class="ag-wslot-eta">{timeUntilMs(slot.startMs)}</span>
312
- {/if}
313
- </div>
405
+
314
406
  <div class="ag-wslot-cards" class:ag-wslot-cards--multi={slot.events.length > 1}>
315
407
  {#each slot.events as ev (ev.id)}
316
- {@render eventCard(ev, false)}
408
+ {@render eventCard(ev, false, day.tier === 'today' ? timeUntilMs(ev.start.getTime()) : undefined)}
317
409
  {/each}
318
410
  </div>
319
411
  </div>
@@ -325,7 +417,7 @@
325
417
  {:else}
326
418
  <!-- Compact: future days get minimal rows -->
327
419
  <div class="ag-wday-compact">
328
- {#each day.events.slice(0, 3) as ev (ev.id)}
420
+ {#each day.timedEvents.slice(0, 4) as ev (ev.id)}
329
421
  <div
330
422
  class="ag-compact"
331
423
  class:ag-compact--selected={selectedEventId === ev.id}
@@ -333,8 +425,7 @@
333
425
  role="button"
334
426
  tabindex="0"
335
427
  aria-label="{ev.title}, {fmtTime(ev.start)}, {duration(ev)}"
336
- onclick={() => handleClick(ev)}
337
- onkeydown={(e) => handleKeydown(e, ev)}
428
+ onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
338
429
  >
339
430
  <span class="ag-compact-dot"></span>
340
431
  <span class="ag-compact-time">{fmtTime(ev.start)}</span>
@@ -350,8 +441,8 @@
350
441
  <span class="ag-compact-dur">{duration(ev)}</span>
351
442
  </div>
352
443
  {/each}
353
- {#if day.events.length > 3}
354
- <div class="ag-compact-more">{L.nMore(day.events.length - 3)}</div>
444
+ {#if day.timedEvents.length > 4}
445
+ <div class="ag-compact-more">{L.nMore(day.timedEvents.length - 4)}</div>
355
446
  {/if}
356
447
  </div>
357
448
  {/if}
@@ -360,7 +451,8 @@
360
451
  {/each}
361
452
  </div>
362
453
 
363
- <!-- ── Floating nav pills ── -->
454
+ <!-- ── Floating nav pills (desktop only — mobile uses Calendar header) ── -->
455
+ {#if showNav && !isMobile}
364
456
  <nav class="ag-nav" aria-label={L.weekNavigation}>
365
457
  <button
366
458
  class="ag-nav-pill ag-nav-today"
@@ -386,6 +478,7 @@
386
478
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="12" height="12" aria-hidden="true"><path d="M6 3l5 5-5 5"/></svg>
387
479
  </button>
388
480
  </nav>
481
+ {/if}
389
482
  </div>
390
483
 
391
484
  <style>
@@ -514,15 +607,16 @@
514
607
  .ag-card {
515
608
  display: flex;
516
609
  align-items: stretch;
517
- border-radius: 10px;
518
- background: var(--dt-surface, #191919);
519
- border: 1px solid var(--dt-border, rgba(255, 255, 255, 0.06));
610
+ border-radius: 6px;
611
+ background: color-mix(in srgb, var(--ev-color) 12%, var(--dt-surface, #191919));
612
+ border: 1px solid color-mix(in srgb, var(--ev-color) 8%, var(--dt-border, rgba(255, 255, 255, 0.06)));
520
613
  overflow: hidden;
521
614
  cursor: pointer;
522
- transition: border-color 150ms;
615
+ transition: background 150ms, border-color 150ms;
523
616
  }
524
617
  .ag-card:hover {
525
- border-color: color-mix(in srgb, var(--ev-color) 40%, transparent);
618
+ background: color-mix(in srgb, var(--ev-color) 20%, var(--dt-surface, #191919));
619
+ border-color: color-mix(in srgb, var(--ev-color) 30%, transparent);
526
620
  }
527
621
  .ag-card:focus-visible {
528
622
  outline: 2px solid var(--dt-accent, #ff6b4a);
@@ -530,18 +624,13 @@
530
624
  }
531
625
  .ag-card--selected {
532
626
  border-color: var(--ev-color);
533
- background: color-mix(in srgb, var(--ev-color) 6%, var(--dt-surface, #191919));
534
- }
535
- .ag-card-stripe {
536
- width: 3px;
537
- background: var(--ev-color, var(--dt-accent));
538
- flex-shrink: 0;
627
+ background: color-mix(in srgb, var(--ev-color) 20%, var(--dt-surface, #191919));
539
628
  }
540
629
  .ag-card-body {
541
- padding: 10px 12px;
630
+ padding: 7px 10px;
542
631
  display: flex;
543
632
  flex-direction: column;
544
- gap: 4px;
633
+ gap: 2px;
545
634
  min-width: 0;
546
635
  flex: 1;
547
636
  }
@@ -555,6 +644,8 @@
555
644
  flex: 1;
556
645
  }
557
646
  .ag-card-meta {
647
+ display: flex;
648
+ align-items: center;
558
649
  font-size: 11px;
559
650
  color: var(--dt-text-2, rgba(255, 255, 255, 0.5));
560
651
  font-family: var(--dt-mono, monospace);
@@ -564,6 +655,14 @@
564
655
  margin-left: 6px;
565
656
  color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
566
657
  }
658
+ .ag-card-eta {
659
+ margin-left: auto;
660
+ font-size: 10px;
661
+ font-weight: 600;
662
+ color: color-mix(in srgb, var(--ev-color) 80%, var(--dt-text));
663
+ opacity: 0.85;
664
+ letter-spacing: 0.02em;
665
+ }
567
666
  .ag-card-sub {
568
667
  font-size: 11px;
569
668
  color: var(--dt-text-2, rgba(255, 255, 255, 0.45));
@@ -601,19 +700,39 @@
601
700
  border-bottom: 1px solid var(--dt-border, rgba(255, 255, 255, 0.06));
602
701
  }
603
702
  .ag-wday--today {
604
- background: color-mix(in srgb, var(--dt-accent, #ff6b4a) 4%, transparent);
703
+ background: color-mix(in srgb, var(--dt-accent, #ff6b4a) 2%, transparent);
704
+ }
705
+ .ag-wday--tomorrow .ag-card {
706
+ opacity: 0.82;
605
707
  }
606
708
  .ag-wday--past {
607
- opacity: 0.35;
709
+ opacity: 0.4;
608
710
  }
609
711
  .ag-wday--past .ag-wday-head {
610
- padding: 6px 20px;
712
+ padding: 8px 20px;
611
713
  }
612
- .ag-wday-done {
613
- font-size: 10px;
614
- font-family: var(--dt-mono, monospace);
615
- color: var(--dt-success, rgba(120, 200, 140, 0.6));
714
+ .ag-wday--disabled {
715
+ opacity: 0.35;
716
+ pointer-events: none;
717
+ position: relative;
718
+ }
719
+ .ag-wday--disabled::after {
720
+ content: '';
721
+ position: absolute;
722
+ inset: 0;
723
+ background: repeating-linear-gradient(
724
+ 135deg,
725
+ transparent,
726
+ transparent 4px,
727
+ rgba(128, 128, 128, 0.08) 4px,
728
+ rgba(128, 128, 128, 0.08) 8px
729
+ );
730
+ pointer-events: none;
616
731
  }
732
+ .ag-wday-custom-header {
733
+ padding: 2px 0 4px;
734
+ }
735
+
617
736
  .ag-wday-head {
618
737
  display: flex;
619
738
  justify-content: space-between;
@@ -652,29 +771,26 @@
652
771
  font-family: var(--dt-mono, monospace);
653
772
  color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
654
773
  }
774
+
655
775
  .ag-wday-empty {
656
- padding: 4px 20px 10px;
657
- font-size: 12px;
658
- color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
776
+ padding: 2px 20px 10px;
777
+ font-size: 11px;
778
+ color: var(--dt-text-3, rgba(255, 255, 255, 0.2));
779
+ font-style: italic;
659
780
  }
660
781
 
661
782
  /* Expanded day */
662
783
  .ag-wday-expanded {
663
- padding: 0 20px 12px;
784
+ padding: 0 20px 10px;
664
785
  }
665
786
  .ag-wslot {
666
- margin-bottom: 6px;
787
+ margin-bottom: 4px;
667
788
  }
668
789
  .ag-wslot-header {
669
790
  display: flex;
670
791
  align-items: baseline;
671
792
  gap: 8px;
672
- padding: 4px 0;
673
- }
674
- .ag-wslot-time {
675
- font-size: 12px;
676
- font-weight: 600;
677
- font-family: var(--dt-mono, monospace);
793
+ padding: 2px 0;
678
794
  }
679
795
  .ag-wslot-now {
680
796
  font-size: 9px;
@@ -683,11 +799,6 @@
683
799
  text-transform: uppercase;
684
800
  color: var(--dt-accent, #ff6b4a);
685
801
  }
686
- .ag-wslot-eta {
687
- font-size: 11px;
688
- font-weight: 500;
689
- color: var(--dt-accent, #ff6b4a);
690
- }
691
802
  .ag-wslot-cards {
692
803
  display: flex;
693
804
  flex-direction: column;
@@ -716,6 +827,12 @@
716
827
  padding: 4px 0;
717
828
  cursor: pointer;
718
829
  }
830
+ .ag-compact--selected {
831
+ background: color-mix(in srgb, var(--ev-color) 10%, transparent);
832
+ border-radius: 4px;
833
+ padding-left: 6px;
834
+ padding-right: 6px;
835
+ }
719
836
  .ag-compact:hover .ag-compact-title {
720
837
  color: var(--dt-text);
721
838
  }
@@ -772,4 +889,36 @@
772
889
  color: var(--dt-text-3);
773
890
  padding: 2px 0 0 13px;
774
891
  }
892
+
893
+ /* ═══ Mobile adaptations ═══ */
894
+ .ag--mobile .ag-wday-head {
895
+ padding: 12px 16px;
896
+ }
897
+ .ag--mobile .ag-wday-expanded {
898
+ padding: 0 16px 12px;
899
+ }
900
+ .ag--mobile .ag-wday-compact {
901
+ padding: 0 16px 12px;
902
+ }
903
+ .ag--mobile .ag-card-body {
904
+ padding: 12px 14px;
905
+ }
906
+ .ag--mobile .ag-card-title {
907
+ font-size: 14px;
908
+ }
909
+ .ag--mobile .ag-compact {
910
+ padding: 8px 0;
911
+ }
912
+ .ag--mobile .ag-compact-title {
913
+ font-size: 14px;
914
+ }
915
+ .ag--mobile .ag-compact-time {
916
+ font-size: 12px;
917
+ }
918
+ .ag--mobile .ag-nav-pill {
919
+ padding: 10px 16px;
920
+ }
921
+ .ag--mobile .ag-wslot-cards--multi {
922
+ grid-template-columns: 1fr;
923
+ }
775
924
  </style>
@@ -1,3 +1 @@
1
1
  export { default as Agenda } from './Agenda.svelte';
2
- export { default as AgendaDay } from './AgendaDay.svelte';
3
- export { default as AgendaWeek } from './AgendaWeek.svelte';
@@ -1,3 +1 @@
1
1
  export { default as Agenda } from './Agenda.svelte';
2
- export { default as AgendaDay } from './AgendaDay.svelte';
3
- export { default as AgendaWeek } from './AgendaWeek.svelte';
@@ -1,2 +1,3 @@
1
- export { Planner } from './planner/index.js';
2
- export { Agenda, AgendaDay, AgendaWeek } from './agenda/index.js';
1
+ export { default as Planner } from './planner/Planner.svelte';
2
+ export { default as Agenda } from './agenda/Agenda.svelte';
3
+ export { default as Mobile } from './mobile/Mobile.svelte';
@@ -1,2 +1,3 @@
1
- export { Planner } from './planner/index.js';
2
- export { Agenda, AgendaDay, AgendaWeek } from './agenda/index.js';
1
+ export { default as Planner } from './planner/Planner.svelte';
2
+ export { default as Agenda } from './agenda/Agenda.svelte';
3
+ export { default as Mobile } from './mobile/Mobile.svelte';
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import MobileDay from './MobileDay.svelte';
3
+ import MobileWeek from './MobileWeek.svelte';
4
+
5
+ interface Props {
6
+ mode?: 'day' | 'week';
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ let { mode = 'day', ...rest }: Props = $props();
11
+ </script>
12
+
13
+ {#if mode === 'day'}
14
+ <MobileDay {...rest} />
15
+ {:else}
16
+ <MobileWeek {...rest} />
17
+ {/if}
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ mode?: 'day' | 'week';
3
+ [key: string]: unknown;
4
+ }
5
+ declare const Mobile: import("svelte").Component<Props, {}, "">;
6
+ type Mobile = ReturnType<typeof Mobile>;
7
+ export default Mobile;