gantt-renderer 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -1
- package/README.md +3 -4
- package/dist/index.d.mts +107 -192
- package/dist/index.mjs +191 -327
- package/dist/index.mjs.map +1 -1
- package/dist/locale-BQKHOEOx.d.mts +66 -0
- package/dist/locales/all.d.mts +7 -0
- package/dist/locales/all.mjs +95 -0
- package/dist/locales/all.mjs.map +1 -0
- package/dist/locales/ar.d.mts +7 -0
- package/dist/locales/ar.mjs +21 -0
- package/dist/locales/ar.mjs.map +1 -0
- package/dist/locales/be.d.mts +7 -0
- package/dist/locales/be.mjs +21 -0
- package/dist/locales/be.mjs.map +1 -0
- package/dist/locales/bg.d.mts +7 -0
- package/dist/locales/bg.mjs +21 -0
- package/dist/locales/bg.mjs.map +1 -0
- package/dist/locales/ca.d.mts +7 -0
- package/dist/locales/ca.mjs +21 -0
- package/dist/locales/ca.mjs.map +1 -0
- package/dist/locales/cs.d.mts +7 -0
- package/dist/locales/cs.mjs +21 -0
- package/dist/locales/cs.mjs.map +1 -0
- package/dist/locales/cy.d.mts +7 -0
- package/dist/locales/cy.mjs +21 -0
- package/dist/locales/cy.mjs.map +1 -0
- package/dist/locales/da.d.mts +7 -0
- package/dist/locales/da.mjs +21 -0
- package/dist/locales/da.mjs.map +1 -0
- package/dist/locales/de.d.mts +7 -0
- package/dist/locales/de.mjs +21 -0
- package/dist/locales/de.mjs.map +1 -0
- package/dist/locales/el.d.mts +7 -0
- package/dist/locales/el.mjs +21 -0
- package/dist/locales/el.mjs.map +1 -0
- package/dist/locales/en.d.mts +7 -0
- package/dist/locales/en.mjs +21 -0
- package/dist/locales/en.mjs.map +1 -0
- package/dist/locales/es.d.mts +7 -0
- package/dist/locales/es.mjs +21 -0
- package/dist/locales/es.mjs.map +1 -0
- package/dist/locales/et.d.mts +7 -0
- package/dist/locales/et.mjs +21 -0
- package/dist/locales/et.mjs.map +1 -0
- package/dist/locales/eu.d.mts +7 -0
- package/dist/locales/eu.mjs +21 -0
- package/dist/locales/eu.mjs.map +1 -0
- package/dist/locales/fi.d.mts +7 -0
- package/dist/locales/fi.mjs +21 -0
- package/dist/locales/fi.mjs.map +1 -0
- package/dist/locales/fr.d.mts +7 -0
- package/dist/locales/fr.mjs +21 -0
- package/dist/locales/fr.mjs.map +1 -0
- package/dist/locales/ga.d.mts +7 -0
- package/dist/locales/ga.mjs +21 -0
- package/dist/locales/ga.mjs.map +1 -0
- package/dist/locales/hi.d.mts +7 -0
- package/dist/locales/hi.mjs +21 -0
- package/dist/locales/hi.mjs.map +1 -0
- package/dist/locales/hr.d.mts +7 -0
- package/dist/locales/hr.mjs +21 -0
- package/dist/locales/hr.mjs.map +1 -0
- package/dist/locales/hu.d.mts +7 -0
- package/dist/locales/hu.mjs +21 -0
- package/dist/locales/hu.mjs.map +1 -0
- package/dist/locales/id.d.mts +7 -0
- package/dist/locales/id.mjs +21 -0
- package/dist/locales/id.mjs.map +1 -0
- package/dist/locales/it.d.mts +7 -0
- package/dist/locales/it.mjs +21 -0
- package/dist/locales/it.mjs.map +1 -0
- package/dist/locales/ja.d.mts +7 -0
- package/dist/locales/ja.mjs +21 -0
- package/dist/locales/ja.mjs.map +1 -0
- package/dist/locales/ko.d.mts +7 -0
- package/dist/locales/ko.mjs +21 -0
- package/dist/locales/ko.mjs.map +1 -0
- package/dist/locales/load.d.mts +8 -0
- package/dist/locales/load.mjs +57 -0
- package/dist/locales/load.mjs.map +1 -0
- package/dist/locales/lt.d.mts +7 -0
- package/dist/locales/lt.mjs +21 -0
- package/dist/locales/lt.mjs.map +1 -0
- package/dist/locales/lv.d.mts +7 -0
- package/dist/locales/lv.mjs +21 -0
- package/dist/locales/lv.mjs.map +1 -0
- package/dist/locales/mk.d.mts +7 -0
- package/dist/locales/mk.mjs +21 -0
- package/dist/locales/mk.mjs.map +1 -0
- package/dist/locales/mt.d.mts +7 -0
- package/dist/locales/mt.mjs +21 -0
- package/dist/locales/mt.mjs.map +1 -0
- package/dist/locales/nb.d.mts +7 -0
- package/dist/locales/nb.mjs +21 -0
- package/dist/locales/nb.mjs.map +1 -0
- package/dist/locales/nl.d.mts +7 -0
- package/dist/locales/nl.mjs +21 -0
- package/dist/locales/nl.mjs.map +1 -0
- package/dist/locales/pl.d.mts +7 -0
- package/dist/locales/pl.mjs +21 -0
- package/dist/locales/pl.mjs.map +1 -0
- package/dist/locales/pt-BR.d.mts +7 -0
- package/dist/locales/pt-BR.mjs +21 -0
- package/dist/locales/pt-BR.mjs.map +1 -0
- package/dist/locales/pt-PT.d.mts +7 -0
- package/dist/locales/pt-PT.mjs +21 -0
- package/dist/locales/pt-PT.mjs.map +1 -0
- package/dist/locales/registry.d.mts +9 -0
- package/dist/locales/registry.mjs +15 -0
- package/dist/locales/registry.mjs.map +1 -0
- package/dist/locales/ro.d.mts +7 -0
- package/dist/locales/ro.mjs +21 -0
- package/dist/locales/ro.mjs.map +1 -0
- package/dist/locales/ru.d.mts +7 -0
- package/dist/locales/ru.mjs +21 -0
- package/dist/locales/ru.mjs.map +1 -0
- package/dist/locales/sk.d.mts +7 -0
- package/dist/locales/sk.mjs +21 -0
- package/dist/locales/sk.mjs.map +1 -0
- package/dist/locales/sl.d.mts +7 -0
- package/dist/locales/sl.mjs +21 -0
- package/dist/locales/sl.mjs.map +1 -0
- package/dist/locales/sq.d.mts +7 -0
- package/dist/locales/sq.mjs +21 -0
- package/dist/locales/sq.mjs.map +1 -0
- package/dist/locales/sr.d.mts +7 -0
- package/dist/locales/sr.mjs +21 -0
- package/dist/locales/sr.mjs.map +1 -0
- package/dist/locales/sv.d.mts +7 -0
- package/dist/locales/sv.mjs +21 -0
- package/dist/locales/sv.mjs.map +1 -0
- package/dist/locales/th.d.mts +7 -0
- package/dist/locales/th.mjs +21 -0
- package/dist/locales/th.mjs.map +1 -0
- package/dist/locales/tr.d.mts +7 -0
- package/dist/locales/tr.mjs +21 -0
- package/dist/locales/tr.mjs.map +1 -0
- package/dist/locales/uk.d.mts +7 -0
- package/dist/locales/uk.mjs +21 -0
- package/dist/locales/uk.mjs.map +1 -0
- package/dist/locales/zh-Hans.d.mts +7 -0
- package/dist/locales/zh-Hans.mjs +21 -0
- package/dist/locales/zh-Hans.mjs.map +1 -0
- package/dist/locales/zh-Hant.d.mts +7 -0
- package/dist/locales/zh-Hant.mjs +21 -0
- package/dist/locales/zh-Hant.mjs.map +1 -0
- package/package.json +201 -15
package/dist/index.mjs
CHANGED
|
@@ -1,174 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
//#region src/lib/validation/schemas.ts
|
|
3
|
-
const LinkTypeSchema = z.enum([
|
|
4
|
-
"FS",
|
|
5
|
-
"SS",
|
|
6
|
-
"FF",
|
|
7
|
-
"SF"
|
|
8
|
-
]);
|
|
9
|
-
const TaskKindSchema = z.enum([
|
|
10
|
-
"task",
|
|
11
|
-
"project",
|
|
12
|
-
"milestone"
|
|
13
|
-
]);
|
|
14
|
-
const SpecialDayKindSchema = z.enum(["holiday", "custom"]);
|
|
15
|
-
const SpecialDaySchema = z.object({
|
|
16
|
-
/** ISO date: YYYY-MM-DD */
|
|
17
|
-
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
18
|
-
kind: SpecialDayKindSchema,
|
|
19
|
-
label: z.string().min(1).optional(),
|
|
20
|
-
className: z.string().min(1).optional()
|
|
21
|
-
});
|
|
22
|
-
const taskBase = {
|
|
23
|
-
/** Unique positive integer identifier for the task. */
|
|
24
|
-
id: z.number().int().positive(),
|
|
25
|
-
/** Display name / label of the task. */
|
|
26
|
-
text: z.string().min(1),
|
|
27
|
-
/** ISO date: YYYY-MM-DD */
|
|
28
|
-
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
29
|
-
/** Optional id of the parent task. When set, this task is a child in the hierarchy. */
|
|
30
|
-
parent: z.number().int().positive().optional(),
|
|
31
|
-
/** Optional CSS color value for the task bar. Overrides the default color assignment. */
|
|
32
|
-
color: z.string().optional(),
|
|
33
|
-
/** When `true`, the task bar cannot be dragged or resized. */
|
|
34
|
-
readonly: z.boolean().optional(),
|
|
35
|
-
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
36
|
-
data: z.record(z.string(), z.unknown()).optional()
|
|
37
|
-
};
|
|
38
|
-
/** @internal */
|
|
39
|
-
const TaskLeafSchema = z.object({
|
|
40
|
-
...taskBase,
|
|
41
|
-
kind: z.literal("task"),
|
|
42
|
-
/** ISO date: YYYY-MM-DD */
|
|
43
|
-
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
44
|
-
/** 0–100 completion percentage (integer). */
|
|
45
|
-
percentComplete: z.number().int().min(0).max(100).default(0)
|
|
46
|
-
}).refine((t) => t.endDate >= t.startDate, {
|
|
47
|
-
message: "endDate must be on or after startDate",
|
|
48
|
-
path: ["endDate"]
|
|
49
|
-
});
|
|
50
|
-
/** @internal */
|
|
51
|
-
const TaskProjectSchema = z.object({
|
|
52
|
-
...taskBase,
|
|
53
|
-
kind: z.literal("project"),
|
|
54
|
-
/** ISO date: YYYY-MM-DD */
|
|
55
|
-
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
56
|
-
/** 0–100 completion percentage (integer). */
|
|
57
|
-
percentComplete: z.number().int().min(0).max(100).default(0),
|
|
58
|
-
/**
|
|
59
|
-
* Initial expanded state for tree hierarchy.
|
|
60
|
-
* When `false`, children of this task are hidden on initial render.
|
|
61
|
-
*
|
|
62
|
-
* @default true
|
|
63
|
-
*/
|
|
64
|
-
open: z.boolean().default(true)
|
|
65
|
-
}).refine((t) => t.endDate >= t.startDate, {
|
|
66
|
-
message: "endDate must be on or after startDate",
|
|
67
|
-
path: ["endDate"]
|
|
68
|
-
});
|
|
69
|
-
/** @internal */
|
|
70
|
-
const TaskMilestoneSchema = z.object({
|
|
71
|
-
...taskBase,
|
|
72
|
-
kind: z.literal("milestone")
|
|
73
|
-
});
|
|
74
|
-
const TaskSchema = z.discriminatedUnion("kind", [
|
|
75
|
-
TaskLeafSchema,
|
|
76
|
-
TaskProjectSchema,
|
|
77
|
-
TaskMilestoneSchema
|
|
78
|
-
]);
|
|
79
|
-
const LinkSchema = z.object({
|
|
80
|
-
/** Unique positive integer identifier for the dependency link. */
|
|
81
|
-
id: z.number().int().positive(),
|
|
82
|
-
/** The `id` of the predecessor task (the task that drives the dependency). */
|
|
83
|
-
source: z.number().int().positive(),
|
|
84
|
-
/** The `id` of the successor task (the task that depends on the predecessor). */
|
|
85
|
-
target: z.number().int().positive(),
|
|
86
|
-
/**
|
|
87
|
-
* Dependency type.
|
|
88
|
-
*
|
|
89
|
-
* - `'FS'` — Finish-to-start: successor starts after predecessor finishes.
|
|
90
|
-
* - `'SS'` — Start-to-start: successor starts at the same time as predecessor.
|
|
91
|
-
* - `'FF'` — Finish-to-finish: successor finishes at the same time as predecessor.
|
|
92
|
-
* - `'SF'` — Start-to-finish: successor finishes after predecessor starts.
|
|
93
|
-
*
|
|
94
|
-
* @default 'FS'
|
|
95
|
-
*/
|
|
96
|
-
type: LinkTypeSchema.default("FS"),
|
|
97
|
-
/** When `true`, the link cannot be modified or deleted through the UI. */
|
|
98
|
-
readonly: z.boolean().optional(),
|
|
99
|
-
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
100
|
-
data: z.record(z.string(), z.unknown()).optional()
|
|
101
|
-
}).refine((l) => l.source !== l.target, {
|
|
102
|
-
message: "A link cannot connect a task to itself",
|
|
103
|
-
path: ["target"]
|
|
104
|
-
});
|
|
105
|
-
const GanttInputSchema = z.object({
|
|
106
|
-
/** Array of task objects. At least one task is required. */
|
|
107
|
-
tasks: z.array(TaskSchema).min(1),
|
|
108
|
-
/** Optional array of dependency link objects. Defaults to empty array. */
|
|
109
|
-
links: z.array(LinkSchema).default([])
|
|
110
|
-
}).superRefine((data, ctx) => {
|
|
111
|
-
const taskIds = /* @__PURE__ */ new Set();
|
|
112
|
-
for (let i = 0; i < data.tasks.length; i++) {
|
|
113
|
-
const task = data.tasks[i];
|
|
114
|
-
if (task !== void 0) {
|
|
115
|
-
if (taskIds.has(task.id)) ctx.addIssue({
|
|
116
|
-
code: "custom",
|
|
117
|
-
message: `Duplicate task id: ${task.id}`,
|
|
118
|
-
path: [
|
|
119
|
-
"tasks",
|
|
120
|
-
i,
|
|
121
|
-
"id"
|
|
122
|
-
]
|
|
123
|
-
});
|
|
124
|
-
taskIds.add(task.id);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
const linkIds = /* @__PURE__ */ new Set();
|
|
128
|
-
for (let i = 0; i < data.links.length; i++) {
|
|
129
|
-
const link = data.links[i];
|
|
130
|
-
if (link !== void 0) {
|
|
131
|
-
if (linkIds.has(link.id)) ctx.addIssue({
|
|
132
|
-
code: "custom",
|
|
133
|
-
message: `Duplicate link id: ${link.id}`,
|
|
134
|
-
path: [
|
|
135
|
-
"links",
|
|
136
|
-
i,
|
|
137
|
-
"id"
|
|
138
|
-
]
|
|
139
|
-
});
|
|
140
|
-
linkIds.add(link.id);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const pairKeys = /* @__PURE__ */ new Set();
|
|
144
|
-
for (let i = 0; i < data.links.length; i++) {
|
|
145
|
-
const link = data.links[i];
|
|
146
|
-
if (link !== void 0) {
|
|
147
|
-
const key = `${link.source}:${link.target}`;
|
|
148
|
-
if (pairKeys.has(key)) ctx.addIssue({
|
|
149
|
-
code: "custom",
|
|
150
|
-
message: `Duplicate link pair: source=${link.source} target=${link.target}`,
|
|
151
|
-
path: ["links", i]
|
|
152
|
-
});
|
|
153
|
-
pairKeys.add(key);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
/**
|
|
158
|
-
* Parses raw external data.
|
|
159
|
-
*
|
|
160
|
-
* Compile-time generics enforce the `data` shape on tasks and links.
|
|
161
|
-
* Runtime validation is done by the zod schemas; the `data` field is
|
|
162
|
-
* validated as a generic object at runtime regardless of the type parameter.
|
|
163
|
-
*
|
|
164
|
-
* @param raw - The unvalidated input from the consumer.
|
|
165
|
-
* @returns The parsed and validated input with `data` typed per the type parameters.
|
|
166
|
-
* @throws {import('zod').ZodError} On schema validation failure.
|
|
167
|
-
*/
|
|
168
|
-
function parseGanttInput(raw) {
|
|
169
|
-
return GanttInputSchema.parse(raw);
|
|
170
|
-
}
|
|
171
|
-
//#endregion
|
|
172
2
|
//#region src/lib/errors.ts
|
|
173
3
|
/**
|
|
174
4
|
* Domain-specific error with a machine-readable {@link GanttErrorCode}.
|
|
@@ -189,12 +19,10 @@ var GanttError = class extends Error {
|
|
|
189
19
|
//#region src/lib/domain/tree.ts
|
|
190
20
|
function detectParentCycles(tasks) {
|
|
191
21
|
const adj = /* @__PURE__ */ new Map();
|
|
192
|
-
for (const task of tasks)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (parents !== void 0) parents.push(task.id);
|
|
197
|
-
}
|
|
22
|
+
for (const task of tasks) adj.set(task.id, []);
|
|
23
|
+
for (const task of tasks) if (task.parent !== void 0) {
|
|
24
|
+
const parents = adj.get(task.parent);
|
|
25
|
+
if (parents !== void 0) parents.push(task.id);
|
|
198
26
|
}
|
|
199
27
|
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
200
28
|
const color = /* @__PURE__ */ new Map();
|
|
@@ -481,141 +309,6 @@ const EN_US_LABELS = {
|
|
|
481
309
|
columnDuration: "Duration",
|
|
482
310
|
columnQuarter: "Q"
|
|
483
311
|
};
|
|
484
|
-
const CHART_LOCALE_EN_US = {
|
|
485
|
-
code: "en-US",
|
|
486
|
-
labels: EN_US_LABELS,
|
|
487
|
-
weekStartsOn: 0,
|
|
488
|
-
weekNumbering: "iso",
|
|
489
|
-
weekendDays: [0, 6]
|
|
490
|
-
};
|
|
491
|
-
const CHART_LOCALE_EN_GB = {
|
|
492
|
-
code: "en-GB",
|
|
493
|
-
labels: {
|
|
494
|
-
ariaTask: "Task {0}",
|
|
495
|
-
ariaMilestone: "Milestone {0}",
|
|
496
|
-
addSubtaskTitle: "Add subtask",
|
|
497
|
-
columnTaskName: "Task name",
|
|
498
|
-
columnStartDate: "Start",
|
|
499
|
-
columnEndDate: "End",
|
|
500
|
-
columnDuration: "Duration",
|
|
501
|
-
columnQuarter: "Q"
|
|
502
|
-
},
|
|
503
|
-
weekStartsOn: 1,
|
|
504
|
-
weekNumbering: "iso",
|
|
505
|
-
weekendDays: [0, 6]
|
|
506
|
-
};
|
|
507
|
-
const CHART_LOCALE_DE_DE = {
|
|
508
|
-
code: "de-DE",
|
|
509
|
-
labels: {
|
|
510
|
-
ariaTask: "Aufgabe {0}",
|
|
511
|
-
ariaMilestone: "Meilenstein {0}",
|
|
512
|
-
addSubtaskTitle: "Teilaufgabe hinzufügen",
|
|
513
|
-
columnTaskName: "Aufgabenname",
|
|
514
|
-
columnStartDate: "Start",
|
|
515
|
-
columnEndDate: "Ende",
|
|
516
|
-
columnDuration: "Dauer",
|
|
517
|
-
columnQuarter: "Q"
|
|
518
|
-
},
|
|
519
|
-
weekStartsOn: 1,
|
|
520
|
-
weekNumbering: "iso",
|
|
521
|
-
weekendDays: [0, 6]
|
|
522
|
-
};
|
|
523
|
-
const CHART_LOCALE_FR_FR = {
|
|
524
|
-
code: "fr-FR",
|
|
525
|
-
labels: {
|
|
526
|
-
ariaTask: "Tâche {0}",
|
|
527
|
-
ariaMilestone: "Jalon {0}",
|
|
528
|
-
addSubtaskTitle: "Ajouter une sous-tâche",
|
|
529
|
-
columnTaskName: "Nom de la tâche",
|
|
530
|
-
columnStartDate: "Début",
|
|
531
|
-
columnEndDate: "Fin",
|
|
532
|
-
columnDuration: "Durée",
|
|
533
|
-
columnQuarter: "T"
|
|
534
|
-
},
|
|
535
|
-
weekStartsOn: 1,
|
|
536
|
-
weekNumbering: "iso",
|
|
537
|
-
weekendDays: [0, 6]
|
|
538
|
-
};
|
|
539
|
-
const CHART_LOCALE_ES_ES = {
|
|
540
|
-
code: "es-ES",
|
|
541
|
-
labels: {
|
|
542
|
-
ariaTask: "Tarea {0}",
|
|
543
|
-
ariaMilestone: "Hito {0}",
|
|
544
|
-
addSubtaskTitle: "Añadir subtarea",
|
|
545
|
-
columnTaskName: "Nombre de tarea",
|
|
546
|
-
columnStartDate: "Inicio",
|
|
547
|
-
columnEndDate: "Fin",
|
|
548
|
-
columnDuration: "Duración",
|
|
549
|
-
columnQuarter: "T"
|
|
550
|
-
},
|
|
551
|
-
weekStartsOn: 1,
|
|
552
|
-
weekNumbering: "iso",
|
|
553
|
-
weekendDays: [0, 6]
|
|
554
|
-
};
|
|
555
|
-
const CHART_LOCALE_IT_IT = {
|
|
556
|
-
code: "it-IT",
|
|
557
|
-
labels: {
|
|
558
|
-
ariaTask: "Attività {0}",
|
|
559
|
-
ariaMilestone: "Pietra miliare {0}",
|
|
560
|
-
addSubtaskTitle: "Aggiungi sottoattività",
|
|
561
|
-
columnTaskName: "Nome attività",
|
|
562
|
-
columnStartDate: "Inizio",
|
|
563
|
-
columnEndDate: "Fine",
|
|
564
|
-
columnDuration: "Durata",
|
|
565
|
-
columnQuarter: "T"
|
|
566
|
-
},
|
|
567
|
-
weekStartsOn: 1,
|
|
568
|
-
weekNumbering: "iso",
|
|
569
|
-
weekendDays: [0, 6]
|
|
570
|
-
};
|
|
571
|
-
const CHART_LOCALE_PT_PT = {
|
|
572
|
-
code: "pt-PT",
|
|
573
|
-
labels: {
|
|
574
|
-
ariaTask: "Tarefa {0}",
|
|
575
|
-
ariaMilestone: "Marco {0}",
|
|
576
|
-
addSubtaskTitle: "Adicionar subtarefa",
|
|
577
|
-
columnTaskName: "Nome da tarefa",
|
|
578
|
-
columnStartDate: "Início",
|
|
579
|
-
columnEndDate: "Fim",
|
|
580
|
-
columnDuration: "Duração",
|
|
581
|
-
columnQuarter: "T"
|
|
582
|
-
},
|
|
583
|
-
weekStartsOn: 1,
|
|
584
|
-
weekNumbering: "iso",
|
|
585
|
-
weekendDays: [0, 6]
|
|
586
|
-
};
|
|
587
|
-
const CHART_LOCALE_ZH_CN = {
|
|
588
|
-
code: "zh-CN",
|
|
589
|
-
labels: {
|
|
590
|
-
ariaTask: "任务 {0}",
|
|
591
|
-
ariaMilestone: "里程碑 {0}",
|
|
592
|
-
addSubtaskTitle: "添加子任务",
|
|
593
|
-
columnTaskName: "任务名称",
|
|
594
|
-
columnStartDate: "开始",
|
|
595
|
-
columnEndDate: "结束",
|
|
596
|
-
columnDuration: "工期",
|
|
597
|
-
columnQuarter: "季"
|
|
598
|
-
},
|
|
599
|
-
weekStartsOn: 1,
|
|
600
|
-
weekNumbering: "us",
|
|
601
|
-
weekendDays: [0, 6]
|
|
602
|
-
};
|
|
603
|
-
const CHART_LOCALE_JA_JP = {
|
|
604
|
-
code: "ja-JP",
|
|
605
|
-
labels: {
|
|
606
|
-
ariaTask: "タスク {0}",
|
|
607
|
-
ariaMilestone: "マイルストーン {0}",
|
|
608
|
-
addSubtaskTitle: "サブタスクを追加",
|
|
609
|
-
columnTaskName: "タスク名",
|
|
610
|
-
columnStartDate: "開始",
|
|
611
|
-
columnEndDate: "終了",
|
|
612
|
-
columnDuration: "期間",
|
|
613
|
-
columnQuarter: "Q"
|
|
614
|
-
},
|
|
615
|
-
weekStartsOn: 0,
|
|
616
|
-
weekNumbering: "us",
|
|
617
|
-
weekendDays: [0, 6]
|
|
618
|
-
};
|
|
619
312
|
function tryGetWeekInfo(code) {
|
|
620
313
|
try {
|
|
621
314
|
if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
|
|
@@ -703,7 +396,13 @@ function deriveWeekendDays(code) {
|
|
|
703
396
|
* @returns A fully resolved {@link ChartLocale} with defaults applied.
|
|
704
397
|
*/
|
|
705
398
|
function resolveChartLocale(raw) {
|
|
706
|
-
if (raw === void 0) return
|
|
399
|
+
if (raw === void 0) return {
|
|
400
|
+
code: "en",
|
|
401
|
+
labels: EN_US_LABELS,
|
|
402
|
+
weekStartsOn: 0,
|
|
403
|
+
weekNumbering: "iso",
|
|
404
|
+
weekendDays: [0, 6]
|
|
405
|
+
};
|
|
707
406
|
if (typeof raw !== "string") {
|
|
708
407
|
const locale = {
|
|
709
408
|
code: raw.code,
|
|
@@ -1447,6 +1146,164 @@ function routeLinks(links, layouts) {
|
|
|
1447
1146
|
}).filter((r) => r !== null);
|
|
1448
1147
|
}
|
|
1449
1148
|
//#endregion
|
|
1149
|
+
//#region src/lib/validation/schemas.ts
|
|
1150
|
+
const LinkTypeSchema = z.enum([
|
|
1151
|
+
"FS",
|
|
1152
|
+
"SS",
|
|
1153
|
+
"FF",
|
|
1154
|
+
"SF"
|
|
1155
|
+
]);
|
|
1156
|
+
z.enum([
|
|
1157
|
+
"task",
|
|
1158
|
+
"project",
|
|
1159
|
+
"milestone"
|
|
1160
|
+
]);
|
|
1161
|
+
const SpecialDayKindSchema = z.enum(["holiday", "custom"]);
|
|
1162
|
+
/** @internal */
|
|
1163
|
+
const SpecialDaySchema = z.object({
|
|
1164
|
+
/** ISO date: YYYY-MM-DD */
|
|
1165
|
+
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
1166
|
+
kind: SpecialDayKindSchema,
|
|
1167
|
+
label: z.string().min(1).optional(),
|
|
1168
|
+
className: z.string().min(1).optional()
|
|
1169
|
+
});
|
|
1170
|
+
const taskBase = {
|
|
1171
|
+
/** Unique positive integer identifier for the task. */
|
|
1172
|
+
id: z.number().int().positive(),
|
|
1173
|
+
/** Display name / label of the task. */
|
|
1174
|
+
text: z.string().min(1),
|
|
1175
|
+
/** ISO date: YYYY-MM-DD */
|
|
1176
|
+
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
1177
|
+
/** Optional id of the parent task. When set, this task is a child in the hierarchy. */
|
|
1178
|
+
parent: z.number().int().positive().optional(),
|
|
1179
|
+
/** Optional CSS color value for the task bar. Overrides the default color assignment. */
|
|
1180
|
+
color: z.string().optional(),
|
|
1181
|
+
/** When `true`, the task bar cannot be dragged or resized. */
|
|
1182
|
+
readonly: z.boolean().optional(),
|
|
1183
|
+
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
1184
|
+
data: z.record(z.string(), z.unknown()).optional()
|
|
1185
|
+
};
|
|
1186
|
+
/** @internal */
|
|
1187
|
+
const TaskLeafSchema = z.object({
|
|
1188
|
+
...taskBase,
|
|
1189
|
+
kind: z.literal("task"),
|
|
1190
|
+
/** ISO date: YYYY-MM-DD */
|
|
1191
|
+
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
1192
|
+
/** 0–100 completion percentage (integer). */
|
|
1193
|
+
percentComplete: z.number().int().min(0).max(100).default(0)
|
|
1194
|
+
}).refine((t) => t.endDate >= t.startDate, {
|
|
1195
|
+
message: "endDate must be on or after startDate",
|
|
1196
|
+
path: ["endDate"]
|
|
1197
|
+
});
|
|
1198
|
+
/** @internal */
|
|
1199
|
+
const TaskProjectSchema = z.object({
|
|
1200
|
+
...taskBase,
|
|
1201
|
+
kind: z.literal("project"),
|
|
1202
|
+
/** ISO date: YYYY-MM-DD */
|
|
1203
|
+
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
1204
|
+
/** 0–100 completion percentage (integer). */
|
|
1205
|
+
percentComplete: z.number().int().min(0).max(100).default(0),
|
|
1206
|
+
/**
|
|
1207
|
+
* Initial expanded state for tree hierarchy.
|
|
1208
|
+
* When `false`, children of this task are hidden on initial render.
|
|
1209
|
+
*
|
|
1210
|
+
* @default true
|
|
1211
|
+
*/
|
|
1212
|
+
open: z.boolean().default(true)
|
|
1213
|
+
}).refine((t) => t.endDate >= t.startDate, {
|
|
1214
|
+
message: "endDate must be on or after startDate",
|
|
1215
|
+
path: ["endDate"]
|
|
1216
|
+
});
|
|
1217
|
+
/** @internal */
|
|
1218
|
+
const TaskMilestoneSchema = z.object({
|
|
1219
|
+
...taskBase,
|
|
1220
|
+
kind: z.literal("milestone")
|
|
1221
|
+
});
|
|
1222
|
+
const TaskSchema = z.discriminatedUnion("kind", [
|
|
1223
|
+
TaskLeafSchema,
|
|
1224
|
+
TaskProjectSchema,
|
|
1225
|
+
TaskMilestoneSchema
|
|
1226
|
+
]);
|
|
1227
|
+
const LinkSchema = z.object({
|
|
1228
|
+
/** Unique positive integer identifier for the dependency link. */
|
|
1229
|
+
id: z.number().int().positive(),
|
|
1230
|
+
/** The `id` of the predecessor task (the task that drives the dependency). */
|
|
1231
|
+
source: z.number().int().positive(),
|
|
1232
|
+
/** The `id` of the successor task (the task that depends on the predecessor). */
|
|
1233
|
+
target: z.number().int().positive(),
|
|
1234
|
+
/**
|
|
1235
|
+
* Dependency type.
|
|
1236
|
+
*
|
|
1237
|
+
* - `'FS'` — Finish-to-start: successor starts after predecessor finishes.
|
|
1238
|
+
* - `'SS'` — Start-to-start: successor starts at the same time as predecessor.
|
|
1239
|
+
* - `'FF'` — Finish-to-finish: successor finishes at the same time as predecessor.
|
|
1240
|
+
* - `'SF'` — Start-to-finish: successor finishes after predecessor starts.
|
|
1241
|
+
*
|
|
1242
|
+
* @default 'FS'
|
|
1243
|
+
*/
|
|
1244
|
+
type: LinkTypeSchema.default("FS"),
|
|
1245
|
+
/** When `true`, the link cannot be modified or deleted through the UI. */
|
|
1246
|
+
readonly: z.boolean().optional(),
|
|
1247
|
+
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
1248
|
+
data: z.record(z.string(), z.unknown()).optional()
|
|
1249
|
+
}).refine((l) => l.source !== l.target, {
|
|
1250
|
+
message: "A link cannot connect a task to itself",
|
|
1251
|
+
path: ["target"]
|
|
1252
|
+
});
|
|
1253
|
+
/** @internal */
|
|
1254
|
+
const GanttInputSchema = z.object({
|
|
1255
|
+
/** Array of task objects. At least one task is required. */
|
|
1256
|
+
tasks: z.array(TaskSchema).min(1),
|
|
1257
|
+
/** Optional array of dependency link objects. Defaults to empty array. */
|
|
1258
|
+
links: z.array(LinkSchema).default([])
|
|
1259
|
+
}).superRefine((data, ctx) => {
|
|
1260
|
+
const taskIds = /* @__PURE__ */ new Set();
|
|
1261
|
+
for (let i = 0; i < data.tasks.length; i++) {
|
|
1262
|
+
const task = data.tasks[i];
|
|
1263
|
+
if (task !== void 0) {
|
|
1264
|
+
if (taskIds.has(task.id)) ctx.addIssue({
|
|
1265
|
+
code: "custom",
|
|
1266
|
+
message: `Duplicate task id: ${task.id}`,
|
|
1267
|
+
path: [
|
|
1268
|
+
"tasks",
|
|
1269
|
+
i,
|
|
1270
|
+
"id"
|
|
1271
|
+
]
|
|
1272
|
+
});
|
|
1273
|
+
taskIds.add(task.id);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const linkIds = /* @__PURE__ */ new Set();
|
|
1277
|
+
for (let i = 0; i < data.links.length; i++) {
|
|
1278
|
+
const link = data.links[i];
|
|
1279
|
+
if (link !== void 0) {
|
|
1280
|
+
if (linkIds.has(link.id)) ctx.addIssue({
|
|
1281
|
+
code: "custom",
|
|
1282
|
+
message: `Duplicate link id: ${link.id}`,
|
|
1283
|
+
path: [
|
|
1284
|
+
"links",
|
|
1285
|
+
i,
|
|
1286
|
+
"id"
|
|
1287
|
+
]
|
|
1288
|
+
});
|
|
1289
|
+
linkIds.add(link.id);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const pairKeys = /* @__PURE__ */ new Set();
|
|
1293
|
+
for (let i = 0; i < data.links.length; i++) {
|
|
1294
|
+
const link = data.links[i];
|
|
1295
|
+
if (link !== void 0) {
|
|
1296
|
+
const key = `${link.source}:${link.target}`;
|
|
1297
|
+
if (pairKeys.has(key)) ctx.addIssue({
|
|
1298
|
+
code: "custom",
|
|
1299
|
+
message: `Duplicate link pair: source=${link.source} target=${link.target}`,
|
|
1300
|
+
path: ["links", i]
|
|
1301
|
+
});
|
|
1302
|
+
pairKeys.add(key);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
//#endregion
|
|
1450
1307
|
//#region src/lib/vanilla/dom/helpers.ts
|
|
1451
1308
|
/**
|
|
1452
1309
|
* Batches style assignments; avoids repeated style recalculations.
|
|
@@ -2300,19 +2157,10 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2300
2157
|
window.addEventListener("pointermove", onMove);
|
|
2301
2158
|
window.addEventListener("pointerup", onUp);
|
|
2302
2159
|
}
|
|
2303
|
-
function onBarClick(event) {
|
|
2304
|
-
if (event.detail !== 2) return;
|
|
2305
|
-
cbs.onTaskDoubleClick?.({
|
|
2306
|
-
id: task.id,
|
|
2307
|
-
task: toTask(task)
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
2160
|
barEl.addEventListener("pointerdown", onBarDown);
|
|
2311
|
-
barEl.addEventListener("click", onBarClick);
|
|
2312
2161
|
resizeHandleEl.addEventListener("pointerdown", onResizeDown);
|
|
2313
2162
|
return () => {
|
|
2314
2163
|
barEl.removeEventListener("pointerdown", onBarDown);
|
|
2315
|
-
barEl.removeEventListener("click", onBarClick);
|
|
2316
2164
|
resizeHandleEl.removeEventListener("pointerdown", onResizeDown);
|
|
2317
2165
|
};
|
|
2318
2166
|
}
|
|
@@ -2925,7 +2773,7 @@ function renderRightPane(refs, state, cbs) {
|
|
|
2925
2773
|
}
|
|
2926
2774
|
const todayX = mapper.toX(/* @__PURE__ */ new Date());
|
|
2927
2775
|
const todayLineWidth = 2;
|
|
2928
|
-
if (todayX >= 0 && todayX <= totalWidth - todayLineWidth) {
|
|
2776
|
+
if (state.showTodayMarker && todayX >= 0 && todayX <= totalWidth - todayLineWidth) {
|
|
2929
2777
|
const todayLine = el("div");
|
|
2930
2778
|
todayLine.className = "gantt-today-marker";
|
|
2931
2779
|
css(todayLine, {
|
|
@@ -3122,8 +2970,9 @@ var GanttChart = class {
|
|
|
3122
2970
|
this.#taskIndex = /* @__PURE__ */ new Map();
|
|
3123
2971
|
this.#locale = resolveChartLocale(opts.locale);
|
|
3124
2972
|
this.#columns = opts.gridColumns ?? gridColumnDefaults(this.#locale);
|
|
3125
|
-
this.#leftPaneDefaultWidth = opts.leftPaneWidth ?? gridNaturalWidth(this.#columns);
|
|
3126
2973
|
this.#showAddTaskButton = opts.showAddTaskButton ?? true;
|
|
2974
|
+
this.#syncActionsColumnVisibility();
|
|
2975
|
+
this.#leftPaneDefaultWidth = opts.leftPaneWidth ?? gridNaturalWidth(this.#columns);
|
|
3127
2976
|
this.#height = opts.height ?? 500;
|
|
3128
2977
|
this.#timelineMinWidth = opts.timelineMinWidth ?? 220;
|
|
3129
2978
|
this.#weekendDays = normalizeWeekendDays(opts.weekendDays ?? this.#locale.weekendDays);
|
|
@@ -3328,7 +3177,7 @@ var GanttChart = class {
|
|
|
3328
3177
|
*/
|
|
3329
3178
|
update(newInput) {
|
|
3330
3179
|
this.#assertAlive();
|
|
3331
|
-
const input = newInput;
|
|
3180
|
+
const input = GanttInputSchema.parse(newInput);
|
|
3332
3181
|
validateLinkRefs(input.tasks, input.links);
|
|
3333
3182
|
detectCycles(input.tasks, input.links);
|
|
3334
3183
|
this.#input = structuredClone(input);
|
|
@@ -3382,11 +3231,20 @@ var GanttChart = class {
|
|
|
3382
3231
|
if (opts.weekendDays !== void 0) this.#weekendDays = normalizeWeekendDays(opts.weekendDays);
|
|
3383
3232
|
if (opts.specialDays !== void 0) this.#specialDaysByDate = buildSpecialDayIndex(opts.specialDays);
|
|
3384
3233
|
if (opts.theme !== void 0) this.#applyTheme();
|
|
3385
|
-
if (opts.showAddTaskButton !== void 0)
|
|
3234
|
+
if (opts.showAddTaskButton !== void 0) {
|
|
3235
|
+
this.#showAddTaskButton = opts.showAddTaskButton;
|
|
3236
|
+
this.#syncActionsColumnVisibility();
|
|
3237
|
+
const naturalWidth = gridNaturalWidth(this.#columns);
|
|
3238
|
+
if (naturalWidth !== this.#leftPaneDefaultWidth) {
|
|
3239
|
+
this.#leftPaneDefaultWidth = naturalWidth;
|
|
3240
|
+
columnsChanged = true;
|
|
3241
|
+
this.#rebuildLeftPaneHeader();
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3386
3244
|
const hasLayoutChange = opts.leftPaneWidth !== void 0 || opts.responsiveSplitPane !== void 0 || opts.mobileBreakpoint !== void 0 || opts.mobileLeftPaneMinWidth !== void 0 || opts.mobileLeftPaneMaxRatio !== void 0 || opts.timelineMinWidth !== void 0;
|
|
3387
3245
|
if (hasLayoutChange) this.#applyResponsivePaneStyles();
|
|
3388
3246
|
const hasLeftPaneChange = columnsChanged || opts.locale !== void 0 || opts.showAddTaskButton !== void 0;
|
|
3389
|
-
const hasRightPaneChange = opts.scale !== void 0 || opts.showWeekends !== void 0 || opts.weekendDays !== void 0 || opts.specialDays !== void 0 || opts.highlightLinkedDependenciesOnSelect !== void 0 || opts.linkCreationEnabled !== void 0 || opts.progressDragEnabled !== void 0 || opts.viewportStart !== void 0 || opts.viewportEnd !== void 0 || opts.locale !== void 0 || opts.timelineMinWidth !== void 0;
|
|
3247
|
+
const hasRightPaneChange = opts.scale !== void 0 || opts.showTodayMarker !== void 0 || opts.showWeekends !== void 0 || opts.weekendDays !== void 0 || opts.specialDays !== void 0 || opts.highlightLinkedDependenciesOnSelect !== void 0 || opts.linkCreationEnabled !== void 0 || opts.progressDragEnabled !== void 0 || opts.viewportStart !== void 0 || opts.viewportEnd !== void 0 || opts.locale !== void 0 || opts.timelineMinWidth !== void 0;
|
|
3390
3248
|
if (!(hasLeftPaneChange || hasRightPaneChange || hasLayoutChange)) return;
|
|
3391
3249
|
if (this.#rafPending && this.#rafId !== null) {
|
|
3392
3250
|
cancelAnimationFrame(this.#rafId);
|
|
@@ -3487,6 +3345,11 @@ var GanttChart = class {
|
|
|
3487
3345
|
#findTask(id) {
|
|
3488
3346
|
return this.#input?.tasks.find((t) => t.id === id);
|
|
3489
3347
|
}
|
|
3348
|
+
#syncActionsColumnVisibility() {
|
|
3349
|
+
const actionsCol = this.#columns.find((c) => c.id === "actions");
|
|
3350
|
+
if (actionsCol !== void 0) if (this.#showAddTaskButton) delete actionsCol.visible;
|
|
3351
|
+
else actionsCol.visible = false;
|
|
3352
|
+
}
|
|
3490
3353
|
#handleGridClick = (payload) => {
|
|
3491
3354
|
const now = Date.now();
|
|
3492
3355
|
const prev = this.#lastGridClick;
|
|
@@ -3559,6 +3422,7 @@ var GanttChart = class {
|
|
|
3559
3422
|
paddingTop,
|
|
3560
3423
|
paddingBottom,
|
|
3561
3424
|
showWeekends: this.#opts.showWeekends ?? true,
|
|
3425
|
+
showTodayMarker: this.#opts.showTodayMarker ?? true,
|
|
3562
3426
|
weekendDays: this.#weekendDays,
|
|
3563
3427
|
specialDaysByDate: this.#specialDaysByDate,
|
|
3564
3428
|
locale: this.#locale
|
|
@@ -3736,6 +3600,6 @@ var GanttChart = class {
|
|
|
3736
3600
|
}
|
|
3737
3601
|
};
|
|
3738
3602
|
//#endregion
|
|
3739
|
-
export { BAR_HEIGHT, BAR_Y_OFFSET,
|
|
3603
|
+
export { BAR_HEIGHT, BAR_Y_OFFSET, DEFAULT_GRID_COLUMNS, DENSITY, EN_US_LABELS, GRID_COLUMN_FR_MIN_WIDTH, GanttChart, GanttError, MILESTONE_HALF, MILESTONE_SIZE, ROW_HEIGHT, SCALE_CONFIGS, addDays, addHours, buildTaskTree, computeLayout, createPixelMapper, deriveViewport, deriveWeekNumbering, deriveWeekStartsOn, deriveWeekendDays, detectCycles, diffDays, diffHours, flattenTree, formatLabel, formatWeekNumber, gridColumnDefaults, gridNaturalWidth, gridTemplateColumns, isParent, parseDate, resolveChartLocale, routeLinks, validateLinkRefs, visibleColumns };
|
|
3740
3604
|
|
|
3741
3605
|
//# sourceMappingURL=index.mjs.map
|