@nomideusz/svelte-calendar 0.4.0 → 0.5.1
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 +331 -568
- package/dist/adapters/recurring.d.ts +45 -8
- package/dist/adapters/recurring.js +227 -49
- package/dist/adapters/rest.js +6 -1
- package/dist/calendar/Calendar.svelte +118 -30
- package/dist/calendar/Calendar.svelte.d.ts +9 -8
- package/dist/calendar/index.d.ts +0 -1
- package/dist/calendar/index.js +0 -1
- package/dist/core/index.d.ts +5 -4
- package/dist/core/index.js +2 -3
- package/dist/core/locale.d.ts +74 -0
- package/dist/core/locale.js +96 -4
- package/dist/core/time.d.ts +30 -0
- package/dist/core/time.js +42 -0
- package/dist/core/types.d.ts +1 -72
- package/dist/core/types.js +1 -8
- package/dist/engine/event-store.svelte.js +2 -2
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/view-state.svelte.d.ts +11 -6
- package/dist/engine/view-state.svelte.js +5 -4
- package/dist/index.d.ts +6 -7
- package/dist/index.js +4 -4
- package/dist/primitives/EmptySlot.svelte +6 -16
- package/dist/primitives/EventBlock.svelte +6 -19
- package/dist/primitives/NowIndicator.svelte +7 -4
- package/dist/primitives/TimeGutter.svelte +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/presets.d.ts +11 -19
- package/dist/theme/presets.js +25 -111
- package/dist/views/agenda/Agenda.svelte +17 -1516
- package/dist/views/agenda/Agenda.svelte.d.ts +0 -14
- package/dist/views/agenda/AgendaDay.svelte +976 -0
- package/dist/views/agenda/AgendaDay.svelte.d.ts +14 -0
- package/dist/views/agenda/AgendaWeek.svelte +775 -0
- package/dist/views/agenda/AgendaWeek.svelte.d.ts +15 -0
- package/dist/views/agenda/index.d.ts +2 -0
- package/dist/views/agenda/index.js +2 -0
- package/dist/views/index.d.ts +2 -6
- package/dist/views/index.js +2 -6
- package/dist/views/planner/Planner.svelte +17 -0
- package/dist/views/planner/Planner.svelte.d.ts +7 -0
- package/dist/views/planner/PlannerDay.svelte +957 -0
- package/dist/views/{day/DayGrid.svelte.d.ts → planner/PlannerDay.svelte.d.ts} +6 -16
- package/dist/views/planner/PlannerWeek.svelte +826 -0
- package/dist/views/{week/WeekGrid.svelte.d.ts → planner/PlannerWeek.svelte.d.ts} +4 -9
- package/dist/views/planner/index.d.ts +1 -0
- package/dist/views/planner/index.js +1 -0
- package/dist/widget/CalendarWidget.svelte +54 -33
- package/dist/widget/CalendarWidget.svelte.d.ts +1 -1
- package/package.json +70 -65
- package/widget/svelte-calendar.css +1 -0
- package/widget/widget.js +122 -260
- package/dist/calendar/Toolbar.svelte +0 -244
- package/dist/calendar/Toolbar.svelte.d.ts +0 -21
- package/dist/views/day/DayGrid.svelte +0 -1104
- package/dist/views/day/DayTimeline.svelte +0 -533
- package/dist/views/day/DayTimeline.svelte.d.ts +0 -8
- package/dist/views/day/index.d.ts +0 -2
- package/dist/views/day/index.js +0 -2
- package/dist/views/schedule/WeekSchedule.svelte +0 -137
- package/dist/views/schedule/WeekSchedule.svelte.d.ts +0 -40
- package/dist/views/schedule/index.d.ts +0 -1
- package/dist/views/schedule/index.js +0 -2
- package/dist/views/settings/Settings.svelte +0 -585
- package/dist/views/settings/Settings.svelte.d.ts +0 -39
- package/dist/views/settings/index.d.ts +0 -2
- package/dist/views/settings/index.js +0 -1
- package/dist/views/week/WeekGrid.svelte +0 -632
- package/dist/views/week/WeekHeatmap.svelte +0 -461
- package/dist/views/week/WeekHeatmap.svelte.d.ts +0 -9
- package/dist/views/week/index.d.ts +0 -2
- package/dist/views/week/index.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Component } from 'svelte';
|
|
1
|
+
import { type Component, type Snippet } from 'svelte';
|
|
2
2
|
import type { CalendarAdapter } from '../adapters/types.js';
|
|
3
3
|
import type { CalendarViewId } from '../engine/view-state.svelte.js';
|
|
4
4
|
import type { TimelineEvent } from '../core/types.js';
|
|
@@ -26,13 +26,6 @@ interface Props {
|
|
|
26
26
|
mondayStart?: boolean;
|
|
27
27
|
/** Total height */
|
|
28
28
|
height?: number;
|
|
29
|
-
/** Show toolbar */
|
|
30
|
-
showToolbar?: boolean;
|
|
31
|
-
/** Links to display in toolbar */
|
|
32
|
-
links?: {
|
|
33
|
-
href: string;
|
|
34
|
-
label: string;
|
|
35
|
-
}[];
|
|
36
29
|
/** Text direction: 'ltr' (default), 'rtl', or 'auto' */
|
|
37
30
|
dir?: 'ltr' | 'rtl' | 'auto';
|
|
38
31
|
/** BCP 47 locale tag (e.g. 'en-US', 'ar-SA') — sets lang and locale for formatting */
|
|
@@ -41,6 +34,14 @@ interface Props {
|
|
|
41
34
|
readOnly?: boolean;
|
|
42
35
|
/** Visible hour range: [startHour, endHour). Crops the grid to these hours. */
|
|
43
36
|
visibleHours?: [number, number];
|
|
37
|
+
/** Initial date to focus on (defaults to today) */
|
|
38
|
+
initialDate?: Date;
|
|
39
|
+
/** Drag snap interval in minutes (default: 15) */
|
|
40
|
+
snapInterval?: number;
|
|
41
|
+
/** Custom event rendering snippet */
|
|
42
|
+
event?: Snippet<[TimelineEvent]>;
|
|
43
|
+
/** Content to show when no events are loaded */
|
|
44
|
+
empty?: Snippet;
|
|
44
45
|
oneventclick?: (event: TimelineEvent) => void;
|
|
45
46
|
oneventcreate?: (range: {
|
|
46
47
|
start: Date;
|
package/dist/calendar/index.d.ts
CHANGED
package/dist/calendar/index.js
CHANGED
package/dist/core/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { createClock } from './clock.svelte.js';
|
|
2
2
|
export type { Clock } from './clock.svelte.js';
|
|
3
|
-
export { DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, } from './time.js';
|
|
4
|
-
export {
|
|
3
|
+
export { DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, isMultiDay, isAllDay, segmentForDay, } from './time.js';
|
|
4
|
+
export type { DaySegment } from './time.js';
|
|
5
|
+
export { setDefaultLocale, getDefaultLocale, is24HourLocale, fmtH, fmtTime, fmtDuration, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, defaultLabels, setLabels, resetLabels, getLabels, } from './locale.js';
|
|
6
|
+
export type { CalendarLabels } from './locale.js';
|
|
5
7
|
export { toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './timezone.js';
|
|
6
|
-
export type { TimelineEvent,
|
|
7
|
-
export { timeToX } from './types.js';
|
|
8
|
+
export type { TimelineEvent, } from './types.js';
|
|
8
9
|
export { generatePalette, VIVID_PALETTE } from './palette.js';
|
package/dist/core/index.js
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Clock
|
|
3
3
|
export { createClock } from './clock.svelte.js';
|
|
4
4
|
// Time constants & math
|
|
5
|
-
export { DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, } from './time.js';
|
|
5
|
+
export { DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, isMultiDay, isAllDay, segmentForDay, } from './time.js';
|
|
6
6
|
// Locale-aware formatting
|
|
7
|
-
export { setDefaultLocale, getDefaultLocale, is24HourLocale, fmtH, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, } from './locale.js';
|
|
7
|
+
export { setDefaultLocale, getDefaultLocale, is24HourLocale, fmtH, fmtTime, fmtDuration, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, defaultLabels, setLabels, resetLabels, getLabels, } from './locale.js';
|
|
8
8
|
// Timezone utilities
|
|
9
9
|
export { toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './timezone.js';
|
|
10
|
-
export { timeToX } from './types.js';
|
|
11
10
|
// Palette
|
|
12
11
|
export { generatePalette, VIVID_PALETTE } from './palette.js';
|
package/dist/core/locale.d.ts
CHANGED
|
@@ -7,6 +7,68 @@
|
|
|
7
7
|
* For date-fns locale integration later, this module can import from
|
|
8
8
|
* `date-fns/locale/*` and pass to `format()`.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* All user-visible text in the calendar UI.
|
|
12
|
+
* Override via `setLabels()` for full localisation.
|
|
13
|
+
*/
|
|
14
|
+
export interface CalendarLabels {
|
|
15
|
+
today: string;
|
|
16
|
+
yesterday: string;
|
|
17
|
+
tomorrow: string;
|
|
18
|
+
day: string;
|
|
19
|
+
week: string;
|
|
20
|
+
planner: string;
|
|
21
|
+
agenda: string;
|
|
22
|
+
now: string;
|
|
23
|
+
free: string;
|
|
24
|
+
allDay: string;
|
|
25
|
+
done: string;
|
|
26
|
+
upNext: string;
|
|
27
|
+
until: string;
|
|
28
|
+
noEvents: string;
|
|
29
|
+
nothingScheduled: string;
|
|
30
|
+
nothingScheduledYet: string;
|
|
31
|
+
nothingWasScheduled: string;
|
|
32
|
+
allDoneForToday: string;
|
|
33
|
+
goToToday: string;
|
|
34
|
+
previousDay: string;
|
|
35
|
+
nextDay: string;
|
|
36
|
+
previousWeek: string;
|
|
37
|
+
nextWeek: string;
|
|
38
|
+
calendar: string;
|
|
39
|
+
viewMode: string;
|
|
40
|
+
dayNavigation: string;
|
|
41
|
+
weekNavigation: string;
|
|
42
|
+
dayPlanner: string;
|
|
43
|
+
scrollableDayPlanner: string;
|
|
44
|
+
todaysLineup: string;
|
|
45
|
+
weekAhead: string;
|
|
46
|
+
multiWeekGrid: string;
|
|
47
|
+
currentTime: string;
|
|
48
|
+
createEvent: string;
|
|
49
|
+
happeningNow: string;
|
|
50
|
+
past: string;
|
|
51
|
+
completed: string;
|
|
52
|
+
inProgress: string;
|
|
53
|
+
/** e.g. "+3 more" */
|
|
54
|
+
nMore: (n: number) => string;
|
|
55
|
+
/** e.g. "5 events" */
|
|
56
|
+
nEvents: (n: number) => string;
|
|
57
|
+
/** e.g. "3 completed" */
|
|
58
|
+
nCompleted: (n: number) => string;
|
|
59
|
+
/** e.g. "day 2 of 4" */
|
|
60
|
+
dayNOfTotal: (current: number, total: number) => string;
|
|
61
|
+
/** e.g. "75% complete" */
|
|
62
|
+
percentComplete: (pct: number) => string;
|
|
63
|
+
}
|
|
64
|
+
/** English defaults — used unless overridden via `setLabels()`. */
|
|
65
|
+
export declare const defaultLabels: CalendarLabels;
|
|
66
|
+
/** Replace one or more UI labels. Merges with current labels. */
|
|
67
|
+
export declare function setLabels(overrides: Partial<CalendarLabels>): void;
|
|
68
|
+
/** Reset all labels to English defaults. */
|
|
69
|
+
export declare function resetLabels(): void;
|
|
70
|
+
/** Get the currently active labels. */
|
|
71
|
+
export declare function getLabels(): Readonly<CalendarLabels>;
|
|
10
72
|
/** Change the default locale for all formatting functions */
|
|
11
73
|
export declare function setDefaultLocale(tag: string): void;
|
|
12
74
|
/** Get the current default locale */
|
|
@@ -37,3 +99,15 @@ export declare function fmtDay(ms: number, todayMs: number, opts?: {
|
|
|
37
99
|
* Format a week range label: "Feb 17 – 23, 2026" or "Jan 27 – Feb 2, 2026"
|
|
38
100
|
*/
|
|
39
101
|
export declare function fmtWeekRange(weekStartMs: number, locale?: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Format a Date as a compact time string.
|
|
104
|
+
*
|
|
105
|
+
* 12-hour locales → "9a", "12:30p"
|
|
106
|
+
* 24-hour locales → "9:00", "14:30"
|
|
107
|
+
*/
|
|
108
|
+
export declare function fmtTime(d: Date, locale?: string): string;
|
|
109
|
+
/**
|
|
110
|
+
* Format the duration between two Dates as a compact string.
|
|
111
|
+
* e.g. "45m", "1h", "1h 30m"
|
|
112
|
+
*/
|
|
113
|
+
export declare function fmtDuration(start: Date, end: Date): string;
|
package/dist/core/locale.js
CHANGED
|
@@ -7,6 +7,67 @@
|
|
|
7
7
|
* For date-fns locale integration later, this module can import from
|
|
8
8
|
* `date-fns/locale/*` and pass to `format()`.
|
|
9
9
|
*/
|
|
10
|
+
import { DAY_MS } from './time.js';
|
|
11
|
+
/** English defaults — used unless overridden via `setLabels()`. */
|
|
12
|
+
export const defaultLabels = {
|
|
13
|
+
today: 'Today',
|
|
14
|
+
yesterday: 'Yesterday',
|
|
15
|
+
tomorrow: 'Tomorrow',
|
|
16
|
+
day: 'Day',
|
|
17
|
+
week: 'Week',
|
|
18
|
+
planner: 'Planner',
|
|
19
|
+
agenda: 'Agenda',
|
|
20
|
+
now: 'now',
|
|
21
|
+
free: 'free',
|
|
22
|
+
allDay: 'All day',
|
|
23
|
+
done: 'Done',
|
|
24
|
+
upNext: 'Up next',
|
|
25
|
+
until: 'until',
|
|
26
|
+
noEvents: 'No events',
|
|
27
|
+
nothingScheduled: 'Nothing scheduled',
|
|
28
|
+
nothingScheduledYet: 'Nothing scheduled yet',
|
|
29
|
+
nothingWasScheduled: 'Nothing was scheduled',
|
|
30
|
+
allDoneForToday: 'All done for today',
|
|
31
|
+
goToToday: 'Go to today',
|
|
32
|
+
previousDay: 'Previous day',
|
|
33
|
+
nextDay: 'Next day',
|
|
34
|
+
previousWeek: 'Previous week',
|
|
35
|
+
nextWeek: 'Next week',
|
|
36
|
+
calendar: 'Calendar',
|
|
37
|
+
viewMode: 'View mode',
|
|
38
|
+
dayNavigation: 'Day navigation',
|
|
39
|
+
weekNavigation: 'Week navigation',
|
|
40
|
+
dayPlanner: 'Day planner',
|
|
41
|
+
scrollableDayPlanner: 'Scrollable day planner',
|
|
42
|
+
todaysLineup: "Today's lineup",
|
|
43
|
+
weekAhead: 'Week ahead',
|
|
44
|
+
multiWeekGrid: 'Multi-week calendar grid',
|
|
45
|
+
currentTime: 'Current time',
|
|
46
|
+
createEvent: 'Create event',
|
|
47
|
+
happeningNow: 'happening now',
|
|
48
|
+
past: 'past',
|
|
49
|
+
completed: 'completed',
|
|
50
|
+
inProgress: 'in progress',
|
|
51
|
+
nMore: (n) => `+${n} more`,
|
|
52
|
+
nEvents: (n) => `${n} event${n === 1 ? '' : 's'}`,
|
|
53
|
+
nCompleted: (n) => `${n} completed`,
|
|
54
|
+
dayNOfTotal: (current, total) => `day ${current} of ${total}`,
|
|
55
|
+
percentComplete: (pct) => `${pct}% complete`,
|
|
56
|
+
};
|
|
57
|
+
let _labels = { ...defaultLabels };
|
|
58
|
+
/** Replace one or more UI labels. Merges with current labels. */
|
|
59
|
+
export function setLabels(overrides) {
|
|
60
|
+
_labels = { ..._labels, ...overrides };
|
|
61
|
+
}
|
|
62
|
+
/** Reset all labels to English defaults. */
|
|
63
|
+
export function resetLabels() {
|
|
64
|
+
_labels = { ...defaultLabels };
|
|
65
|
+
}
|
|
66
|
+
/** Get the currently active labels. */
|
|
67
|
+
export function getLabels() {
|
|
68
|
+
return _labels;
|
|
69
|
+
}
|
|
70
|
+
// ─── Default locale ─────────────────────────────────────
|
|
10
71
|
/** Module-level default locale — consumers can override via setDefaultLocale() */
|
|
11
72
|
let defaultLocale = 'en-US';
|
|
12
73
|
/** Change the default locale for all formatting functions */
|
|
@@ -73,7 +134,6 @@ export function dateWithWeekday(ms, locale) {
|
|
|
73
134
|
day: 'numeric',
|
|
74
135
|
});
|
|
75
136
|
}
|
|
76
|
-
import { DAY_MS } from './time.js';
|
|
77
137
|
/**
|
|
78
138
|
* Format a timestamp as a smart day label:
|
|
79
139
|
* "Today · Feb 21", "Yesterday · Feb 20", "Mon, Feb 17", etc.
|
|
@@ -81,12 +141,13 @@ import { DAY_MS } from './time.js';
|
|
|
81
141
|
export function fmtDay(ms, todayMs, opts, locale) {
|
|
82
142
|
const loc = locale ?? defaultLocale;
|
|
83
143
|
const short = new Date(ms).toLocaleDateString(loc, { month: 'short', day: 'numeric' });
|
|
144
|
+
const L = _labels;
|
|
84
145
|
if (ms === todayMs)
|
|
85
|
-
return opts?.short ?
|
|
146
|
+
return opts?.short ? L.today : `${L.today} · ${short}`;
|
|
86
147
|
if (ms === todayMs - DAY_MS)
|
|
87
|
-
return opts?.short ?
|
|
148
|
+
return opts?.short ? L.yesterday : `${L.yesterday} · ${short}`;
|
|
88
149
|
if (ms === todayMs + DAY_MS)
|
|
89
|
-
return opts?.short ?
|
|
150
|
+
return opts?.short ? L.tomorrow : `${L.tomorrow} · ${short}`;
|
|
90
151
|
if (opts?.short) {
|
|
91
152
|
return new Date(ms).toLocaleDateString(loc, { weekday: 'short', day: 'numeric' });
|
|
92
153
|
}
|
|
@@ -111,3 +172,34 @@ export function fmtWeekRange(weekStartMs, locale) {
|
|
|
111
172
|
}
|
|
112
173
|
return `${sm} ${s.getDate()} – ${e.getDate()}, ${ey}`;
|
|
113
174
|
}
|
|
175
|
+
// ─── Shared time / duration formatting ──────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Format a Date as a compact time string.
|
|
178
|
+
*
|
|
179
|
+
* 12-hour locales → "9a", "12:30p"
|
|
180
|
+
* 24-hour locales → "9:00", "14:30"
|
|
181
|
+
*/
|
|
182
|
+
export function fmtTime(d, locale) {
|
|
183
|
+
if (is24HourLocale(locale)) {
|
|
184
|
+
const h = d.getHours();
|
|
185
|
+
const m = d.getMinutes();
|
|
186
|
+
return m === 0 ? String(h) : `${h}:${String(m).padStart(2, '0')}`;
|
|
187
|
+
}
|
|
188
|
+
const h = d.getHours();
|
|
189
|
+
const m = d.getMinutes();
|
|
190
|
+
const suffix = h >= 12 ? 'p' : 'a';
|
|
191
|
+
const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
192
|
+
return m === 0 ? `${h12}${suffix}` : `${h12}:${String(m).padStart(2, '0')}${suffix}`;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Format the duration between two Dates as a compact string.
|
|
196
|
+
* e.g. "45m", "1h", "1h 30m"
|
|
197
|
+
*/
|
|
198
|
+
export function fmtDuration(start, end) {
|
|
199
|
+
const mins = Math.round((end.getTime() - start.getTime()) / 60_000);
|
|
200
|
+
if (mins < 60)
|
|
201
|
+
return `${mins}m`;
|
|
202
|
+
const h = Math.floor(mins / 60);
|
|
203
|
+
const m = mins % 60;
|
|
204
|
+
return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
205
|
+
}
|
package/dist/core/time.d.ts
CHANGED
|
@@ -35,3 +35,33 @@ export declare function fmtS(ms: number): string;
|
|
|
35
35
|
export declare function dayNum(ms: number): number;
|
|
36
36
|
/** Day-of-week index (0 = Sun … 6 = Sat) */
|
|
37
37
|
export declare function dayOfWeek(ms: number): number;
|
|
38
|
+
import type { TimelineEvent } from './types.js';
|
|
39
|
+
/** Does an event span more than one calendar day? */
|
|
40
|
+
export declare function isMultiDay(ev: TimelineEvent): boolean;
|
|
41
|
+
/** Is an event effectively all-day? (allDay flag, or spans ≥24h with midnight boundaries) */
|
|
42
|
+
export declare function isAllDay(ev: TimelineEvent): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Describes how an event appears on a specific day.
|
|
45
|
+
*/
|
|
46
|
+
export interface DaySegment {
|
|
47
|
+
ev: TimelineEvent;
|
|
48
|
+
/** Effective start for this day (clamped to day start) */
|
|
49
|
+
start: Date;
|
|
50
|
+
/** Effective end for this day (clamped to day end) */
|
|
51
|
+
end: Date;
|
|
52
|
+
/** Is this the first day of the event? */
|
|
53
|
+
isStart: boolean;
|
|
54
|
+
/** Is this the last day of the event? */
|
|
55
|
+
isEnd: boolean;
|
|
56
|
+
/** 1-based day index within the span */
|
|
57
|
+
dayIndex: number;
|
|
58
|
+
/** Total number of days the event spans */
|
|
59
|
+
totalDays: number;
|
|
60
|
+
/** Is this an all-day event? */
|
|
61
|
+
allDay: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Compute how an event appears on a specific day.
|
|
65
|
+
* Returns null if the event doesn't overlap the day.
|
|
66
|
+
*/
|
|
67
|
+
export declare function segmentForDay(ev: TimelineEvent, dayMs: number): DaySegment | null;
|
package/dist/core/time.js
CHANGED
|
@@ -66,3 +66,45 @@ export function dayNum(ms) {
|
|
|
66
66
|
export function dayOfWeek(ms) {
|
|
67
67
|
return getDay(new Date(ms));
|
|
68
68
|
}
|
|
69
|
+
/** Does an event span more than one calendar day? */
|
|
70
|
+
export function isMultiDay(ev) {
|
|
71
|
+
return sod(ev.start.getTime()) !== sod(ev.end.getTime() - 1);
|
|
72
|
+
}
|
|
73
|
+
/** Is an event effectively all-day? (allDay flag, or spans ≥24h with midnight boundaries) */
|
|
74
|
+
export function isAllDay(ev) {
|
|
75
|
+
if (ev.allDay)
|
|
76
|
+
return true;
|
|
77
|
+
const duration = ev.end.getTime() - ev.start.getTime();
|
|
78
|
+
if (duration < DAY_MS)
|
|
79
|
+
return false;
|
|
80
|
+
const s = ev.start;
|
|
81
|
+
return s.getHours() === 0 && s.getMinutes() === 0 && s.getSeconds() === 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Compute how an event appears on a specific day.
|
|
85
|
+
* Returns null if the event doesn't overlap the day.
|
|
86
|
+
*/
|
|
87
|
+
export function segmentForDay(ev, dayMs) {
|
|
88
|
+
const dayStart = sod(dayMs);
|
|
89
|
+
const dayEnd = dayStart + DAY_MS;
|
|
90
|
+
const evStart = ev.start.getTime();
|
|
91
|
+
const evEnd = ev.end.getTime();
|
|
92
|
+
// No overlap
|
|
93
|
+
if (evStart >= dayEnd || evEnd <= dayStart)
|
|
94
|
+
return null;
|
|
95
|
+
const firstDayMs = sod(evStart);
|
|
96
|
+
// For end time: if event ends exactly at midnight, last day is the day before
|
|
97
|
+
const lastDayMs = sod(evEnd - 1);
|
|
98
|
+
const totalDays = Math.round((lastDayMs - firstDayMs) / DAY_MS) + 1;
|
|
99
|
+
const dayIndex = Math.round((dayStart - firstDayMs) / DAY_MS) + 1;
|
|
100
|
+
return {
|
|
101
|
+
ev,
|
|
102
|
+
start: new Date(Math.max(evStart, dayStart)),
|
|
103
|
+
end: new Date(Math.min(evEnd, dayEnd)),
|
|
104
|
+
isStart: dayStart === firstDayMs,
|
|
105
|
+
isEnd: dayStart === lastDayMs,
|
|
106
|
+
dayIndex,
|
|
107
|
+
totalDays,
|
|
108
|
+
allDay: isAllDay(ev),
|
|
109
|
+
};
|
|
110
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -11,14 +11,10 @@ export interface TimelineEvent {
|
|
|
11
11
|
start: Date;
|
|
12
12
|
end: Date;
|
|
13
13
|
color?: string;
|
|
14
|
-
/**
|
|
14
|
+
/** True for all-day events (no specific start/end time) */
|
|
15
15
|
allDay?: boolean;
|
|
16
16
|
/** Category for grouping (e.g. "wellness", "work", "life") */
|
|
17
17
|
category?: string;
|
|
18
|
-
/** Recurrence rule — iCalendar RRULE string (e.g. "FREQ=WEEKLY;BYDAY=MO") */
|
|
19
|
-
recurrence?: string;
|
|
20
|
-
/** Whether this event can be moved / resized by the user */
|
|
21
|
-
editable?: boolean;
|
|
22
18
|
/** Subtitle displayed below the title (e.g. instructor name, level) */
|
|
23
19
|
subtitle?: string;
|
|
24
20
|
/** Tags displayed as small pills (e.g. ["Beginner", "Yoga"]) */
|
|
@@ -26,70 +22,3 @@ export interface TimelineEvent {
|
|
|
26
22
|
/** Arbitrary payload from the source app (bookings, attendees, etc.) */
|
|
27
23
|
data?: Record<string, unknown>;
|
|
28
24
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Shared props contract for all WeekTimeline variants.
|
|
31
|
-
* Every variant MUST accept this exact interface so they can be
|
|
32
|
-
* hot-swapped without changing bindings.
|
|
33
|
-
*/
|
|
34
|
-
export interface WeekTimelineProps {
|
|
35
|
-
/** Week offset: 0 = this week, -1 = last, +1 = next (ignored when focusDate is set) */
|
|
36
|
-
weekOffset?: number;
|
|
37
|
-
/** Start on Monday (true) or Sunday (false) */
|
|
38
|
-
mondayStart?: boolean;
|
|
39
|
-
/** Pixel height of one hour row */
|
|
40
|
-
hourHeight?: number;
|
|
41
|
-
/** Pixel width of each day column (0 = equal flex) */
|
|
42
|
-
dayWidth?: number;
|
|
43
|
-
/** Where "now" sits vertically (0 = top edge, 1 = bottom edge) */
|
|
44
|
-
nowPosition?: number;
|
|
45
|
-
/** Total visible height of the component */
|
|
46
|
-
height?: number;
|
|
47
|
-
/** Events to render */
|
|
48
|
-
events?: TimelineEvent[];
|
|
49
|
-
/** Inline style for CSS variable overrides (theme) */
|
|
50
|
-
style?: string;
|
|
51
|
-
/** The date to centre this view on. Overrides weekOffset when set. */
|
|
52
|
-
focusDate?: Date;
|
|
53
|
-
/** Called when the user clicks an event */
|
|
54
|
-
oneventclick?: (event: TimelineEvent) => void;
|
|
55
|
-
/** Called when the user clicks an empty time slot */
|
|
56
|
-
oneventcreate?: (range: {
|
|
57
|
-
start: Date;
|
|
58
|
-
end: Date;
|
|
59
|
-
}) => void;
|
|
60
|
-
/** Currently selected event ID (for highlight) */
|
|
61
|
-
selectedEventId?: string | null;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Shared props contract for the DayTimeline component.
|
|
65
|
-
*/
|
|
66
|
-
export interface DayTimelineProps {
|
|
67
|
-
/** Number of past days visible */
|
|
68
|
-
pastDays?: number;
|
|
69
|
-
/** Number of future days visible */
|
|
70
|
-
futureDays?: number;
|
|
71
|
-
/** Pixel width of one hour */
|
|
72
|
-
hourWidth?: number;
|
|
73
|
-
/** Where "now" sits in the viewport (0 = left edge, 1 = right edge) */
|
|
74
|
-
nowPosition?: number;
|
|
75
|
-
/** Events to render */
|
|
76
|
-
events?: TimelineEvent[];
|
|
77
|
-
/** Inline style for CSS custom property overrides */
|
|
78
|
-
style?: string;
|
|
79
|
-
/** The date to centre this view on */
|
|
80
|
-
focusDate?: Date;
|
|
81
|
-
/** Called when the user clicks an event */
|
|
82
|
-
oneventclick?: (event: TimelineEvent) => void;
|
|
83
|
-
/** Called when the user clicks an empty time slot */
|
|
84
|
-
oneventcreate?: (range: {
|
|
85
|
-
start: Date;
|
|
86
|
-
end: Date;
|
|
87
|
-
}) => void;
|
|
88
|
-
/** Currently selected event ID (for highlight) */
|
|
89
|
-
selectedEventId?: string | null;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Convert a Date (or timestamp) to a pixel offset on the timeline.
|
|
93
|
-
* Useful for external apps that need to position overlays.
|
|
94
|
-
*/
|
|
95
|
-
export declare function timeToX(time: Date | number, rangeStartMs: number, hourWidth: number): number;
|
package/dist/core/types.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared TypeScript types for the timeline design system.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
* Convert a Date (or timestamp) to a pixel offset on the timeline.
|
|
6
|
-
* Useful for external apps that need to position overlays.
|
|
7
|
-
*/
|
|
8
|
-
export function timeToX(time, rangeStartMs, hourWidth) {
|
|
9
|
-
const ms = typeof time === 'number' ? time : time.getTime();
|
|
10
|
-
return ((ms - rangeStartMs) / 3_600_000) * hourWidth;
|
|
11
|
-
}
|
|
4
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sod } from '../core/time.js';
|
|
1
|
+
import { sod, DAY_MS } from '../core/time.js';
|
|
2
2
|
/**
|
|
3
3
|
* Create a reactive event store backed by a CalendarAdapter.
|
|
4
4
|
*/
|
|
@@ -61,7 +61,7 @@ export function createEventStore(adapter) {
|
|
|
61
61
|
},
|
|
62
62
|
forDay(date) {
|
|
63
63
|
const dayStart = new Date(sod(date.getTime()));
|
|
64
|
-
const dayEnd = new Date(dayStart.getTime() +
|
|
64
|
+
const dayEnd = new Date(dayStart.getTime() + DAY_MS);
|
|
65
65
|
return events.filter((ev) => overlaps(ev, dayStart, dayEnd));
|
|
66
66
|
},
|
|
67
67
|
byId(id) {
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createEventStore } from './event-store.svelte.js';
|
|
2
2
|
export type { EventStore } from './event-store.svelte.js';
|
|
3
3
|
export { createViewState } from './view-state.svelte.js';
|
|
4
|
-
export type { ViewState, ViewStateOptions, CalendarViewId, BuiltInViewId, ViewGranularity,
|
|
4
|
+
export type { ViewState, ViewStateOptions, CalendarViewId, BuiltInViewId, ViewGranularity, } from './view-state.svelte.js';
|
|
5
5
|
export { createSelection } from './selection.svelte.js';
|
|
6
6
|
export type { Selection } from './selection.svelte.js';
|
|
7
7
|
export { createDragState } from './drag.svelte.js';
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import type { DateRange } from '../adapters/types.js';
|
|
2
|
+
export type { DateRange };
|
|
1
3
|
/**
|
|
2
4
|
* Built-in view IDs. Custom view IDs are also supported — CalendarViewId
|
|
3
5
|
* is typed as `string` so consumers can register any ID.
|
|
4
6
|
*/
|
|
5
|
-
export type BuiltInViewId = 'day-
|
|
7
|
+
export type BuiltInViewId = 'day-planner' | 'day-agenda' | 'week-planner' | 'week-agenda';
|
|
6
8
|
/**
|
|
7
|
-
* Any view identifier. Use built-in strings like 'day-
|
|
9
|
+
* Any view identifier. Use built-in strings like 'day-planner' or your own
|
|
8
10
|
* custom IDs like 'day-kanban', 'week-resource', etc.
|
|
9
11
|
*/
|
|
10
12
|
export type CalendarViewId = string;
|
|
@@ -14,10 +16,13 @@ export interface ViewStateOptions {
|
|
|
14
16
|
mondayStart?: boolean;
|
|
15
17
|
/** IANA timezone string (e.g. 'America/New_York'). Defaults to local timezone. */
|
|
16
18
|
timezone?: string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/** Initial date to focus on (defaults to today). */
|
|
20
|
+
initialDate?: Date;
|
|
21
|
+
/**
|
|
22
|
+
* Optional resolver for view granularity.
|
|
23
|
+
* Useful for custom IDs that don't follow "day-*" / "week-*" naming.
|
|
24
|
+
*/
|
|
25
|
+
granularityForView?: (viewId: CalendarViewId) => ViewGranularity | undefined;
|
|
21
26
|
}
|
|
22
27
|
export interface ViewState {
|
|
23
28
|
readonly view: CalendarViewId;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* vs.goToday() — jump to today
|
|
13
13
|
*/
|
|
14
14
|
import { startOfWeek as calcStartOfWeek, addDaysMs, DAY_MS } from '../core/time.js';
|
|
15
|
-
function
|
|
15
|
+
function inferGranularity(view) {
|
|
16
16
|
if (view.startsWith('day'))
|
|
17
17
|
return 'day';
|
|
18
18
|
return 'week';
|
|
@@ -32,11 +32,12 @@ function computeRange(focus, granularity, mondayStart) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
export function createViewState(options = {}) {
|
|
35
|
-
let view = $state(options.defaultView ?? 'week-
|
|
36
|
-
let focusDate = $state(new Date());
|
|
35
|
+
let view = $state(options.defaultView ?? 'week-planner');
|
|
36
|
+
let focusDate = $state(options.initialDate ?? new Date());
|
|
37
37
|
let mondayStart = $state(options.mondayStart ?? true);
|
|
38
38
|
const timezone = options.timezone;
|
|
39
|
-
const
|
|
39
|
+
const granularityResolver = options.granularityForView;
|
|
40
|
+
const granularity = $derived(granularityResolver?.(view) ?? inferGranularity(view));
|
|
40
41
|
const range = $derived(computeRange(focusDate, granularity, mondayStart));
|
|
41
42
|
return {
|
|
42
43
|
get view() {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { SettingsField } from './views/index.js';
|
|
1
|
+
export { Planner, Agenda, AgendaDay, AgendaWeek, } from './views/index.js';
|
|
3
2
|
export { NowIndicator, EventBlock, TimeGutter, DayHeader, EmptySlot, } from './primitives/index.js';
|
|
4
|
-
export { Calendar
|
|
3
|
+
export { Calendar } from './calendar/index.js';
|
|
5
4
|
export type { CalendarView } from './calendar/index.js';
|
|
6
5
|
export { createEventStore, createViewState, createSelection, createDragState, } from './engine/index.js';
|
|
7
|
-
export type { EventStore, ViewState, ViewStateOptions, CalendarViewId, BuiltInViewId, ViewGranularity,
|
|
6
|
+
export type { EventStore, ViewState, ViewStateOptions, CalendarViewId, BuiltInViewId, ViewGranularity, Selection, DragState, DragMode, DragPayload, } from './engine/index.js';
|
|
8
7
|
export { createMemoryAdapter, createRestAdapter, createRecurringAdapter } from './adapters/index.js';
|
|
9
8
|
export type { CalendarAdapter, DateRange, RestAdapterOptions, RecurringEvent, RecurringAdapterOptions, MemoryAdapterOptions, } from './adapters/index.js';
|
|
10
|
-
export { createClock,
|
|
11
|
-
export type { Clock, TimelineEvent,
|
|
12
|
-
export { midnight,
|
|
9
|
+
export { createClock, startOfWeek, fmtH, fmtTime, fmtDuration, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, defaultLabels, setLabels, resetLabels, getLabels, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, generatePalette, VIVID_PALETTE, isMultiDay, isAllDay, segmentForDay, } from './core/index.js';
|
|
10
|
+
export type { Clock, TimelineEvent, DaySegment, CalendarLabels, } from './core/index.js';
|
|
11
|
+
export { midnight, neutral, presets } from './theme/index.js';
|
|
13
12
|
export type { PresetName } from './theme/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// ─── Views ──────────────────────────────────────────────
|
|
2
|
-
export {
|
|
2
|
+
export { Planner, Agenda, AgendaDay, AgendaWeek, } from './views/index.js';
|
|
3
3
|
// ─── Primitives ─────────────────────────────────────────
|
|
4
4
|
export { NowIndicator, EventBlock, TimeGutter, DayHeader, EmptySlot, } from './primitives/index.js';
|
|
5
5
|
// ─── Calendar shell ─────────────────────────────────────
|
|
6
|
-
export { Calendar
|
|
6
|
+
export { Calendar } from './calendar/index.js';
|
|
7
7
|
// ─── Engine (reactive state) ────────────────────────────
|
|
8
8
|
export { createEventStore, createViewState, createSelection, createDragState, } from './engine/index.js';
|
|
9
9
|
// ─── Adapters ───────────────────────────────────────────
|
|
10
10
|
export { createMemoryAdapter, createRestAdapter, createRecurringAdapter } from './adapters/index.js';
|
|
11
11
|
// ─── Core: clock, time, locale, types ───────────────────
|
|
12
|
-
export { createClock,
|
|
12
|
+
export { createClock, startOfWeek, fmtH, fmtTime, fmtDuration, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, defaultLabels, setLabels, resetLabels, getLabels, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, generatePalette, VIVID_PALETTE, isMultiDay, isAllDay, segmentForDay, } from './core/index.js';
|
|
13
13
|
// ─── Themes ─────────────────────────────────────────────
|
|
14
|
-
export { midnight,
|
|
14
|
+
export { midnight, neutral, presets } from './theme/index.js';
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
between events.
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
+
import { fmtTime, fmtDuration, getLabels } from '../core/locale.js';
|
|
10
|
+
|
|
11
|
+
const L = $derived(getLabels());
|
|
12
|
+
|
|
9
13
|
interface Props {
|
|
10
14
|
/** Start time of the empty slot */
|
|
11
15
|
start: Date;
|
|
@@ -24,21 +28,7 @@
|
|
|
24
28
|
orientation = 'vertical',
|
|
25
29
|
}: Props = $props();
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
const h = d.getHours();
|
|
29
|
-
const m = d.getMinutes();
|
|
30
|
-
const suffix = h >= 12 ? 'p' : 'a';
|
|
31
|
-
const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
32
|
-
return m === 0 ? `${h12}${suffix}` : `${h12}:${String(m).padStart(2, '0')}${suffix}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const dur = $derived.by(() => {
|
|
36
|
-
const mins = Math.round((end.getTime() - start.getTime()) / 60_000);
|
|
37
|
-
if (mins < 60) return `${mins}m free`;
|
|
38
|
-
const h = Math.floor(mins / 60);
|
|
39
|
-
const m = mins % 60;
|
|
40
|
-
return m > 0 ? `${h}h ${m}m free` : `${h}h free`;
|
|
41
|
-
});
|
|
31
|
+
const dur = $derived(`${fmtDuration(start, end)} ${L.free}`);
|
|
42
32
|
|
|
43
33
|
function handleKeydown(e: KeyboardEvent) {
|
|
44
34
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
@@ -54,7 +44,7 @@
|
|
|
54
44
|
class:es-h={orientation === 'horizontal'}
|
|
55
45
|
role="button"
|
|
56
46
|
tabindex="0"
|
|
57
|
-
aria-label="
|
|
47
|
+
aria-label="{L.createEvent}, {fmtTime(start)} to {fmtTime(end)}, {dur}"
|
|
58
48
|
onclick={() => onclick?.({ start, end })}
|
|
59
49
|
onkeydown={handleKeydown}
|
|
60
50
|
>
|