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.
Files changed (148) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/README.md +3 -4
  3. package/dist/index.d.mts +107 -192
  4. package/dist/index.mjs +191 -327
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/locale-BQKHOEOx.d.mts +66 -0
  7. package/dist/locales/all.d.mts +7 -0
  8. package/dist/locales/all.mjs +95 -0
  9. package/dist/locales/all.mjs.map +1 -0
  10. package/dist/locales/ar.d.mts +7 -0
  11. package/dist/locales/ar.mjs +21 -0
  12. package/dist/locales/ar.mjs.map +1 -0
  13. package/dist/locales/be.d.mts +7 -0
  14. package/dist/locales/be.mjs +21 -0
  15. package/dist/locales/be.mjs.map +1 -0
  16. package/dist/locales/bg.d.mts +7 -0
  17. package/dist/locales/bg.mjs +21 -0
  18. package/dist/locales/bg.mjs.map +1 -0
  19. package/dist/locales/ca.d.mts +7 -0
  20. package/dist/locales/ca.mjs +21 -0
  21. package/dist/locales/ca.mjs.map +1 -0
  22. package/dist/locales/cs.d.mts +7 -0
  23. package/dist/locales/cs.mjs +21 -0
  24. package/dist/locales/cs.mjs.map +1 -0
  25. package/dist/locales/cy.d.mts +7 -0
  26. package/dist/locales/cy.mjs +21 -0
  27. package/dist/locales/cy.mjs.map +1 -0
  28. package/dist/locales/da.d.mts +7 -0
  29. package/dist/locales/da.mjs +21 -0
  30. package/dist/locales/da.mjs.map +1 -0
  31. package/dist/locales/de.d.mts +7 -0
  32. package/dist/locales/de.mjs +21 -0
  33. package/dist/locales/de.mjs.map +1 -0
  34. package/dist/locales/el.d.mts +7 -0
  35. package/dist/locales/el.mjs +21 -0
  36. package/dist/locales/el.mjs.map +1 -0
  37. package/dist/locales/en.d.mts +7 -0
  38. package/dist/locales/en.mjs +21 -0
  39. package/dist/locales/en.mjs.map +1 -0
  40. package/dist/locales/es.d.mts +7 -0
  41. package/dist/locales/es.mjs +21 -0
  42. package/dist/locales/es.mjs.map +1 -0
  43. package/dist/locales/et.d.mts +7 -0
  44. package/dist/locales/et.mjs +21 -0
  45. package/dist/locales/et.mjs.map +1 -0
  46. package/dist/locales/eu.d.mts +7 -0
  47. package/dist/locales/eu.mjs +21 -0
  48. package/dist/locales/eu.mjs.map +1 -0
  49. package/dist/locales/fi.d.mts +7 -0
  50. package/dist/locales/fi.mjs +21 -0
  51. package/dist/locales/fi.mjs.map +1 -0
  52. package/dist/locales/fr.d.mts +7 -0
  53. package/dist/locales/fr.mjs +21 -0
  54. package/dist/locales/fr.mjs.map +1 -0
  55. package/dist/locales/ga.d.mts +7 -0
  56. package/dist/locales/ga.mjs +21 -0
  57. package/dist/locales/ga.mjs.map +1 -0
  58. package/dist/locales/hi.d.mts +7 -0
  59. package/dist/locales/hi.mjs +21 -0
  60. package/dist/locales/hi.mjs.map +1 -0
  61. package/dist/locales/hr.d.mts +7 -0
  62. package/dist/locales/hr.mjs +21 -0
  63. package/dist/locales/hr.mjs.map +1 -0
  64. package/dist/locales/hu.d.mts +7 -0
  65. package/dist/locales/hu.mjs +21 -0
  66. package/dist/locales/hu.mjs.map +1 -0
  67. package/dist/locales/id.d.mts +7 -0
  68. package/dist/locales/id.mjs +21 -0
  69. package/dist/locales/id.mjs.map +1 -0
  70. package/dist/locales/it.d.mts +7 -0
  71. package/dist/locales/it.mjs +21 -0
  72. package/dist/locales/it.mjs.map +1 -0
  73. package/dist/locales/ja.d.mts +7 -0
  74. package/dist/locales/ja.mjs +21 -0
  75. package/dist/locales/ja.mjs.map +1 -0
  76. package/dist/locales/ko.d.mts +7 -0
  77. package/dist/locales/ko.mjs +21 -0
  78. package/dist/locales/ko.mjs.map +1 -0
  79. package/dist/locales/load.d.mts +8 -0
  80. package/dist/locales/load.mjs +57 -0
  81. package/dist/locales/load.mjs.map +1 -0
  82. package/dist/locales/lt.d.mts +7 -0
  83. package/dist/locales/lt.mjs +21 -0
  84. package/dist/locales/lt.mjs.map +1 -0
  85. package/dist/locales/lv.d.mts +7 -0
  86. package/dist/locales/lv.mjs +21 -0
  87. package/dist/locales/lv.mjs.map +1 -0
  88. package/dist/locales/mk.d.mts +7 -0
  89. package/dist/locales/mk.mjs +21 -0
  90. package/dist/locales/mk.mjs.map +1 -0
  91. package/dist/locales/mt.d.mts +7 -0
  92. package/dist/locales/mt.mjs +21 -0
  93. package/dist/locales/mt.mjs.map +1 -0
  94. package/dist/locales/nb.d.mts +7 -0
  95. package/dist/locales/nb.mjs +21 -0
  96. package/dist/locales/nb.mjs.map +1 -0
  97. package/dist/locales/nl.d.mts +7 -0
  98. package/dist/locales/nl.mjs +21 -0
  99. package/dist/locales/nl.mjs.map +1 -0
  100. package/dist/locales/pl.d.mts +7 -0
  101. package/dist/locales/pl.mjs +21 -0
  102. package/dist/locales/pl.mjs.map +1 -0
  103. package/dist/locales/pt-BR.d.mts +7 -0
  104. package/dist/locales/pt-BR.mjs +21 -0
  105. package/dist/locales/pt-BR.mjs.map +1 -0
  106. package/dist/locales/pt-PT.d.mts +7 -0
  107. package/dist/locales/pt-PT.mjs +21 -0
  108. package/dist/locales/pt-PT.mjs.map +1 -0
  109. package/dist/locales/registry.d.mts +9 -0
  110. package/dist/locales/registry.mjs +15 -0
  111. package/dist/locales/registry.mjs.map +1 -0
  112. package/dist/locales/ro.d.mts +7 -0
  113. package/dist/locales/ro.mjs +21 -0
  114. package/dist/locales/ro.mjs.map +1 -0
  115. package/dist/locales/ru.d.mts +7 -0
  116. package/dist/locales/ru.mjs +21 -0
  117. package/dist/locales/ru.mjs.map +1 -0
  118. package/dist/locales/sk.d.mts +7 -0
  119. package/dist/locales/sk.mjs +21 -0
  120. package/dist/locales/sk.mjs.map +1 -0
  121. package/dist/locales/sl.d.mts +7 -0
  122. package/dist/locales/sl.mjs +21 -0
  123. package/dist/locales/sl.mjs.map +1 -0
  124. package/dist/locales/sq.d.mts +7 -0
  125. package/dist/locales/sq.mjs +21 -0
  126. package/dist/locales/sq.mjs.map +1 -0
  127. package/dist/locales/sr.d.mts +7 -0
  128. package/dist/locales/sr.mjs +21 -0
  129. package/dist/locales/sr.mjs.map +1 -0
  130. package/dist/locales/sv.d.mts +7 -0
  131. package/dist/locales/sv.mjs +21 -0
  132. package/dist/locales/sv.mjs.map +1 -0
  133. package/dist/locales/th.d.mts +7 -0
  134. package/dist/locales/th.mjs +21 -0
  135. package/dist/locales/th.mjs.map +1 -0
  136. package/dist/locales/tr.d.mts +7 -0
  137. package/dist/locales/tr.mjs +21 -0
  138. package/dist/locales/tr.mjs.map +1 -0
  139. package/dist/locales/uk.d.mts +7 -0
  140. package/dist/locales/uk.mjs +21 -0
  141. package/dist/locales/uk.mjs.map +1 -0
  142. package/dist/locales/zh-Hans.d.mts +7 -0
  143. package/dist/locales/zh-Hans.mjs +21 -0
  144. package/dist/locales/zh-Hans.mjs.map +1 -0
  145. package/dist/locales/zh-Hant.d.mts +7 -0
  146. package/dist/locales/zh-Hant.mjs +21 -0
  147. package/dist/locales/zh-Hant.mjs.map +1 -0
  148. 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
