@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.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +163 -2
- package/dist/calendar.css +1358 -0
- package/dist/cjs/core.cjs +716 -0
- package/dist/cjs/index.cjs +4240 -0
- package/dist/cjs/react.cjs +4068 -0
- package/dist/esm/chunk-6RRDNLZM.js +3435 -0
- package/dist/esm/chunk-DVMZ5OXN.js +694 -0
- package/dist/esm/chunk-L4WKZ6WT.js +0 -0
- package/dist/esm/core.js +85 -0
- package/dist/esm/index.js +223 -0
- package/dist/esm/react.js +46 -0
- package/dist/types/core/date.d.cts +30 -0
- package/dist/types/core/date.d.ts +30 -0
- package/dist/types/core/index.d.cts +7 -0
- package/dist/types/core/index.d.ts +7 -0
- package/dist/types/core/layout.d.cts +82 -0
- package/dist/types/core/layout.d.ts +82 -0
- package/dist/types/core/order.d.cts +7 -0
- package/dist/types/core/order.d.ts +7 -0
- package/dist/types/core/plan.d.cts +42 -0
- package/dist/types/core/plan.d.ts +42 -0
- package/dist/types/core/store.d.cts +60 -0
- package/dist/types/core/store.d.ts +60 -0
- package/dist/types/core/types.d.cts +69 -0
- package/dist/types/core/types.d.ts +69 -0
- package/dist/types/core/zone.d.cts +43 -0
- package/dist/types/core/zone.d.ts +43 -0
- package/dist/types/dom/calendar.d.cts +215 -0
- package/dist/types/dom/calendar.d.ts +215 -0
- package/dist/types/dom/context.d.cts +107 -0
- package/dist/types/dom/context.d.ts +107 -0
- package/dist/types/dom/dom.d.cts +8 -0
- package/dist/types/dom/dom.d.ts +8 -0
- package/dist/types/dom/format.d.cts +30 -0
- package/dist/types/dom/format.d.ts +30 -0
- package/dist/types/dom/options.d.cts +401 -0
- package/dist/types/dom/options.d.ts +401 -0
- package/dist/types/dom/views/daygrid.d.cts +42 -0
- package/dist/types/dom/views/daygrid.d.ts +42 -0
- package/dist/types/dom/views/list.d.cts +27 -0
- package/dist/types/dom/views/list.d.ts +27 -0
- package/dist/types/dom/views/timegrid.d.cts +54 -0
- package/dist/types/dom/views/timegrid.d.ts +54 -0
- package/dist/types/dom/views/timeline.d.cts +83 -0
- package/dist/types/dom/views/timeline.d.ts +83 -0
- package/dist/types/dom/views/year.d.cts +26 -0
- package/dist/types/dom/views/year.d.ts +26 -0
- package/dist/types/index.d.cts +6 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/react/index.d.cts +20 -0
- package/dist/types/react/index.d.ts +20 -0
- package/dist/types/tasknotes/bases.d.cts +50 -0
- package/dist/types/tasknotes/bases.d.ts +50 -0
- package/package.json +108 -5
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Day } from "./date.js";
|
|
2
|
+
import type { ItemStore } from "./store.js";
|
|
3
|
+
import type { CalendarItem, DependencyType, NormalizedItem } from "./types.js";
|
|
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;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type Day, DAY_MS } from "./date.cjs";
|
|
2
|
+
import type { CalendarItem, NormalizedItem } from "./types.cjs";
|
|
3
|
+
import { type TimeZone } from "./zone.cjs";
|
|
4
|
+
export interface StoreOptions {
|
|
5
|
+
/** Length of timed items without an end, in minutes. */
|
|
6
|
+
defaultTimedMinutes: number;
|
|
7
|
+
/** Length of all-day items without an end, in days. */
|
|
8
|
+
defaultAllDayDays: number;
|
|
9
|
+
}
|
|
10
|
+
export interface StoreChange {
|
|
11
|
+
added: string[];
|
|
12
|
+
updated: string[];
|
|
13
|
+
removed: string[];
|
|
14
|
+
/** Days whose timed content changed. */
|
|
15
|
+
days: Set<Day>;
|
|
16
|
+
/** True when any all-day (or multi-day) placement changed. */
|
|
17
|
+
spans: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Items indexed by the civil days they touch. Every mutation records which days
|
|
21
|
+
* changed so layouts and DOM columns for untouched days can be reused.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ItemStore<D = unknown> {
|
|
24
|
+
private zone;
|
|
25
|
+
private opts;
|
|
26
|
+
private readonly items;
|
|
27
|
+
private readonly timedByDay;
|
|
28
|
+
private readonly spanning;
|
|
29
|
+
private pending;
|
|
30
|
+
/** Bumped on every change; cheap staleness check for caches. */
|
|
31
|
+
version: number;
|
|
32
|
+
constructor(zone: TimeZone, opts?: Partial<StoreOptions>);
|
|
33
|
+
private static emptyChange;
|
|
34
|
+
get size(): number;
|
|
35
|
+
get timeZone(): TimeZone;
|
|
36
|
+
/** Re-resolves every item; used when the zone or defaults change. */
|
|
37
|
+
reconfigure(zone: TimeZone, opts?: Partial<StoreOptions>): void;
|
|
38
|
+
normalize(item: CalendarItem<D>): NormalizedItem<D> | null;
|
|
39
|
+
private insert;
|
|
40
|
+
private index;
|
|
41
|
+
private unindex;
|
|
42
|
+
/** Replaces the whole item set, touching only items whose signature changed. */
|
|
43
|
+
setAll(items: Iterable<CalendarItem<D>>): StoreChange;
|
|
44
|
+
/** Upserts items without removing others. */
|
|
45
|
+
upsert(items: Iterable<CalendarItem<D>>): StoreChange;
|
|
46
|
+
remove(ids: Iterable<string>): StoreChange;
|
|
47
|
+
private upsertOne;
|
|
48
|
+
private flush;
|
|
49
|
+
get(id: string): NormalizedItem<D> | undefined;
|
|
50
|
+
all(): IterableIterator<NormalizedItem<D>>;
|
|
51
|
+
/** Timed items touching `day`. */
|
|
52
|
+
timedOn(day: Day): NormalizedItem<D>[];
|
|
53
|
+
/** All-day items (and, if asked, multi-day timed items) overlapping [startDay, endDay). */
|
|
54
|
+
spanningIn(startDay: Day, endDay: Day, includeTimedMultiDay: boolean): NormalizedItem<D>[];
|
|
55
|
+
/** Every visible item overlapping [startDay, endDay). */
|
|
56
|
+
inRange(startDay: Day, endDay: Day): NormalizedItem<D>[];
|
|
57
|
+
}
|
|
58
|
+
/** Days spanned by an instant range in a zone, end exclusive. */
|
|
59
|
+
export declare function daysOfRange(startMs: number, endMs: number, zone: TimeZone): [Day, Day];
|
|
60
|
+
export { DAY_MS };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type Day, DAY_MS } from "./date.js";
|
|
2
|
+
import type { CalendarItem, NormalizedItem } from "./types.js";
|
|
3
|
+
import { type TimeZone } from "./zone.js";
|
|
4
|
+
export interface StoreOptions {
|
|
5
|
+
/** Length of timed items without an end, in minutes. */
|
|
6
|
+
defaultTimedMinutes: number;
|
|
7
|
+
/** Length of all-day items without an end, in days. */
|
|
8
|
+
defaultAllDayDays: number;
|
|
9
|
+
}
|
|
10
|
+
export interface StoreChange {
|
|
11
|
+
added: string[];
|
|
12
|
+
updated: string[];
|
|
13
|
+
removed: string[];
|
|
14
|
+
/** Days whose timed content changed. */
|
|
15
|
+
days: Set<Day>;
|
|
16
|
+
/** True when any all-day (or multi-day) placement changed. */
|
|
17
|
+
spans: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Items indexed by the civil days they touch. Every mutation records which days
|
|
21
|
+
* changed so layouts and DOM columns for untouched days can be reused.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ItemStore<D = unknown> {
|
|
24
|
+
private zone;
|
|
25
|
+
private opts;
|
|
26
|
+
private readonly items;
|
|
27
|
+
private readonly timedByDay;
|
|
28
|
+
private readonly spanning;
|
|
29
|
+
private pending;
|
|
30
|
+
/** Bumped on every change; cheap staleness check for caches. */
|
|
31
|
+
version: number;
|
|
32
|
+
constructor(zone: TimeZone, opts?: Partial<StoreOptions>);
|
|
33
|
+
private static emptyChange;
|
|
34
|
+
get size(): number;
|
|
35
|
+
get timeZone(): TimeZone;
|
|
36
|
+
/** Re-resolves every item; used when the zone or defaults change. */
|
|
37
|
+
reconfigure(zone: TimeZone, opts?: Partial<StoreOptions>): void;
|
|
38
|
+
normalize(item: CalendarItem<D>): NormalizedItem<D> | null;
|
|
39
|
+
private insert;
|
|
40
|
+
private index;
|
|
41
|
+
private unindex;
|
|
42
|
+
/** Replaces the whole item set, touching only items whose signature changed. */
|
|
43
|
+
setAll(items: Iterable<CalendarItem<D>>): StoreChange;
|
|
44
|
+
/** Upserts items without removing others. */
|
|
45
|
+
upsert(items: Iterable<CalendarItem<D>>): StoreChange;
|
|
46
|
+
remove(ids: Iterable<string>): StoreChange;
|
|
47
|
+
private upsertOne;
|
|
48
|
+
private flush;
|
|
49
|
+
get(id: string): NormalizedItem<D> | undefined;
|
|
50
|
+
all(): IterableIterator<NormalizedItem<D>>;
|
|
51
|
+
/** Timed items touching `day`. */
|
|
52
|
+
timedOn(day: Day): NormalizedItem<D>[];
|
|
53
|
+
/** All-day items (and, if asked, multi-day timed items) overlapping [startDay, endDay). */
|
|
54
|
+
spanningIn(startDay: Day, endDay: Day, includeTimedMultiDay: boolean): NormalizedItem<D>[];
|
|
55
|
+
/** Every visible item overlapping [startDay, endDay). */
|
|
56
|
+
inRange(startDay: Day, endDay: Day): NormalizedItem<D>[];
|
|
57
|
+
}
|
|
58
|
+
/** Days spanned by an instant range in a zone, end exclusive. */
|
|
59
|
+
export declare function daysOfRange(startMs: number, endMs: number, zone: TimeZone): [Day, Day];
|
|
60
|
+
export { DAY_MS };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Day } from "./date.cjs";
|
|
2
|
+
export type DateInput = Date | string | number;
|
|
3
|
+
/** Dependency relationship, predecessor edge then successor edge: "FS" = finish-to-start. */
|
|
4
|
+
export type DependencyType = "FS" | "SS" | "FF" | "SF";
|
|
5
|
+
export interface ItemDependency {
|
|
6
|
+
id: string;
|
|
7
|
+
/** Default "FS". */
|
|
8
|
+
type?: DependencyType;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* One thing on the calendar. `end` is exclusive, as in FullCalendar and iCal:
|
|
12
|
+
* an all-day item on the 3rd has start "2026-10-03", end "2026-10-04".
|
|
13
|
+
*/
|
|
14
|
+
export interface CalendarItem<D = unknown> {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
start: DateInput;
|
|
18
|
+
end?: DateInput | null;
|
|
19
|
+
/** Defaults to true for date-only `start` strings. */
|
|
20
|
+
allDay?: boolean;
|
|
21
|
+
/** Free-form kind used for styling and behaviour, e.g. "task", "event", "timeblock", "timeEntry". */
|
|
22
|
+
kind?: string;
|
|
23
|
+
/** Main colour. Background, border and text derive from it unless set. */
|
|
24
|
+
color?: string;
|
|
25
|
+
backgroundColor?: string;
|
|
26
|
+
borderColor?: string;
|
|
27
|
+
textColor?: string;
|
|
28
|
+
className?: string | string[];
|
|
29
|
+
/** Overrides the calendar-wide `editable` option. */
|
|
30
|
+
editable?: boolean;
|
|
31
|
+
startEditable?: boolean;
|
|
32
|
+
durationEditable?: boolean;
|
|
33
|
+
/** "background" items shade their time range without taking layout space. */
|
|
34
|
+
display?: "auto" | "background" | "none";
|
|
35
|
+
completed?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Items this one depends on. A plain id is finish-to-start; `{ id, type }`
|
|
38
|
+
* sets the relationship ("SS": this starts no earlier than `id` starts, and so on).
|
|
39
|
+
*/
|
|
40
|
+
dependsOn?: readonly (string | ItemDependency)[];
|
|
41
|
+
/** Time-grid lane. "actual" items (tracked time) sit in a narrow side lane. */
|
|
42
|
+
lane?: "plan" | "actual";
|
|
43
|
+
/** Items sharing a groupId move together when one is dragged. */
|
|
44
|
+
groupId?: string;
|
|
45
|
+
/** Change token. When set, the store compares it instead of serialising the item. */
|
|
46
|
+
rev?: string | number;
|
|
47
|
+
data?: D;
|
|
48
|
+
}
|
|
49
|
+
/** An item resolved against the calendar's time zone. */
|
|
50
|
+
export interface NormalizedItem<D = unknown> {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly item: CalendarItem<D>;
|
|
53
|
+
readonly allDay: boolean;
|
|
54
|
+
/** Instants (ms). For all-day items these are the zone's midnights. */
|
|
55
|
+
readonly startMs: number;
|
|
56
|
+
readonly endMs: number;
|
|
57
|
+
/** Civil days, end exclusive. */
|
|
58
|
+
readonly startDay: Day;
|
|
59
|
+
readonly endDay: Day;
|
|
60
|
+
readonly sig: string;
|
|
61
|
+
}
|
|
62
|
+
export interface DateRange {
|
|
63
|
+
/** First visible day. */
|
|
64
|
+
startDay: Day;
|
|
65
|
+
/** Day after the last visible day. */
|
|
66
|
+
endDay: Day;
|
|
67
|
+
start: Date;
|
|
68
|
+
end: Date;
|
|
69
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Day } from "./date.js";
|
|
2
|
+
export type DateInput = Date | string | number;
|
|
3
|
+
/** Dependency relationship, predecessor edge then successor edge: "FS" = finish-to-start. */
|
|
4
|
+
export type DependencyType = "FS" | "SS" | "FF" | "SF";
|
|
5
|
+
export interface ItemDependency {
|
|
6
|
+
id: string;
|
|
7
|
+
/** Default "FS". */
|
|
8
|
+
type?: DependencyType;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* One thing on the calendar. `end` is exclusive, as in FullCalendar and iCal:
|
|
12
|
+
* an all-day item on the 3rd has start "2026-10-03", end "2026-10-04".
|
|
13
|
+
*/
|
|
14
|
+
export interface CalendarItem<D = unknown> {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
start: DateInput;
|
|
18
|
+
end?: DateInput | null;
|
|
19
|
+
/** Defaults to true for date-only `start` strings. */
|
|
20
|
+
allDay?: boolean;
|
|
21
|
+
/** Free-form kind used for styling and behaviour, e.g. "task", "event", "timeblock", "timeEntry". */
|
|
22
|
+
kind?: string;
|
|
23
|
+
/** Main colour. Background, border and text derive from it unless set. */
|
|
24
|
+
color?: string;
|
|
25
|
+
backgroundColor?: string;
|
|
26
|
+
borderColor?: string;
|
|
27
|
+
textColor?: string;
|
|
28
|
+
className?: string | string[];
|
|
29
|
+
/** Overrides the calendar-wide `editable` option. */
|
|
30
|
+
editable?: boolean;
|
|
31
|
+
startEditable?: boolean;
|
|
32
|
+
durationEditable?: boolean;
|
|
33
|
+
/** "background" items shade their time range without taking layout space. */
|
|
34
|
+
display?: "auto" | "background" | "none";
|
|
35
|
+
completed?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Items this one depends on. A plain id is finish-to-start; `{ id, type }`
|
|
38
|
+
* sets the relationship ("SS": this starts no earlier than `id` starts, and so on).
|
|
39
|
+
*/
|
|
40
|
+
dependsOn?: readonly (string | ItemDependency)[];
|
|
41
|
+
/** Time-grid lane. "actual" items (tracked time) sit in a narrow side lane. */
|
|
42
|
+
lane?: "plan" | "actual";
|
|
43
|
+
/** Items sharing a groupId move together when one is dragged. */
|
|
44
|
+
groupId?: string;
|
|
45
|
+
/** Change token. When set, the store compares it instead of serialising the item. */
|
|
46
|
+
rev?: string | number;
|
|
47
|
+
data?: D;
|
|
48
|
+
}
|
|
49
|
+
/** An item resolved against the calendar's time zone. */
|
|
50
|
+
export interface NormalizedItem<D = unknown> {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly item: CalendarItem<D>;
|
|
53
|
+
readonly allDay: boolean;
|
|
54
|
+
/** Instants (ms). For all-day items these are the zone's midnights. */
|
|
55
|
+
readonly startMs: number;
|
|
56
|
+
readonly endMs: number;
|
|
57
|
+
/** Civil days, end exclusive. */
|
|
58
|
+
readonly startDay: Day;
|
|
59
|
+
readonly endDay: Day;
|
|
60
|
+
readonly sig: string;
|
|
61
|
+
}
|
|
62
|
+
export interface DateRange {
|
|
63
|
+
/** First visible day. */
|
|
64
|
+
startDay: Day;
|
|
65
|
+
/** Day after the last visible day. */
|
|
66
|
+
endDay: Day;
|
|
67
|
+
start: Date;
|
|
68
|
+
end: Date;
|
|
69
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Day } from "./date.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Converts between instants (epoch ms) and wall-clock time in one zone.
|
|
4
|
+
* "local" uses the runtime's zone; any IANA name uses Intl.
|
|
5
|
+
*/
|
|
6
|
+
export interface TimeZone {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
/** UTC offset in minutes (east positive) at an instant. */
|
|
9
|
+
offsetAt(ms: number): number;
|
|
10
|
+
}
|
|
11
|
+
export interface WallTime {
|
|
12
|
+
day: Day;
|
|
13
|
+
/** Minutes since local midnight, 0..1439. */
|
|
14
|
+
minute: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function getTimeZone(id: string | undefined | null): TimeZone;
|
|
17
|
+
export declare function isValidTimeZone(id: string): boolean;
|
|
18
|
+
export declare function wallTime(ms: number, zone: TimeZone): WallTime;
|
|
19
|
+
export declare function dayOf(ms: number, zone: TimeZone): Day;
|
|
20
|
+
/**
|
|
21
|
+
* Wall-clock time to instant. Times inside a DST gap move forward by the gap
|
|
22
|
+
* (02:30 on a spring-forward day becomes 03:30); ambiguous times resolve to the
|
|
23
|
+
* earlier instant. This matches how Date resolves local times.
|
|
24
|
+
*/
|
|
25
|
+
export declare function toInstant(day: Day, minute: number, zone: TimeZone): number;
|
|
26
|
+
export declare function startOfDayInstant(day: Day, zone: TimeZone): number;
|
|
27
|
+
/** Length of a civil day in minutes (1380 or 1500 on DST change days). */
|
|
28
|
+
export declare function dayLengthMinutes(day: Day, zone: TimeZone): number;
|
|
29
|
+
export type ParsedDate = {
|
|
30
|
+
kind: "day";
|
|
31
|
+
day: Day;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "instant";
|
|
34
|
+
ms: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Parses calendar input. Date-only strings are civil days; date-times without
|
|
38
|
+
* an offset are wall-clock times in `zone`; anything with Z or an offset is an
|
|
39
|
+
* absolute instant.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseDateInput(value: Date | string | number, zone: TimeZone): ParsedDate | null;
|
|
42
|
+
/** Formats an instant as a floating local date-time ("YYYY-MM-DDTHH:mm") in `zone`. */
|
|
43
|
+
export declare function formatFloating(ms: number, zone: TimeZone): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Day } from "./date.js";
|
|
2
|
+
/**
|
|
3
|
+
* Converts between instants (epoch ms) and wall-clock time in one zone.
|
|
4
|
+
* "local" uses the runtime's zone; any IANA name uses Intl.
|
|
5
|
+
*/
|
|
6
|
+
export interface TimeZone {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
/** UTC offset in minutes (east positive) at an instant. */
|
|
9
|
+
offsetAt(ms: number): number;
|
|
10
|
+
}
|
|
11
|
+
export interface WallTime {
|
|
12
|
+
day: Day;
|
|
13
|
+
/** Minutes since local midnight, 0..1439. */
|
|
14
|
+
minute: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function getTimeZone(id: string | undefined | null): TimeZone;
|
|
17
|
+
export declare function isValidTimeZone(id: string): boolean;
|
|
18
|
+
export declare function wallTime(ms: number, zone: TimeZone): WallTime;
|
|
19
|
+
export declare function dayOf(ms: number, zone: TimeZone): Day;
|
|
20
|
+
/**
|
|
21
|
+
* Wall-clock time to instant. Times inside a DST gap move forward by the gap
|
|
22
|
+
* (02:30 on a spring-forward day becomes 03:30); ambiguous times resolve to the
|
|
23
|
+
* earlier instant. This matches how Date resolves local times.
|
|
24
|
+
*/
|
|
25
|
+
export declare function toInstant(day: Day, minute: number, zone: TimeZone): number;
|
|
26
|
+
export declare function startOfDayInstant(day: Day, zone: TimeZone): number;
|
|
27
|
+
/** Length of a civil day in minutes (1380 or 1500 on DST change days). */
|
|
28
|
+
export declare function dayLengthMinutes(day: Day, zone: TimeZone): number;
|
|
29
|
+
export type ParsedDate = {
|
|
30
|
+
kind: "day";
|
|
31
|
+
day: Day;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "instant";
|
|
34
|
+
ms: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Parses calendar input. Date-only strings are civil days; date-times without
|
|
38
|
+
* an offset are wall-clock times in `zone`; anything with Z or an offset is an
|
|
39
|
+
* absolute instant.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseDateInput(value: Date | string | number, zone: TimeZone): ParsedDate | null;
|
|
42
|
+
/** Formats an instant as a floating local date-time ("YYYY-MM-DDTHH:mm") in `zone`. */
|
|
43
|
+
export declare function formatFloating(ms: number, zone: TimeZone): string;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { type Day } from "../core/date.cjs";
|
|
2
|
+
import type { ItemOrder } from "../core/layout.cjs";
|
|
3
|
+
import { ItemStore } from "../core/store.cjs";
|
|
4
|
+
import type { CalendarItem, DateInput, NormalizedItem } from "../core/types.cjs";
|
|
5
|
+
import { type TimeZone } from "../core/zone.cjs";
|
|
6
|
+
import { type ElMeta, type ViewContext } from "./context.cjs";
|
|
7
|
+
import { Formatter } from "./format.cjs";
|
|
8
|
+
import { type CalendarOptions, type CalendarText, type DayArg, type ExternalDragSource, type Hit, type TimelineAnchor, type ViewInfo, type ViewType } from "./options.cjs";
|
|
9
|
+
/**
|
|
10
|
+
* A calendar bound to one element. Works in any document, including Obsidian
|
|
11
|
+
* popout windows: all listeners, timers and observers use the element's window.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Calendar<D = unknown> implements ViewContext<D> {
|
|
14
|
+
readonly host: HTMLElement;
|
|
15
|
+
/** The document/window the calendar currently lives in; see `adopt()`. */
|
|
16
|
+
doc: Document;
|
|
17
|
+
win: Window;
|
|
18
|
+
readonly root: HTMLElement;
|
|
19
|
+
readonly store: ItemStore<D>;
|
|
20
|
+
readonly meta: WeakMap<Element, ElMeta<D>>;
|
|
21
|
+
opts: CalendarOptions<D>;
|
|
22
|
+
zone: TimeZone;
|
|
23
|
+
fmt: Formatter;
|
|
24
|
+
text: CalendarText;
|
|
25
|
+
order: ItemOrder<D>;
|
|
26
|
+
view: ViewInfo;
|
|
27
|
+
firstDay: number;
|
|
28
|
+
days: Day[];
|
|
29
|
+
today: Day;
|
|
30
|
+
conflicts: Map<string, string[]>;
|
|
31
|
+
slot: {
|
|
32
|
+
min: number;
|
|
33
|
+
max: number;
|
|
34
|
+
duration: number;
|
|
35
|
+
snap: number;
|
|
36
|
+
scroll: number;
|
|
37
|
+
};
|
|
38
|
+
draggingId: string | null;
|
|
39
|
+
dragRow: {
|
|
40
|
+
id: string;
|
|
41
|
+
rowId: string;
|
|
42
|
+
} | null;
|
|
43
|
+
private readonly toolbarEl;
|
|
44
|
+
private readonly viewHost;
|
|
45
|
+
private current;
|
|
46
|
+
private anchor;
|
|
47
|
+
private session;
|
|
48
|
+
private selection;
|
|
49
|
+
private suppressClick;
|
|
50
|
+
private selectedIds;
|
|
51
|
+
private popover;
|
|
52
|
+
private timer;
|
|
53
|
+
private resizeObs;
|
|
54
|
+
private destroyed;
|
|
55
|
+
private pendingChange;
|
|
56
|
+
private frame;
|
|
57
|
+
constructor(host: HTMLElement, options?: Partial<CalendarOptions<D>>, items?: Iterable<CalendarItem<D>>);
|
|
58
|
+
private observeResize;
|
|
59
|
+
private readonly onAdoptCheck;
|
|
60
|
+
/**
|
|
61
|
+
* Hosts may move a live calendar into another window (e.g. Obsidian's
|
|
62
|
+
* "Move to new window"). Listeners, timers and observers belong to a window,
|
|
63
|
+
* so rebind them to the root's current document and rebuild the view there.
|
|
64
|
+
*/
|
|
65
|
+
adopt(): void;
|
|
66
|
+
/** Replaces all items, re-rendering only what changed. */
|
|
67
|
+
setItems(items: Iterable<CalendarItem<D>>): void;
|
|
68
|
+
upsertItems(items: Iterable<CalendarItem<D>>): void;
|
|
69
|
+
removeItems(ids: Iterable<string>): void;
|
|
70
|
+
getItem(id: string): CalendarItem<D> | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Updates options. Only what the change affects is rebuilt.
|
|
73
|
+
*
|
|
74
|
+
* In the timeline, a new `date`, range or scale keeps what is on screen
|
|
75
|
+
* instead of jumping back to the anchor date: a scale change keeps the
|
|
76
|
+
* instant at the middle of the visible track, other changes keep the left
|
|
77
|
+
* edge. Pass `anchor` to pin a different instant, e.g. the one under the
|
|
78
|
+
* pointer when zooming with the wheel.
|
|
79
|
+
*/
|
|
80
|
+
setOptions(next: Partial<CalendarOptions<D>>, how?: {
|
|
81
|
+
anchor?: TimelineAnchor;
|
|
82
|
+
}): void;
|
|
83
|
+
/** What a timeline rebuild should keep on screen. */
|
|
84
|
+
private timelineKeep;
|
|
85
|
+
/** Re-renders everything, e.g. after render hooks start returning different content. */
|
|
86
|
+
rerender(): void;
|
|
87
|
+
getOptions(): Readonly<CalendarOptions<D>>;
|
|
88
|
+
changeView(view: ViewType, date?: DateInput): void;
|
|
89
|
+
gotoDate(date: DateInput): void;
|
|
90
|
+
prev(): void;
|
|
91
|
+
next(): void;
|
|
92
|
+
goToday(): void;
|
|
93
|
+
/** The anchor date, as local midnight of that civil day in the calendar zone. */
|
|
94
|
+
getDate(): Date;
|
|
95
|
+
getView(): ViewInfo;
|
|
96
|
+
/** Clears the current selection. */
|
|
97
|
+
unselect(): void;
|
|
98
|
+
/** Re-measures (e.g. after the host was shown or resized by a parent). */
|
|
99
|
+
updateSize(): void;
|
|
100
|
+
getScroll(): {
|
|
101
|
+
top: number;
|
|
102
|
+
left: number;
|
|
103
|
+
};
|
|
104
|
+
setScroll(s: {
|
|
105
|
+
top: number;
|
|
106
|
+
left: number;
|
|
107
|
+
}): void;
|
|
108
|
+
scrollToTime(time: string | number): void;
|
|
109
|
+
/**
|
|
110
|
+
* Scrolls a date (or instant) into view. In the timeline `align` places it
|
|
111
|
+
* at the start, middle or end of the visible track; other views move to
|
|
112
|
+
* the date if it is outside the range, and time grids scroll to its time.
|
|
113
|
+
*/
|
|
114
|
+
scrollToDate(date: DateInput, opts?: {
|
|
115
|
+
align?: "start" | "center" | "end";
|
|
116
|
+
smooth?: boolean;
|
|
117
|
+
}): void;
|
|
118
|
+
/** What is under a client point: a time slot, a day, or a timeline row and date. Null outside the view. */
|
|
119
|
+
hitTest(clientX: number, clientY: number): Hit | null;
|
|
120
|
+
/** Item elements currently rendered for an id. */
|
|
121
|
+
elementsFor(id: string): HTMLElement[];
|
|
122
|
+
/**
|
|
123
|
+
* Starts dragging something from outside the calendar (a task tray, an
|
|
124
|
+
* unscheduled list). Call from a pointerdown handler; the calendar follows
|
|
125
|
+
* the pointer and fires `onExternalDrop` on release.
|
|
126
|
+
*/
|
|
127
|
+
beginExternalDrag(ev: PointerEvent, source: ExternalDragSource<D>): void;
|
|
128
|
+
destroy(): void;
|
|
129
|
+
now(): number;
|
|
130
|
+
suppressNextClick(): void;
|
|
131
|
+
dayArg(day: Day): DayArg;
|
|
132
|
+
dayClasses(day: Day): string[];
|
|
133
|
+
isEditable(n: NormalizedItem<D>, what: "start" | "duration"): boolean;
|
|
134
|
+
navLink(day: Day, label: Node | string, cls: string): HTMLElement;
|
|
135
|
+
navigate(day: Day, jsEvent: MouseEvent | KeyboardEvent, view?: "day" | "month"): void;
|
|
136
|
+
createItemEl(meta: ElMeta<D>): HTMLElement;
|
|
137
|
+
refreshItemEl(el: HTMLElement, meta: ElMeta<D>): void;
|
|
138
|
+
private selectedSet;
|
|
139
|
+
private applySelected;
|
|
140
|
+
destroyItemEl(el: HTMLElement): void;
|
|
141
|
+
openMore(day: Day, anchor: HTMLElement, jsEvent: MouseEvent | KeyboardEvent): void;
|
|
142
|
+
private resolveText;
|
|
143
|
+
private todayDay;
|
|
144
|
+
private toDay;
|
|
145
|
+
private toMs;
|
|
146
|
+
private step;
|
|
147
|
+
private computeRange;
|
|
148
|
+
private computeTitle;
|
|
149
|
+
private rebuild;
|
|
150
|
+
private createView;
|
|
151
|
+
private renderToolbar;
|
|
152
|
+
private applyChange;
|
|
153
|
+
/** Renders pending changes now. Called automatically on the next frame. */
|
|
154
|
+
flush(): void;
|
|
155
|
+
private merge;
|
|
156
|
+
private scheduleTick;
|
|
157
|
+
private itemArg;
|
|
158
|
+
/** Start and (for timed and list items) end labels; the end is hidden on short items by CSS. */
|
|
159
|
+
private timeLabel;
|
|
160
|
+
private fillTime;
|
|
161
|
+
private ariaLabel;
|
|
162
|
+
private defaultContent;
|
|
163
|
+
private addHandles;
|
|
164
|
+
/** Start and finish link handles (a milestone has one). */
|
|
165
|
+
private addPorts;
|
|
166
|
+
private refreshMore;
|
|
167
|
+
private closeMore;
|
|
168
|
+
private bindEvents;
|
|
169
|
+
private unbindEvents;
|
|
170
|
+
private listenDoc;
|
|
171
|
+
private itemElFrom;
|
|
172
|
+
private readonly onDocPointerDown;
|
|
173
|
+
private readonly onPointerDown;
|
|
174
|
+
private activate;
|
|
175
|
+
private readonly onTouchMove;
|
|
176
|
+
private readonly onPointerMove;
|
|
177
|
+
private readonly onPointerUp;
|
|
178
|
+
private readonly onPointerCancel;
|
|
179
|
+
private readonly onDocKeyDown;
|
|
180
|
+
/** Proposal for the current pointer position. */
|
|
181
|
+
private track;
|
|
182
|
+
private trackExternal;
|
|
183
|
+
private externalItem;
|
|
184
|
+
private timelineSnap;
|
|
185
|
+
private civil;
|
|
186
|
+
private civilToMs;
|
|
187
|
+
private hitCivil;
|
|
188
|
+
private dayProposal;
|
|
189
|
+
private timedProposal;
|
|
190
|
+
private moveProposal;
|
|
191
|
+
private resizeProposal;
|
|
192
|
+
private withProposal;
|
|
193
|
+
private selectionFor;
|
|
194
|
+
private clickSlot;
|
|
195
|
+
private fireSelect;
|
|
196
|
+
private unselectSilently;
|
|
197
|
+
/** Ends the current pointer session, committing unless `cancel`. */
|
|
198
|
+
private endSession;
|
|
199
|
+
private commit;
|
|
200
|
+
/** Runs in the capture phase so the click ending a drag never reaches content inside the calendar, React portals included. */
|
|
201
|
+
private readonly onClickCapture;
|
|
202
|
+
private readonly onClick;
|
|
203
|
+
private readonly onDblClick;
|
|
204
|
+
private readonly onContextMenu;
|
|
205
|
+
private hoverId;
|
|
206
|
+
private readonly onMouseOver;
|
|
207
|
+
private readonly onMouseOut;
|
|
208
|
+
private leaveHover;
|
|
209
|
+
private readonly onKeyDown;
|
|
210
|
+
/** Alt+Arrow moves an item; Alt+Shift+Arrow changes its end; arrows on a focused grip move that edge. */
|
|
211
|
+
private nudge;
|
|
212
|
+
private readonly onNativeDragOver;
|
|
213
|
+
private readonly onNativeDragLeave;
|
|
214
|
+
private readonly onNativeDrop;
|
|
215
|
+
}
|