@nomideusz/svelte-calendar 0.20.1 → 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 -267
- 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 +43 -25
- 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 +16 -16
- package/dist/core/time.d.ts +9 -0
- package/dist/core/time.js +12 -5
- package/dist/core/timezone.d.ts +7 -3
- package/dist/core/timezone.js +10 -4
- package/dist/engine/event-store.svelte.js +94 -57
- package/dist/engine/view-state.svelte.d.ts +5 -1
- package/dist/engine/view-state.svelte.js +8 -6
- package/dist/headless/create-agenda.svelte.d.ts +8 -1
- package/dist/headless/create-agenda.svelte.js +36 -18
- package/dist/headless/create-calendar.svelte.js +57 -21
- package/dist/headless/create-range-agenda.svelte.d.ts +7 -0
- package/dist/headless/create-range-agenda.svelte.js +38 -17
- 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 +43 -35
- package/dist/views/agenda/AgendaWeek.svelte +75 -38
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +2 -0
- package/dist/views/mobile/MobileDay.svelte +99 -42
- package/dist/views/mobile/MobileWeek.svelte +31 -15
- package/dist/views/mobile/swipe.js +6 -1
- package/dist/views/month/MonthGrid.svelte +33 -21
- package/dist/views/month/MonthGrid.svelte.d.ts +1 -1
- package/dist/views/planner/PlannerScroll.svelte +438 -164
- package/dist/views/planner/PlannerWeek.svelte +150 -50
- package/dist/views/shared/context.svelte.d.ts +1 -1
- package/package.json +24 -9
- package/widget/widget.js +10000 -9520
- 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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
-->
|
|
9
9
|
<script lang="ts">import { useCalendarContext } from "../shared/context.svelte.js";
|
|
10
10
|
import { createClock } from "../../core/clock.svelte.js";
|
|
11
|
-
import { DAY_MS, sod, isAllDay } from "../../core/time.js";
|
|
11
|
+
import { DAY_MS, sod, addDaysMs, isAllDay } from "../../core/time.js";
|
|
12
12
|
import { fmtTime, weekdayShort } from "../../core/locale.js";
|
|
13
13
|
let { events = [], style = "", height = null, locale, focusDate, oneventclick, selectedEventId = null } = $props();
|
|
14
14
|
const ctx = useCalendarContext();
|
|
@@ -44,23 +44,41 @@ let expandedMs = $state(null);
|
|
|
44
44
|
let focusMs = $state(null);
|
|
45
45
|
let bodyEl = $state(null);
|
|
46
46
|
const cellsInteractive = $derived(!!ondayclick);
|
|
47
|
+
/** A cell takes focus when it can act: drill down, or open its overflow. */
|
|
48
|
+
function isFocusable(cell) {
|
|
49
|
+
return !cell.isDisabled && (cellsInteractive || cell.overflow > 0);
|
|
50
|
+
}
|
|
51
|
+
/** Focusable cells by day, in grid order. */
|
|
52
|
+
const focusable = $derived.by(() => {
|
|
53
|
+
const map = new Map();
|
|
54
|
+
for (const row of weeks) for (const cell of row) if (isFocusable(cell)) map.set(cell.ms, cell);
|
|
55
|
+
return map;
|
|
56
|
+
});
|
|
57
|
+
// The roving anchor always lands on a focusable cell, so the grid keeps
|
|
58
|
+
// its one Tab stop even when the remembered day or today is disabled.
|
|
47
59
|
const rovingMs = $derived.by(() => {
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
if (todayMs >= start && todayMs < end) return todayMs;
|
|
53
|
-
return start;
|
|
60
|
+
if (focusMs !== null && focusable.has(focusMs)) return focusMs;
|
|
61
|
+
if (focusable.has(todayMs)) return todayMs;
|
|
62
|
+
const first = focusable.keys().next();
|
|
63
|
+
return first.done ? null : first.value;
|
|
54
64
|
});
|
|
55
65
|
function moveFocus(fromMs, deltaDays) {
|
|
56
66
|
if (!range) return;
|
|
57
|
-
const
|
|
58
|
-
|
|
67
|
+
const lo = sod(range.start.getTime());
|
|
68
|
+
const hi = range.end.getTime();
|
|
69
|
+
// Skip days that can't take focus, continuing in the same direction.
|
|
70
|
+
let target = addDaysMs(fromMs, deltaDays);
|
|
71
|
+
while (target >= lo && target < hi && !focusable.has(target)) target = addDaysMs(target, deltaDays);
|
|
72
|
+
if (target < lo || target >= hi) return;
|
|
59
73
|
focusMs = target;
|
|
60
74
|
const el = bodyEl?.querySelector(`[data-ms="${target}"]`);
|
|
61
75
|
el?.focus();
|
|
62
76
|
}
|
|
63
77
|
function cellKeydown(e, cell) {
|
|
78
|
+
// Enter/Space on an event chip or "+N more" belong to that button (a
|
|
79
|
+
// chip opens its event, not the day); arrows still move between days.
|
|
80
|
+
const onCell = e.target === e.currentTarget;
|
|
81
|
+
if (!onCell && (e.key === "Enter" || e.key === " ")) return;
|
|
64
82
|
switch (e.key) {
|
|
65
83
|
case "ArrowRight":
|
|
66
84
|
e.preventDefault();
|
|
@@ -108,7 +126,7 @@ function cellLabel(cell) {
|
|
|
108
126
|
}
|
|
109
127
|
function eventsForDay(ms) {
|
|
110
128
|
const dayStart = ms;
|
|
111
|
-
const dayEnd = ms
|
|
129
|
+
const dayEnd = addDaysMs(ms, 1);
|
|
112
130
|
return events.filter((e) => e.start.getTime() < dayEnd && e.end.getTime() > dayStart).sort((a, b) => {
|
|
113
131
|
const aAll = isAllDay(a) ? 0 : 1;
|
|
114
132
|
const bAll = isAllDay(b) ? 0 : 1;
|
|
@@ -118,10 +136,10 @@ function eventsForDay(ms) {
|
|
|
118
136
|
const weeks = $derived.by(() => {
|
|
119
137
|
if (!range) return [];
|
|
120
138
|
const rows = [];
|
|
121
|
-
for (let ms = sod(range.start.getTime()); ms < range.end.getTime(); ms
|
|
139
|
+
for (let ms = sod(range.start.getTime()); ms < range.end.getTime(); ms = addDaysMs(ms, 7)) {
|
|
122
140
|
const row = [];
|
|
123
141
|
for (let i = 0; i < 7; i++) {
|
|
124
|
-
const cellMs = ms
|
|
142
|
+
const cellMs = addDaysMs(ms, i);
|
|
125
143
|
const date = new Date(cellMs);
|
|
126
144
|
const jsDay = date.getDay();
|
|
127
145
|
const dayEvents = eventsForDay(cellMs);
|
|
@@ -183,13 +201,7 @@ function chipTime(e) {
|
|
|
183
201
|
aria-label={cellLabel(cell)}
|
|
184
202
|
aria-current={cell.isToday ? 'date' : undefined}
|
|
185
203
|
data-ms={cell.ms}
|
|
186
|
-
tabindex={cell.
|
|
187
|
-
? undefined
|
|
188
|
-
: cellsInteractive || cell.overflow > 0
|
|
189
|
-
? cell.ms === rovingMs
|
|
190
|
-
? 0
|
|
191
|
-
: -1
|
|
192
|
-
: undefined}
|
|
204
|
+
tabindex={isFocusable(cell) ? (cell.ms === rovingMs ? 0 : -1) : undefined}
|
|
193
205
|
onclick={() => {
|
|
194
206
|
if (!cell.isDisabled) ondayclick?.(cell.date);
|
|
195
207
|
}}
|
|
@@ -208,7 +220,7 @@ function chipTime(e) {
|
|
|
208
220
|
class:mg-chip--selected={ev.id === selectedEventId}
|
|
209
221
|
onclick={(e) => {
|
|
210
222
|
e.stopPropagation();
|
|
211
|
-
oneventclick?.(ev);
|
|
223
|
+
oneventclick?.(ev, e.currentTarget.getBoundingClientRect());
|
|
212
224
|
}}
|
|
213
225
|
onmouseenter={() => oneventhover?.(ev)}
|
|
214
226
|
>
|
|
@@ -225,7 +237,7 @@ function chipTime(e) {
|
|
|
225
237
|
aria-label="{ev.title}{chipTime(ev) ? `, ${chipTime(ev)}` : ''}"
|
|
226
238
|
onclick={(e) => {
|
|
227
239
|
e.stopPropagation();
|
|
228
|
-
oneventclick?.(ev);
|
|
240
|
+
oneventclick?.(ev, e.currentTarget.getBoundingClientRect());
|
|
229
241
|
}}
|
|
230
242
|
onmouseenter={() => oneventhover?.(ev)}
|
|
231
243
|
>
|
|
@@ -5,7 +5,7 @@ interface Props {
|
|
|
5
5
|
height?: number | null;
|
|
6
6
|
locale?: string;
|
|
7
7
|
focusDate?: Date;
|
|
8
|
-
oneventclick?: (event: TimelineEvent) => void;
|
|
8
|
+
oneventclick?: (event: TimelineEvent, anchor?: DOMRect) => void;
|
|
9
9
|
selectedEventId?: string | null;
|
|
10
10
|
readOnly?: boolean;
|
|
11
11
|
[key: string]: unknown;
|