@nomideusz/svelte-calendar 0.5.2 → 0.6.3

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.
Files changed (49) hide show
  1. package/README.md +408 -178
  2. package/dist/adapters/memory.d.ts +7 -9
  3. package/dist/adapters/memory.js +8 -23
  4. package/dist/adapters/recurring.d.ts +8 -6
  5. package/dist/adapters/recurring.js +28 -30
  6. package/dist/calendar/Calendar.svelte +776 -356
  7. package/dist/calendar/Calendar.svelte.d.ts +57 -6
  8. package/dist/core/index.d.ts +2 -2
  9. package/dist/core/index.js +1 -1
  10. package/dist/core/locale.js +2 -2
  11. package/dist/core/palette.d.ts +5 -0
  12. package/dist/core/palette.js +8 -0
  13. package/dist/core/types.d.ts +14 -0
  14. package/dist/engine/event-store.svelte.d.ts +0 -17
  15. package/dist/engine/event-store.svelte.js +30 -17
  16. package/dist/engine/index.d.ts +1 -1
  17. package/dist/engine/view-state.svelte.d.ts +10 -5
  18. package/dist/engine/view-state.svelte.js +32 -16
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.js +3 -4
  21. package/dist/primitives/DayHeader.svelte +103 -103
  22. package/dist/primitives/EmptySlot.svelte +105 -105
  23. package/dist/primitives/EventBlock.svelte +312 -312
  24. package/dist/primitives/NowIndicator.svelte +178 -178
  25. package/dist/primitives/TimeGutter.svelte +104 -104
  26. package/dist/theme/auto.d.ts +53 -0
  27. package/dist/theme/auto.js +534 -0
  28. package/dist/theme/index.d.ts +3 -1
  29. package/dist/theme/index.js +3 -1
  30. package/dist/theme/presets.d.ts +20 -6
  31. package/dist/theme/presets.js +55 -42
  32. package/dist/views/agenda/AgendaDay.svelte +1073 -976
  33. package/dist/views/agenda/AgendaWeek.svelte +934 -775
  34. package/dist/views/agenda/index.d.ts +0 -2
  35. package/dist/views/agenda/index.js +0 -2
  36. package/dist/views/index.d.ts +3 -2
  37. package/dist/views/index.js +3 -2
  38. package/dist/views/mobile/Mobile.svelte +17 -0
  39. package/dist/views/mobile/Mobile.svelte.d.ts +7 -0
  40. package/dist/views/mobile/MobileDay.svelte +702 -0
  41. package/dist/views/mobile/MobileDay.svelte.d.ts +20 -0
  42. package/dist/views/mobile/MobileWeek.svelte +461 -0
  43. package/dist/views/mobile/MobileWeek.svelte.d.ts +20 -0
  44. package/dist/views/mobile/index.d.ts +1 -0
  45. package/dist/views/mobile/index.js +1 -0
  46. package/dist/views/planner/PlannerDay.svelte +1129 -957
  47. package/dist/views/planner/PlannerWeek.svelte +949 -840
  48. package/dist/widget/CalendarWidget.svelte +142 -157
  49. package/package.json +2 -1
