@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.
- package/README.md +408 -178
- package/dist/adapters/memory.d.ts +7 -9
- package/dist/adapters/memory.js +8 -23
- package/dist/adapters/recurring.d.ts +3 -8
- package/dist/adapters/recurring.js +27 -30
- package/dist/calendar/Calendar.svelte +457 -46
- package/dist/calendar/Calendar.svelte.d.ts +55 -4
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/core/locale.js +2 -2
- package/dist/core/palette.d.ts +5 -0
- package/dist/core/palette.js +8 -0
- package/dist/core/types.d.ts +14 -0
- package/dist/engine/event-store.svelte.d.ts +0 -17
- package/dist/engine/event-store.svelte.js +30 -17
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/view-state.svelte.d.ts +10 -5
- package/dist/engine/view-state.svelte.js +32 -16
- package/dist/index.d.ts +7 -7
- package/dist/index.js +3 -4
- package/dist/theme/auto.d.ts +53 -0
- package/dist/theme/auto.js +534 -0
- package/dist/theme/index.d.ts +3 -1
- package/dist/theme/index.js +3 -1
- package/dist/theme/presets.d.ts +20 -6
- package/dist/theme/presets.js +19 -6
- package/dist/views/agenda/AgendaDay.svelte +116 -37
- package/dist/views/agenda/AgendaWeek.svelte +219 -70
- package/dist/views/agenda/index.d.ts +0 -2
- package/dist/views/agenda/index.js +0 -2
- package/dist/views/index.d.ts +3 -2
- package/dist/views/index.js +3 -2
- package/dist/views/mobile/Mobile.svelte +17 -0
- package/dist/views/mobile/Mobile.svelte.d.ts +7 -0
- package/dist/views/mobile/MobileDay.svelte +665 -0
- package/dist/views/mobile/MobileDay.svelte.d.ts +20 -0
- package/dist/views/mobile/MobileWeek.svelte +456 -0
- package/dist/views/mobile/MobileWeek.svelte.d.ts +20 -0
- package/dist/views/mobile/index.d.ts +1 -0
- package/dist/views/mobile/index.js +1 -0
- package/dist/views/planner/PlannerDay.svelte +144 -10
- package/dist/views/planner/PlannerWeek.svelte +145 -40
- package/dist/widget/CalendarWidget.svelte +2 -17
- package/package.json +2 -1
|
@@ -44,6 +44,42 @@
|
|
|
44
44
|
|
|
45
45
|
const clock = createClock();
|
|
46
46
|
const viewState = getContext<ViewState>('calendar:viewState') as ViewState | undefined;
|
|
47
|
+
const showNavCtx = getContext<{ current: boolean }>('calendar:showNavigation') as { current: boolean } | undefined;
|
|
48
|
+
const equalDaysCtx = getContext<{ current: boolean }>('calendar:equalDays') as { current: boolean } | undefined;
|
|
49
|
+
const showDatesCtx = getContext<{ current: boolean }>('calendar:showDates') as { current: boolean } | 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 isMobile = $derived(mobileCtx?.current ?? false);
|
|
55
|
+
|
|
56
|
+
// ── Swipe navigation (mobile) ──────────────────────
|
|
57
|
+
let swipeStartX = 0;
|
|
58
|
+
let swipeStartY = 0;
|
|
59
|
+
const SWIPE_THRESHOLD = 50;
|
|
60
|
+
|
|
61
|
+
function onPointerDown(e: PointerEvent) {
|
|
62
|
+
if (!isMobile) return;
|
|
63
|
+
swipeStartX = e.clientX;
|
|
64
|
+
swipeStartY = e.clientY;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onPointerUp(e: PointerEvent) {
|
|
68
|
+
if (!isMobile) return;
|
|
69
|
+
const dx = e.clientX - swipeStartX;
|
|
70
|
+
const dy = e.clientY - swipeStartY;
|
|
71
|
+
if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
|
|
72
|
+
if (dx > 0) viewState?.prev();
|
|
73
|
+
else viewState?.next();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── New feature contexts ──
|
|
78
|
+
const callbacksCtx = getContext<{ oneventhover?: (event: TimelineEvent) => void }>('calendar:callbacks') as { oneventhover?: (event: TimelineEvent) => void } | undefined;
|
|
79
|
+
const disabledDatesCtx = getContext<{ current: Date[] | undefined }>('calendar:disabledDates') as { current: Date[] | undefined } | undefined;
|
|
80
|
+
const oneventhover = $derived(callbacksCtx?.oneventhover);
|
|
81
|
+
const disabledDates = $derived(disabledDatesCtx?.current);
|
|
82
|
+
const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
47
83
|
|
|
48
84
|
// ── Format helpers ──────────────────────────────────
|
|
49
85
|
function fmtTime(d: Date): string {
|
|
@@ -114,14 +150,18 @@
|
|
|
114
150
|
const dayMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
|
|
115
151
|
const dayEnd = $derived(dayMs + DAY_MS);
|
|
116
152
|
const isToday = $derived(dayMs === clock.today);
|
|
117
|
-
const isPastDay = $derived(dayMs < clock.today);
|
|
153
|
+
const isPastDay = $derived(equalDays ? false : dayMs < clock.today);
|
|
118
154
|
|
|
119
155
|
const dateLabel = $derived(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
156
|
+
showDates
|
|
157
|
+
? new Date(dayMs).toLocaleDateString(locale ?? 'en-US', {
|
|
158
|
+
weekday: 'long',
|
|
159
|
+
month: 'long',
|
|
160
|
+
day: 'numeric',
|
|
161
|
+
})
|
|
162
|
+
: new Date(dayMs).toLocaleDateString(locale ?? 'en-US', {
|
|
163
|
+
weekday: 'long',
|
|
164
|
+
})
|
|
125
165
|
);
|
|
126
166
|
|
|
127
167
|
/** All events for this day, sorted chronologically */
|
|
@@ -165,10 +205,15 @@
|
|
|
165
205
|
});
|
|
166
206
|
</script>
|
|
167
207
|
|
|
208
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
168
209
|
<div
|
|
169
210
|
class="ag ag--day"
|
|
211
|
+
class:ag--disabled={disabledSet.has(dayMs)}
|
|
212
|
+
class:ag--mobile={isMobile}
|
|
170
213
|
style={style || undefined}
|
|
171
214
|
style:height={height ? `${height}px` : undefined}
|
|
215
|
+
onpointerdown={onPointerDown}
|
|
216
|
+
onpointerup={onPointerUp}
|
|
172
217
|
>
|
|
173
218
|
<div class="ag-body" role="list" aria-label={L.todaysLineup}>
|
|
174
219
|
{#if allDayBanner.length > 0}
|
|
@@ -185,6 +230,7 @@
|
|
|
185
230
|
tabindex="0"
|
|
186
231
|
aria-label="{ev.title}, {L.allDay}"
|
|
187
232
|
onclick={() => handleClick(ev)}
|
|
233
|
+
onpointerenter={() => oneventhover?.(ev)}
|
|
188
234
|
onkeydown={(e) => handleKeydown(e, ev)}
|
|
189
235
|
>
|
|
190
236
|
<span class="ag-allday-dot"></span>
|
|
@@ -230,8 +276,7 @@
|
|
|
230
276
|
role="button"
|
|
231
277
|
tabindex="0"
|
|
232
278
|
aria-label="{ev.title}, {L.happeningNow}, {L.percentComplete(Math.round(progress(ev) * 100))}"
|
|
233
|
-
onclick={() => handleClick(ev)}
|
|
234
|
-
onkeydown={(e) => handleKeydown(e, ev)}
|
|
279
|
+
onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
|
|
235
280
|
>
|
|
236
281
|
<div class="ag-q-now-dot"></div>
|
|
237
282
|
<div class="ag-q-now-title">{ev.title}</div>
|
|
@@ -266,9 +311,9 @@
|
|
|
266
311
|
tabindex="0"
|
|
267
312
|
aria-label="{ev.title}, {fmtTime(ev.start)}, {duration(ev)}"
|
|
268
313
|
onclick={() => handleClick(ev)}
|
|
314
|
+
onpointerenter={() => oneventhover?.(ev)}
|
|
269
315
|
onkeydown={(e) => handleKeydown(e, ev)}
|
|
270
316
|
>
|
|
271
|
-
<div class="ag-card-stripe"></div>
|
|
272
317
|
<div class="ag-card-body">
|
|
273
318
|
<div class="ag-card-top">
|
|
274
319
|
<span class="ag-card-title">{ev.title}</span>
|
|
@@ -309,8 +354,7 @@
|
|
|
309
354
|
role="button"
|
|
310
355
|
tabindex="0"
|
|
311
356
|
aria-label="{ev.title}, {fmtTime(ev.start)} to {fmtTime(ev.end)}"
|
|
312
|
-
onclick={() => handleClick(ev)}
|
|
313
|
-
onkeydown={(e) => handleKeydown(e, ev)}
|
|
357
|
+
onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
|
|
314
358
|
>
|
|
315
359
|
<span class="ag-log-check">✓</span>
|
|
316
360
|
<span class="ag-log-time">{fmtTime(ev.start)}</span>
|
|
@@ -337,10 +381,8 @@
|
|
|
337
381
|
role="button"
|
|
338
382
|
tabindex="0"
|
|
339
383
|
aria-label="{ev.title}, {fmtTime(ev.start)} to {fmtTime(ev.end)}, {duration(ev)}"
|
|
340
|
-
onclick={() => handleClick(ev)}
|
|
341
|
-
onkeydown={(e) => handleKeydown(e, ev)}
|
|
384
|
+
onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
|
|
342
385
|
>
|
|
343
|
-
<div class="ag-card-stripe"></div>
|
|
344
386
|
<div class="ag-card-body">
|
|
345
387
|
<div class="ag-card-top">
|
|
346
388
|
<span class="ag-card-order">{i + 1}</span>
|
|
@@ -368,9 +410,12 @@
|
|
|
368
410
|
{/if}
|
|
369
411
|
</div>
|
|
370
412
|
|
|
371
|
-
|
|
413
|
+
{#if !isMobile}
|
|
414
|
+
<div class="ag-date-label">{dateLabel}</div>
|
|
415
|
+
{/if}
|
|
372
416
|
|
|
373
|
-
<!-- ── Floating nav pills ── -->
|
|
417
|
+
<!-- ── Floating nav pills (desktop only — mobile uses Calendar header) ── -->
|
|
418
|
+
{#if showNav && !isMobile}
|
|
374
419
|
<nav class="ag-nav" aria-label={L.dayNavigation}>
|
|
375
420
|
<button
|
|
376
421
|
class="ag-nav-pill ag-nav-today"
|
|
@@ -396,6 +441,7 @@
|
|
|
396
441
|
<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>
|
|
397
442
|
</button>
|
|
398
443
|
</nav>
|
|
444
|
+
{/if}
|
|
399
445
|
</div>
|
|
400
446
|
|
|
401
447
|
<style>
|
|
@@ -483,6 +529,11 @@
|
|
|
483
529
|
font-family: var(--dt-sans, system-ui, sans-serif);
|
|
484
530
|
}
|
|
485
531
|
|
|
532
|
+
.ag--disabled {
|
|
533
|
+
opacity: 0.4;
|
|
534
|
+
pointer-events: none;
|
|
535
|
+
}
|
|
536
|
+
|
|
486
537
|
/* ═══ Body ═══ */
|
|
487
538
|
.ag-body {
|
|
488
539
|
flex: 1;
|
|
@@ -563,13 +614,14 @@
|
|
|
563
614
|
display: flex;
|
|
564
615
|
align-items: stretch;
|
|
565
616
|
border-radius: 10px;
|
|
566
|
-
background: var(--dt-surface, #191919);
|
|
567
|
-
border: 1px solid var(--dt-border, rgba(255, 255, 255, 0.06));
|
|
617
|
+
background: color-mix(in srgb, var(--ev-color) 15%, var(--dt-surface, #191919));
|
|
618
|
+
border: 1px solid color-mix(in srgb, var(--ev-color) 10%, var(--dt-border, rgba(255, 255, 255, 0.06)));
|
|
568
619
|
overflow: hidden;
|
|
569
620
|
cursor: pointer;
|
|
570
|
-
transition: border-color 150ms;
|
|
621
|
+
transition: background 150ms, border-color 150ms;
|
|
571
622
|
}
|
|
572
623
|
.ag-card:hover {
|
|
624
|
+
background: color-mix(in srgb, var(--ev-color) 25%, var(--dt-surface, #191919));
|
|
573
625
|
border-color: color-mix(in srgb, var(--ev-color) 40%, transparent);
|
|
574
626
|
}
|
|
575
627
|
.ag-card:focus-visible {
|
|
@@ -578,12 +630,7 @@
|
|
|
578
630
|
}
|
|
579
631
|
.ag-card--selected {
|
|
580
632
|
border-color: var(--ev-color);
|
|
581
|
-
background: color-mix(in srgb, var(--ev-color)
|
|
582
|
-
}
|
|
583
|
-
.ag-card-stripe {
|
|
584
|
-
width: 3px;
|
|
585
|
-
background: var(--ev-color, var(--dt-accent));
|
|
586
|
-
flex-shrink: 0;
|
|
633
|
+
background: color-mix(in srgb, var(--ev-color) 22%, var(--dt-surface, #191919));
|
|
587
634
|
}
|
|
588
635
|
.ag-card-body {
|
|
589
636
|
padding: 10px 12px;
|
|
@@ -642,9 +689,7 @@
|
|
|
642
689
|
margin-bottom: 6px;
|
|
643
690
|
transition: border-color 150ms, transform 100ms;
|
|
644
691
|
}
|
|
645
|
-
|
|
646
|
-
width: 3.5px;
|
|
647
|
-
}
|
|
692
|
+
|
|
648
693
|
.ag-card--q .ag-card-body {
|
|
649
694
|
gap: 3px;
|
|
650
695
|
}
|
|
@@ -660,8 +705,8 @@
|
|
|
660
705
|
white-space: nowrap;
|
|
661
706
|
}
|
|
662
707
|
.ag-card--hero {
|
|
663
|
-
background: color-mix(in srgb, var(--ev-color)
|
|
664
|
-
border-color: color-mix(in srgb, var(--ev-color)
|
|
708
|
+
background: color-mix(in srgb, var(--ev-color) 22%, var(--dt-surface, #191919));
|
|
709
|
+
border-color: color-mix(in srgb, var(--ev-color) 30%, transparent);
|
|
665
710
|
}
|
|
666
711
|
.ag-card--hero .ag-card-title {
|
|
667
712
|
font-size: 16px;
|
|
@@ -678,9 +723,7 @@
|
|
|
678
723
|
}
|
|
679
724
|
|
|
680
725
|
/* ── Plan card variant ── */
|
|
681
|
-
|
|
682
|
-
width: 3.5px;
|
|
683
|
-
}
|
|
726
|
+
|
|
684
727
|
.ag-card--plan .ag-card-body {
|
|
685
728
|
padding: 12px 14px;
|
|
686
729
|
gap: 3px;
|
|
@@ -699,8 +742,8 @@
|
|
|
699
742
|
font-size: 14px;
|
|
700
743
|
}
|
|
701
744
|
.ag-card--first {
|
|
702
|
-
background: color-mix(in srgb, var(--ev-color)
|
|
703
|
-
border-color: color-mix(in srgb, var(--ev-color)
|
|
745
|
+
background: color-mix(in srgb, var(--ev-color) 20%, var(--dt-surface, #191919));
|
|
746
|
+
border-color: color-mix(in srgb, var(--ev-color) 25%, transparent);
|
|
704
747
|
}
|
|
705
748
|
.ag-card--first .ag-card-title {
|
|
706
749
|
font-size: 16px;
|
|
@@ -723,6 +766,41 @@
|
|
|
723
766
|
padding: 8px 0 10px;
|
|
724
767
|
min-height: 0;
|
|
725
768
|
}
|
|
769
|
+
/* Mobile: stack queue columns vertically */
|
|
770
|
+
.ag--mobile .ag-q {
|
|
771
|
+
grid-template-columns: 1fr;
|
|
772
|
+
}
|
|
773
|
+
.ag--mobile .ag-q-status {
|
|
774
|
+
border-right: none;
|
|
775
|
+
border-bottom: 1px solid var(--dt-border, rgba(255, 255, 255, 0.06));
|
|
776
|
+
padding-bottom: 10px;
|
|
777
|
+
margin-bottom: 8px;
|
|
778
|
+
}
|
|
779
|
+
/* Mobile: larger touch targets */
|
|
780
|
+
.ag--mobile .ag-card-body {
|
|
781
|
+
padding: 14px 16px;
|
|
782
|
+
}
|
|
783
|
+
.ag--mobile .ag-card-title {
|
|
784
|
+
font-size: 15px;
|
|
785
|
+
}
|
|
786
|
+
.ag--mobile .ag-card--hero .ag-card-title {
|
|
787
|
+
font-size: 18px;
|
|
788
|
+
}
|
|
789
|
+
.ag--mobile .ag-card--hero .ag-card-body {
|
|
790
|
+
padding: 16px 18px;
|
|
791
|
+
}
|
|
792
|
+
.ag--mobile .ag-log-row {
|
|
793
|
+
padding: 12px 0;
|
|
794
|
+
}
|
|
795
|
+
.ag--mobile .ag-card--plan .ag-card-body {
|
|
796
|
+
padding: 14px 16px;
|
|
797
|
+
}
|
|
798
|
+
.ag--mobile .ag-card--plan .ag-card-title {
|
|
799
|
+
font-size: 15px;
|
|
800
|
+
}
|
|
801
|
+
.ag--mobile .ag-nav-pill {
|
|
802
|
+
padding: 10px 16px;
|
|
803
|
+
}
|
|
726
804
|
.ag-q-label {
|
|
727
805
|
font-size: 8px;
|
|
728
806
|
font-weight: 600;
|
|
@@ -770,13 +848,14 @@
|
|
|
770
848
|
.ag-q-now {
|
|
771
849
|
padding: 8px 10px;
|
|
772
850
|
border-radius: 8px;
|
|
773
|
-
background: var(--dt-surface, #191919);
|
|
851
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, var(--dt-surface, #191919));
|
|
774
852
|
border: 1px solid color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
775
853
|
cursor: pointer;
|
|
776
|
-
transition: border-color 150ms;
|
|
854
|
+
transition: background 150ms, border-color 150ms;
|
|
777
855
|
margin-right: 10px;
|
|
778
856
|
}
|
|
779
857
|
.ag-q-now:hover {
|
|
858
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 25%, var(--dt-surface, #191919));
|
|
780
859
|
border-color: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 35%, transparent);
|
|
781
860
|
}
|
|
782
861
|
.ag-q-now:focus-visible {
|