@nomideusz/svelte-calendar 0.2.2 → 0.3.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.
Files changed (40) hide show
  1. package/README.md +207 -6
  2. package/dist/adapters/index.d.ts +3 -0
  3. package/dist/adapters/index.js +1 -0
  4. package/dist/adapters/memory.d.ts +7 -1
  5. package/dist/adapters/memory.js +35 -4
  6. package/dist/adapters/recurring.d.ts +39 -0
  7. package/dist/adapters/recurring.js +117 -0
  8. package/dist/calendar/Calendar.svelte +20 -5
  9. package/dist/calendar/Calendar.svelte.d.ts +4 -0
  10. package/dist/core/types.d.ts +4 -0
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +3 -3
  13. package/dist/primitives/EmptySlot.svelte +2 -2
  14. package/dist/primitives/EventBlock.svelte +75 -14
  15. package/dist/theme/index.d.ts +1 -1
  16. package/dist/theme/index.js +1 -1
  17. package/dist/theme/presets.d.ts +10 -15
  18. package/dist/theme/presets.js +5 -11
  19. package/dist/views/agenda/Agenda.svelte +3 -3
  20. package/dist/views/day/DayGrid.svelte +5 -0
  21. package/dist/views/day/DayGrid.svelte.d.ts +5 -0
  22. package/dist/views/index.d.ts +1 -0
  23. package/dist/views/index.js +1 -0
  24. package/dist/views/schedule/WeekSchedule.svelte +133 -0
  25. package/dist/views/schedule/WeekSchedule.svelte.d.ts +38 -0
  26. package/dist/views/schedule/index.d.ts +1 -0
  27. package/dist/views/schedule/index.js +2 -0
  28. package/dist/views/settings/Settings.svelte +6 -2
  29. package/dist/views/week/WeekGrid.svelte +7 -2
  30. package/dist/views/week/WeekGrid.svelte.d.ts +3 -0
  31. package/dist/views/week/WeekHeatmap.svelte +12 -5
  32. package/dist/views/week/WeekHeatmap.svelte.d.ts +5 -1
  33. package/dist/widget/CalendarWidget.svelte +138 -0
  34. package/dist/widget/CalendarWidget.svelte.d.ts +23 -0
  35. package/dist/widget/index.d.ts +1 -0
  36. package/dist/widget/index.js +1 -0
  37. package/dist/widget/widget.d.ts +7 -0
  38. package/dist/widget/widget.js +9 -0
  39. package/package.json +5 -2
  40. package/widget/widget.js +260 -0
@@ -62,7 +62,7 @@
62
62
 
63
63
  const accentColor = $derived(event.color || 'var(--dt-accent, #ef4444)');
64
64
 
