@tasknotes/calendar 0.0.0-stage → 0.1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +163 -2
  4. package/dist/calendar.css +1358 -0
  5. package/dist/cjs/core.cjs +716 -0
  6. package/dist/cjs/index.cjs +4240 -0
  7. package/dist/cjs/react.cjs +4068 -0
  8. package/dist/esm/chunk-6RRDNLZM.js +3435 -0
  9. package/dist/esm/chunk-DVMZ5OXN.js +694 -0
  10. package/dist/esm/chunk-L4WKZ6WT.js +0 -0
  11. package/dist/esm/core.js +85 -0
  12. package/dist/esm/index.js +223 -0
  13. package/dist/esm/react.js +46 -0
  14. package/dist/types/core/date.d.cts +30 -0
  15. package/dist/types/core/date.d.ts +30 -0
  16. package/dist/types/core/index.d.cts +7 -0
  17. package/dist/types/core/index.d.ts +7 -0
  18. package/dist/types/core/layout.d.cts +82 -0
  19. package/dist/types/core/layout.d.ts +82 -0
  20. package/dist/types/core/order.d.cts +7 -0
  21. package/dist/types/core/order.d.ts +7 -0
  22. package/dist/types/core/plan.d.cts +42 -0
  23. package/dist/types/core/plan.d.ts +42 -0
  24. package/dist/types/core/store.d.cts +60 -0
  25. package/dist/types/core/store.d.ts +60 -0
  26. package/dist/types/core/types.d.cts +69 -0
  27. package/dist/types/core/types.d.ts +69 -0
  28. package/dist/types/core/zone.d.cts +43 -0
  29. package/dist/types/core/zone.d.ts +43 -0
  30. package/dist/types/dom/calendar.d.cts +215 -0
  31. package/dist/types/dom/calendar.d.ts +215 -0
  32. package/dist/types/dom/context.d.cts +107 -0
  33. package/dist/types/dom/context.d.ts +107 -0
  34. package/dist/types/dom/dom.d.cts +8 -0
  35. package/dist/types/dom/dom.d.ts +8 -0
  36. package/dist/types/dom/format.d.cts +30 -0
  37. package/dist/types/dom/format.d.ts +30 -0
  38. package/dist/types/dom/options.d.cts +401 -0
  39. package/dist/types/dom/options.d.ts +401 -0
  40. package/dist/types/dom/views/daygrid.d.cts +42 -0
  41. package/dist/types/dom/views/daygrid.d.ts +42 -0
  42. package/dist/types/dom/views/list.d.cts +27 -0
  43. package/dist/types/dom/views/list.d.ts +27 -0
  44. package/dist/types/dom/views/timegrid.d.cts +54 -0
  45. package/dist/types/dom/views/timegrid.d.ts +54 -0
  46. package/dist/types/dom/views/timeline.d.cts +83 -0
  47. package/dist/types/dom/views/timeline.d.ts +83 -0
  48. package/dist/types/dom/views/year.d.cts +26 -0
  49. package/dist/types/dom/views/year.d.ts +26 -0
  50. package/dist/types/index.d.cts +6 -0
  51. package/dist/types/index.d.ts +6 -0
  52. package/dist/types/react/index.d.cts +20 -0
  53. package/dist/types/react/index.d.ts +20 -0
  54. package/dist/types/tasknotes/bases.d.cts +50 -0
  55. package/dist/types/tasknotes/bases.d.ts +50 -0
  56. package/package.json +108 -5
