@nomideusz/svelte-calendar 0.6.3 → 0.6.4
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 +183 -5
- package/dist/adapters/composite.d.ts +15 -0
- package/dist/adapters/composite.js +65 -0
- package/dist/adapters/index.d.ts +5 -1
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/mapped.d.ts +170 -0
- package/dist/adapters/mapped.js +343 -0
- package/dist/adapters/recurring.d.ts +4 -0
- package/dist/adapters/recurring.js +5 -10
- package/dist/adapters/types.d.ts +11 -2
- package/dist/calendar/Calendar.svelte +147 -76
- package/dist/calendar/Calendar.svelte.d.ts +12 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +2 -0
- package/dist/core/measure.d.ts +104 -0
- package/dist/core/measure.js +195 -0
- package/dist/core/time.js +2 -2
- package/dist/core/types.d.ts +32 -0
- package/dist/engine/event-store.svelte.js +22 -7
- package/dist/headless/create-agenda.svelte.d.ts +67 -0
- package/dist/headless/create-agenda.svelte.js +139 -0
- package/dist/headless/create-calendar.svelte.d.ts +2 -0
- package/dist/headless/create-calendar.svelte.js +380 -0
- package/dist/headless/index.d.ts +4 -0
- package/dist/headless/index.js +2 -0
- package/dist/headless/types.d.ts +219 -0
- package/dist/headless/types.js +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -2
- package/dist/primitives/EventBlock.svelte +87 -2
- package/dist/views/agenda/AgendaDay.svelte +183 -89
- package/dist/views/agenda/AgendaWeek.svelte +148 -95
- package/dist/views/mobile/MobileDay.svelte +43 -16
- package/dist/views/mobile/MobileWeek.svelte +32 -17
- package/dist/views/planner/PlannerDay.svelte +91 -36
- package/dist/views/planner/PlannerWeek.svelte +107 -132
- package/dist/views/shared/context.svelte.d.ts +47 -0
- package/dist/views/shared/context.svelte.js +47 -0
- package/dist/views/shared/format.d.ts +19 -0
- package/dist/views/shared/format.js +46 -0
- package/dist/views/shared/index.d.ts +4 -0
- package/dist/views/shared/index.js +2 -0
- package/package.json +76 -71
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Answers: "What's coming up and when do I need to be ready?"
|
|
11
11
|
*/
|
|
12
|
-
import { getContext, type Snippet } from 'svelte';
|
|
13
12
|
import { createClock } from '../../core/clock.svelte.js';
|
|
14
13
|
import type { TimelineEvent } from '../../core/types.js';
|
|
15
14
|
import { sod, DAY_MS, startOfWeek, dayNum, isAllDay, isMultiDay } from '../../core/time.js';
|
|
16
|
-
import { weekdayLong, monthLong,
|
|
17
|
-
import
|
|
15
|
+
import { weekdayLong, monthLong, getLabels } from '../../core/locale.js';
|
|
16
|
+
import { useCalendarContext } from '../shared/context.svelte.js';
|
|
17
|
+
import { fmtTime, duration, timeUntilMs, progress, groupIntoSlots } from '../shared/format.js';
|
|
18
18
|
|
|
19
19
|
const L = $derived(getLabels());
|
|
20
|
+
const ctx = useCalendarContext();
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
mondayStart?: boolean;
|
|
@@ -42,19 +43,17 @@
|
|
|
42
43
|
}: Props = $props();
|
|
43
44
|
|
|
44
45
|
const clock = createClock();
|
|
45
|
-
const viewState =
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const isMobile = $derived(mobileCtx?.current ?? false);
|
|
57
|
-
const autoHeight = $derived(autoHeightCtx?.current ?? false);
|
|
46
|
+
const viewState = $derived(ctx.viewState);
|
|
47
|
+
const showNav = $derived(ctx.showNav);
|
|
48
|
+
const equalDays = $derived(ctx.equalDays);
|
|
49
|
+
const showDates = $derived(ctx.showDates);
|
|
50
|
+
const hideDays = $derived(ctx.hideDays);
|
|
51
|
+
const isMobile = $derived(ctx.isMobile);
|
|
52
|
+
const autoHeight = $derived(ctx.autoHeight);
|
|
53
|
+
const compact = $derived(ctx.compact);
|
|
54
|
+
const dayHeaderSnippet = $derived(ctx.dayHeaderSnippet);
|
|
55
|
+
const oneventhover = $derived(ctx.oneventhover);
|
|
56
|
+
const disabledSet = $derived(ctx.disabledSet);
|
|
58
57
|
|
|
59
58
|
// ── Swipe navigation (mobile) ──────────────────────
|
|
60
59
|
let swipeStartX = 0;
|
|
@@ -77,68 +76,12 @@
|
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
// ──
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
const oneventhover = $derived(callbacksCtx?.oneventhover);
|
|
87
|
-
const disabledDates = $derived(disabledDatesCtx?.current);
|
|
88
|
-
const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
89
|
-
|
|
90
|
-
// ── Format helpers ──────────────────────────────────
|
|
91
|
-
function fmtTime(d: Date): string {
|
|
92
|
-
return _fmtTime(d, locale);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function duration(ev: TimelineEvent): string {
|
|
96
|
-
return fmtDuration(ev.start, ev.end);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function timeUntilMs(ms: number): string {
|
|
100
|
-
const diff = ms - clock.tick;
|
|
101
|
-
if (diff <= 0) return L.now;
|
|
102
|
-
const tMins = Math.floor(diff / 60000);
|
|
103
|
-
if (tMins < 60) return `in ${tMins}m`;
|
|
104
|
-
const hrs = Math.floor(tMins / 60);
|
|
105
|
-
const rm = tMins % 60;
|
|
106
|
-
if (hrs < 24) return rm > 0 ? `in ${hrs}h ${rm}m` : `in ${hrs}h`;
|
|
107
|
-
const days = Math.floor(hrs / 24);
|
|
108
|
-
return `in ${days}d`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function progress(ev: TimelineEvent): number {
|
|
112
|
-
const s = ev.start.getTime();
|
|
113
|
-
const e = ev.end.getTime();
|
|
114
|
-
return Math.min(1, Math.max(0, (clock.tick - s) / (e - s)));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ── Overlap grouping ────────────────────────────────
|
|
118
|
-
interface TimeSlot {
|
|
119
|
-
startMs: number;
|
|
120
|
-
endMs: number;
|
|
121
|
-
events: TimelineEvent[];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function groupIntoSlots(evts: TimelineEvent[]): TimeSlot[] {
|
|
125
|
-
const sorted = [...evts].sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
126
|
-
const slots: TimeSlot[] = [];
|
|
127
|
-
for (const ev of sorted) {
|
|
128
|
-
const last = slots[slots.length - 1];
|
|
129
|
-
if (last && ev.start.getTime() < last.endMs) {
|
|
130
|
-
last.events.push(ev);
|
|
131
|
-
last.endMs = Math.max(last.endMs, ev.end.getTime());
|
|
132
|
-
} else {
|
|
133
|
-
slots.push({
|
|
134
|
-
startMs: ev.start.getTime(),
|
|
135
|
-
endMs: ev.end.getTime(),
|
|
136
|
-
events: [ev],
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return slots;
|
|
141
|
-
}
|
|
79
|
+
// ── Format helpers (delegated to shared/format.ts) ──
|
|
80
|
+
// fmtTime, duration, groupIntoSlots imported at top
|
|
81
|
+
// Thin wrappers that bind locale / clock.tick:
|
|
82
|
+
const fmt = (d: Date) => fmtTime(d, locale);
|
|
83
|
+
const eta = (ms: number) => timeUntilMs(ms, clock.tick);
|
|
84
|
+
const prog = (ev: TimelineEvent) => progress(ev, clock.tick);
|
|
142
85
|
|
|
143
86
|
// ── Event handlers ──────────────────────────────────
|
|
144
87
|
function handleClick(ev: TimelineEvent): void {
|
|
@@ -259,10 +202,14 @@
|
|
|
259
202
|
<div
|
|
260
203
|
class="ag-card"
|
|
261
204
|
class:ag-card--selected={selectedEventId === ev.id}
|
|
205
|
+
class:ag-card--cancelled={ev.status === 'cancelled'}
|
|
206
|
+
class:ag-card--tentative={ev.status === 'tentative'}
|
|
207
|
+
class:ag-card--full={ev.status === 'full'}
|
|
208
|
+
class:ag-card--limited={ev.status === 'limited'}
|
|
262
209
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
263
210
|
role="button"
|
|
264
211
|
tabindex="0"
|
|
265
|
-
aria-label="{ev.title}, {
|
|
212
|
+
aria-label="{ev.title}{ev.status === 'cancelled' ? ' (cancelled)' : ''}{ev.status === 'tentative' ? ' (tentative)' : ''}{ev.status === 'full' ? ' (full)' : ''}{ev.status === 'limited' ? ' (limited)' : ''}, {fmt(ev.start)} to {fmt(ev.end)}, {duration(ev)}"
|
|
266
213
|
onclick={() => handleClick(ev)}
|
|
267
214
|
onpointerenter={() => oneventhover?.(ev)}
|
|
268
215
|
onkeydown={(e) => handleKeydown(e, ev)}
|
|
@@ -272,11 +219,14 @@
|
|
|
272
219
|
{#if ev.subtitle}
|
|
273
220
|
<span class="ag-card-sub">{ev.subtitle}</span>
|
|
274
221
|
{/if}
|
|
222
|
+
{#if ev.location}
|
|
223
|
+
<span class="ag-card-loc">{ev.location}</span>
|
|
224
|
+
{/if}
|
|
275
225
|
<span class="ag-card-meta">
|
|
276
226
|
{#if isNow}
|
|
277
|
-
{L.until} {
|
|
227
|
+
{L.until} {fmt(ev.end)}
|
|
278
228
|
{:else}
|
|
279
|
-
{
|
|
229
|
+
{fmt(ev.start)} – {fmt(ev.end)}
|
|
280
230
|
{/if}
|
|
281
231
|
<span class="ag-card-dur">{duration(ev)}</span>
|
|
282
232
|
{#if eta}
|
|
@@ -292,7 +242,7 @@
|
|
|
292
242
|
{/if}
|
|
293
243
|
{#if isNow}
|
|
294
244
|
<div class="ag-card-progress">
|
|
295
|
-
<div class="ag-card-progress-fill" style:width="{
|
|
245
|
+
<div class="ag-card-progress-fill" style:width="{prog(ev) * 100}%"></div>
|
|
296
246
|
</div>
|
|
297
247
|
{/if}
|
|
298
248
|
</div>
|
|
@@ -376,6 +326,40 @@
|
|
|
376
326
|
|
|
377
327
|
{#if day.events.length === 0}
|
|
378
328
|
<div class="ag-wday-empty">{L.noEvents}</div>
|
|
329
|
+
{:else if compact}
|
|
330
|
+
<!-- Compact: minimal dot + time + title rows for all days -->
|
|
331
|
+
<div class="ag-wday-compact">
|
|
332
|
+
{#each day.timedEvents as ev (ev.id)}
|
|
333
|
+
<div
|
|
334
|
+
class="ag-compact"
|
|
335
|
+
class:ag-compact--selected={selectedEventId === ev.id}
|
|
336
|
+
class:ag-compact--cancelled={ev.status === 'cancelled'}
|
|
337
|
+
class:ag-compact--tentative={ev.status === 'tentative'}
|
|
338
|
+
class:ag-compact--full={ev.status === 'full'}
|
|
339
|
+
class:ag-compact--limited={ev.status === 'limited'}
|
|
340
|
+
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
341
|
+
role="button"
|
|
342
|
+
tabindex="0"
|
|
343
|
+
aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
|
|
344
|
+
onclick={() => handleClick(ev)}
|
|
345
|
+
onpointerenter={() => oneventhover?.(ev)}
|
|
346
|
+
onkeydown={(e) => handleKeydown(e, ev)}
|
|
347
|
+
>
|
|
348
|
+
<span class="ag-compact-dot"></span>
|
|
349
|
+
<span class="ag-compact-time">{fmt(ev.start)}</span>
|
|
350
|
+
<span class="ag-compact-title">{ev.title}</span>
|
|
351
|
+
{#if ev.subtitle}
|
|
352
|
+
<span class="ag-compact-sub">{ev.subtitle}</span>
|
|
353
|
+
{/if}
|
|
354
|
+
{#if ev.tags?.length}
|
|
355
|
+
{#each ev.tags as tag}
|
|
356
|
+
<span class="ag-compact-tag">{tag}</span>
|
|
357
|
+
{/each}
|
|
358
|
+
{/if}
|
|
359
|
+
<span class="ag-compact-dur">{duration(ev)}</span>
|
|
360
|
+
</div>
|
|
361
|
+
{/each}
|
|
362
|
+
</div>
|
|
379
363
|
{:else if equalDays}
|
|
380
364
|
<!-- Equal days: card layout for all days, no time-relative badges -->
|
|
381
365
|
<div class="ag-wday-expanded">
|
|
@@ -408,7 +392,7 @@
|
|
|
408
392
|
|
|
409
393
|
<div class="ag-wslot-cards" class:ag-wslot-cards--multi={slot.events.length > 1}>
|
|
410
394
|
{#each slot.events as ev (ev.id)}
|
|
411
|
-
{@render eventCard(ev, false, day.tier === 'today' ?
|
|
395
|
+
{@render eventCard(ev, false, day.tier === 'today' ? eta(ev.start.getTime()) : undefined)}
|
|
412
396
|
{/each}
|
|
413
397
|
</div>
|
|
414
398
|
</div>
|
|
@@ -424,15 +408,22 @@
|
|
|
424
408
|
<div
|
|
425
409
|
class="ag-compact"
|
|
426
410
|
class:ag-compact--selected={selectedEventId === ev.id}
|
|
411
|
+
class:ag-compact--cancelled={ev.status === 'cancelled'}
|
|
412
|
+
class:ag-compact--tentative={ev.status === 'tentative'}
|
|
413
|
+
class:ag-compact--full={ev.status === 'full'}
|
|
414
|
+
class:ag-compact--limited={ev.status === 'limited'}
|
|
427
415
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
428
416
|
role="button"
|
|
429
417
|
tabindex="0"
|
|
430
|
-
aria-label="{ev.title}, {
|
|
418
|
+
aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
|
|
431
419
|
onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
|
|
432
420
|
>
|
|
433
421
|
<span class="ag-compact-dot"></span>
|
|
434
|
-
<span class="ag-compact-time">{
|
|
422
|
+
<span class="ag-compact-time">{fmt(ev.start)}</span>
|
|
435
423
|
<span class="ag-compact-title">{ev.title}</span>
|
|
424
|
+
{#if ev.location}
|
|
425
|
+
<span class="ag-compact-loc">{ev.location}</span>
|
|
426
|
+
{/if}
|
|
436
427
|
{#if ev.subtitle}
|
|
437
428
|
<span class="ag-compact-sub">{ev.subtitle}</span>
|
|
438
429
|
{/if}
|
|
@@ -544,6 +535,9 @@
|
|
|
544
535
|
display: flex;
|
|
545
536
|
flex-direction: column;
|
|
546
537
|
height: 100%;
|
|
538
|
+
width: 100%;
|
|
539
|
+
min-width: 0;
|
|
540
|
+
box-sizing: border-box;
|
|
547
541
|
color: var(--dt-text, rgba(255, 255, 255, 0.92));
|
|
548
542
|
font-family: var(--dt-sans, system-ui, sans-serif);
|
|
549
543
|
}
|
|
@@ -555,7 +549,9 @@
|
|
|
555
549
|
/* ═══ Body ═══ */
|
|
556
550
|
.ag-body {
|
|
557
551
|
flex: 1;
|
|
552
|
+
min-width: 0;
|
|
558
553
|
overflow-y: auto;
|
|
554
|
+
overflow-x: hidden;
|
|
559
555
|
scrollbar-width: thin;
|
|
560
556
|
scrollbar-color: var(--dt-border) transparent;
|
|
561
557
|
}
|
|
@@ -636,6 +632,23 @@
|
|
|
636
632
|
border-color: var(--ev-color);
|
|
637
633
|
background: color-mix(in srgb, var(--ev-color) 20%, var(--dt-surface, #191919));
|
|
638
634
|
}
|
|
635
|
+
.ag-card--cancelled {
|
|
636
|
+
opacity: 0.5;
|
|
637
|
+
}
|
|
638
|
+
.ag-card--cancelled .ag-card-title {
|
|
639
|
+
text-decoration: line-through;
|
|
640
|
+
}
|
|
641
|
+
.ag-card--tentative {
|
|
642
|
+
opacity: 0.65;
|
|
643
|
+
border-style: dashed;
|
|
644
|
+
}
|
|
645
|
+
.ag-card--full {
|
|
646
|
+
opacity: 0.55;
|
|
647
|
+
}
|
|
648
|
+
.ag-card--limited {
|
|
649
|
+
opacity: 0.65;
|
|
650
|
+
border-style: dashed;
|
|
651
|
+
}
|
|
639
652
|
.ag-card-body {
|
|
640
653
|
padding: 7px 10px;
|
|
641
654
|
display: flex;
|
|
@@ -647,11 +660,10 @@
|
|
|
647
660
|
.ag-card-title {
|
|
648
661
|
font-size: 13px;
|
|
649
662
|
font-weight: 600;
|
|
650
|
-
line-height: 1.
|
|
651
|
-
|
|
652
|
-
overflow: hidden;
|
|
653
|
-
text-overflow: ellipsis;
|
|
663
|
+
line-height: 1.3;
|
|
664
|
+
word-break: break-word;
|
|
654
665
|
flex: 1;
|
|
666
|
+
min-width: 0;
|
|
655
667
|
}
|
|
656
668
|
.ag-card-meta {
|
|
657
669
|
display: flex;
|
|
@@ -678,6 +690,11 @@
|
|
|
678
690
|
color: var(--dt-text-2, rgba(255, 255, 255, 0.45));
|
|
679
691
|
line-height: 1;
|
|
680
692
|
}
|
|
693
|
+
.ag-card-loc {
|
|
694
|
+
font-size: 10px;
|
|
695
|
+
color: var(--dt-text-3, rgba(255, 255, 255, 0.35));
|
|
696
|
+
line-height: 1;
|
|
697
|
+
}
|
|
681
698
|
.ag-card-tags {
|
|
682
699
|
display: flex;
|
|
683
700
|
gap: 4px;
|
|
@@ -747,7 +764,7 @@
|
|
|
747
764
|
display: flex;
|
|
748
765
|
justify-content: space-between;
|
|
749
766
|
align-items: center;
|
|
750
|
-
padding:
|
|
767
|
+
padding: 8px 20px;
|
|
751
768
|
}
|
|
752
769
|
.ag-wday-head-left {
|
|
753
770
|
display: flex;
|
|
@@ -783,7 +800,7 @@
|
|
|
783
800
|
}
|
|
784
801
|
|
|
785
802
|
.ag-wday-empty {
|
|
786
|
-
padding: 2px 20px
|
|
803
|
+
padding: 2px 20px 6px;
|
|
787
804
|
font-size: 11px;
|
|
788
805
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.2));
|
|
789
806
|
font-style: italic;
|
|
@@ -828,14 +845,16 @@
|
|
|
828
845
|
|
|
829
846
|
/* Compact day events */
|
|
830
847
|
.ag-wday-compact {
|
|
831
|
-
padding: 0 20px
|
|
848
|
+
padding: 0 20px 6px;
|
|
832
849
|
}
|
|
833
850
|
.ag-compact {
|
|
834
851
|
display: flex;
|
|
835
852
|
align-items: center;
|
|
836
|
-
gap:
|
|
837
|
-
padding:
|
|
853
|
+
gap: 6px;
|
|
854
|
+
padding: 3px 0;
|
|
838
855
|
cursor: pointer;
|
|
856
|
+
min-width: 0;
|
|
857
|
+
overflow: hidden;
|
|
839
858
|
}
|
|
840
859
|
.ag-compact--selected {
|
|
841
860
|
background: color-mix(in srgb, var(--ev-color) 10%, transparent);
|
|
@@ -861,14 +880,16 @@
|
|
|
861
880
|
font-size: 11px;
|
|
862
881
|
font-family: var(--dt-mono, monospace);
|
|
863
882
|
color: var(--dt-text-2, rgba(255, 255, 255, 0.5));
|
|
864
|
-
min-width:
|
|
883
|
+
min-width: 40px;
|
|
865
884
|
flex-shrink: 0;
|
|
885
|
+
white-space: nowrap;
|
|
866
886
|
}
|
|
867
887
|
.ag-compact-title {
|
|
868
888
|
font-size: 12px;
|
|
869
889
|
font-weight: 500;
|
|
870
890
|
color: var(--dt-text-2, rgba(255, 255, 255, 0.5));
|
|
871
891
|
flex: 1;
|
|
892
|
+
min-width: 0;
|
|
872
893
|
white-space: nowrap;
|
|
873
894
|
overflow: hidden;
|
|
874
895
|
text-overflow: ellipsis;
|
|
@@ -879,11 +900,40 @@
|
|
|
879
900
|
font-family: var(--dt-mono, monospace);
|
|
880
901
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
881
902
|
flex-shrink: 0;
|
|
903
|
+
white-space: nowrap;
|
|
882
904
|
}
|
|
883
905
|
.ag-compact-sub {
|
|
884
906
|
font-size: 10px;
|
|
885
907
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.35));
|
|
886
908
|
flex-shrink: 0;
|
|
909
|
+
white-space: nowrap;
|
|
910
|
+
overflow: hidden;
|
|
911
|
+
text-overflow: ellipsis;
|
|
912
|
+
max-width: 120px;
|
|
913
|
+
}
|
|
914
|
+
.ag-compact-loc {
|
|
915
|
+
font-size: 9px;
|
|
916
|
+
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
917
|
+
flex-shrink: 0;
|
|
918
|
+
white-space: nowrap;
|
|
919
|
+
overflow: hidden;
|
|
920
|
+
text-overflow: ellipsis;
|
|
921
|
+
max-width: 100px;
|
|
922
|
+
}
|
|
923
|
+
.ag-compact--cancelled {
|
|
924
|
+
opacity: 0.5;
|
|
925
|
+
}
|
|
926
|
+
.ag-compact--cancelled .ag-compact-title {
|
|
927
|
+
text-decoration: line-through;
|
|
928
|
+
}
|
|
929
|
+
.ag-compact--tentative {
|
|
930
|
+
opacity: 0.65;
|
|
931
|
+
}
|
|
932
|
+
.ag-compact--full {
|
|
933
|
+
opacity: 0.55;
|
|
934
|
+
}
|
|
935
|
+
.ag-compact--limited {
|
|
936
|
+
opacity: 0.65;
|
|
887
937
|
}
|
|
888
938
|
.ag-compact-tag {
|
|
889
939
|
font: 500 8px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
@@ -893,6 +943,9 @@
|
|
|
893
943
|
border-radius: 3px;
|
|
894
944
|
white-space: nowrap;
|
|
895
945
|
flex-shrink: 0;
|
|
946
|
+
max-width: 80px;
|
|
947
|
+
overflow: hidden;
|
|
948
|
+
text-overflow: ellipsis;
|
|
896
949
|
}
|
|
897
950
|
.ag-compact-more {
|
|
898
951
|
font-size: 11px;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
Events positioned absolutely within hour lanes.
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import {
|
|
9
|
+
import { onMount, tick, untrack } from 'svelte';
|
|
10
|
+
import { useCalendarContext } from '../shared/context.svelte.js';
|
|
10
11
|
import { createClock } from '../../core/clock.svelte.js';
|
|
11
12
|
import type { TimelineEvent, BlockedSlot } from '../../core/types.js';
|
|
12
13
|
import type { DragState } from '../../engine/drag.svelte.js';
|
|
@@ -45,20 +46,14 @@
|
|
|
45
46
|
}: Props = $props();
|
|
46
47
|
|
|
47
48
|
// ── Context ────────────────────────────────────────
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const blockedSlots = $derived(blockedSlotsCtx?.current);
|
|
57
|
-
const minDuration = $derived(minDurationCtx?.current);
|
|
58
|
-
const autoHeight = $derived(autoHeightCtx?.current ?? false);
|
|
59
|
-
const oneventhover = $derived(callbacksCtx?.oneventhover);
|
|
60
|
-
const disabledDates = $derived(disabledDatesCtx?.current);
|
|
61
|
-
const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
49
|
+
const ctx = useCalendarContext();
|
|
50
|
+
const viewState = $derived(ctx.viewState);
|
|
51
|
+
const autoHeight = $derived(ctx.autoHeight);
|
|
52
|
+
const oneventhover = $derived(ctx.oneventhover);
|
|
53
|
+
const disabledSet = $derived(ctx.disabledSet);
|
|
54
|
+
const loadRangeCtx = $derived(ctx.loadRange);
|
|
55
|
+
const minDuration = $derived(ctx.minDuration);
|
|
56
|
+
const blockedSlots = $derived(ctx.blockedSlots);
|
|
62
57
|
|
|
63
58
|
const clock = createClock();
|
|
64
59
|
|
|
@@ -360,6 +355,10 @@
|
|
|
360
355
|
class:mb-event--selected={selectedEventId === p.ev.id}
|
|
361
356
|
class:mb-event--current={p.isCurrent}
|
|
362
357
|
class:mb-event--next={p.isNext}
|
|
358
|
+
class:mb-event--cancelled={p.ev.status === 'cancelled'}
|
|
359
|
+
class:mb-event--tentative={p.ev.status === 'tentative'}
|
|
360
|
+
class:mb-event--full={p.ev.status === 'full'}
|
|
361
|
+
class:mb-event--limited={p.ev.status === 'limited'}
|
|
363
362
|
style:top="{p.top}px"
|
|
364
363
|
style:height="{p.height}px"
|
|
365
364
|
style:left={p.left}
|
|
@@ -367,7 +366,7 @@
|
|
|
367
366
|
style:--ev-color={p.ev.color ?? 'var(--dt-accent)'}
|
|
368
367
|
onclick={(e) => { e.stopPropagation(); oneventclick?.(p.ev); }}
|
|
369
368
|
onpointerenter={() => oneventhover?.(p.ev)}
|
|
370
|
-
aria-label="{p.ev.title}{p.isCurrent ? `, ${L.inProgress}` : ''}{p.isNext ? `, ${L.upNext}` : ''}"
|
|
369
|
+
aria-label="{p.ev.title}{p.ev.status === 'cancelled' ? ' (cancelled)' : ''}{p.ev.status === 'tentative' ? ' (tentative)' : ''}{p.ev.status === 'full' ? ' (full)' : ''}{p.ev.status === 'limited' ? ' (limited)' : ''}{p.isCurrent ? `, ${L.inProgress}` : ''}{p.isNext ? `, ${L.upNext}` : ''}"
|
|
371
370
|
>
|
|
372
371
|
<div class="mb-ev-stripe"></div>
|
|
373
372
|
<div class="mb-ev-body">
|
|
@@ -378,6 +377,9 @@
|
|
|
378
377
|
{#if p.ev.subtitle && p.height > 48}
|
|
379
378
|
<span class="mb-ev-sub">{p.ev.subtitle}</span>
|
|
380
379
|
{/if}
|
|
380
|
+
{#if p.ev.location && p.height > 56}
|
|
381
|
+
<span class="mb-ev-loc">{p.ev.location}</span>
|
|
382
|
+
{/if}
|
|
381
383
|
{#if p.ev.tags?.length && p.height > 56}
|
|
382
384
|
<div class="mb-ev-tags">
|
|
383
385
|
{#each p.ev.tags as tag}
|
|
@@ -611,6 +613,23 @@
|
|
|
611
613
|
background: color-mix(in srgb, var(--ev-color) 8%, var(--dt-surface, #f9fafb));
|
|
612
614
|
border: 1px dashed color-mix(in srgb, var(--ev-color) 35%, transparent);
|
|
613
615
|
}
|
|
616
|
+
.mb-event--cancelled {
|
|
617
|
+
opacity: 0.5;
|
|
618
|
+
}
|
|
619
|
+
.mb-event--cancelled .mb-ev-title {
|
|
620
|
+
text-decoration: line-through;
|
|
621
|
+
}
|
|
622
|
+
.mb-event--tentative {
|
|
623
|
+
opacity: 0.65;
|
|
624
|
+
border: 1px dashed color-mix(in srgb, var(--ev-color) 35%, transparent);
|
|
625
|
+
}
|
|
626
|
+
.mb-event--full {
|
|
627
|
+
opacity: 0.55;
|
|
628
|
+
}
|
|
629
|
+
.mb-event--limited {
|
|
630
|
+
opacity: 0.65;
|
|
631
|
+
border: 1px dashed color-mix(in srgb, var(--ev-color) 35%, transparent);
|
|
632
|
+
}
|
|
614
633
|
|
|
615
634
|
.mb-ev-stripe {
|
|
616
635
|
width: 4px;
|
|
@@ -650,6 +669,14 @@
|
|
|
650
669
|
text-overflow: ellipsis;
|
|
651
670
|
}
|
|
652
671
|
|
|
672
|
+
.mb-ev-loc {
|
|
673
|
+
font: 400 10px/1 var(--dt-sans, system-ui, sans-serif);
|
|
674
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.3));
|
|
675
|
+
white-space: nowrap;
|
|
676
|
+
overflow: hidden;
|
|
677
|
+
text-overflow: ellipsis;
|
|
678
|
+
}
|
|
679
|
+
|
|
653
680
|
.mb-ev-tags {
|
|
654
681
|
display: flex;
|
|
655
682
|
gap: 4px;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
left/right to navigate weeks.
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import {
|
|
9
|
+
import { useCalendarContext } from '../shared/context.svelte.js';
|
|
10
10
|
import { createClock } from '../../core/clock.svelte.js';
|
|
11
11
|
import type { TimelineEvent } from '../../core/types.js';
|
|
12
12
|
import type { ViewState } from '../../engine/view-state.svelte.js';
|
|
@@ -44,22 +44,16 @@
|
|
|
44
44
|
}: Props = $props();
|
|
45
45
|
|
|
46
46
|
// ── Context ────────────────────────────────────────
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const autoHeight = $derived(autoHeightCtx?.current ?? false);
|
|
58
|
-
const showDates = $derived(showDatesCtx?.current ?? true);
|
|
59
|
-
const hideDays = $derived(hideDaysCtx?.current);
|
|
60
|
-
const oneventhover = $derived(callbacksCtx?.oneventhover);
|
|
61
|
-
const disabledDates = $derived(disabledDatesCtx?.current);
|
|
62
|
-
const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
47
|
+
const ctx = useCalendarContext();
|
|
48
|
+
const viewState = $derived(ctx.viewState);
|
|
49
|
+
const equalDays = $derived(ctx.equalDays);
|
|
50
|
+
const showDates = $derived(ctx.showDates);
|
|
51
|
+
const hideDays = $derived(ctx.hideDays);
|
|
52
|
+
const autoHeight = $derived(ctx.autoHeight);
|
|
53
|
+
const oneventhover = $derived(ctx.oneventhover);
|
|
54
|
+
const disabledSet = $derived(ctx.disabledSet);
|
|
55
|
+
const loadRangeCtx = $derived(ctx.loadRange);
|
|
56
|
+
const minDuration = $derived(ctx.minDuration);
|
|
63
57
|
|
|
64
58
|
const clock = createClock();
|
|
65
59
|
|
|
@@ -236,6 +230,10 @@
|
|
|
236
230
|
class:mw-ev--selected={selectedEventId === ev.id}
|
|
237
231
|
class:mw-ev--allday={isAllDay(ev) || isMultiDay(ev)}
|
|
238
232
|
class:mw-ev--current={!isAllDay(ev) && !isMultiDay(ev) && ev.start.getTime() <= clock.tick && ev.end.getTime() > clock.tick}
|
|
233
|
+
class:mw-ev--cancelled={ev.status === 'cancelled'}
|
|
234
|
+
class:mw-ev--tentative={ev.status === 'tentative'}
|
|
235
|
+
class:mw-ev--full={ev.status === 'full'}
|
|
236
|
+
class:mw-ev--limited={ev.status === 'limited'}
|
|
239
237
|
style:--ev-color={ev.color ?? 'var(--dt-accent)'}
|
|
240
238
|
role="button"
|
|
241
239
|
tabindex="0"
|
|
@@ -402,6 +400,23 @@
|
|
|
402
400
|
.mw-ev--allday {
|
|
403
401
|
background: color-mix(in srgb, var(--ev-color) 14%, var(--dt-surface, #f9fafb));
|
|
404
402
|
}
|
|
403
|
+
.mw-ev--cancelled {
|
|
404
|
+
opacity: 0.5;
|
|
405
|
+
}
|
|
406
|
+
.mw-ev--cancelled .mw-ev-title {
|
|
407
|
+
text-decoration: line-through;
|
|
408
|
+
}
|
|
409
|
+
.mw-ev--tentative {
|
|
410
|
+
opacity: 0.65;
|
|
411
|
+
border: 1px dashed color-mix(in srgb, var(--ev-color) 35%, transparent);
|
|
412
|
+
}
|
|
413
|
+
.mw-ev--full {
|
|
414
|
+
opacity: 0.55;
|
|
415
|
+
}
|
|
416
|
+
.mw-ev--limited {
|
|
417
|
+
opacity: 0.65;
|
|
418
|
+
border: 1px dashed color-mix(in srgb, var(--ev-color) 35%, transparent);
|
|
419
|
+
}
|
|
405
420
|
|
|
406
421
|
.mw-ev-stripe {
|
|
407
422
|
width: 3px;
|