- adj.set(task.id, []);
194
- if (task.parent !== void 0) {
195
- const parents = adj.get(task.parent);
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 CHART_LOCALE_EN_US;
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) this.#showAddTaskButton = opts.showAddTaskButton;
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, CHART_LOCALE_DE_DE, CHART_LOCALE_EN_GB, CHART_LOCALE_EN_US, CHART_LOCALE_ES_ES, CHART_LOCALE_FR_FR, CHART_LOCALE_IT_IT, CHART_LOCALE_JA_JP, CHART_LOCALE_PT_PT, CHART_LOCALE_ZH_CN, DEFAULT_GRID_COLUMNS, DENSITY, EN_US_LABELS, GRID_COLUMN_FR_MIN_WIDTH, GanttChart, GanttError, GanttInputSchema, LinkSchema, LinkTypeSchema, MILESTONE_HALF, MILESTONE_SIZE, ROW_HEIGHT, SCALE_CONFIGS, SpecialDayKindSchema, SpecialDaySchema, TaskKindSchema, TaskSchema, addDays, addHours, buildTaskTree, computeLayout, createPixelMapper, deriveViewport, deriveWeekNumbering, deriveWeekStartsOn, deriveWeekendDays, detectCycles, diffDays, diffHours, flattenTree, formatLabel, formatWeekNumber, gridColumnDefaults, gridNaturalWidth, gridTemplateColumns, isParent, parseDate, parseGanttInput, resolveChartLocale, routeLinks, validateLinkRefs, visibleColumns };
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