@@ -1,157 +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 type { CalendarView } from '../calendar/Calendar.svelte';
19
- import { Planner } from '../views/planner/index.js';
20
- import { Agenda } from '../views/agenda/index.js';
21
- import { createRestAdapter } from '../adapters/rest.js';
22
- import { createMemoryAdapter } from '../adapters/memory.js';
23
- import { presets } from '../theme/presets.js';
24
- import type { PresetName } from '../theme/presets.js';
25
- import type { TimelineEvent } from '../core/types.js';
26
- import { getLabels } from '../core/locale.js';
27
-
28
- const L = $derived(getLabels());
29
-
30
- interface Props {
31
- /** REST API base URL — if provided, fetches events from this endpoint */
32
- api?: string;
33
- /** JSON string of events for static/inline data (alternative to api) */
34
- events?: string;
35
- /** Theme preset name: midnight, neutral */
36
- theme?: string;
37
- /** Default view ID */
38
- view?: string;
39
- /** Calendar height in pixels */
40
- height?: string;
41
- /** BCP 47 locale tag (e.g. 'en-US', 'pl-PL') */
42
- locale?: string;
43
- /** Text direction: ltr, rtl, auto */
44
- dir?: string;
45
- /** Start week on Monday (default: true) */
46
- mondaystart?: string;
47
- /** Custom HTTP headers as JSON string for REST adapter */
48
- headers?: string;
49
- }
50
-
51
- let {
52
- api,
53
- events,
54
- theme = 'neutral',
55
- view = 'week-planner',
56
- height = '600',
57
- locale,
58
- dir,
59
- mondaystart = 'true',
60
- headers,
61
- }: Props = $props();
62
-
63
- // ── Parse attributes ──
64
- const heightPx = $derived(parseInt(height, 10) || 600);
65
- const isMondayStart = $derived(mondaystart !== 'false');
66
- const themeStyle = $derived(
67
- (presets as Record<string, string>)[theme] ?? presets.neutral
68
- );
69
- const dirValue = $derived(
70
- (dir === 'rtl' || dir === 'ltr' || dir === 'auto') ? dir as 'ltr' | 'rtl' | 'auto' : undefined
71
- );
72
-
73
- // ── Parse static events from JSON attribute ──
74
- function parseHeaders(json?: string): Record<string, string> | undefined {
75
- if (!json) return undefined;
76
- try {
77
- const parsed = JSON.parse(json) as Record<string, unknown>;
78
- const out: Record<string, string> = {};
79
- for (const [k, v] of Object.entries(parsed)) {
80
- out[k] = String(v);
81
- }
82
- return out;
83
- } catch {
84
- console.warn('[day-calendar] Failed to parse headers JSON:', json);
85
- return undefined;
86
- }
87
- }
88
-
89
- function toEvent(raw: Record<string, unknown>, fallbackId: string): TimelineEvent | null {
90
- const start = new Date(String(raw.start ?? ''));
91
- const end = new Date(String(raw.end ?? ''));
92
- if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) {
93
- return null;
94
- }
95
- return {
96
- id: String(raw.id ?? fallbackId),
97
- title: String(raw.title ?? 'Untitled'),
98
- start,
99
- end,
100
- color: raw.color ? String(raw.color) : undefined,
101
- };
102
- }
103
-
104
- function parseEvents(json?: string): TimelineEvent[] {
105
- if (!json) return [];
106
- try {
107
- const raw = JSON.parse(json) as Array<Record<string, unknown>>;
108
- const parsed = raw
109
- .map((e, idx) => toEvent(e, `inline-${idx}`))
110
- .filter((ev): ev is TimelineEvent => ev !== null);
111
- if (parsed.length !== raw.length) {
112
- console.warn(`[day-calendar] Ignored ${raw.length - parsed.length} invalid event(s) from events JSON.`);
113
- }
114
- return parsed;
115
- } catch {
116
- console.warn('[day-calendar] Failed to parse events JSON:', json);
117
- return [];
118
- }
119
- }
120
-
121
- // ── Create adapter ──
122
- const adapter = $derived.by(() => {
123
- if (api) {
124
- const parsedHeaders = parseHeaders(headers);
125
- return createRestAdapter({
126
- baseUrl: api,
127
- headers: parsedHeaders,
128
- mapEvents: (data: unknown) => {
129
- const arr = Array.isArray(data) ? data : (data as Record<string, unknown>).events as unknown[] ?? [];
130
- return arr
131
- .map((e: unknown, idx) => toEvent(e as Record<string, unknown>, `api-${idx}`))
132
- .filter((ev): ev is TimelineEvent => ev !== null);
133
- },
134
- });
135
- }
136
- return createMemoryAdapter(parseEvents(events));
137
- });
138
-
139
- // ── Default views ──
140
- const defaultViews: CalendarView[] = $derived([
141
- { id: 'day-planner', label: L.planner, granularity: 'day', component: Planner, props: { mode: 'day' } },
142
- { id: 'week-planner', label: L.planner, granularity: 'week', component: Planner, props: { mode: 'week' } },
143
- { id: 'day-agenda', label: L.agenda, granularity: 'day', component: Agenda, props: { mode: 'day' } },
144
- { id: 'week-agenda', label: L.agenda, granularity: 'week', component: Agenda, props: { mode: 'week' } },
145
- ]);
146
- </script>
147
-
148
- <Calendar
149
- {adapter}
150
- views={defaultViews}
151
- defaultView={view}
152
- theme={themeStyle}
153
- height={heightPx}
154
- mondayStart={isMondayStart}
155
- dir={dirValue}
156
- {locale}
157
- />
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomideusz/svelte-calendar",
3
- "version": "0.5.2",
3
+ "version": "0.6.3",
4
4
  "description": "A themeable Svelte 5 calendar with Day and Week views — Planner and Agenda.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,6 +43,7 @@
43
43
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
44
44
  "@types/node": "^22.0.0",
45
45
  "@vercel/analytics": "^1.6.1",
46
+ "jsdom": "^28.1.0",
46
47
  "marked": "^17.0.3",
47
48
  "svelte": "^5.51.0",
48
49
  "svelte-check": "^4.3.6",