65
- const ariaLabel = $derived(() => {
65
+ const ariaLabel = $derived.by(() => {
66
66
  const t = event.title;
67
67
  const time = `${fmtTime(event.start)} to ${fmtTime(event.end)}`;
68
68
  const dur = fmtDuration(event.start, event.end);
@@ -78,19 +78,7 @@
78
78
  }
79
79
  </script>
80
80
 
81
- <div
82
- class="eb eb-{variant}"
83
- class:eb-active={active}
84
- class:eb-past={past}
85
- class:eb-editable={editable}
86
- style="--eb-color: {accentColor}"
87
- role={onclick ? 'button' : 'article'}
88
- tabindex={onclick ? 0 : -1}
89
- aria-label={ariaLabel()}
90
- aria-current={active ? 'true' : undefined}
91
- onclick={() => onclick?.(event)}
92
- onkeydown={handleKeydown}
93
- >
81
+ {#snippet content()}
94
82
  {#if children}
95
83
  {@render children(event)}
96
84
  {:else if variant === 'chip'}
@@ -101,12 +89,22 @@
101
89
  <div class="eb-stripe"></div>
102
90
  <div class="eb-body">
103
91
  <span class="eb-title">{event.title}</span>
92
+ {#if event.subtitle}
93
+ <span class="eb-subtitle">{event.subtitle}</span>
94
+ {/if}
104
95
  {#if showTime}
105
96
  <span class="eb-time">{fmtTime(event.start)} – {fmtTime(event.end)}</span>
106
97
  {/if}
107
98
  {#if showDuration}
108
99
  <span class="eb-dur">{fmtDuration(event.start, event.end)}</span>
109
100
  {/if}
101
+ {#if event.tags && event.tags.length > 0}
102
+ <div class="eb-tags">
103
+ {#each event.tags as tag}
104
+ <span class="eb-tag">{tag}</span>
105
+ {/each}
106
+ </div>
107
+ {/if}
110
108
  {#if active}<span class="eb-live-badge">now</span>{/if}
111
109
  </div>
112
110
  {:else}
@@ -114,12 +112,53 @@
114
112
  <span class="eb-stripe"></span>
115
113
  <span class="eb-time">{fmtTime(event.start)} – {fmtTime(event.end)}</span>
116
114
  <span class="eb-title">{event.title}</span>
115
+ {#if event.subtitle}
116
+ <span class="eb-subtitle">{event.subtitle}</span>
117
+ {/if}
117
118
  {#if showDuration}
118
119
  <span class="eb-dur">{fmtDuration(event.start, event.end)}</span>
119
120
  {/if}
121
+ {#if event.tags && event.tags.length > 0}
122
+ <span class="eb-tags">
123
+ {#each event.tags as tag}
124
+ <span class="eb-tag">{tag}</span>
125
+ {/each}
126
+ </span>
127
+ {/if}
120
128
  {#if active}<span class="eb-live-badge">now</span>{/if}
121
129
  {/if}
130
+ {/snippet}
131
+
132
+ {#if onclick}
133
+ <div
134
+ class="eb eb-{variant}"
135
+ class:eb-active={active}
136
+ class:eb-past={past}
137
+ class:eb-editable={editable}
138
+ style="--eb-color: {accentColor}"
139
+ role="button"
140
+ tabindex="0"
141
+ aria-label={ariaLabel}
142
+ aria-current={active ? 'true' : undefined}
143
+ onclick={() => onclick?.(event)}
144
+ onkeydown={handleKeydown}
145
+ >
146
+ {@render content()}
147
+ </div>
148
+ {:else}
149
+ <div
150
+ class="eb eb-{variant}"
151
+ class:eb-active={active}
152
+ class:eb-past={past}
153
+ class:eb-editable={editable}
154
+ style="--eb-color: {accentColor}"
155
+ role="article"
156
+ aria-label={ariaLabel}
157
+ aria-current={active ? 'true' : undefined}
158
+ >
159
+ {@render content()}
122
160
  </div>
161
+ {/if}
123
162
 
124
163
  <style>
125
164
  .eb {
@@ -243,6 +282,28 @@
243
282
  }
244
283
 
245
284
  /* ── Shared ── */
285
+ .eb-subtitle {
286
+ font: 400 10px / 1.2 var(--dt-sans, 'Outfit', system-ui, sans-serif);
287
+ color: var(--dt-text-2, rgba(148, 163, 184, 0.65));
288
+ overflow: hidden;
289
+ text-overflow: ellipsis;
290
+ white-space: nowrap;
291
+ }
292
+ .eb-tags {
293
+ display: inline-flex;
294
+ flex-wrap: wrap;
295
+ gap: 3px;
296
+ }
297
+ .eb-tag {
298
+ display: inline-block;
299
+ font: 500 8px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
300
+ letter-spacing: 0.04em;
301
+ text-transform: uppercase;
302
+ padding: 2px 5px;
303
+ border-radius: 3px;
304
+ background: color-mix(in srgb, var(--_color) 18%, transparent);
305
+ color: var(--_color);
306
+ }
246
307
  .eb-live-badge {
247
308
  display: inline-flex;
248
309
  align-items: center;
@@ -1,2 +1,2 @@
1
- export { midnight, parchment, indigo, neutral, bare, presets, stageBg, } from './presets.js';
1
+ export { midnight, parchment, indigo, neutral, bare, presets, } from './presets.js';
2
2
  export type { PresetName } from './presets.js';
@@ -1,2 +1,2 @@
1
1
  // ─── Theme barrel export ────────────────────────────────
2
- export { midnight, parchment, indigo, neutral, bare, presets, stageBg, } from './presets.js';
2
+ export { midnight, parchment, indigo, neutral, bare, presets, } from './presets.js';
@@ -5,33 +5,28 @@
5
5
  * Pass to the `style` prop of any timeline component.
6
6
  */
7
7
  /** Midnight Industrial — dark charcoal + red accent, tech monitoring */
8
- export declare const midnight = "\n\t--dt-bg: #0b0e14;\n\t--dt-surface: #10141c;\n\t--dt-border: rgba(148, 163, 184, 0.07);\n\t--dt-border-day: rgba(148, 163, 184, 0.14);\n\t--dt-text: rgba(226, 232, 240, 0.85);\n\t--dt-text-2: rgba(148, 163, 184, 0.55);\n\t--dt-text-3: rgba(100, 116, 139, 0.55);\n\t--dt-accent: #ef4444;\n\t--dt-accent-dim: rgba(239, 68, 68, 0.18);\n\t--dt-glow: rgba(239, 68, 68, 0.35);\n\t--dt-today-bg: rgba(239, 68, 68, 0.02);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(148, 163, 184, 0.12);\n\t--dt-success: rgba(74, 222, 128, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-hm-empty: rgba(80, 80, 120, 0.06);\n\t--dt-hm-low: rgba(239, 68, 68, 0.15);\n\t--dt-hm-mid: rgba(239, 68, 68, 0.3);\n\t--dt-hm-high: rgba(239, 68, 68, 0.5);\n\t--dt-hm-max: rgba(239, 68, 68, 0.72);\n";
8
+ export declare const midnight = "\n\t--dt-stage-bg: #080a0f;\n\t--dt-bg: #0b0e14;\n\t--dt-surface: #10141c;\n\t--dt-border: rgba(148, 163, 184, 0.07);\n\t--dt-border-day: rgba(148, 163, 184, 0.14);\n\t--dt-text: rgba(226, 232, 240, 0.85);\n\t--dt-text-2: rgba(148, 163, 184, 0.55);\n\t--dt-text-3: rgba(100, 116, 139, 0.55);\n\t--dt-accent: #ef4444;\n\t--dt-accent-dim: rgba(239, 68, 68, 0.18);\n\t--dt-glow: rgba(239, 68, 68, 0.35);\n\t--dt-today-bg: rgba(239, 68, 68, 0.02);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(148, 163, 184, 0.12);\n\t--dt-success: rgba(74, 222, 128, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-hm-empty: rgba(80, 80, 120, 0.06);\n\t--dt-hm-low: rgba(239, 68, 68, 0.15);\n\t--dt-hm-mid: rgba(239, 68, 68, 0.3);\n\t--dt-hm-high: rgba(239, 68, 68, 0.5);\n\t--dt-hm-max: rgba(239, 68, 68, 0.72);\n";
9
9
  /** Desert Parchment — warm analog planner, burnt sienna on aged paper */
10
- export declare const parchment = "\n\t--dt-bg: #f0e9dd;\n\t--dt-surface: #e8dfd0;\n\t--dt-border: rgba(139, 110, 70, 0.12);\n\t--dt-border-day: rgba(139, 110, 70, 0.22);\n\t--dt-text: rgba(55, 40, 22, 0.88);\n\t--dt-text-2: rgba(110, 85, 52, 0.62);\n\t--dt-text-3: rgba(139, 110, 70, 0.42);\n\t--dt-accent: #b85c2f;\n\t--dt-accent-dim: rgba(184, 92, 47, 0.14);\n\t--dt-glow: rgba(184, 92, 47, 0.25);\n\t--dt-today-bg: rgba(184, 92, 47, 0.04);\n\t--dt-btn-text: #fdf6ee;\n\t--dt-scrollbar: rgba(139, 110, 70, 0.15);\n\t--dt-success: rgba(76, 153, 76, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-mono: 'Courier New', 'Courier', monospace;\n\t--dt-sans: 'Outfit', Georgia, serif;\n\t--dt-hm-empty: rgba(139, 110, 70, 0.06);\n\t--dt-hm-low: rgba(184, 92, 47, 0.15);\n\t--dt-hm-mid: rgba(184, 92, 47, 0.3);\n\t--dt-hm-high: rgba(184, 92, 47, 0.5);\n\t--dt-hm-max: rgba(184, 92, 47, 0.72);\n";
10
+ export declare const parchment = "\n\t--dt-stage-bg: #e4dace;\n\t--dt-bg: #f0e9dd;\n\t--dt-surface: #e8dfd0;\n\t--dt-border: rgba(139, 110, 70, 0.12);\n\t--dt-border-day: rgba(139, 110, 70, 0.22);\n\t--dt-text: rgba(55, 40, 22, 0.88);\n\t--dt-text-2: rgba(110, 85, 52, 0.62);\n\t--dt-text-3: rgba(139, 110, 70, 0.42);\n\t--dt-accent: #b85c2f;\n\t--dt-accent-dim: rgba(184, 92, 47, 0.14);\n\t--dt-glow: rgba(184, 92, 47, 0.25);\n\t--dt-today-bg: rgba(184, 92, 47, 0.04);\n\t--dt-btn-text: #fdf6ee;\n\t--dt-scrollbar: rgba(139, 110, 70, 0.15);\n\t--dt-success: rgba(76, 153, 76, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-mono: 'Courier New', 'Courier', monospace;\n\t--dt-sans: 'Outfit', Georgia, serif;\n\t--dt-hm-empty: rgba(139, 110, 70, 0.06);\n\t--dt-hm-low: rgba(184, 92, 47, 0.15);\n\t--dt-hm-mid: rgba(184, 92, 47, 0.3);\n\t--dt-hm-high: rgba(184, 92, 47, 0.5);\n\t--dt-hm-max: rgba(184, 92, 47, 0.72);\n";
11
11
  /** Soft Indigo — calm productivity, muted lavender with indigo markers */
12
- export declare const indigo = "\n\t--dt-bg: #f7f6fc;\n\t--dt-surface: #eeedf8;\n\t--dt-border: rgba(99, 102, 241, 0.08);\n\t--dt-border-day: rgba(99, 102, 241, 0.14);\n\t--dt-text: rgba(30, 27, 55, 0.82);\n\t--dt-text-2: rgba(75, 70, 120, 0.55);\n\t--dt-text-3: rgba(99, 102, 241, 0.3);\n\t--dt-accent: #6366f1;\n\t--dt-accent-dim: rgba(99, 102, 241, 0.12);\n\t--dt-glow: rgba(99, 102, 241, 0.25);\n\t--dt-today-bg: rgba(99, 102, 241, 0.03);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(99, 102, 241, 0.1);\n\t--dt-success: rgba(34, 197, 94, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-sans: 'Outfit', system-ui, sans-serif;\n\t--dt-hm-empty: rgba(99, 102, 241, 0.04);\n\t--dt-hm-low: rgba(99, 102, 241, 0.12);\n\t--dt-hm-mid: rgba(99, 102, 241, 0.28);\n\t--dt-hm-high: rgba(99, 102, 241, 0.48);\n\t--dt-hm-max: rgba(99, 102, 241, 0.7);\n";
12
+ export declare const indigo = "\n\t--dt-stage-bg: #efedf8;\n\t--dt-bg: #f7f6fc;\n\t--dt-surface: #eeedf8;\n\t--dt-border: rgba(99, 102, 241, 0.08);\n\t--dt-border-day: rgba(99, 102, 241, 0.14);\n\t--dt-text: rgba(30, 27, 55, 0.82);\n\t--dt-text-2: rgba(75, 70, 120, 0.55);\n\t--dt-text-3: rgba(99, 102, 241, 0.3);\n\t--dt-accent: #6366f1;\n\t--dt-accent-dim: rgba(99, 102, 241, 0.12);\n\t--dt-glow: rgba(99, 102, 241, 0.25);\n\t--dt-today-bg: rgba(99, 102, 241, 0.03);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(99, 102, 241, 0.1);\n\t--dt-success: rgba(34, 197, 94, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-sans: 'Outfit', system-ui, sans-serif;\n\t--dt-hm-empty: rgba(99, 102, 241, 0.04);\n\t--dt-hm-low: rgba(99, 102, 241, 0.12);\n\t--dt-hm-mid: rgba(99, 102, 241, 0.28);\n\t--dt-hm-high: rgba(99, 102, 241, 0.48);\n\t--dt-hm-max: rgba(99, 102, 241, 0.7);\n";
13
13
  /**
14
14
  * Neutral — inherits fonts from host page, uses standard grays + safe blue accent.
15
15
  * Drop-in preset that blends into any website without fighting its design.
16
16
  */
17
- export declare const neutral = "\n\t--dt-bg: #ffffff;\n\t--dt-surface: #f9fafb;\n\t--dt-border: rgba(0, 0, 0, 0.08);\n\t--dt-border-day: rgba(0, 0, 0, 0.14);\n\t--dt-text: rgba(0, 0, 0, 0.87);\n\t--dt-text-2: rgba(0, 0, 0, 0.54);\n\t--dt-text-3: rgba(0, 0, 0, 0.38);\n\t--dt-accent: #2563eb;\n\t--dt-accent-dim: rgba(37, 99, 235, 0.12);\n\t--dt-glow: rgba(37, 99, 235, 0.25);\n\t--dt-today-bg: rgba(37, 99, 235, 0.04);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(0, 0, 0, 0.1);\n\t--dt-success: rgba(22, 163, 74, 0.7);\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: ui-monospace, 'SFMono-Regular', monospace;\n\t--dt-hm-empty: rgba(0, 0, 0, 0.03);\n\t--dt-hm-low: rgba(37, 99, 235, 0.12);\n\t--dt-hm-mid: rgba(37, 99, 235, 0.28);\n\t--dt-hm-high: rgba(37, 99, 235, 0.48);\n\t--dt-hm-max: rgba(37, 99, 235, 0.7);\n";
17
+ export declare const neutral = "\n\t--dt-stage-bg: #ffffff;\n\t--dt-bg: #ffffff;\n\t--dt-surface: #f9fafb;\n\t--dt-border: rgba(0, 0, 0, 0.08);\n\t--dt-border-day: rgba(0, 0, 0, 0.14);\n\t--dt-text: rgba(0, 0, 0, 0.87);\n\t--dt-text-2: rgba(0, 0, 0, 0.54);\n\t--dt-text-3: rgba(0, 0, 0, 0.38);\n\t--dt-accent: #2563eb;\n\t--dt-accent-dim: rgba(37, 99, 235, 0.12);\n\t--dt-glow: rgba(37, 99, 235, 0.25);\n\t--dt-today-bg: rgba(37, 99, 235, 0.04);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(0, 0, 0, 0.1);\n\t--dt-success: rgba(22, 163, 74, 0.7);\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: ui-monospace, 'SFMono-Regular', monospace;\n\t--dt-hm-empty: rgba(0, 0, 0, 0.03);\n\t--dt-hm-low: rgba(37, 99, 235, 0.12);\n\t--dt-hm-mid: rgba(37, 99, 235, 0.28);\n\t--dt-hm-high: rgba(37, 99, 235, 0.48);\n\t--dt-hm-max: rgba(37, 99, 235, 0.7);\n";
18
18
  /**
19
19
  * Bare — transparent skeleton that inherits everything from the host page.
20
20
  * All colors use currentColor/transparent/inherit so the calendar fully absorbs
21
21
  * the parent site's design. Override individual --dt-* tokens as needed.
22
22
  */
23
- export declare const bare = "\n\t--dt-bg: transparent;\n\t--dt-surface: transparent;\n\t--dt-border: currentColor;\n\t--dt-border-day: currentColor;\n\t--dt-text: inherit;\n\t--dt-text-2: inherit;\n\t--dt-text-3: inherit;\n\t--dt-accent: currentColor;\n\t--dt-accent-dim: transparent;\n\t--dt-glow: transparent;\n\t--dt-today-bg: transparent;\n\t--dt-btn-text: inherit;\n\t--dt-scrollbar: currentColor;\n\t--dt-success: currentColor;\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: inherit;\n\t--dt-hm-empty: transparent;\n\t--dt-hm-low: currentColor;\n\t--dt-hm-mid: currentColor;\n\t--dt-hm-high: currentColor;\n\t--dt-hm-max: currentColor;\n";
23
+ export declare const bare = "\n\t--dt-stage-bg: transparent;\n\t--dt-bg: transparent;\n\t--dt-surface: transparent;\n\t--dt-border: currentColor;\n\t--dt-border-day: currentColor;\n\t--dt-text: inherit;\n\t--dt-text-2: inherit;\n\t--dt-text-3: inherit;\n\t--dt-accent: currentColor;\n\t--dt-accent-dim: transparent;\n\t--dt-glow: transparent;\n\t--dt-today-bg: transparent;\n\t--dt-btn-text: inherit;\n\t--dt-scrollbar: currentColor;\n\t--dt-success: currentColor;\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: inherit;\n\t--dt-hm-empty: transparent;\n\t--dt-hm-low: currentColor;\n\t--dt-hm-mid: currentColor;\n\t--dt-hm-high: currentColor;\n\t--dt-hm-max: currentColor;\n";
24
24
  /** All available presets keyed by name */
25
25
  export declare const presets: {
26
- readonly midnight: "\n\t--dt-bg: #0b0e14;\n\t--dt-surface: #10141c;\n\t--dt-border: rgba(148, 163, 184, 0.07);\n\t--dt-border-day: rgba(148, 163, 184, 0.14);\n\t--dt-text: rgba(226, 232, 240, 0.85);\n\t--dt-text-2: rgba(148, 163, 184, 0.55);\n\t--dt-text-3: rgba(100, 116, 139, 0.55);\n\t--dt-accent: #ef4444;\n\t--dt-accent-dim: rgba(239, 68, 68, 0.18);\n\t--dt-glow: rgba(239, 68, 68, 0.35);\n\t--dt-today-bg: rgba(239, 68, 68, 0.02);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(148, 163, 184, 0.12);\n\t--dt-success: rgba(74, 222, 128, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-hm-empty: rgba(80, 80, 120, 0.06);\n\t--dt-hm-low: rgba(239, 68, 68, 0.15);\n\t--dt-hm-mid: rgba(239, 68, 68, 0.3);\n\t--dt-hm-high: rgba(239, 68, 68, 0.5);\n\t--dt-hm-max: rgba(239, 68, 68, 0.72);\n";
27
- readonly parchment: "\n\t--dt-bg: #f0e9dd;\n\t--dt-surface: #e8dfd0;\n\t--dt-border: rgba(139, 110, 70, 0.12);\n\t--dt-border-day: rgba(139, 110, 70, 0.22);\n\t--dt-text: rgba(55, 40, 22, 0.88);\n\t--dt-text-2: rgba(110, 85, 52, 0.62);\n\t--dt-text-3: rgba(139, 110, 70, 0.42);\n\t--dt-accent: #b85c2f;\n\t--dt-accent-dim: rgba(184, 92, 47, 0.14);\n\t--dt-glow: rgba(184, 92, 47, 0.25);\n\t--dt-today-bg: rgba(184, 92, 47, 0.04);\n\t--dt-btn-text: #fdf6ee;\n\t--dt-scrollbar: rgba(139, 110, 70, 0.15);\n\t--dt-success: rgba(76, 153, 76, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-mono: 'Courier New', 'Courier', monospace;\n\t--dt-sans: 'Outfit', Georgia, serif;\n\t--dt-hm-empty: rgba(139, 110, 70, 0.06);\n\t--dt-hm-low: rgba(184, 92, 47, 0.15);\n\t--dt-hm-mid: rgba(184, 92, 47, 0.3);\n\t--dt-hm-high: rgba(184, 92, 47, 0.5);\n\t--dt-hm-max: rgba(184, 92, 47, 0.72);\n";
28
- readonly indigo: "\n\t--dt-bg: #f7f6fc;\n\t--dt-surface: #eeedf8;\n\t--dt-border: rgba(99, 102, 241, 0.08);\n\t--dt-border-day: rgba(99, 102, 241, 0.14);\n\t--dt-text: rgba(30, 27, 55, 0.82);\n\t--dt-text-2: rgba(75, 70, 120, 0.55);\n\t--dt-text-3: rgba(99, 102, 241, 0.3);\n\t--dt-accent: #6366f1;\n\t--dt-accent-dim: rgba(99, 102, 241, 0.12);\n\t--dt-glow: rgba(99, 102, 241, 0.25);\n\t--dt-today-bg: rgba(99, 102, 241, 0.03);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(99, 102, 241, 0.1);\n\t--dt-success: rgba(34, 197, 94, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-sans: 'Outfit', system-ui, sans-serif;\n\t--dt-hm-empty: rgba(99, 102, 241, 0.04);\n\t--dt-hm-low: rgba(99, 102, 241, 0.12);\n\t--dt-hm-mid: rgba(99, 102, 241, 0.28);\n\t--dt-hm-high: rgba(99, 102, 241, 0.48);\n\t--dt-hm-max: rgba(99, 102, 241, 0.7);\n";
29
- readonly neutral: "\n\t--dt-bg: #ffffff;\n\t--dt-surface: #f9fafb;\n\t--dt-border: rgba(0, 0, 0, 0.08);\n\t--dt-border-day: rgba(0, 0, 0, 0.14);\n\t--dt-text: rgba(0, 0, 0, 0.87);\n\t--dt-text-2: rgba(0, 0, 0, 0.54);\n\t--dt-text-3: rgba(0, 0, 0, 0.38);\n\t--dt-accent: #2563eb;\n\t--dt-accent-dim: rgba(37, 99, 235, 0.12);\n\t--dt-glow: rgba(37, 99, 235, 0.25);\n\t--dt-today-bg: rgba(37, 99, 235, 0.04);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(0, 0, 0, 0.1);\n\t--dt-success: rgba(22, 163, 74, 0.7);\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: ui-monospace, 'SFMono-Regular', monospace;\n\t--dt-hm-empty: rgba(0, 0, 0, 0.03);\n\t--dt-hm-low: rgba(37, 99, 235, 0.12);\n\t--dt-hm-mid: rgba(37, 99, 235, 0.28);\n\t--dt-hm-high: rgba(37, 99, 235, 0.48);\n\t--dt-hm-max: rgba(37, 99, 235, 0.7);\n";
30
- readonly bare: "\n\t--dt-bg: transparent;\n\t--dt-surface: transparent;\n\t--dt-border: currentColor;\n\t--dt-border-day: currentColor;\n\t--dt-text: inherit;\n\t--dt-text-2: inherit;\n\t--dt-text-3: inherit;\n\t--dt-accent: currentColor;\n\t--dt-accent-dim: transparent;\n\t--dt-glow: transparent;\n\t--dt-today-bg: transparent;\n\t--dt-btn-text: inherit;\n\t--dt-scrollbar: currentColor;\n\t--dt-success: currentColor;\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: inherit;\n\t--dt-hm-empty: transparent;\n\t--dt-hm-low: currentColor;\n\t--dt-hm-mid: currentColor;\n\t--dt-hm-high: currentColor;\n\t--dt-hm-max: currentColor;\n";
26
+ readonly midnight: "\n\t--dt-stage-bg: #080a0f;\n\t--dt-bg: #0b0e14;\n\t--dt-surface: #10141c;\n\t--dt-border: rgba(148, 163, 184, 0.07);\n\t--dt-border-day: rgba(148, 163, 184, 0.14);\n\t--dt-text: rgba(226, 232, 240, 0.85);\n\t--dt-text-2: rgba(148, 163, 184, 0.55);\n\t--dt-text-3: rgba(100, 116, 139, 0.55);\n\t--dt-accent: #ef4444;\n\t--dt-accent-dim: rgba(239, 68, 68, 0.18);\n\t--dt-glow: rgba(239, 68, 68, 0.35);\n\t--dt-today-bg: rgba(239, 68, 68, 0.02);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(148, 163, 184, 0.12);\n\t--dt-success: rgba(74, 222, 128, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-hm-empty: rgba(80, 80, 120, 0.06);\n\t--dt-hm-low: rgba(239, 68, 68, 0.15);\n\t--dt-hm-mid: rgba(239, 68, 68, 0.3);\n\t--dt-hm-high: rgba(239, 68, 68, 0.5);\n\t--dt-hm-max: rgba(239, 68, 68, 0.72);\n";
27
+ readonly parchment: "\n\t--dt-stage-bg: #e4dace;\n\t--dt-bg: #f0e9dd;\n\t--dt-surface: #e8dfd0;\n\t--dt-border: rgba(139, 110, 70, 0.12);\n\t--dt-border-day: rgba(139, 110, 70, 0.22);\n\t--dt-text: rgba(55, 40, 22, 0.88);\n\t--dt-text-2: rgba(110, 85, 52, 0.62);\n\t--dt-text-3: rgba(139, 110, 70, 0.42);\n\t--dt-accent: #b85c2f;\n\t--dt-accent-dim: rgba(184, 92, 47, 0.14);\n\t--dt-glow: rgba(184, 92, 47, 0.25);\n\t--dt-today-bg: rgba(184, 92, 47, 0.04);\n\t--dt-btn-text: #fdf6ee;\n\t--dt-scrollbar: rgba(139, 110, 70, 0.15);\n\t--dt-success: rgba(76, 153, 76, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-mono: 'Courier New', 'Courier', monospace;\n\t--dt-sans: 'Outfit', Georgia, serif;\n\t--dt-hm-empty: rgba(139, 110, 70, 0.06);\n\t--dt-hm-low: rgba(184, 92, 47, 0.15);\n\t--dt-hm-mid: rgba(184, 92, 47, 0.3);\n\t--dt-hm-high: rgba(184, 92, 47, 0.5);\n\t--dt-hm-max: rgba(184, 92, 47, 0.72);\n";
28
+ readonly indigo: "\n\t--dt-stage-bg: #efedf8;\n\t--dt-bg: #f7f6fc;\n\t--dt-surface: #eeedf8;\n\t--dt-border: rgba(99, 102, 241, 0.08);\n\t--dt-border-day: rgba(99, 102, 241, 0.14);\n\t--dt-text: rgba(30, 27, 55, 0.82);\n\t--dt-text-2: rgba(75, 70, 120, 0.55);\n\t--dt-text-3: rgba(99, 102, 241, 0.3);\n\t--dt-accent: #6366f1;\n\t--dt-accent-dim: rgba(99, 102, 241, 0.12);\n\t--dt-glow: rgba(99, 102, 241, 0.25);\n\t--dt-today-bg: rgba(99, 102, 241, 0.03);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(99, 102, 241, 0.1);\n\t--dt-success: rgba(34, 197, 94, 0.7);\n\t--dt-serif: Georgia, 'Times New Roman', serif;\n\t--dt-sans: 'Outfit', system-ui, sans-serif;\n\t--dt-hm-empty: rgba(99, 102, 241, 0.04);\n\t--dt-hm-low: rgba(99, 102, 241, 0.12);\n\t--dt-hm-mid: rgba(99, 102, 241, 0.28);\n\t--dt-hm-high: rgba(99, 102, 241, 0.48);\n\t--dt-hm-max: rgba(99, 102, 241, 0.7);\n";
29
+ readonly neutral: "\n\t--dt-stage-bg: #ffffff;\n\t--dt-bg: #ffffff;\n\t--dt-surface: #f9fafb;\n\t--dt-border: rgba(0, 0, 0, 0.08);\n\t--dt-border-day: rgba(0, 0, 0, 0.14);\n\t--dt-text: rgba(0, 0, 0, 0.87);\n\t--dt-text-2: rgba(0, 0, 0, 0.54);\n\t--dt-text-3: rgba(0, 0, 0, 0.38);\n\t--dt-accent: #2563eb;\n\t--dt-accent-dim: rgba(37, 99, 235, 0.12);\n\t--dt-glow: rgba(37, 99, 235, 0.25);\n\t--dt-today-bg: rgba(37, 99, 235, 0.04);\n\t--dt-btn-text: #fff;\n\t--dt-scrollbar: rgba(0, 0, 0, 0.1);\n\t--dt-success: rgba(22, 163, 74, 0.7);\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: ui-monospace, 'SFMono-Regular', monospace;\n\t--dt-hm-empty: rgba(0, 0, 0, 0.03);\n\t--dt-hm-low: rgba(37, 99, 235, 0.12);\n\t--dt-hm-mid: rgba(37, 99, 235, 0.28);\n\t--dt-hm-high: rgba(37, 99, 235, 0.48);\n\t--dt-hm-max: rgba(37, 99, 235, 0.7);\n";
30
+ readonly bare: "\n\t--dt-stage-bg: transparent;\n\t--dt-bg: transparent;\n\t--dt-surface: transparent;\n\t--dt-border: currentColor;\n\t--dt-border-day: currentColor;\n\t--dt-text: inherit;\n\t--dt-text-2: inherit;\n\t--dt-text-3: inherit;\n\t--dt-accent: currentColor;\n\t--dt-accent-dim: transparent;\n\t--dt-glow: transparent;\n\t--dt-today-bg: transparent;\n\t--dt-btn-text: inherit;\n\t--dt-scrollbar: currentColor;\n\t--dt-success: currentColor;\n\t--dt-serif: inherit;\n\t--dt-sans: inherit;\n\t--dt-mono: inherit;\n\t--dt-hm-empty: transparent;\n\t--dt-hm-low: currentColor;\n\t--dt-hm-mid: currentColor;\n\t--dt-hm-high: currentColor;\n\t--dt-hm-max: currentColor;\n";
31
31
  };
32
32
  export type PresetName = keyof typeof presets;
33
- /**
34
- * Stage background color matching each theme.
35
- * Useful for the container behind the timeline component.
36
- */
37
- export declare const stageBg: Record<PresetName, string>;
@@ -6,6 +6,7 @@
6
6
  */
7
7
  /** Midnight Industrial — dark charcoal + red accent, tech monitoring */
8
8
  export const midnight = `
9
+ --dt-stage-bg: #080a0f;
9
10
  --dt-bg: #0b0e14;
10
11
  --dt-surface: #10141c;
11
12
  --dt-border: rgba(148, 163, 184, 0.07);
@@ -29,6 +30,7 @@ export const midnight = `
29
30
  `;
30
31
  /** Desert Parchment — warm analog planner, burnt sienna on aged paper */
31
32
  export const parchment = `
33
+ --dt-stage-bg: #e4dace;
32
34
  --dt-bg: #f0e9dd;
33
35
  --dt-surface: #e8dfd0;
34
36
  --dt-border: rgba(139, 110, 70, 0.12);
@@ -54,6 +56,7 @@ export const parchment = `
54
56
  `;
55
57
  /** Soft Indigo — calm productivity, muted lavender with indigo markers */
56
58
  export const indigo = `
59
+ --dt-stage-bg: #efedf8;
57
60
  --dt-bg: #f7f6fc;
58
61
  --dt-surface: #eeedf8;
59
62
  --dt-border: rgba(99, 102, 241, 0.08);
@@ -81,6 +84,7 @@ export const indigo = `
81
84
  * Drop-in preset that blends into any website without fighting its design.
82
85
  */
83
86
  export const neutral = `
87
+ --dt-stage-bg: #ffffff;
84
88
  --dt-bg: #ffffff;
85
89
  --dt-surface: #f9fafb;
86
90
  --dt-border: rgba(0, 0, 0, 0.08);
@@ -110,6 +114,7 @@ export const neutral = `
110
114
  * the parent site's design. Override individual --dt-* tokens as needed.
111
115
  */
112
116
  export const bare = `
117
+ --dt-stage-bg: transparent;
113
118
  --dt-bg: transparent;
114
119
  --dt-surface: transparent;
115
120
  --dt-border: currentColor;
@@ -135,14 +140,3 @@ export const bare = `
135
140
  `;
136
141
  /** All available presets keyed by name */
137
142
  export const presets = { midnight, parchment, indigo, neutral, bare };
138
- /**
139
- * Stage background color matching each theme.
140
- * Useful for the container behind the timeline component.
141
- */
142
- export const stageBg = {
143
- midnight: '#080a0f',
144
- parchment: '#e4dace',
145
- indigo: '#efedf8',
146
- neutral: '#ffffff',
147
- bare: 'transparent',
148
- };
@@ -754,7 +754,7 @@
754
754
  transition: width 1s linear;
755
755
  }
756
756
 
757
- .ag-empty {
757
+ :global(.ag-empty) {
758
758
  display: flex;
759
759
  align-items: center;
760
760
  justify-content: center;
@@ -880,7 +880,7 @@
880
880
  color: var(--dt-text-3, rgba(255, 255, 255, 0.25));
881
881
  margin-bottom: 2px;
882
882
  }
883
- .ag-q-free-next {
883
+ :global(.ag-q-free-next) {
884
884
  font-size: 10px;
885
885
  font-weight: 500;
886
886
  line-height: 1.2;
@@ -890,7 +890,7 @@
890
890
  text-overflow: ellipsis;
891
891
  margin-bottom: 2px;
892
892
  }
893
- .ag-q-free-until {
893
+ :global(.ag-q-free-until) {
894
894
  font-size: 9px;
895
895
  font-family: var(--dt-mono, monospace);
896
896
  color: var(--dt-accent, #ff6b4a);
@@ -43,6 +43,11 @@
43
43
  selectedEventId?: string | null;
44
44
  /** Start weeks on Monday */
45
45
  mondayStart?: boolean;
46
+ /** Read-only mode */
47
+ readOnly?: boolean;
48
+ /** Visible hour range [startHour, endHour) — adjusts night collapse */
49
+ visibleHours?: [number, number];
50
+ [key: string]: unknown;
46
51
  }
47
52
 
48
53
  let {
@@ -27,6 +27,11 @@ interface Props {
27
27
  selectedEventId?: string | null;
28
28
  /** Start weeks on Monday */
29
29
  mondayStart?: boolean;
30
+ /** Read-only mode */
31
+ readOnly?: boolean;
32
+ /** Visible hour range [startHour, endHour) — adjusts night collapse */
33
+ visibleHours?: [number, number];
34
+ [key: string]: unknown;
30
35
  }
31
36
  declare const DayGrid: import("svelte").Component<Props, {}, "">;
32
37
  type DayGrid = ReturnType<typeof DayGrid>;
@@ -3,3 +3,4 @@ export { WeekGrid, WeekHeatmap } from './week/index.js';
3
3
  export { Agenda } from './agenda/index.js';
4
4
  export { Settings } from './settings/index.js';
5
5
  export type { SettingsField } from './settings/index.js';
6
+ export { WeekSchedule } from './schedule/index.js';
@@ -3,3 +3,4 @@ export { DayGrid, DayTimeline } from './day/index.js';
3
3
  export { WeekGrid, WeekHeatmap } from './week/index.js';
4
4
  export { Agenda } from './agenda/index.js';
5
5
  export { Settings } from './settings/index.js';
6
+ export { WeekSchedule } from './schedule/index.js';
@@ -0,0 +1,133 @@
1
+ <!--
2
+ WeekSchedule — zero-config weekly schedule display.
3
+
4
+ A convenience wrapper that pre-wires Calendar + WeekGrid + Agenda
5
+ so consumers can render a read-only weekly schedule with a single import.
6
+
7
+ Usage:
8
+ <WeekSchedule
9
+ events={events}
10
+ theme={neutral}
11
+ locale="pl-PL"
12
+ height={560}
13
+ readOnly
14
+ />
15
+
16
+ Or with a recurring adapter:
17
+ <WeekSchedule
18
+ schedule={weeklyClasses}
19
+ theme={neutral}
20
+ readOnly
21
+ />
22
+ -->
23
+ <script lang="ts">
24
+ import type { TimelineEvent } from '../../core/types.js';
25
+ import type { RecurringEvent } from '../../adapters/recurring.js';
26
+ import type { MemoryAdapterOptions } from '../../adapters/memory.js';
27
+ import { createMemoryAdapter } from '../../adapters/memory.js';
28
+ import { createRecurringAdapter, type RecurringAdapterOptions } from '../../adapters/recurring.js';
29
+ import Calendar from '../../calendar/Calendar.svelte';
30
+ import type { CalendarView } from '../../calendar/Calendar.svelte';
31
+ import { WeekGrid } from '../week/index.js';
32
+ import { Agenda } from '../agenda/index.js';
33
+
34
+ interface Props {
35
+ /** Concrete events (mutually exclusive with `schedule`) */
36
+ events?: TimelineEvent[];
37
+ /** Recurring weekly schedule (mutually exclusive with `events`) */
38
+ schedule?: RecurringEvent[];
39
+ /** CSS theme string */
40
+ theme?: string;
41
+ /** BCP 47 locale */
42
+ locale?: string;
43
+ /** Total height */
44
+ height?: number;
45
+ /** Start week on Monday */
46
+ mondayStart?: boolean;
47
+ /** Read-only mode (default: true for schedule display) */
48
+ readOnly?: boolean;
49
+ /** Visible hour range [startHour, endHour) */
50
+ visibleHours?: [number, number];
51
+ /** Show toolbar */
52
+ showToolbar?: boolean;
53
+ /** Show agenda view toggle */
54
+ showAgenda?: boolean;
55
+ /** Map of category/title to color */
56
+ colorMap?: Record<string, string>;
57
+ /** Auto-assign colors */
58
+ autoColor?: boolean;
59
+ /** Event click handler */
60
+ oneventclick?: (event: TimelineEvent) => void;
61
+ /** Event create handler (only works when readOnly is false) */
62
+ oneventcreate?: (range: { start: Date; end: Date }) => void;
63
+ }
64
+
65
+ let {
66
+ events,
67
+ schedule,
68
+ theme = '',
69
+ locale,
70
+ height = 560,
71
+ mondayStart = true,
72
+ readOnly = true,
73
+ visibleHours,
74
+ showToolbar = true,
75
+ showAgenda = true,
76
+ colorMap,
77
+ autoColor,
78
+ oneventclick,
79
+ oneventcreate,
80
+ }: Props = $props();
81
+
82
+ // Auto-create the adapter from events or schedule
83
+ const adapter = $derived.by(() => {
84
+ if (schedule) {
85
+ return createRecurringAdapter(schedule, {
86
+ mondayStart,
87
+ colorMap,
88
+ autoColor,
89
+ } satisfies RecurringAdapterOptions);
90
+ }
91
+ return createMemoryAdapter(events ?? [], {
92
+ colorMap,
93
+ autoColor,
94
+ } satisfies MemoryAdapterOptions);
95
+ });
96
+
97
+ // Pre-wired views
98
+ const views = $derived.by((): CalendarView[] => {
99
+ const v: CalendarView[] = [
100
+ {
101
+ id: 'week-grid',
102
+ label: 'Week',
103
+ granularity: 'week',
104
+ component: WeekGrid,
105
+ },
106
+ ];
107
+ if (showAgenda) {
108
+ v.push({
109
+ id: 'agenda',
110
+ label: 'Agenda',
111
+ granularity: 'week',
112
+ component: Agenda,
113
+ props: { mode: 'week' },
114
+ });
115
+ }
116
+ return v;
117
+ });
118
+ </script>
119
+
120
+ <Calendar
121
+ {adapter}
122
+ {views}
123
+ defaultView="week-grid"
124
+ {theme}
125
+ {locale}
126
+ {height}
127
+ {mondayStart}
128
+ {readOnly}
129
+ {visibleHours}
130
+ {showToolbar}
131
+ {oneventclick}
132
+ oneventcreate={readOnly ? undefined : oneventcreate}
133
+ />
@@ -0,0 +1,38 @@
1
+ import type { TimelineEvent } from '../../core/types.js';
2
+ import type { RecurringEvent } from '../../adapters/recurring.js';
3
+ interface Props {
4
+ /** Concrete events (mutually exclusive with `schedule`) */
5
+ events?: TimelineEvent[];
6
+ /** Recurring weekly schedule (mutually exclusive with `events`) */
7
+ schedule?: RecurringEvent[];
8
+ /** CSS theme string */
9
+ theme?: string;
10
+ /** BCP 47 locale */
11
+ locale?: string;
12
+ /** Total height */
13
+ height?: number;
14
+ /** Start week on Monday */
15
+ mondayStart?: boolean;
16
+ /** Read-only mode (default: true for schedule display) */
17
+ readOnly?: boolean;
18
+ /** Visible hour range [startHour, endHour) */
19
+ visibleHours?: [number, number];
20
+ /** Show toolbar */
21
+ showToolbar?: boolean;
22
+ /** Show agenda view toggle */
23
+ showAgenda?: boolean;
24
+ /** Map of category/title to color */
25
+ colorMap?: Record<string, string>;
26
+ /** Auto-assign colors */
27
+ autoColor?: boolean;
28
+ /** Event click handler */
29
+ oneventclick?: (event: TimelineEvent) => void;
30
+ /** Event create handler (only works when readOnly is false) */
31
+ oneventcreate?: (range: {
32
+ start: Date;
33
+ end: Date;
34
+ }) => void;
35
+ }
36
+ declare const WeekSchedule: import("svelte").Component<Props, {}, "">;
37
+ type WeekSchedule = ReturnType<typeof WeekSchedule>;
38
+ export default WeekSchedule;
@@ -0,0 +1 @@
1
+ export { default as WeekSchedule } from './WeekSchedule.svelte';
@@ -0,0 +1,2 @@
1
+ // ─── Schedule views barrel export ───────────────────────
2
+ export { default as WeekSchedule } from './WeekSchedule.svelte';
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { slide } from 'svelte/transition';
3
3
  import type { PresetName } from '../../theme/presets.js';
4
- import { presets, stageBg } from '../../theme/presets.js';
4
+ import { presets } from '../../theme/presets.js';
5
5
 
6
6
  /* ─── Field definition types ─────────────────────── */
7
7
  type RangeField = {
@@ -30,16 +30,20 @@
30
30
 
31
31
  let { fields, values = $bindable(), theme = $bindable() }: Props = $props();
32
32
 
33
- const themeKeys: PresetName[] = ['midnight', 'parchment', 'indigo'];
33
+ const themeKeys: PresetName[] = ['midnight', 'parchment', 'indigo', 'neutral', 'bare'];
34
34
  const themeLabels: Record<PresetName, string> = {
35
35
  midnight: 'Midnight',
36
36
  parchment: 'Parchment',
37
37
  indigo: 'Indigo',
38
+ neutral: 'Neutral',
39
+ bare: 'Bare',
38
40
  };
39
41
  const themeAccents: Record<PresetName, string> = {
40
42
  midnight: '#ef4444',
41
43
  parchment: '#b85c2f',
42
44
  indigo: '#6366f1',
45
+ neutral: '#94a0b4',
46
+ bare: '#94a0b4',
43
47
  };
44
48
 
45
49
  let open = $state(true);
@@ -30,6 +30,9 @@
30
30
  oneventclick?: (event: TimelineEvent) => void;
31
31
  oneventcreate?: (range: { start: Date; end: Date }) => void;
32
32
  selectedEventId?: string | null;
33
+ readOnly?: boolean;
34
+ visibleHours?: [number, number];
35
+ [key: string]: unknown;
33
36
  }
34
37
 
35
38
  let {
@@ -41,6 +44,8 @@
41
44
  oneventclick,
42
45
  oneventcreate,
43
46
  selectedEventId = null,
47
+ readOnly = false,
48
+ ...rest
44
49
  }: Props = $props();
45
50
 
46
51
  // ── Drag support (available when inside Calendar) ──
@@ -162,7 +167,7 @@
162
167
  function handleDayCellClick(ms: number, e: Event) {
163
168
  const target = e.target as HTMLElement;
164
169
  if (target.closest('.wg-ev')) return;
165
- if (!oneventcreate) return;
170
+ if (readOnly || !oneventcreate) return;
166
171
  const start = new Date(ms + 9 * 3_600_000);
167
172
  const end = new Date(ms + 10 * 3_600_000);
168
173
  oneventcreate({ start, end });
@@ -182,7 +187,7 @@
182
187
  }
183
188
 
184
189
  function onEventPointerDown(e: PointerEvent, ev: TimelineEvent) {
185
- if (e.button !== 0 || !drag) return;
190
+ if (e.button !== 0 || !drag || readOnly) return;
186
191
  e.stopPropagation();
187
192
  evDragStartX = e.clientX;
188
193
  evDragStarted = false;
@@ -15,6 +15,9 @@ interface Props {
15
15
  end: Date;
16
16
  }) => void;
17
17
  selectedEventId?: string | null;
18
+ readOnly?: boolean;
19
+ visibleHours?: [number, number];
20
+ [key: string]: unknown;
18
21
  }
19
22
  declare const WeekGrid: import("svelte").Component<Props, {}, "">;
20
23
  type WeekGrid = ReturnType<typeof WeekGrid>;
@@ -27,7 +27,13 @@
27
27
  focusDate,
28
28
  oneventclick,
29
29
  selectedEventId = null,
30
- }: WeekTimelineProps = $props();
30
+ visibleHours,
31
+ ...rest
32
+ }: WeekTimelineProps & { visibleHours?: [number, number]; [key: string]: unknown } = $props();
33
+
34
+ const startHour = $derived(visibleHours?.[0] ?? 0);
35
+ const endHour = $derived(visibleHours?.[1] ?? 24);
36
+ const visibleHourCount = $derived(endHour - startHour);
31
37
 
32
38
  const clock = createClock();
33
39
 
@@ -66,7 +72,7 @@
66
72
  const isToday = ms === clock.today;
67
73
 
68
74
  const cells: HeatCell[] = [];
69
- for (let h = 0; h < 24; h++) {
75
+ for (let h = startHour; h < endHour; h++) {
70
76
  const cellStart = ms + h * 3600000;
71
77
  const cellEnd = cellStart + 3600000;
72
78
  const overlaps = events.filter((ev) => ev.start.getTime() < cellEnd && ev.end.getTime() > cellStart);
@@ -179,9 +185,10 @@
179
185
  <!-- Hour labels row -->
180
186
  <div class="hm-hours-row">
181
187
  <div class="hm-row-label"></div>
182
- {#each Array(24) as _, h}
183
- {#if h % 3 === 0}
184
- <span class="hm-hour-mark" style:left="calc({h} * (100% / 24))">{fmtH(h)}</span>
188
+ {#each Array(visibleHourCount) as _, idx}
189
+ {@const h = startHour + idx}
190
+ {#if (h - startHour) % 3 === 0}
191
+ <span class="hm-hour-mark" style:left="calc({idx} * (100% / {visibleHourCount}))">{fmtH(h)}</span>
185
192
  {/if}
186
193
  {/each}
187
194
  </div>
@@ -1,4 +1,8 @@
1
1
  import type { WeekTimelineProps } from '../../core/types.js';
2
- declare const WeekHeatmap: import("svelte").Component<WeekTimelineProps, {}, "">;
2
+ type $$ComponentProps = WeekTimelineProps & {
3
+ visibleHours?: [number, number];
4
+ [key: string]: unknown;
5
+ };
6
+ declare const WeekHeatmap: import("svelte").Component<$$ComponentProps, {}, "">;
3
7
  type WeekHeatmap = ReturnType<typeof WeekHeatmap>;
4
8
  export default WeekHeatmap;