@nomideusz/svelte-calendar 0.21.0 → 1.0.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 +502 -269
- package/dist/adapters/composite.d.ts +12 -1
- package/dist/adapters/composite.js +109 -47
- package/dist/adapters/errors.d.ts +22 -0
- package/dist/adapters/errors.js +32 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/jmap.js +26 -17
- package/dist/adapters/mapped.d.ts +2 -2
- package/dist/adapters/mapped.js +18 -9
- package/dist/adapters/memory.d.ts +1 -1
- package/dist/adapters/memory.js +5 -3
- package/dist/adapters/recurring.d.ts +1 -1
- package/dist/adapters/recurring.js +37 -19
- package/dist/adapters/rest.d.ts +2 -2
- package/dist/adapters/rest.js +9 -4
- package/dist/calendar/Calendar.svelte +122 -78
- package/dist/calendar/Calendar.svelte.d.ts +69 -14
- package/dist/calendar/index.d.ts +2 -2
- package/dist/calendar/index.js +1 -1
- package/dist/core/clock.svelte.js +3 -2
- package/dist/core/locale.d.ts +11 -1
- package/dist/core/locale.js +12 -12
- package/dist/core/time.d.ts +9 -0
- package/dist/core/time.js +8 -0
- package/dist/core/timezone.d.ts +7 -3
- package/dist/core/timezone.js +10 -4
- package/dist/engine/event-store.svelte.js +93 -56
- package/dist/engine/view-state.svelte.d.ts +5 -1
- package/dist/engine/view-state.svelte.js +5 -3
- package/dist/headless/create-agenda.svelte.d.ts +8 -1
- package/dist/headless/create-agenda.svelte.js +32 -14
- package/dist/headless/create-calendar.svelte.js +53 -17
- package/dist/headless/create-range-agenda.svelte.d.ts +7 -0
- package/dist/headless/create-range-agenda.svelte.js +33 -12
- package/dist/headless/index.d.ts +1 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +8 -5
- package/dist/primitives/DayHeader.svelte +3 -2
- package/dist/primitives/DayHeader.svelte.d.ts +1 -1
- package/dist/primitives/EmptySlot.svelte +5 -2
- package/dist/primitives/EventBlock.svelte +21 -14
- package/dist/primitives/FloatingPanel.svelte +110 -15
- package/dist/primitives/FloatingPanel.svelte.d.ts +7 -5
- package/dist/primitives/NowIndicator.svelte +11 -4
- package/dist/primitives/TimeGutter.svelte +10 -6
- package/dist/primitives/index.d.ts +1 -0
- package/dist/text-fit.d.ts +8 -2
- package/dist/views/agenda/AgendaDay.svelte +41 -33
- package/dist/views/agenda/AgendaWeek.svelte +71 -34
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +2 -0
- package/dist/views/mobile/MobileDay.svelte +98 -41
- package/dist/views/mobile/MobileWeek.svelte +29 -13
- package/dist/views/mobile/swipe.js +6 -1
- package/dist/views/month/MonthGrid.svelte +29 -17
- package/dist/views/month/MonthGrid.svelte.d.ts +1 -1
- package/dist/views/planner/PlannerScroll.svelte +183 -51
- package/dist/views/planner/PlannerWeek.svelte +148 -48
- package/dist/views/shared/context.svelte.d.ts +1 -1
- package/package.json +24 -9
- package/widget/widget.js +7983 -7688
- package/dist/assets/favicon.svg +0 -9
- package/dist/widget/CalendarWidget.svelte +0 -114
- package/dist/widget/CalendarWidget.svelte.d.ts +0 -37
- package/dist/widget/index.d.ts +0 -1
- package/dist/widget/index.js +0 -1
- package/dist/widget/widget.d.ts +0 -10
- package/dist/widget/widget.js +0 -96
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* Clean numbered schedule list.
|
|
13
13
|
*/
|
|
14
14
|
import { createClock } from "../../core/clock.svelte.js";
|
|
15
|
-
import { sod, addDaysMs, dayNum, isAllDay
|
|
15
|
+
import { sod, addDaysMs, dayNum, isAllDay } from "../../core/time.js";
|
|
16
16
|
import { weekdayLong, monthLong } from "../../core/locale.js";
|
|
17
17
|
import { useCalendarContext } from "../shared/context.svelte.js";
|
|
18
18
|
import EventContent from "../shared/EventContent.svelte";
|
|
19
19
|
import { fmtTime, duration, timeUntilMs, progress, groupIntoSlots } from "../shared/format.js";
|
|
20
|
+
import { createSwipe } from "../mobile/swipe.js";
|
|
20
21
|
const ctx = useCalendarContext();
|
|
21
22
|
const L = $derived(ctx.labels);
|
|
22
23
|
const emptySnippet = $derived(ctx.emptySnippet);
|
|
@@ -36,28 +37,28 @@ const compact = $derived(ctx.compact);
|
|
|
36
37
|
const oneventhover = $derived(ctx.oneventhover);
|
|
37
38
|
const disabledSet = $derived(ctx.disabledSet);
|
|
38
39
|
// ── Swipe navigation (mobile, touch only) ──────────
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
swipeStartY = e.clientY;
|
|
48
|
-
}
|
|
49
|
-
function onPointerUp(e) {
|
|
50
|
-
if (!swipeActive || e.pointerType !== "touch") return;
|
|
51
|
-
swipeActive = false;
|
|
52
|
-
const dx = e.clientX - swipeStartX;
|
|
53
|
-
const dy = e.clientY - swipeStartY;
|
|
54
|
-
if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
|
|
55
|
-
if (dx > 0) viewState?.prev();
|
|
56
|
-
else viewState?.next();
|
|
40
|
+
// Touch events, not pointer events: once the browser claims a touch for
|
|
41
|
+
// panning it sends pointercancel and the pointerup never arrives.
|
|
42
|
+
const swipe = createSwipe({
|
|
43
|
+
disabled: () => !isMobile,
|
|
44
|
+
onmove: () => {},
|
|
45
|
+
onend: (dir) => {
|
|
46
|
+
if (dir > 0) viewState?.prev();
|
|
47
|
+
else if (dir < 0) viewState?.next();
|
|
57
48
|
}
|
|
49
|
+
});
|
|
50
|
+
// ── Status suffix (aria) ────────────────────────────
|
|
51
|
+
function statusText(ev) {
|
|
52
|
+
if (ev.status === "cancelled") return ` (${L.cancelled})`;
|
|
53
|
+
if (ev.status === "tentative") return ` (${L.tentative})`;
|
|
54
|
+
if (ev.status === "full") return ` (${L.full})`;
|
|
55
|
+
if (ev.status === "limited") return ` (${L.limited})`;
|
|
56
|
+
return "";
|
|
58
57
|
}
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
/** All-day events and timed ones at least a (wall-clock) day long go to
|
|
59
|
+
* the all-day strip; a shorter overnight event is listed with its time. */
|
|
60
|
+
function inAllDayStrip(ev) {
|
|
61
|
+
return isAllDay(ev) || ev.end.getTime() >= addDaysMs(ev.start.getTime(), 1);
|
|
61
62
|
}
|
|
62
63
|
// ── Format helpers (delegated to shared/format.ts) ──
|
|
63
64
|
const fmt = (d) => fmtTime(d, locale);
|
|
@@ -78,9 +79,9 @@ const dayEvents = $derived.by(() => {
|
|
|
78
79
|
return events.filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > dayMs).sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
79
80
|
});
|
|
80
81
|
/** All-day / multi-day events shown in a separate strip */
|
|
81
|
-
const allDayBanner = $derived(dayEvents.filter(
|
|
82
|
+
const allDayBanner = $derived(dayEvents.filter(inAllDayStrip));
|
|
82
83
|
/** Timed events (non-all-day) for normal slot rendering */
|
|
83
|
-
const timedDayEvents = $derived(dayEvents.filter((ev) => !
|
|
84
|
+
const timedDayEvents = $derived(dayEvents.filter((ev) => !inAllDayStrip(ev)));
|
|
84
85
|
const dayCat = $derived.by(() => {
|
|
85
86
|
const now = clock.tick;
|
|
86
87
|
const past = [];
|
|
@@ -118,7 +119,7 @@ const visibleDone = $derived(showAllDone ? dayCat.past : dayCat.past.slice(-DONE
|
|
|
118
119
|
const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.length - DONE_VISIBLE));
|
|
119
120
|
</script>
|
|
120
121
|
|
|
121
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
122
|
+
<!-- svelte-ignore a11y_no_static_element_interactions (touch swipe only; no pointer/keyboard role) -->
|
|
122
123
|
<div
|
|
123
124
|
class="ag ag--day"
|
|
124
125
|
class:ag--disabled={disabledSet.has(dayMs)}
|
|
@@ -126,9 +127,10 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
126
127
|
class:ag--auto={autoHeight}
|
|
127
128
|
style={style || undefined}
|
|
128
129
|
style:height={height ? `${height}px` : undefined}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
ontouchstart={swipe.ontouchstart}
|
|
131
|
+
ontouchmove={swipe.ontouchmove}
|
|
132
|
+
ontouchend={swipe.ontouchend}
|
|
133
|
+
ontouchcancel={swipe.ontouchcancel}
|
|
132
134
|
>
|
|
133
135
|
<div class="ag-body" role="group" aria-label={L.todaysLineup}>
|
|
134
136
|
<!-- ─── In-view date header (only when the chrome doesn't label the day) ─── -->
|
|
@@ -154,7 +156,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
154
156
|
class="ag-allday-chip"
|
|
155
157
|
class:ag-allday-chip--selected={selectedEventId === ev.id}
|
|
156
158
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
157
|
-
aria-label="{ev.title}, {L.allDay}"
|
|
159
|
+
aria-label="{ev.title}{statusText(ev)}, {L.allDay}"
|
|
158
160
|
onclick={() => handleClick(ev)}
|
|
159
161
|
onpointerenter={() => oneventhover?.(ev)}
|
|
160
162
|
>
|
|
@@ -182,7 +184,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
182
184
|
class:ag-compact-row--cancelled={ev.status === 'cancelled'}
|
|
183
185
|
class:ag-compact-row--tentative={ev.status === 'tentative'}
|
|
184
186
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
185
|
-
aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
|
|
187
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)}, {duration(ev)}"
|
|
186
188
|
onclick={() => handleClick(ev)}
|
|
187
189
|
onpointerenter={() => oneventhover?.(ev)}
|
|
188
190
|
>
|
|
@@ -277,7 +279,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
277
279
|
class="ag-compact-row ag-compact-row--queue"
|
|
278
280
|
class:ag-compact-row--selected={selectedEventId === ev.id}
|
|
279
281
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
280
|
-
aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
|
|
282
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)}, {duration(ev)}"
|
|
281
283
|
onclick={() => handleClick(ev)}
|
|
282
284
|
onpointerenter={() => oneventhover?.(ev)}
|
|
283
285
|
>
|
|
@@ -298,7 +300,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
298
300
|
class:ag-card--hero={i === 0}
|
|
299
301
|
class:ag-card--selected={selectedEventId === ev.id}
|
|
300
302
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
301
|
-
aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
|
|
303
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)}, {duration(ev)}"
|
|
302
304
|
onclick={() => handleClick(ev)}
|
|
303
305
|
onpointerenter={() => oneventhover?.(ev)}
|
|
304
306
|
>
|
|
@@ -345,7 +347,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
345
347
|
class="ag-log-row"
|
|
346
348
|
class:ag-log-row--selected={selectedEventId === ev.id}
|
|
347
349
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
348
|
-
aria-label="{ev.title}, {fmt(ev.start)}
|
|
350
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)} – {fmt(ev.end)}"
|
|
349
351
|
onclick={() => handleClick(ev)}
|
|
350
352
|
onpointerenter={() => oneventhover?.(ev)}
|
|
351
353
|
>
|
|
@@ -378,7 +380,7 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
378
380
|
class:ag-card--full={ev.status === 'full'}
|
|
379
381
|
class:ag-card--limited={ev.status === 'limited'}
|
|
380
382
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
381
|
-
aria-label="{ev.title}{
|
|
383
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)} – {fmt(ev.end)}, {duration(ev)}"
|
|
382
384
|
onclick={() => handleClick(ev)}
|
|
383
385
|
onpointerenter={() => oneventhover?.(ev)}
|
|
384
386
|
>
|
|
@@ -767,6 +769,12 @@ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.lengt
|
|
|
767
769
|
}
|
|
768
770
|
/* Mobile: stack queue columns vertically — "Up next" (hero) first,
|
|
769
771
|
Now/Done status column second */
|
|
772
|
+
/* Horizontal swipes are ours (day navigation); the browser keeps
|
|
773
|
+
vertical scrolling and pinch-zoom. */
|
|
774
|
+
.ag--mobile,
|
|
775
|
+
.ag--mobile .ag-body {
|
|
776
|
+
touch-action: pan-y pinch-zoom;
|
|
777
|
+
}
|
|
770
778
|
.ag--mobile .ag-q {
|
|
771
779
|
grid-template-columns: 1fr;
|
|
772
780
|
min-height: auto;
|
|
@@ -14,6 +14,7 @@ import { weekdayLong, monthLong } from "../../core/locale.js";
|
|
|
14
14
|
import { useCalendarContext } from "../shared/context.svelte.js";
|
|
15
15
|
import EventContent from "../shared/EventContent.svelte";
|
|
16
16
|
import { fmtTime, duration, timeUntilMs, progress, groupIntoSlots } from "../shared/format.js";
|
|
17
|
+
import { createSwipe } from "../mobile/swipe.js";
|
|
17
18
|
const ctx = useCalendarContext();
|
|
18
19
|
const L = $derived(ctx.labels);
|
|
19
20
|
let { mondayStart = true, locale, height, events = [], style = "", focusDate, oneventclick, selectedEventId = null } = $props();
|
|
@@ -34,6 +35,28 @@ const disabledSet = $derived(ctx.disabledSet);
|
|
|
34
35
|
function clickDay(ms) {
|
|
35
36
|
ondayclick?.(new Date(ms));
|
|
36
37
|
}
|
|
38
|
+
/** Keyboard activation for the day header / empty-day row (role="button").
|
|
39
|
+
* Keys aimed at a control inside a custom dayHeader snippet stay its own. */
|
|
40
|
+
function dayKeydown(e, ms) {
|
|
41
|
+
if (e.target !== e.currentTarget) return;
|
|
42
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
clickDay(ms);
|
|
45
|
+
}
|
|
46
|
+
// ── Status suffix (aria) ────────────────────────────
|
|
47
|
+
function statusText(ev) {
|
|
48
|
+
if (ev.status === "cancelled") return ` (${L.cancelled})`;
|
|
49
|
+
if (ev.status === "tentative") return ` (${L.tentative})`;
|
|
50
|
+
if (ev.status === "full") return ` (${L.full})`;
|
|
51
|
+
if (ev.status === "limited") return ` (${L.limited})`;
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
/** All-day events and timed ones at least a (wall-clock) day long go to
|
|
55
|
+
* the all-day chips; a shorter overnight event is listed with its time on
|
|
56
|
+
* each day it touches. */
|
|
57
|
+
function inAllDayStrip(ev) {
|
|
58
|
+
return isAllDay(ev) || ev.end.getTime() >= addDaysMs(ev.start.getTime(), 1);
|
|
59
|
+
}
|
|
37
60
|
// ── Day-to-day card drag (timetable columns) ────────
|
|
38
61
|
// This view has no hour axis, so a drag can only choose a DAY: the card
|
|
39
62
|
// keeps its time of day. Wired through the same oneventmove the planner
|
|
@@ -62,34 +85,24 @@ function onDayDrop(e, day) {
|
|
|
62
85
|
if (!ev) return;
|
|
63
86
|
// Same-day drop is a no-op, not a zero-minute "move" for the host to reject.
|
|
64
87
|
if (sod(ev.start.getTime()) === day.ms) return;
|
|
65
|
-
|
|
88
|
+
// Same wall-clock time on the target day (a fixed ms offset from
|
|
89
|
+
// midnight is an hour off when a DST change lies between the two).
|
|
90
|
+
const s = ev.start;
|
|
91
|
+
const start = new Date(new Date(day.ms).setHours(s.getHours(), s.getMinutes(), s.getSeconds(), s.getMilliseconds()));
|
|
66
92
|
const end = new Date(start.getTime() + (ev.end.getTime() - ev.start.getTime()));
|
|
67
93
|
oneventmove?.(ev, start, end);
|
|
68
94
|
}
|
|
69
95
|
// ── Swipe navigation (mobile, touch only) ──────────
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
swipeStartY = e.clientY;
|
|
79
|
-
}
|
|
80
|
-
function onPointerUp(e) {
|
|
81
|
-
if (!swipeActive || e.pointerType !== "touch") return;
|
|
82
|
-
swipeActive = false;
|
|
83
|
-
const dx = e.clientX - swipeStartX;
|
|
84
|
-
const dy = e.clientY - swipeStartY;
|
|
85
|
-
if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
|
|
86
|
-
if (dx > 0) viewState?.prev();
|
|
87
|
-
else viewState?.next();
|
|
96
|
+
// Touch events, not pointer events: once the browser claims a touch for
|
|
97
|
+
// panning it sends pointercancel and the pointerup never arrives.
|
|
98
|
+
const swipe = createSwipe({
|
|
99
|
+
disabled: () => !isMobile,
|
|
100
|
+
onmove: () => {},
|
|
101
|
+
onend: (dir) => {
|
|
102
|
+
if (dir > 0) viewState?.prev();
|
|
103
|
+
else if (dir < 0) viewState?.next();
|
|
88
104
|
}
|
|
89
|
-
}
|
|
90
|
-
function onPointerCancel() {
|
|
91
|
-
swipeActive = false;
|
|
92
|
-
}
|
|
105
|
+
});
|
|
93
106
|
// ── Compact-day disclosure ("+N more" → expand) ─────
|
|
94
107
|
let expandedDays = $state([]);
|
|
95
108
|
function toggleDayExpand(ms) {
|
|
@@ -121,8 +134,8 @@ const weekDays = $derived.by(() => {
|
|
|
121
134
|
const ms = addDaysMs(weekStartMs, i);
|
|
122
135
|
const dEnd = addDaysMs(ms, 1);
|
|
123
136
|
const dayEvts = events.filter((ev) => ev.start.getTime() < dEnd && ev.end.getTime() > ms).sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
124
|
-
const allDayEvts = dayEvts.filter(
|
|
125
|
-
const timedEvts = dayEvts.filter((ev) => !
|
|
137
|
+
const allDayEvts = dayEvts.filter(inAllDayStrip);
|
|
138
|
+
const timedEvts = dayEvts.filter((ev) => !inAllDayStrip(ev));
|
|
126
139
|
const totalMinutes = timedEvts.reduce((sum, ev) => {
|
|
127
140
|
const s = Math.max(ev.start.getTime(), ms);
|
|
128
141
|
const e = Math.min(ev.end.getTime(), dEnd);
|
|
@@ -186,7 +199,7 @@ const weekDays = $derived.by(() => {
|
|
|
186
199
|
class:ag-card--full={ev.status === 'full'}
|
|
187
200
|
class:ag-card--limited={ev.status === 'limited'}
|
|
188
201
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
189
|
-
aria-label="{ev.title}{
|
|
202
|
+
aria-label="{ev.title}{statusText(ev)}, {fmt(ev.start)} – {fmt(ev.end)}, {duration(ev)}"
|
|
190
203
|
class:ag-card--drag={dragId === String(ev.id)}
|
|
191
204
|
draggable={canDrag(ev)}
|
|
192
205
|
ondragstart={(e) => onCardDragStart(e, ev)}
|
|
@@ -243,7 +256,7 @@ const weekDays = $derived.by(() => {
|
|
|
243
256
|
class:ag-compact--full={ev.status === 'full'}
|
|
244
257
|
class:ag-compact--limited={ev.status === 'limited'}
|
|
245
258
|
style:--ev-color={ev.color || 'var(--dt-accent)'}
|
|
246
|
-
aria-label="{ev.title}{done ? `, ${L.completed}` : ''}, {fmt(ev.start)}, {duration(ev)}"
|
|
259
|
+
aria-label="{ev.title}{statusText(ev)}{done ? `, ${L.completed}` : ''}, {fmt(ev.start)}, {duration(ev)}"
|
|
247
260
|
onclick={() => handleClick(ev)}
|
|
248
261
|
onpointerenter={() => oneventhover?.(ev)}
|
|
249
262
|
>
|
|
@@ -283,7 +296,7 @@ const weekDays = $derived.by(() => {
|
|
|
283
296
|
</button>
|
|
284
297
|
{/snippet}
|
|
285
298
|
|
|
286
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
299
|
+
<!-- svelte-ignore a11y_no_static_element_interactions (touch swipe only; no pointer/keyboard role) -->
|
|
287
300
|
<div
|
|
288
301
|
class="ag ag--week"
|
|
289
302
|
class:ag--mobile={isMobile}
|
|
@@ -291,9 +304,10 @@ const weekDays = $derived.by(() => {
|
|
|
291
304
|
class:ag--cols={cols}
|
|
292
305
|
style={style || undefined}
|
|
293
306
|
style:height={height ? `${height}px` : undefined}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
307
|
+
ontouchstart={swipe.ontouchstart}
|
|
308
|
+
ontouchmove={swipe.ontouchmove}
|
|
309
|
+
ontouchend={swipe.ontouchend}
|
|
310
|
+
ontouchcancel={swipe.ontouchcancel}
|
|
297
311
|
>
|
|
298
312
|
<div class="ag-body" role="list" aria-label={L.weekAhead} style:--ag-cols={weekDays.length}>
|
|
299
313
|
{#each weekDays as day (day.ms)}
|
|
@@ -301,11 +315,15 @@ const weekDays = $derived.by(() => {
|
|
|
301
315
|
{#if day.tier === 'past'}
|
|
302
316
|
<!-- Past day: single collapsed line -->
|
|
303
317
|
<div class="ag-wday ag-wday--past" class:ag-wday--disabled={disabledSet.has(day.ms)} data-day={day.ms} role="listitem">
|
|
304
|
-
<!--
|
|
318
|
+
<!-- role="button" exactly when ondayclick is set (Calendar always sets it) -->
|
|
319
|
+
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
|
305
320
|
<div
|
|
306
321
|
class="ag-wday-head"
|
|
307
322
|
class:ag-wday-head--click={!!ondayclick}
|
|
323
|
+
role={ondayclick ? 'button' : undefined}
|
|
324
|
+
tabindex={ondayclick ? 0 : undefined}
|
|
308
325
|
onclick={ondayclick ? () => clickDay(day.ms) : undefined}
|
|
326
|
+
onkeydown={ondayclick ? (e) => dayKeydown(e, day.ms) : undefined}
|
|
309
327
|
>
|
|
310
328
|
<div class="ag-wday-head-left">
|
|
311
329
|
<span class="ag-wday-name">{day.dayName}</span>
|
|
@@ -353,11 +371,15 @@ const weekDays = $derived.by(() => {
|
|
|
353
371
|
ondrop={(e) => onDayDrop(e, day)}
|
|
354
372
|
>
|
|
355
373
|
<!-- Day header -->
|
|
356
|
-
<!--
|
|
374
|
+
<!-- role="button" exactly when ondayclick is set (Calendar always sets it) -->
|
|
375
|
+
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
|
357
376
|
<div
|
|
358
377
|
class="ag-wday-head"
|
|
359
378
|
class:ag-wday-head--click={!!ondayclick}
|
|
379
|
+
role={ondayclick ? 'button' : undefined}
|
|
380
|
+
tabindex={ondayclick ? 0 : undefined}
|
|
360
381
|
onclick={ondayclick ? () => clickDay(day.ms) : undefined}
|
|
382
|
+
onkeydown={ondayclick ? (e) => dayKeydown(e, day.ms) : undefined}
|
|
361
383
|
>
|
|
362
384
|
<div class="ag-wday-head-left">
|
|
363
385
|
{#if day.isToday}
|
|
@@ -400,11 +422,14 @@ const weekDays = $derived.by(() => {
|
|
|
400
422
|
{/if}
|
|
401
423
|
|
|
402
424
|
{#if day.events.length === 0}
|
|
403
|
-
<!-- svelte-ignore
|
|
425
|
+
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
|
404
426
|
<div
|
|
405
427
|
class="ag-wday-empty"
|
|
406
428
|
class:ag-wday-empty--click={!!ondayclick}
|
|
429
|
+
role={ondayclick ? 'button' : undefined}
|
|
430
|
+
tabindex={ondayclick ? 0 : undefined}
|
|
407
431
|
onclick={ondayclick ? () => clickDay(day.ms) : undefined}
|
|
432
|
+
onkeydown={ondayclick ? (e) => dayKeydown(e, day.ms) : undefined}
|
|
408
433
|
>{L.noEvents}</div>
|
|
409
434
|
{:else if compact && !cols}
|
|
410
435
|
<!-- Compact: minimal dot + time + title rows for all days.
|
|
@@ -772,6 +797,12 @@ const weekDays = $derived.by(() => {
|
|
|
772
797
|
.ag-wday-empty--click {
|
|
773
798
|
cursor: pointer;
|
|
774
799
|
}
|
|
800
|
+
.ag-wday-head--click:focus-visible,
|
|
801
|
+
.ag-wday-empty--click:focus-visible {
|
|
802
|
+
outline: 2px solid var(--dt-accent, #2563eb);
|
|
803
|
+
outline-offset: -2px;
|
|
804
|
+
border-radius: 4px;
|
|
805
|
+
}
|
|
775
806
|
.ag-wday-head {
|
|
776
807
|
display: flex;
|
|
777
808
|
justify-content: space-between;
|
|
@@ -1119,6 +1150,12 @@ const weekDays = $derived.by(() => {
|
|
|
1119
1150
|
}
|
|
1120
1151
|
|
|
1121
1152
|
/* ═══ Mobile adaptations ═══ */
|
|
1153
|
+
/* Horizontal swipes are ours (week navigation); the browser keeps
|
|
1154
|
+
vertical scrolling and pinch-zoom. */
|
|
1155
|
+
.ag--mobile,
|
|
1156
|
+
.ag--mobile .ag-body {
|
|
1157
|
+
touch-action: pan-y pinch-zoom;
|
|
1158
|
+
}
|
|
1122
1159
|
.ag--mobile .ag-wday-head {
|
|
1123
1160
|
padding: 12px 16px;
|
|
1124
1161
|
}
|
package/dist/views/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as Planner } from './planner/Planner.svelte';
|
|
2
|
+
export { default as PlannerScroll } from './planner/PlannerScroll.svelte';
|
|
2
3
|
export { default as Agenda } from './agenda/Agenda.svelte';
|
|
3
4
|
export { default as Mobile } from './mobile/Mobile.svelte';
|
|
5
|
+
export { default as MonthGrid } from './month/MonthGrid.svelte';
|
package/dist/views/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as Planner } from './planner/Planner.svelte';
|
|
2
|
+
export { default as PlannerScroll } from './planner/PlannerScroll.svelte';
|
|
2
3
|
export { default as Agenda } from './agenda/Agenda.svelte';
|
|
3
4
|
export { default as Mobile } from './mobile/Mobile.svelte';
|
|
5
|
+
export { default as MonthGrid } from './month/MonthGrid.svelte';
|