@nomideusz/svelte-calendar 0.6.0 → 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 +11 -0
- package/dist/adapters/recurring.js +9 -13
- package/dist/adapters/types.d.ts +11 -2
- package/dist/calendar/Calendar.svelte +847 -767
- package/dist/calendar/Calendar.svelte.d.ts +14 -2
- 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/DayHeader.svelte +103 -103
- package/dist/primitives/EmptySlot.svelte +105 -105
- package/dist/primitives/EventBlock.svelte +397 -312
- package/dist/primitives/NowIndicator.svelte +178 -178
- package/dist/primitives/TimeGutter.svelte +104 -104
- package/dist/theme/presets.js +36 -36
- package/dist/views/agenda/AgendaDay.svelte +1167 -1055
- package/dist/views/agenda/AgendaWeek.svelte +987 -924
- package/dist/views/mobile/Mobile.svelte +17 -17
- package/dist/views/mobile/MobileDay.svelte +729 -665
- package/dist/views/mobile/MobileWeek.svelte +476 -456
- package/dist/views/planner/PlannerDay.svelte +1184 -1091
- package/dist/views/planner/PlannerWeek.svelte +924 -945
- 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/dist/widget/CalendarWidget.svelte +142 -142
- package/package.json +76 -71
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { TimelineEvent, BlockedSlot } from '../../core/types.js';
|
|
3
|
+
import type { DragState } from '../../engine/drag.svelte.js';
|
|
4
|
+
import type { ViewState } from '../../engine/view-state.svelte.js';
|
|
5
|
+
export interface CalendarContext {
|
|
6
|
+
readonly viewState: ViewState | undefined;
|
|
7
|
+
readonly drag: DragState | undefined;
|
|
8
|
+
readonly commitDrag: (() => void) | undefined;
|
|
9
|
+
readonly snapInterval: number;
|
|
10
|
+
readonly showNav: boolean;
|
|
11
|
+
readonly equalDays: boolean;
|
|
12
|
+
readonly showDates: boolean;
|
|
13
|
+
readonly hideDays: number[] | undefined;
|
|
14
|
+
readonly isMobile: boolean;
|
|
15
|
+
readonly autoHeight: boolean;
|
|
16
|
+
readonly compact: boolean;
|
|
17
|
+
readonly readOnly: boolean;
|
|
18
|
+
readonly blockedSlots: BlockedSlot[] | undefined;
|
|
19
|
+
readonly dayHeaderSnippet: Snippet<[{
|
|
20
|
+
date: Date;
|
|
21
|
+
isToday: boolean;
|
|
22
|
+
dayName: string;
|
|
23
|
+
}]> | undefined;
|
|
24
|
+
readonly minDuration: number | undefined;
|
|
25
|
+
readonly maxDuration: number | undefined;
|
|
26
|
+
readonly oneventhover: ((event: TimelineEvent) => void) | undefined;
|
|
27
|
+
readonly disabledDates: Date[] | undefined;
|
|
28
|
+
readonly disabledSet: Set<number>;
|
|
29
|
+
readonly loadRange: {
|
|
30
|
+
current: {
|
|
31
|
+
start: Date;
|
|
32
|
+
end: Date;
|
|
33
|
+
} | null;
|
|
34
|
+
set: (r: {
|
|
35
|
+
start: Date;
|
|
36
|
+
end: Date;
|
|
37
|
+
} | null) => void;
|
|
38
|
+
} | undefined;
|
|
39
|
+
readonly eventSnippet: Snippet<[TimelineEvent]> | undefined;
|
|
40
|
+
readonly emptySnippet: Snippet | undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the calendar context.
|
|
44
|
+
* Call at component init time (top-level script).
|
|
45
|
+
* Returns an object with getters that delegate to the raw context.
|
|
46
|
+
*/
|
|
47
|
+
export declare function useCalendarContext(): CalendarContext;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared context reader for calendar views.
|
|
3
|
+
*
|
|
4
|
+
* Calendar.svelte sets a single 'calendar' context with reactive getters.
|
|
5
|
+
* This module reads it and returns a typed interface for views.
|
|
6
|
+
*/
|
|
7
|
+
import { getContext } from 'svelte';
|
|
8
|
+
import { sod } from '../../core/time.js';
|
|
9
|
+
/**
|
|
10
|
+
* Read the calendar context.
|
|
11
|
+
* Call at component init time (top-level script).
|
|
12
|
+
* Returns an object with getters that delegate to the raw context.
|
|
13
|
+
*/
|
|
14
|
+
export function useCalendarContext() {
|
|
15
|
+
const raw = getContext('calendar');
|
|
16
|
+
return {
|
|
17
|
+
get viewState() { return raw?.viewState; },
|
|
18
|
+
get drag() { return raw?.drag; },
|
|
19
|
+
get commitDrag() { return raw?.commitDrag; },
|
|
20
|
+
get snapInterval() { return raw?.snapInterval ?? 15; },
|
|
21
|
+
get showNav() { return raw?.showNavigation ?? true; },
|
|
22
|
+
get equalDays() { return raw?.equalDays ?? false; },
|
|
23
|
+
get showDates() { return raw?.showDates ?? true; },
|
|
24
|
+
get hideDays() { return raw?.hideDays; },
|
|
25
|
+
get isMobile() { return raw?.mobile ?? false; },
|
|
26
|
+
get autoHeight() { return raw?.autoHeight ?? false; },
|
|
27
|
+
get compact() { return raw?.compact ?? false; },
|
|
28
|
+
get readOnly() { return raw?.readOnly ?? false; },
|
|
29
|
+
get blockedSlots() { return raw?.blockedSlots; },
|
|
30
|
+
get dayHeaderSnippet() { return raw?.dayHeaderSnippet; },
|
|
31
|
+
get minDuration() { return raw?.minDuration; },
|
|
32
|
+
get maxDuration() { return raw?.maxDuration; },
|
|
33
|
+
get oneventhover() { return raw?.oneventhover; },
|
|
34
|
+
get disabledDates() { return raw?.disabledDates; },
|
|
35
|
+
get disabledSet() { return new Set(raw?.disabledDates?.map(d => sod(d.getTime())) ?? []); },
|
|
36
|
+
get loadRange() {
|
|
37
|
+
if (!raw)
|
|
38
|
+
return undefined;
|
|
39
|
+
return {
|
|
40
|
+
get current() { return raw.loadRange; },
|
|
41
|
+
set: (r) => raw.setLoadRange(r),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
get eventSnippet() { return raw?.eventSnippet; },
|
|
45
|
+
get emptySnippet() { return raw?.emptySnippet; },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared format helpers for agenda views.
|
|
3
|
+
* Extracted from duplicated code in AgendaDay and AgendaWeek.
|
|
4
|
+
*/
|
|
5
|
+
import type { TimelineEvent } from '../../core/types.js';
|
|
6
|
+
export declare function fmtTime(d: Date, locale?: string): string;
|
|
7
|
+
export declare function duration(ev: TimelineEvent): string;
|
|
8
|
+
export declare function timeUntilMs(ms: number, now: number): string;
|
|
9
|
+
export declare function progress(ev: TimelineEvent, now: number): number;
|
|
10
|
+
/**
|
|
11
|
+
* Group overlapping events into time slots.
|
|
12
|
+
* Used by both AgendaDay and AgendaWeek.
|
|
13
|
+
*/
|
|
14
|
+
export interface TimeSlot {
|
|
15
|
+
startMs: number;
|
|
16
|
+
endMs: number;
|
|
17
|
+
events: TimelineEvent[];
|
|
18
|
+
}
|
|
19
|
+
export declare function groupIntoSlots(evts: TimelineEvent[]): TimeSlot[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { fmtTime as _fmtTime, fmtDuration, getLabels } from '../../core/locale.js';
|
|
2
|
+
export function fmtTime(d, locale) {
|
|
3
|
+
return _fmtTime(d, locale);
|
|
4
|
+
}
|
|
5
|
+
export function duration(ev) {
|
|
6
|
+
return fmtDuration(ev.start, ev.end);
|
|
7
|
+
}
|
|
8
|
+
export function timeUntilMs(ms, now) {
|
|
9
|
+
const L = getLabels();
|
|
10
|
+
const diff = ms - now;
|
|
11
|
+
if (diff <= 0)
|
|
12
|
+
return L.now;
|
|
13
|
+
const tMins = Math.floor(diff / 60000);
|
|
14
|
+
if (tMins < 60)
|
|
15
|
+
return `in ${tMins}m`;
|
|
16
|
+
const hrs = Math.floor(tMins / 60);
|
|
17
|
+
const rm = tMins % 60;
|
|
18
|
+
if (hrs < 24)
|
|
19
|
+
return rm > 0 ? `in ${hrs}h ${rm}m` : `in ${hrs}h`;
|
|
20
|
+
const days = Math.floor(hrs / 24);
|
|
21
|
+
return `in ${days}d`;
|
|
22
|
+
}
|
|
23
|
+
export function progress(ev, now) {
|
|
24
|
+
const s = ev.start.getTime();
|
|
25
|
+
const e = ev.end.getTime();
|
|
26
|
+
return Math.min(1, Math.max(0, (now - s) / (e - s)));
|
|
27
|
+
}
|
|
28
|
+
export function groupIntoSlots(evts) {
|
|
29
|
+
const sorted = [...evts].sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
30
|
+
const slots = [];
|
|
31
|
+
for (const ev of sorted) {
|
|
32
|
+
const last = slots[slots.length - 1];
|
|
33
|
+
if (last && ev.start.getTime() < last.endMs) {
|
|
34
|
+
last.events.push(ev);
|
|
35
|
+
last.endMs = Math.max(last.endMs, ev.end.getTime());
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
slots.push({
|
|
39
|
+
startMs: ev.start.getTime(),
|
|
40
|
+
endMs: ev.end.getTime(),
|
|
41
|
+
events: [ev],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return slots;
|
|
46
|
+
}
|
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
CalendarWidget — self-contained calendar for embedding via <day-calendar> custom element.
|
|
3
|
-
|
|
4
|
-
Accepts simple HTML attributes and wires up the full Calendar with sensible defaults.
|
|
5
|
-
Designed for non-Svelte sites (plain HTML, WordPress, Squarespace, etc.).
|
|
6
|
-
|
|
7
|
-
Usage as custom element:
|
|
8
|
-
<day-calendar
|
|
9
|
-
api="https://myschool.com/api/events"
|
|
10
|
-
theme="neutral"
|
|
11
|
-
view="week-planner"
|
|
12
|
-
height="600"
|
|
13
|
-
locale="en-US"
|
|
14
|
-
></day-calendar>
|
|
15
|
-
-->
|
|
16
|
-
<script lang="ts">
|
|
17
|
-
import Calendar from '../calendar/Calendar.svelte';
|
|
18
|
-
import { createRestAdapter } from '../adapters/rest.js';
|
|
19
|
-
import { createMemoryAdapter } from '../adapters/memory.js';
|
|
20
|
-
import { presets } from '../theme/presets.js';
|
|
21
|
-
import type { PresetName } from '../theme/presets.js';
|
|
22
|
-
import type { TimelineEvent } from '../core/types.js';
|
|
23
|
-
|
|
24
|
-
interface Props {
|
|
25
|
-
/** REST API base URL — if provided, fetches events from this endpoint */
|
|
26
|
-
api?: string;
|
|
27
|
-
/** JSON string of events for static/inline data (alternative to api) */
|
|
28
|
-
events?: string;
|
|
29
|
-
/** Theme preset name: midnight, neutral */
|
|
30
|
-
theme?: string;
|
|
31
|
-
/** Default view ID */
|
|
32
|
-
view?: string;
|
|
33
|
-
/** Calendar height in pixels */
|
|
34
|
-
height?: string;
|
|
35
|
-
/** BCP 47 locale tag (e.g. 'en-US', 'pl-PL') */
|
|
36
|
-
locale?: string;
|
|
37
|
-
/** Text direction: ltr, rtl, auto */
|
|
38
|
-
dir?: string;
|
|
39
|
-
/** Start week on Monday (default: true) */
|
|
40
|
-
mondaystart?: string;
|
|
41
|
-
/** Custom HTTP headers as JSON string for REST adapter */
|
|
42
|
-
headers?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let {
|
|
46
|
-
api,
|
|
47
|
-
events,
|
|
48
|
-
theme = 'neutral',
|
|
49
|
-
view = 'week-planner',
|
|
50
|
-
height = '600',
|
|
51
|
-
locale,
|
|
52
|
-
dir,
|
|
53
|
-
mondaystart = 'true',
|
|
54
|
-
headers,
|
|
55
|
-
}: Props = $props();
|
|
56
|
-
|
|
57
|
-
// ── Parse attributes ──
|
|
58
|
-
const heightPx = $derived(parseInt(height, 10) || 600);
|
|
59
|
-
const isMondayStart = $derived(mondaystart !== 'false');
|
|
60
|
-
const themeStyle = $derived(
|
|
61
|
-
(presets as Record<string, string>)[theme] || presets.neutral
|
|
62
|
-
);
|
|
63
|
-
const dirValue = $derived(
|
|
64
|
-
(dir === 'rtl' || dir === 'ltr' || dir === 'auto') ? dir as 'ltr' | 'rtl' | 'auto' : undefined
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// ── Parse static events from JSON attribute ──
|
|
68
|
-
function parseHeaders(json?: string): Record<string, string> | undefined {
|
|
69
|
-
if (!json) return undefined;
|
|
70
|
-
try {
|
|
71
|
-
const parsed = JSON.parse(json) as Record<string, unknown>;
|
|
72
|
-
const out: Record<string, string> = {};
|
|
73
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
74
|
-
out[k] = String(v);
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
} catch {
|
|
78
|
-
console.warn('[day-calendar] Failed to parse headers JSON:', json);
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function toEvent(raw: Record<string, unknown>, fallbackId: string): TimelineEvent | null {
|
|
84
|
-
const start = new Date(String(raw.start ?? ''));
|
|
85
|
-
const end = new Date(String(raw.end ?? ''));
|
|
86
|
-
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
id: String(raw.id ?? fallbackId),
|
|
91
|
-
title: String(raw.title ?? 'Untitled'),
|
|
92
|
-
start,
|
|
93
|
-
end,
|
|
94
|
-
color: raw.color ? String(raw.color) : undefined,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function parseEvents(json?: string): TimelineEvent[] {
|
|
99
|
-
if (!json) return [];
|
|
100
|
-
try {
|
|
101
|
-
const raw = JSON.parse(json) as Array<Record<string, unknown>>;
|
|
102
|
-
const parsed = raw
|
|
103
|
-
.map((e, idx) => toEvent(e, `inline-${idx}`))
|
|
104
|
-
.filter((ev): ev is TimelineEvent => ev !== null);
|
|
105
|
-
if (parsed.length !== raw.length) {
|
|
106
|
-
console.warn(`[day-calendar] Ignored ${raw.length - parsed.length} invalid event(s) from events JSON.`);
|
|
107
|
-
}
|
|
108
|
-
return parsed;
|
|
109
|
-
} catch {
|
|
110
|
-
console.warn('[day-calendar] Failed to parse events JSON:', json);
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Create adapter ──
|
|
116
|
-
const adapter = $derived.by(() => {
|
|
117
|
-
if (api) {
|
|
118
|
-
const parsedHeaders = parseHeaders(headers);
|
|
119
|
-
return createRestAdapter({
|
|
120
|
-
baseUrl: api,
|
|
121
|
-
headers: parsedHeaders,
|
|
122
|
-
mapEvents: (data: unknown) => {
|
|
123
|
-
const arr = Array.isArray(data) ? data : (data as Record<string, unknown>).events as unknown[] ?? [];
|
|
124
|
-
return arr
|
|
125
|
-
.map((e: unknown, idx) => toEvent(e as Record<string, unknown>, `api-${idx}`))
|
|
126
|
-
.filter((ev): ev is TimelineEvent => ev !== null);
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
return createMemoryAdapter(parseEvents(events));
|
|
131
|
-
});
|
|
132
|
-
</script>
|
|
133
|
-
|
|
134
|
-
<Calendar
|
|
135
|
-
{adapter}
|
|
136
|
-
{view}
|
|
137
|
-
theme={themeStyle}
|
|
138
|
-
height={heightPx}
|
|
139
|
-
mondayStart={isMondayStart}
|
|
140
|
-
dir={dirValue}
|
|
141
|
-
{locale}
|
|
142
|
-
/>
|
|
1
|
+
<!--
|
|
2
|
+
CalendarWidget — self-contained calendar for embedding via <day-calendar> custom element.
|
|
3
|
+
|
|
4
|
+
Accepts simple HTML attributes and wires up the full Calendar with sensible defaults.
|
|
5
|
+
Designed for non-Svelte sites (plain HTML, WordPress, Squarespace, etc.).
|
|
6
|
+
|
|
7
|
+
Usage as custom element:
|
|
8
|
+
<day-calendar
|
|
9
|
+
api="https://myschool.com/api/events"
|
|
10
|
+
theme="neutral"
|
|
11
|
+
view="week-planner"
|
|
12
|
+
height="600"
|
|
13
|
+
locale="en-US"
|
|
14
|
+
></day-calendar>
|
|
15
|
+
-->
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import Calendar from '../calendar/Calendar.svelte';
|
|
18
|
+
import { createRestAdapter } from '../adapters/rest.js';
|
|
19
|
+
import { createMemoryAdapter } from '../adapters/memory.js';
|
|
20
|
+
import { presets } from '../theme/presets.js';
|
|
21
|
+
import type { PresetName } from '../theme/presets.js';
|
|
22
|
+
import type { TimelineEvent } from '../core/types.js';
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
/** REST API base URL — if provided, fetches events from this endpoint */
|
|
26
|
+
api?: string;
|
|
27
|
+
/** JSON string of events for static/inline data (alternative to api) */
|
|
28
|
+
events?: string;
|
|
29
|
+
/** Theme preset name: midnight, neutral */
|
|
30
|
+
theme?: string;
|
|
31
|
+
/** Default view ID */
|
|
32
|
+
view?: string;
|
|
33
|
+
/** Calendar height in pixels */
|
|
34
|
+
height?: string;
|
|
35
|
+
/** BCP 47 locale tag (e.g. 'en-US', 'pl-PL') */
|
|
36
|
+
locale?: string;
|
|
37
|
+
/** Text direction: ltr, rtl, auto */
|
|
38
|
+
dir?: string;
|
|
39
|
+
/** Start week on Monday (default: true) */
|
|
40
|
+
mondaystart?: string;
|
|
41
|
+
/** Custom HTTP headers as JSON string for REST adapter */
|
|
42
|
+
headers?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let {
|
|
46
|
+
api,
|
|
47
|
+
events,
|
|
48
|
+
theme = 'neutral',
|
|
49
|
+
view = 'week-planner',
|
|
50
|
+
height = '600',
|
|
51
|
+
locale,
|
|
52
|
+
dir,
|
|
53
|
+
mondaystart = 'true',
|
|
54
|
+
headers,
|
|
55
|
+
}: Props = $props();
|
|
56
|
+
|
|
57
|
+
// ── Parse attributes ──
|
|
58
|
+
const heightPx = $derived(parseInt(height, 10) || 600);
|
|
59
|
+
const isMondayStart = $derived(mondaystart !== 'false');
|
|
60
|
+
const themeStyle = $derived(
|
|
61
|
+
(presets as Record<string, string>)[theme] || presets.neutral
|
|
62
|
+
);
|
|
63
|
+
const dirValue = $derived(
|
|
64
|
+
(dir === 'rtl' || dir === 'ltr' || dir === 'auto') ? dir as 'ltr' | 'rtl' | 'auto' : undefined
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// ── Parse static events from JSON attribute ──
|
|
68
|
+
function parseHeaders(json?: string): Record<string, string> | undefined {
|
|
69
|
+
if (!json) return undefined;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(json) as Record<string, unknown>;
|
|
72
|
+
const out: Record<string, string> = {};
|
|
73
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
74
|
+
out[k] = String(v);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
} catch {
|
|
78
|
+
console.warn('[day-calendar] Failed to parse headers JSON:', json);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function toEvent(raw: Record<string, unknown>, fallbackId: string): TimelineEvent | null {
|
|
84
|
+
const start = new Date(String(raw.start ?? ''));
|
|
85
|
+
const end = new Date(String(raw.end ?? ''));
|
|
86
|
+
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
id: String(raw.id ?? fallbackId),
|
|
91
|
+
title: String(raw.title ?? 'Untitled'),
|
|
92
|
+
start,
|
|
93
|
+
end,
|
|
94
|
+
color: raw.color ? String(raw.color) : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseEvents(json?: string): TimelineEvent[] {
|
|
99
|
+
if (!json) return [];
|
|
100
|
+
try {
|
|
101
|
+
const raw = JSON.parse(json) as Array<Record<string, unknown>>;
|
|
102
|
+
const parsed = raw
|
|
103
|
+
.map((e, idx) => toEvent(e, `inline-${idx}`))
|
|
104
|
+
.filter((ev): ev is TimelineEvent => ev !== null);
|
|
105
|
+
if (parsed.length !== raw.length) {
|
|
106
|
+
console.warn(`[day-calendar] Ignored ${raw.length - parsed.length} invalid event(s) from events JSON.`);
|
|
107
|
+
}
|
|
108
|
+
return parsed;
|
|
109
|
+
} catch {
|
|
110
|
+
console.warn('[day-calendar] Failed to parse events JSON:', json);
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Create adapter ──
|
|
116
|
+
const adapter = $derived.by(() => {
|
|
117
|
+
if (api) {
|
|
118
|
+
const parsedHeaders = parseHeaders(headers);
|
|
119
|
+
return createRestAdapter({
|
|
120
|
+
baseUrl: api,
|
|
121
|
+
headers: parsedHeaders,
|
|
122
|
+
mapEvents: (data: unknown) => {
|
|
123
|
+
const arr = Array.isArray(data) ? data : (data as Record<string, unknown>).events as unknown[] ?? [];
|
|
124
|
+
return arr
|
|
125
|
+
.map((e: unknown, idx) => toEvent(e as Record<string, unknown>, `api-${idx}`))
|
|
126
|
+
.filter((ev): ev is TimelineEvent => ev !== null);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return createMemoryAdapter(parseEvents(events));
|
|
131
|
+
});
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<Calendar
|
|
135
|
+
{adapter}
|
|
136
|
+
{view}
|
|
137
|
+
theme={themeStyle}
|
|
138
|
+
height={heightPx}
|
|
139
|
+
mondayStart={isMondayStart}
|
|
140
|
+
dir={dirValue}
|
|
141
|
+
{locale}
|
|
142
|
+
/>
|
package/package.json
CHANGED
|
@@ -1,72 +1,77 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
2
|
+
"name": "@nomideusz/svelte-calendar",
|
|
3
|
+
"version": "0.6.4",
|
|
4
|
+
"description": "A themeable Svelte 5 calendar with Day and Week views — Planner and Agenda.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"svelte": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"svelte": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./widget": "./widget/widget.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"widget",
|
|
20
|
+
"!dist/**/*.test.*",
|
|
21
|
+
"!dist/**/*.spec.*"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"svelte": "^5.0.0",
|
|
25
|
+
"@chenglou/pretext": "^0.0.4"
|
|
26
|
+
},
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"@chenglou/pretext": {
|
|
29
|
+
"optional": true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@chenglou/pretext": "^0.0.4",
|
|
34
|
+
"@sveltejs/adapter-auto": "^7.0.0",
|
|
35
|
+
"@sveltejs/kit": "^2.50.2",
|
|
36
|
+
"@sveltejs/package": "^2.5.7",
|
|
37
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
38
|
+
"@types/node": "^22.0.0",
|
|
39
|
+
"@vercel/analytics": "^1.6.1",
|
|
40
|
+
"jsdom": "^28.1.0",
|
|
41
|
+
"marked": "^17.0.3",
|
|
42
|
+
"svelte": "^5.51.0",
|
|
43
|
+
"svelte-check": "^4.3.6",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"vite": "^7.3.1",
|
|
46
|
+
"vitest": "^4.0.18"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"date-fns": "^4.1.0",
|
|
50
|
+
"date-fns-tz": "^3.2.0"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"svelte",
|
|
54
|
+
"calendar",
|
|
55
|
+
"scheduler",
|
|
56
|
+
"timeline",
|
|
57
|
+
"week",
|
|
58
|
+
"day",
|
|
59
|
+
"booking",
|
|
60
|
+
"svelte5"
|
|
61
|
+
],
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "https://github.com/nomideusz/svelte-calendar"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"dev": "vite dev",
|
|
68
|
+
"build": "vite build",
|
|
69
|
+
"package": "svelte-kit sync && svelte-package",
|
|
70
|
+
"build:widget": "vite build --config vite.config.widget.ts",
|
|
71
|
+
"preview": "vite preview",
|
|
72
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
73
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
74
|
+
"test": "vitest run",
|
|
75
|
+
"test:watch": "vitest"
|
|
76
|
+
}
|
|
77
|
+
}
|