@@ -0,0 +1,85 @@
1
+ import "./chunk-L4WKZ6WT.js";
2
+ import {
3
+ DAY_MS,
4
+ ItemStore,
5
+ MINUTES_PER_DAY,
6
+ addMonths,
7
+ dayFromYMD,
8
+ dayLengthMinutes,
9
+ dayOf,
10
+ daysInMonth,
11
+ daysOfRange,
12
+ defaultOrder,
13
+ dependencyConflicts,
14
+ dependencySatisfied,
15
+ dependencyType,
16
+ findFreeSlot,
17
+ formatDay,
18
+ formatFloating,
19
+ formatMinutes,
20
+ getTimeZone,
21
+ isValidTimeZone,
22
+ isoWeekNumber,
23
+ itemDependencies,
24
+ layoutLane,
25
+ layoutRow,
26
+ layoutTimedDay,
27
+ packTimed,
28
+ parseDateInput,
29
+ parseDay,
30
+ parseDurationMinutes,
31
+ parseOrder,
32
+ plannedMinutes,
33
+ segmentInWindow,
34
+ segmentOn,
35
+ startOfDayInstant,
36
+ startOfMonth,
37
+ startOfWeek,
38
+ startOfYear,
39
+ toInstant,
40
+ wallTime,
41
+ weekdayOf,
42
+ ymdOfDay
43
+ } from "./chunk-DVMZ5OXN.js";
44
+ export {
45
+ DAY_MS,
46
+ ItemStore,
47
+ MINUTES_PER_DAY,
48
+ addMonths,
49
+ dayFromYMD,
50
+ dayLengthMinutes,
51
+ dayOf,
52
+ daysInMonth,
53
+ daysOfRange,
54
+ defaultOrder,
55
+ dependencyConflicts,
56
+ dependencySatisfied,
57
+ dependencyType,
58
+ findFreeSlot,
59
+ formatDay,
60
+ formatFloating,
61
+ formatMinutes,
62
+ getTimeZone,
63
+ isValidTimeZone,
64
+ isoWeekNumber,
65
+ itemDependencies,
66
+ layoutLane,
67
+ layoutRow,
68
+ layoutTimedDay,
69
+ packTimed,
70
+ parseDateInput,
71
+ parseDay,
72
+ parseDurationMinutes,
73
+ parseOrder,
74
+ plannedMinutes,
75
+ segmentInWindow,
76
+ segmentOn,
77
+ startOfDayInstant,
78
+ startOfMonth,
79
+ startOfWeek,
80
+ startOfYear,
81
+ toInstant,
82
+ wallTime,
83
+ weekdayOf,
84
+ ymdOfDay
85
+ };
@@ -0,0 +1,223 @@
1
+ import "./chunk-L4WKZ6WT.js";
2
+ import {
3
+ Calendar,
4
+ DEFAULT_TEXT,
5
+ Formatter,
6
+ LAYOUT_KEYS,
7
+ TIMELINE_UNIT_DAYS,
8
+ defaultOptions,
9
+ localeFirstDay
10
+ } from "./chunk-6RRDNLZM.js";
11
+ import {
12
+ DAY_MS,
13
+ ItemStore,
14
+ MINUTES_PER_DAY,
15
+ addMonths,
16
+ dayFromYMD,
17
+ dayLengthMinutes,
18
+ dayOf,
19
+ daysInMonth,
20
+ daysOfRange,
21
+ defaultOrder,
22
+ dependencyConflicts,
23
+ dependencySatisfied,
24
+ dependencyType,
25
+ findFreeSlot,
26
+ formatDay,
27
+ formatFloating,
28
+ formatMinutes,
29
+ getTimeZone,
30
+ isValidTimeZone,
31
+ isoWeekNumber,
32
+ itemDependencies,
33
+ layoutLane,
34
+ layoutRow,
35
+ layoutTimedDay,
36
+ packTimed,
37
+ parseDateInput,
38
+ parseDay,
39
+ parseDurationMinutes,
40
+ parseOrder,
41
+ plannedMinutes,
42
+ segmentInWindow,
43
+ segmentOn,
44
+ startOfDayInstant,
45
+ startOfMonth,
46
+ startOfWeek,
47
+ startOfYear,
48
+ toInstant,
49
+ wallTime,
50
+ weekdayOf,
51
+ ymdOfDay
52
+ } from "./chunk-DVMZ5OXN.js";
53
+
54
+ // src/tasknotes/bases.ts
55
+ var FROM_BASES = {
56
+ dayGridMonth: "month",
57
+ timeGridWeek: "week",
58
+ timeGridCustom: "days",
59
+ timeGridDay: "day",
60
+ listWeek: "list",
61
+ multiMonthYear: "year"
62
+ };
63
+ var TO_BASES = {
64
+ month: "dayGridMonth",
65
+ week: "timeGridWeek",
66
+ days: "timeGridCustom",
67
+ day: "timeGridDay",
68
+ list: "listWeek",
69
+ year: "multiMonthYear"
70
+ };
71
+ function viewFromBases(name, fallback = "month") {
72
+ return name && FROM_BASES[name] || fallback;
73
+ }
74
+ function viewToBases(view) {
75
+ return TO_BASES[view] ?? null;
76
+ }
77
+ var BASES_CALENDAR_DEFAULTS = {
78
+ calendarView: "dayGridMonth",
79
+ customDayCount: 3,
80
+ listDayCount: 7,
81
+ heightMode: "fill",
82
+ firstDay: 1,
83
+ timeFormat: "24",
84
+ weekNumbers: false,
85
+ showWeekends: true,
86
+ showAllDaySlot: true,
87
+ showTodayHighlight: true,
88
+ todayColumnWidthMultiplier: 1,
89
+ slotMinTime: "00:00:00",
90
+ slotMaxTime: "24:00:00",
91
+ slotDuration: "00:30:00",
92
+ snapDuration: "00:30:00",
93
+ scrollTime: "08:00:00",
94
+ nowIndicator: true,
95
+ showTimeGrid: true,
96
+ selectMirror: true,
97
+ slotEventOverlap: true,
98
+ eventMinHeight: 15,
99
+ eventMaxStack: 0,
100
+ dayMaxEvents: 0,
101
+ dayMaxEventRows: 0,
102
+ locale: ""
103
+ };
104
+ var num = (v, fallback) => {
105
+ const n = typeof v === "string" ? Number(v) : v;
106
+ return typeof n === "number" && Number.isFinite(n) ? n : fallback;
107
+ };
108
+ var clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
109
+ function isValidSlotTime(value, maxHours = 23) {
110
+ const m = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/.exec(value.trim());
111
+ if (!m) return false;
112
+ const hh = Number(m[1]);
113
+ const mm = Number(m[2]);
114
+ const ss = Number(m[3] ?? 0);
115
+ if (mm > 59 || ss > 59 || hh > maxHours) return false;
116
+ if (hh === 48 && (mm || ss)) return false;
117
+ return true;
118
+ }
119
+ function basesLayoutOptions(s, context = {}) {
120
+ const d = BASES_CALENDAR_DEFAULTS;
121
+ const pick = (k) => s[k] ?? d[k];
122
+ const view = viewFromBases(pick("calendarView"));
123
+ const showTimeGrid = pick("showTimeGrid") !== false;
124
+ const time = (k, max = 23) => {
125
+ const v = s[k];
126
+ return typeof v === "string" && isValidSlotTime(v, max) ? v : void 0;
127
+ };
128
+ let today = num(s.todayColumnWidthMultiplier, 1);
129
+ today = today < 1 || today > 5 ? 1 : Math.round(today * 2) / 2;
130
+ const rawMax = s.dayMaxEvents ?? d.dayMaxEvents;
131
+ let dayMaxEvents = typeof rawMax === "boolean" ? rawMax : num(rawMax, 0) === 0 ? true : clamp(num(rawMax, 0), 1, 20);
132
+ if (!showTimeGrid && dayMaxEvents === true && view !== "month") dayMaxEvents = false;
133
+ const rawRows = s.dayMaxEventRows ?? d.dayMaxEventRows;
134
+ const dayMaxEventRows = typeof rawRows === "boolean" ? 0 : clamp(num(rawRows, 0), 0, 10);
135
+ const slotDuration = time("slotDuration") ?? d.slotDuration;
136
+ const locale = (s.locale ?? "").trim();
137
+ const out = {
138
+ view,
139
+ dayCount: clamp(Math.round(num(s.customDayCount, d.customDayCount)), 1, 14),
140
+ listDayCount: clamp(Math.round(num(s.listDayCount, d.listDayCount)), 1, 365),
141
+ // The plugin overrode FullCalendar's listWeek with `duration: { days: listDayCount }`,
142
+ // which aligns to the day (not the week), so saved list views start at the date.
143
+ listStart: "date",
144
+ height: context.embedded || s.heightMode === "auto" ? "auto" : "fill",
145
+ expandRows: !(context.embedded || s.heightMode === "auto"),
146
+ firstDay: clamp(Math.round(num(s.firstDay, 1)), 0, 6),
147
+ hour12: (s.timeFormat ?? d.timeFormat) === "12",
148
+ weekNumbers: pick("weekNumbers") === true,
149
+ weekends: pick("showWeekends") !== false,
150
+ allDaySlot: pick("showAllDaySlot") !== false,
151
+ todayHighlight: pick("showTodayHighlight") !== false,
152
+ todayWidth: view === "week" || view === "days" ? today : 1,
153
+ slotMinTime: time("slotMinTime") ?? d.slotMinTime,
154
+ slotMaxTime: time("slotMaxTime", 48) ?? d.slotMaxTime,
155
+ slotDuration,
156
+ snapDuration: time("snapDuration") ?? slotDuration,
157
+ scrollTime: time("scrollTime", 48) ?? d.scrollTime,
158
+ nowIndicator: pick("nowIndicator") !== false,
159
+ timeGrid: showTimeGrid,
160
+ selectMirror: pick("selectMirror") !== false,
161
+ slotEventOverlap: pick("slotEventOverlap") !== false,
162
+ eventMinHeight: clamp(num(s.eventMinHeight, d.eventMinHeight), 15, 100),
163
+ eventMaxStack: clamp(Math.round(num(s.eventMaxStack, 0)), 0, 10),
164
+ dayMaxEvents,
165
+ dayMaxEventRows
166
+ };
167
+ if (locale) out.locale = locale;
168
+ return out;
169
+ }
170
+ export {
171
+ BASES_CALENDAR_DEFAULTS,
172
+ Calendar,
173
+ DAY_MS,
174
+ DEFAULT_TEXT,
175
+ Formatter,
176
+ ItemStore,
177
+ LAYOUT_KEYS,
178
+ MINUTES_PER_DAY,
179
+ TIMELINE_UNIT_DAYS,
180
+ addMonths,
181
+ basesLayoutOptions,
182
+ dayFromYMD,
183
+ dayLengthMinutes,
184
+ dayOf,
185
+ daysInMonth,
186
+ daysOfRange,
187
+ defaultOptions,
188
+ defaultOrder,
189
+ dependencyConflicts,
190
+ dependencySatisfied,
191
+ dependencyType,
192
+ findFreeSlot,
193
+ formatDay,
194
+ formatFloating,
195
+ formatMinutes,
196
+ getTimeZone,
197
+ isValidSlotTime,
198
+ isValidTimeZone,
199
+ isoWeekNumber,
200
+ itemDependencies,
201
+ layoutLane,
202
+ layoutRow,
203
+ layoutTimedDay,
204
+ localeFirstDay,
205
+ packTimed,
206
+ parseDateInput,
207
+ parseDay,
208
+ parseDurationMinutes,
209
+ parseOrder,
210
+ plannedMinutes,
211
+ segmentInWindow,
212
+ segmentOn,
213
+ startOfDayInstant,
214
+ startOfMonth,
215
+ startOfWeek,
216
+ startOfYear,
217
+ toInstant,
218
+ viewFromBases,
219
+ viewToBases,
220
+ wallTime,
221
+ weekdayOf,
222
+ ymdOfDay
223
+ };
@@ -0,0 +1,46 @@
1
+ import {
2
+ Calendar
3
+ } from "./chunk-6RRDNLZM.js";
4
+ import "./chunk-DVMZ5OXN.js";
5
+
6
+ // src/react/index.tsx
7
+ import { useEffect, useLayoutEffect, useRef } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ function TaskCalendar(props) {
10
+ const { items, className, style, calendarRef, renderKey, ...options } = props;
11
+ const host = useRef(null);
12
+ const cal = useRef(null);
13
+ const refCb = useRef(calendarRef);
14
+ refCb.current = calendarRef;
15
+ useLayoutEffect(() => {
16
+ const el = host.current;
17
+ if (!el) return;
18
+ const instance = new Calendar(el, options, items);
19
+ cal.current = instance;
20
+ refCb.current?.(instance);
21
+ return () => {
22
+ refCb.current?.(null);
23
+ instance.destroy();
24
+ cal.current = null;
25
+ };
26
+ }, []);
27
+ useLayoutEffect(() => {
28
+ cal.current?.setOptions(options);
29
+ });
30
+ useEffect(() => {
31
+ cal.current?.setItems(items);
32
+ }, [items]);
33
+ const firstKey = useRef(true);
34
+ useEffect(() => {
35
+ if (firstKey.current) {
36
+ firstKey.current = false;
37
+ return;
38
+ }
39
+ cal.current?.rerender();
40
+ }, [renderKey]);
41
+ return /* @__PURE__ */ jsx("div", { ref: host, className, style });
42
+ }
43
+ export {
44
+ Calendar,
45
+ TaskCalendar
46
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Civil (calendar) days are integers: days since 1970-01-01. They carry no time
3
+ * zone. Instants are epoch milliseconds. A {@link TimeZone} converts between the
4
+ * two, so all day arithmetic stays exact across DST changes.
5
+ */
6
+ export type Day = number;
7
+ export declare const DAY_MS = 86400000;
8
+ export declare const MINUTES_PER_DAY = 1440;
9
+ export interface YMD {
10
+ year: number;
11
+ month: number;
12
+ day: number;
13
+ }
14
+ export declare function dayFromYMD(year: number, month: number, day: number): Day;
15
+ export declare function ymdOfDay(day: Day): YMD;
16
+ /** 0 = Sunday … 6 = Saturday. 1970-01-01 was a Thursday. */
17
+ export declare function weekdayOf(day: Day): number;
18
+ export declare function startOfWeek(day: Day, firstDay: number): Day;
19
+ export declare function startOfMonth(day: Day): Day;
20
+ export declare function daysInMonth(year: number, month: number): number;
21
+ /** Adds calendar months, clamping the day of month (Jan 31 + 1 month = Feb 28/29). */
22
+ export declare function addMonths(day: Day, months: number): Day;
23
+ export declare function startOfYear(day: Day): Day;
24
+ export declare function formatDay(day: Day): string;
25
+ export declare function parseDay(value: string): Day | null;
26
+ /** ISO 8601 week number (weeks start Monday; week 1 contains Jan 4). */
27
+ export declare function isoWeekNumber(day: Day): number;
28
+ /** Parses "HH:mm" or "HH:mm:ss" durations (FullCalendar-style, may exceed 24h). */
29
+ export declare function parseDurationMinutes(value: string | number | null | undefined, fallback: number): number;
30
+ export declare function formatMinutes(minutes: number): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Civil (calendar) days are integers: days since 1970-01-01. They carry no time
3
+ * zone. Instants are epoch milliseconds. A {@link TimeZone} converts between the
4
+ * two, so all day arithmetic stays exact across DST changes.
5
+ */
6
+ export type Day = number;
7
+ export declare const DAY_MS = 86400000;
8
+ export declare const MINUTES_PER_DAY = 1440;
9
+ export interface YMD {
10
+ year: number;
11
+ month: number;
12
+ day: number;
13
+ }
14
+ export declare function dayFromYMD(year: number, month: number, day: number): Day;
15
+ export declare function ymdOfDay(day: Day): YMD;
16
+ /** 0 = Sunday … 6 = Saturday. 1970-01-01 was a Thursday. */
17
+ export declare function weekdayOf(day: Day): number;
18
+ export declare function startOfWeek(day: Day, firstDay: number): Day;
19
+ export declare function startOfMonth(day: Day): Day;
20
+ export declare function daysInMonth(year: number, month: number): number;
21
+ /** Adds calendar months, clamping the day of month (Jan 31 + 1 month = Feb 28/29). */
22
+ export declare function addMonths(day: Day, months: number): Day;
23
+ export declare function startOfYear(day: Day): Day;
24
+ export declare function formatDay(day: Day): string;
25
+ export declare function parseDay(value: string): Day | null;
26
+ /** ISO 8601 week number (weeks start Monday; week 1 contains Jan 4). */
27
+ export declare function isoWeekNumber(day: Day): number;
28
+ /** Parses "HH:mm" or "HH:mm:ss" durations (FullCalendar-style, may exceed 24h). */
29
+ export declare function parseDurationMinutes(value: string | number | null | undefined, fallback: number): number;
30
+ export declare function formatMinutes(minutes: number): string;
@@ -0,0 +1,7 @@
1
+ export * from "./date.cjs";
2
+ export * from "./zone.cjs";
3
+ export * from "./types.cjs";
4
+ export * from "./store.cjs";
5
+ export * from "./layout.cjs";
6
+ export * from "./order.cjs";
7
+ export * from "./plan.cjs";
@@ -0,0 +1,7 @@
1
+ export * from "./date.js";
2
+ export * from "./zone.js";
3
+ export * from "./types.js";
4
+ export * from "./store.js";
5
+ export * from "./layout.js";
6
+ export * from "./order.js";
7
+ export * from "./plan.js";
@@ -0,0 +1,82 @@
1
+ import { type Day } from "./date.cjs";
2
+ import type { NormalizedItem } from "./types.cjs";
3
+ import { type TimeZone } from "./zone.cjs";
4
+ export interface TimedSegment<D = unknown> {
5
+ item: NormalizedItem<D>;
6
+ day: Day;
7
+ /** Wall-clock minutes from local midnight. `endMin` may be 1440. */
8
+ startMin: number;
9
+ endMin: number;
10
+ /** Whether the item starts / ends inside this day. */
11
+ isStart: boolean;
12
+ isEnd: boolean;
13
+ }
14
+ export interface TimedPlacement<D = unknown> extends TimedSegment<D> {
15
+ /** Column within its overlap cluster, and the cluster's column count. */
16
+ col: number;
17
+ cols: number;
18
+ /** Columns the item can stretch across without hitting a neighbour. */
19
+ span: number;
20
+ /** Index of the overlap cluster within the day. */
21
+ cluster: number;
22
+ }
23
+ export interface TimedDayLayout<D = unknown> {
24
+ day: Day;
25
+ plan: TimedPlacement<D>[];
26
+ actual: TimedPlacement<D>[];
27
+ background: TimedSegment<D>[];
28
+ }
29
+ export declare function segmentOn<D>(n: NormalizedItem<D>, day: Day, zone: TimeZone): TimedSegment<D> | null;
30
+ /**
31
+ * Clips an item to the wall-clock window [minMinute, maxMinute) of `day`.
32
+ * The window may run past midnight (maxMinute up to 2880) for grids that show
33
+ * "48:00"-style ranges. Returned minutes are relative to `day`'s midnight.
34
+ */
35
+ export declare function segmentInWindow<D>(n: NormalizedItem<D>, day: Day, minMinute: number, maxMinute: number, zone: TimeZone): TimedSegment<D> | null;
36
+ export type ItemOrder<D = unknown> = (a: NormalizedItem<D>, b: NormalizedItem<D>) => number;
37
+ /** FullCalendar's default: start, longer first, all-day first, title. */
38
+ export declare const defaultOrder: ItemOrder;
39
+ /**
40
+ * Packs overlapping segments into columns. Items in the same overlap cluster
41
+ * share a column count; each item stretches right until it meets an overlapping
42
+ * neighbour.
43
+ */
44
+ export declare function packTimed<D>(segs: TimedSegment<D>[], minMinutes: number, order?: ItemOrder<D>): TimedPlacement<D>[];
45
+ export declare function layoutTimedDay<D>(items: NormalizedItem<D>[], day: Day, zone: TimeZone, minMinutes: number, order?: ItemOrder<D>, window?: [number, number]): TimedDayLayout<D>;
46
+ export interface RowPlacement<D = unknown> {
47
+ item: NormalizedItem<D>;
48
+ /** Column range within the row, end exclusive. */
49
+ startCol: number;
50
+ endCol: number;
51
+ level: number;
52
+ /** False when the item continues before / after the row. */
53
+ isStart: boolean;
54
+ isEnd: boolean;
55
+ }
56
+ export interface RowLayout<D = unknown> {
57
+ placements: RowPlacement<D>[];
58
+ /** Number of placements per column (for "+N more"). */
59
+ countByCol: number[];
60
+ levels: number;
61
+ }
62
+ /**
63
+ * Lays out items across a row of day columns (a month week, or the all-day
64
+ * row of a time grid). `days` are the visible days in order; hidden days are
65
+ * simply absent, and items spanning them are clipped.
66
+ */
67
+ export declare function layoutRow<D>(items: NormalizedItem<D>[], days: Day[], order?: ItemOrder<D>): RowLayout<D>;
68
+ export interface TimelineBar<D = unknown> {
69
+ item: NormalizedItem<D>;
70
+ /** Ms range clipped to the visible window. */
71
+ startMs: number;
72
+ endMs: number;
73
+ clippedStart: boolean;
74
+ clippedEnd: boolean;
75
+ /** Sub-row within the lane when bars overlap. */
76
+ level: number;
77
+ }
78
+ /** Stacks overlapping bars within one lane. */
79
+ export declare function layoutLane<D>(items: NormalizedItem<D>[], windowStart: number, windowEnd: number): {
80
+ bars: TimelineBar<D>[];
81
+ levels: number;
82
+ };
@@ -0,0 +1,82 @@
1
+ import { type Day } from "./date.js";
2
+ import type { NormalizedItem } from "./types.js";
3
+ import { type TimeZone } from "./zone.js";
4
+ export interface TimedSegment<D = unknown> {
5
+ item: NormalizedItem<D>;
6
+ day: Day;
7
+ /** Wall-clock minutes from local midnight. `endMin` may be 1440. */
8
+ startMin: number;
9
+ endMin: number;
10
+ /** Whether the item starts / ends inside this day. */
11
+ isStart: boolean;
12
+ isEnd: boolean;
13
+ }
14
+ export interface TimedPlacement<D = unknown> extends TimedSegment<D> {
15
+ /** Column within its overlap cluster, and the cluster's column count. */
16
+ col: number;
17
+ cols: number;
18
+ /** Columns the item can stretch across without hitting a neighbour. */
19
+ span: number;
20
+ /** Index of the overlap cluster within the day. */
21
+ cluster: number;
22
+ }
23
+ export interface TimedDayLayout<D = unknown> {
24
+ day: Day;
25
+ plan: TimedPlacement<D>[];
26
+ actual: TimedPlacement<D>[];
27
+ background: TimedSegment<D>[];
28
+ }
29
+ export declare function segmentOn<D>(n: NormalizedItem<D>, day: Day, zone: TimeZone): TimedSegment<D> | null;
30
+ /**
31
+ * Clips an item to the wall-clock window [minMinute, maxMinute) of `day`.
32
+ * The window may run past midnight (maxMinute up to 2880) for grids that show
33
+ * "48:00"-style ranges. Returned minutes are relative to `day`'s midnight.
34
+ */
35
+ export declare function segmentInWindow<D>(n: NormalizedItem<D>, day: Day, minMinute: number, maxMinute: number, zone: TimeZone): TimedSegment<D> | null;
36
+ export type ItemOrder<D = unknown> = (a: NormalizedItem<D>, b: NormalizedItem<D>) => number;
37
+ /** FullCalendar's default: start, longer first, all-day first, title. */
38
+ export declare const defaultOrder: ItemOrder;
39
+ /**
40
+ * Packs overlapping segments into columns. Items in the same overlap cluster
41
+ * share a column count; each item stretches right until it meets an overlapping
42
+ * neighbour.
43
+ */
44
+ export declare function packTimed<D>(segs: TimedSegment<D>[], minMinutes: number, order?: ItemOrder<D>): TimedPlacement<D>[];
45
+ export declare function layoutTimedDay<D>(items: NormalizedItem<D>[], day: Day, zone: TimeZone, minMinutes: number, order?: ItemOrder<D>, window?: [number, number]): TimedDayLayout<D>;
46
+ export interface RowPlacement<D = unknown> {
47
+ item: NormalizedItem<D>;
48
+ /** Column range within the row, end exclusive. */
49
+ startCol: number;
50
+ endCol: number;
51
+ level: number;
52
+ /** False when the item continues before / after the row. */
53
+ isStart: boolean;
54
+ isEnd: boolean;
55
+ }
56
+ export interface RowLayout<D = unknown> {
57
+ placements: RowPlacement<D>[];
58
+ /** Number of placements per column (for "+N more"). */
59
+ countByCol: number[];
60
+ levels: number;
61
+ }
62
+ /**
63
+ * Lays out items across a row of day columns (a month week, or the all-day
64
+ * row of a time grid). `days` are the visible days in order; hidden days are
65
+ * simply absent, and items spanning them are clipped.
66
+ */
67
+ export declare function layoutRow<D>(items: NormalizedItem<D>[], days: Day[], order?: ItemOrder<D>): RowLayout<D>;
68
+ export interface TimelineBar<D = unknown> {
69
+ item: NormalizedItem<D>;
70
+ /** Ms range clipped to the visible window. */
71
+ startMs: number;
72
+ endMs: number;
73
+ clippedStart: boolean;
74
+ clippedEnd: boolean;
75
+ /** Sub-row within the lane when bars overlap. */
76
+ level: number;
77
+ }
78
+ /** Stacks overlapping bars within one lane. */
79
+ export declare function layoutLane<D>(items: NormalizedItem<D>[], windowStart: number, windowEnd: number): {
80
+ bars: TimelineBar<D>[];
81
+ levels: number;
82
+ };
@@ -0,0 +1,7 @@
1
+ import type { ItemOrder } from "./layout.cjs";
2
+ /**
3
+ * Parses a FullCalendar-style order string such as
4
+ * `"start,-duration,allDay,title"`. Unknown fields are read from the item, then
5
+ * from `item.data`. `allDay` sorts all-day items first, as FullCalendar does.
6
+ */
7
+ export declare function parseOrder<D = unknown>(spec: string | ItemOrder<D> | undefined): ItemOrder<D>;
@@ -0,0 +1,7 @@
1
+ import type { ItemOrder } from "./layout.js";
2
+ /**
3
+ * Parses a FullCalendar-style order string such as
4
+ * `"start,-duration,allDay,title"`. Unknown fields are read from the item, then
5
+ * from `item.data`. `allDay` sorts all-day items first, as FullCalendar does.
6
+ */
7
+ export declare function parseOrder<D = unknown>(spec: string | ItemOrder<D> | undefined): ItemOrder<D>;
@@ -0,0 +1,42 @@
1
+ import type { Day } from "./date.cjs";
2
+ import type { ItemStore } from "./store.cjs";
3
+ import type { CalendarItem, DependencyType, NormalizedItem } from "./types.cjs";
4
+ /** Minutes of planned (timed, plan-lane, non-background) work on each day. */
5
+ export declare function plannedMinutes<D>(store: ItemStore<D>, days: Day[]): Map<Day, number>;
6
+ /** An item's dependencies with their relationship types resolved ("FS" by default). */
7
+ export declare function itemDependencies(item: Pick<CalendarItem, "dependsOn">): {
8
+ id: string;
9
+ type: DependencyType;
10
+ }[];
11
+ /** The relationship created by linking a predecessor edge to a successor edge. */
12
+ export declare function dependencyType(fromEdge: "start" | "finish", toEdge: "start" | "finish"): DependencyType;
13
+ /** Whether a relationship holds: the successor's edge is not before the predecessor's. */
14
+ export declare function dependencySatisfied(type: DependencyType, pred: {
15
+ startMs: number;
16
+ endMs: number;
17
+ }, succ: {
18
+ startMs: number;
19
+ endMs: number;
20
+ }): boolean;
21
+ /** Items whose relationships are broken, mapped to the ids of the predecessors they break. */
22
+ export declare function dependencyConflicts<D>(store: ItemStore<D>): Map<string, string[]>;
23
+ export interface SlotSearch {
24
+ /** First and last (exclusive) days to search. */
25
+ fromDay: Day;
26
+ toDay: Day;
27
+ durationMinutes: number;
28
+ /** Working window as wall minutes, e.g. [540, 1020] for 09:00-17:00. */
29
+ window: [number, number];
30
+ /** Don't place anything before this instant (usually now). */
31
+ notBefore?: number;
32
+ snapMinutes?: number;
33
+ /** Items to treat as free time, e.g. the item being placed. */
34
+ ignore?: (n: NormalizedItem) => boolean;
35
+ /** Days to skip, e.g. weekends. */
36
+ skipDay?: (day: Day) => boolean;
37
+ }
38
+ /** Finds the first gap long enough for `durationMinutes` in the working window. */
39
+ export declare function findFreeSlot<D>(store: ItemStore<D>, q: SlotSearch): {
40
+ startMs: number;
41
+ endMs: number;
42
+ } | null;