gantt-renderer 0.8.0 → 0.10.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 +15 -0
  2. package/dist/index.d.mts +113 -130
  3. package/dist/index.mjs +126 -150
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/locale-B81kcgPd.d.mts +66 -0
  6. package/dist/locales/all.d.mts +7 -0
  7. package/dist/locales/all.mjs +95 -0
  8. package/dist/locales/all.mjs.map +1 -0
  9. package/dist/locales/ar.d.mts +7 -0
  10. package/dist/locales/ar.mjs +23 -0
  11. package/dist/locales/ar.mjs.map +1 -0
  12. package/dist/locales/be.d.mts +7 -0
  13. package/dist/locales/be.mjs +23 -0
  14. package/dist/locales/be.mjs.map +1 -0
  15. package/dist/locales/bg.d.mts +7 -0
  16. package/dist/locales/bg.mjs +23 -0
  17. package/dist/locales/bg.mjs.map +1 -0
  18. package/dist/locales/ca.d.mts +7 -0
  19. package/dist/locales/ca.mjs +23 -0
  20. package/dist/locales/ca.mjs.map +1 -0
  21. package/dist/locales/cs.d.mts +7 -0
  22. package/dist/locales/cs.mjs +23 -0
  23. package/dist/locales/cs.mjs.map +1 -0
  24. package/dist/locales/cy.d.mts +7 -0
  25. package/dist/locales/cy.mjs +23 -0
  26. package/dist/locales/cy.mjs.map +1 -0
  27. package/dist/locales/da.d.mts +7 -0
  28. package/dist/locales/da.mjs +23 -0
  29. package/dist/locales/da.mjs.map +1 -0
  30. package/dist/locales/de.d.mts +7 -0
  31. package/dist/locales/de.mjs +23 -0
  32. package/dist/locales/de.mjs.map +1 -0
  33. package/dist/locales/el.d.mts +7 -0
  34. package/dist/locales/el.mjs +23 -0
  35. package/dist/locales/el.mjs.map +1 -0
  36. package/dist/locales/en.d.mts +7 -0
  37. package/dist/locales/en.mjs +23 -0
  38. package/dist/locales/en.mjs.map +1 -0
  39. package/dist/locales/es.d.mts +7 -0
  40. package/dist/locales/es.mjs +23 -0
  41. package/dist/locales/es.mjs.map +1 -0
  42. package/dist/locales/et.d.mts +7 -0
  43. package/dist/locales/et.mjs +23 -0
  44. package/dist/locales/et.mjs.map +1 -0
  45. package/dist/locales/eu.d.mts +7 -0
  46. package/dist/locales/eu.mjs +23 -0
  47. package/dist/locales/eu.mjs.map +1 -0
  48. package/dist/locales/fi.d.mts +7 -0
  49. package/dist/locales/fi.mjs +23 -0
  50. package/dist/locales/fi.mjs.map +1 -0
  51. package/dist/locales/fr.d.mts +7 -0
  52. package/dist/locales/fr.mjs +23 -0
  53. package/dist/locales/fr.mjs.map +1 -0
  54. package/dist/locales/ga.d.mts +7 -0
  55. package/dist/locales/ga.mjs +23 -0
  56. package/dist/locales/ga.mjs.map +1 -0
  57. package/dist/locales/hi.d.mts +7 -0
  58. package/dist/locales/hi.mjs +23 -0
  59. package/dist/locales/hi.mjs.map +1 -0
  60. package/dist/locales/hr.d.mts +7 -0
  61. package/dist/locales/hr.mjs +23 -0
  62. package/dist/locales/hr.mjs.map +1 -0
  63. package/dist/locales/hu.d.mts +7 -0
  64. package/dist/locales/hu.mjs +23 -0
  65. package/dist/locales/hu.mjs.map +1 -0
  66. package/dist/locales/id.d.mts +7 -0
  67. package/dist/locales/id.mjs +23 -0
  68. package/dist/locales/id.mjs.map +1 -0
  69. package/dist/locales/it.d.mts +7 -0
  70. package/dist/locales/it.mjs +23 -0
  71. package/dist/locales/it.mjs.map +1 -0
  72. package/dist/locales/ja.d.mts +7 -0
  73. package/dist/locales/ja.mjs +23 -0
  74. package/dist/locales/ja.mjs.map +1 -0
  75. package/dist/locales/ko.d.mts +7 -0
  76. package/dist/locales/ko.mjs +23 -0
  77. package/dist/locales/ko.mjs.map +1 -0
  78. package/dist/locales/load.d.mts +8 -0
  79. package/dist/locales/load.mjs +57 -0
  80. package/dist/locales/load.mjs.map +1 -0
  81. package/dist/locales/lt.d.mts +7 -0
  82. package/dist/locales/lt.mjs +23 -0
  83. package/dist/locales/lt.mjs.map +1 -0
  84. package/dist/locales/lv.d.mts +7 -0
  85. package/dist/locales/lv.mjs +23 -0
  86. package/dist/locales/lv.mjs.map +1 -0
  87. package/dist/locales/mk.d.mts +7 -0
  88. package/dist/locales/mk.mjs +23 -0
  89. package/dist/locales/mk.mjs.map +1 -0
  90. package/dist/locales/mt.d.mts +7 -0
  91. package/dist/locales/mt.mjs +23 -0
  92. package/dist/locales/mt.mjs.map +1 -0
  93. package/dist/locales/nb.d.mts +7 -0
  94. package/dist/locales/nb.mjs +23 -0
  95. package/dist/locales/nb.mjs.map +1 -0
  96. package/dist/locales/nl.d.mts +7 -0
  97. package/dist/locales/nl.mjs +23 -0
  98. package/dist/locales/nl.mjs.map +1 -0
  99. package/dist/locales/pl.d.mts +7 -0
  100. package/dist/locales/pl.mjs +23 -0
  101. package/dist/locales/pl.mjs.map +1 -0
  102. package/dist/locales/pt-BR.d.mts +7 -0
  103. package/dist/locales/pt-BR.mjs +23 -0
  104. package/dist/locales/pt-BR.mjs.map +1 -0
  105. package/dist/locales/pt-PT.d.mts +7 -0
  106. package/dist/locales/pt-PT.mjs +23 -0
  107. package/dist/locales/pt-PT.mjs.map +1 -0
  108. package/dist/locales/registry.d.mts +9 -0
  109. package/dist/locales/registry.mjs +15 -0
  110. package/dist/locales/registry.mjs.map +1 -0
  111. package/dist/locales/ro.d.mts +7 -0
  112. package/dist/locales/ro.mjs +23 -0
  113. package/dist/locales/ro.mjs.map +1 -0
  114. package/dist/locales/ru.d.mts +7 -0
  115. package/dist/locales/ru.mjs +23 -0
  116. package/dist/locales/ru.mjs.map +1 -0
  117. package/dist/locales/sk.d.mts +7 -0
  118. package/dist/locales/sk.mjs +23 -0
  119. package/dist/locales/sk.mjs.map +1 -0
  120. package/dist/locales/sl.d.mts +7 -0
  121. package/dist/locales/sl.mjs +23 -0
  122. package/dist/locales/sl.mjs.map +1 -0
  123. package/dist/locales/sq.d.mts +7 -0
  124. package/dist/locales/sq.mjs +23 -0
  125. package/dist/locales/sq.mjs.map +1 -0
  126. package/dist/locales/sr.d.mts +7 -0
  127. package/dist/locales/sr.mjs +23 -0
  128. package/dist/locales/sr.mjs.map +1 -0
  129. package/dist/locales/sv.d.mts +7 -0
  130. package/dist/locales/sv.mjs +23 -0
  131. package/dist/locales/sv.mjs.map +1 -0
  132. package/dist/locales/th.d.mts +7 -0
  133. package/dist/locales/th.mjs +23 -0
  134. package/dist/locales/th.mjs.map +1 -0
  135. package/dist/locales/tr.d.mts +7 -0
  136. package/dist/locales/tr.mjs +23 -0
  137. package/dist/locales/tr.mjs.map +1 -0
  138. package/dist/locales/uk.d.mts +7 -0
  139. package/dist/locales/uk.mjs +23 -0
  140. package/dist/locales/uk.mjs.map +1 -0
  141. package/dist/locales/zh-Hans.d.mts +7 -0
  142. package/dist/locales/zh-Hans.mjs +23 -0
  143. package/dist/locales/zh-Hans.mjs.map +1 -0
  144. package/dist/locales/zh-Hant.d.mts +7 -0
  145. package/dist/locales/zh-Hant.mjs +23 -0
  146. package/dist/locales/zh-Hant.mjs.map +1 -0
  147. package/dist/styles/gantt.css +23 -0
  148. package/package.json +201 -15
package/dist/index.mjs CHANGED
@@ -19,12 +19,10 @@ var GanttError = class extends Error {
19
19
  //#region src/lib/domain/tree.ts
20
20
  function detectParentCycles(tasks) {
21
21
  const adj = /* @__PURE__ */ new Map();
22
- for (const task of tasks) {
23
- adj.set(task.id, []);
24
- if (task.parent !== void 0) {
25
- const parents = adj.get(task.parent);
26
- if (parents !== void 0) parents.push(task.id);
27
- }
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);
28
26
  }
29
27
  const WHITE = 0, GRAY = 1, BLACK = 2;
30
28
  const color = /* @__PURE__ */ new Map();
@@ -305,147 +303,14 @@ const EN_US_LABELS = {
305
303
  ariaTask: "Task {0}",
306
304
  ariaMilestone: "Milestone {0}",
307
305
  addSubtaskTitle: "Add subtask",
306
+ expandAllTitle: "Expand all",
307
+ collapseAllTitle: "Collapse all",
308
308
  columnTaskName: "Task name",
309
309
  columnStartDate: "Start",
310
310
  columnEndDate: "End",
311
311
  columnDuration: "Duration",
312
312
  columnQuarter: "Q"
313
313
  };
314
- const CHART_LOCALE_EN_US = {
315
- code: "en-US",
316
- labels: EN_US_LABELS,
317
- weekStartsOn: 0,
318
- weekNumbering: "iso",
319
- weekendDays: [0, 6]
320
- };
321
- const CHART_LOCALE_EN_GB = {
322
- code: "en-GB",
323
- labels: {
324
- ariaTask: "Task {0}",
325
- ariaMilestone: "Milestone {0}",
326
- addSubtaskTitle: "Add subtask",
327
- columnTaskName: "Task name",
328
- columnStartDate: "Start",
329
- columnEndDate: "End",
330
- columnDuration: "Duration",
331
- columnQuarter: "Q"
332
- },
333
- weekStartsOn: 1,
334
- weekNumbering: "iso",
335
- weekendDays: [0, 6]
336
- };
337
- const CHART_LOCALE_DE_DE = {
338
- code: "de-DE",
339
- labels: {
340
- ariaTask: "Aufgabe {0}",
341
- ariaMilestone: "Meilenstein {0}",
342
- addSubtaskTitle: "Teilaufgabe hinzufügen",
343
- columnTaskName: "Aufgabenname",
344
- columnStartDate: "Start",
345
- columnEndDate: "Ende",
346
- columnDuration: "Dauer",
347
- columnQuarter: "Q"
348
- },
349
- weekStartsOn: 1,
350
- weekNumbering: "iso",
351
- weekendDays: [0, 6]
352
- };
353
- const CHART_LOCALE_FR_FR = {
354
- code: "fr-FR",
355
- labels: {
356
- ariaTask: "Tâche {0}",
357
- ariaMilestone: "Jalon {0}",
358
- addSubtaskTitle: "Ajouter une sous-tâche",
359
- columnTaskName: "Nom de la tâche",
360
- columnStartDate: "Début",
361
- columnEndDate: "Fin",
362
- columnDuration: "Durée",
363
- columnQuarter: "T"
364
- },
365
- weekStartsOn: 1,
366
- weekNumbering: "iso",
367
- weekendDays: [0, 6]
368
- };
369
- const CHART_LOCALE_ES_ES = {
370
- code: "es-ES",
371
- labels: {
372
- ariaTask: "Tarea {0}",
373
- ariaMilestone: "Hito {0}",
374
- addSubtaskTitle: "Añadir subtarea",
375
- columnTaskName: "Nombre de tarea",
376
- columnStartDate: "Inicio",
377
- columnEndDate: "Fin",
378
- columnDuration: "Duración",
379
- columnQuarter: "T"
380
- },
381
- weekStartsOn: 1,
382
- weekNumbering: "iso",
383
- weekendDays: [0, 6]
384
- };
385
- const CHART_LOCALE_IT_IT = {
386
- code: "it-IT",
387
- labels: {
388
- ariaTask: "Attività {0}",
389
- ariaMilestone: "Pietra miliare {0}",
390
- addSubtaskTitle: "Aggiungi sottoattività",
391
- columnTaskName: "Nome attività",
392
- columnStartDate: "Inizio",
393
- columnEndDate: "Fine",
394
- columnDuration: "Durata",
395
- columnQuarter: "T"
396
- },
397
- weekStartsOn: 1,
398
- weekNumbering: "iso",
399
- weekendDays: [0, 6]
400
- };
401
- const CHART_LOCALE_PT_PT = {
402
- code: "pt-PT",
403
- labels: {
404
- ariaTask: "Tarefa {0}",
405
- ariaMilestone: "Marco {0}",
406
- addSubtaskTitle: "Adicionar subtarefa",
407
- columnTaskName: "Nome da tarefa",
408
- columnStartDate: "Início",
409
- columnEndDate: "Fim",
410
- columnDuration: "Duração",
411
- columnQuarter: "T"
412
- },
413
- weekStartsOn: 1,
414
- weekNumbering: "iso",
415
- weekendDays: [0, 6]
416
- };
417
- const CHART_LOCALE_ZH_CN = {
418
- code: "zh-CN",
419
- labels: {
420
- ariaTask: "任务 {0}",
421
- ariaMilestone: "里程碑 {0}",
422
- addSubtaskTitle: "添加子任务",
423
- columnTaskName: "任务名称",
424
- columnStartDate: "开始",
425
- columnEndDate: "结束",
426
- columnDuration: "工期",
427
- columnQuarter: "季"
428
- },
429
- weekStartsOn: 1,
430
- weekNumbering: "us",
431
- weekendDays: [0, 6]
432
- };
433
- const CHART_LOCALE_JA_JP = {
434
- code: "ja-JP",
435
- labels: {
436
- ariaTask: "タスク {0}",
437
- ariaMilestone: "マイルストーン {0}",
438
- addSubtaskTitle: "サブタスクを追加",
439
- columnTaskName: "タスク名",
440
- columnStartDate: "開始",
441
- columnEndDate: "終了",
442
- columnDuration: "期間",
443
- columnQuarter: "Q"
444
- },
445
- weekStartsOn: 0,
446
- weekNumbering: "us",
447
- weekendDays: [0, 6]
448
- };
449
314
  function tryGetWeekInfo(code) {
450
315
  try {
451
316
  if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
@@ -533,7 +398,13 @@ function deriveWeekendDays(code) {
533
398
  * @returns A fully resolved {@link ChartLocale} with defaults applied.
534
399
  */
535
400
  function resolveChartLocale(raw) {
536
- if (raw === void 0) return CHART_LOCALE_EN_US;
401
+ if (raw === void 0) return {
402
+ code: "en",
403
+ labels: EN_US_LABELS,
404
+ weekStartsOn: 0,
405
+ weekNumbering: "iso",
406
+ weekendDays: [0, 6]
407
+ };
537
408
  if (typeof raw !== "string") {
538
409
  const locale = {
539
410
  code: raw.code,
@@ -1915,9 +1786,10 @@ function renderLeftPane(container, state, cbs, columns, showAddTaskButton = true
1915
1786
  * Builds the header row for the left pane.
1916
1787
  *
1917
1788
  * @param columns - The grid column schema.
1789
+ * @param locale - The current chart locale.
1918
1790
  * @returns The header DOM element.
1919
1791
  */
1920
- function buildLeftPaneHeader(columns) {
1792
+ function buildLeftPaneHeader(columns, locale) {
1921
1793
  const header = el("div");
1922
1794
  css(header, {
1923
1795
  display: "grid",
@@ -1940,6 +1812,60 @@ function buildLeftPaneHeader(columns) {
1940
1812
  display: "flex",
1941
1813
  alignItems: "flex-end"
1942
1814
  });
1815
+ if (i === 0 && visible[0]?.id === "name") {
1816
+ const btnContainer = el("div");
1817
+ btnContainer.className = "gantt-header-tree-controls";
1818
+ css(btnContainer, {
1819
+ display: "flex",
1820
+ gap: "4px",
1821
+ marginRight: "6px",
1822
+ paddingBottom: "1px"
1823
+ });
1824
+ const expandBtn = el("button");
1825
+ expandBtn.className = "gantt-header-expand-btn";
1826
+ expandBtn.textContent = "+";
1827
+ expandBtn.title = locale.labels?.expandAllTitle ?? EN_US_LABELS.expandAllTitle;
1828
+ expandBtn.setAttribute("aria-label", expandBtn.title);
1829
+ css(expandBtn, {
1830
+ width: "18px",
1831
+ height: "18px",
1832
+ display: "flex",
1833
+ alignItems: "center",
1834
+ justifyContent: "center",
1835
+ background: "var(--gantt-header-bg)",
1836
+ border: "1px solid var(--gantt-border)",
1837
+ borderRadius: "3px",
1838
+ cursor: "pointer",
1839
+ color: "var(--gantt-text-secondary)",
1840
+ fontSize: "14px",
1841
+ fontWeight: "var(--gantt-font-weight-bold)",
1842
+ lineHeight: "1",
1843
+ padding: "0"
1844
+ });
1845
+ const collapseBtn = el("button");
1846
+ collapseBtn.className = "gantt-header-collapse-btn";
1847
+ collapseBtn.textContent = "−";
1848
+ collapseBtn.title = locale.labels?.collapseAllTitle ?? EN_US_LABELS.collapseAllTitle;
1849
+ collapseBtn.setAttribute("aria-label", collapseBtn.title);
1850
+ css(collapseBtn, {
1851
+ width: "18px",
1852
+ height: "18px",
1853
+ display: "flex",
1854
+ alignItems: "center",
1855
+ justifyContent: "center",
1856
+ background: "var(--gantt-header-bg)",
1857
+ border: "1px solid var(--gantt-border)",
1858
+ borderRadius: "3px",
1859
+ cursor: "pointer",
1860
+ color: "var(--gantt-text-secondary)",
1861
+ fontSize: "14px",
1862
+ fontWeight: "var(--gantt-font-weight-bold)",
1863
+ lineHeight: "1",
1864
+ padding: "0"
1865
+ });
1866
+ btnContainer.append(expandBtn, collapseBtn);
1867
+ wrapper.append(btnContainer);
1868
+ }
1943
1869
  const cell = el("span");
1944
1870
  css(cell, {
1945
1871
  fontSize: "var(--gantt-font-size-xs)",
@@ -2904,7 +2830,7 @@ function renderRightPane(refs, state, cbs) {
2904
2830
  }
2905
2831
  const todayX = mapper.toX(/* @__PURE__ */ new Date());
2906
2832
  const todayLineWidth = 2;
2907
- if (todayX >= 0 && todayX <= totalWidth - todayLineWidth) {
2833
+ if (state.showTodayMarker && todayX >= 0 && todayX <= totalWidth - todayLineWidth) {
2908
2834
  const todayLine = el("div");
2909
2835
  todayLine.className = "gantt-today-marker";
2910
2836
  css(todayLine, {
@@ -3375,7 +3301,7 @@ var GanttChart = class {
3375
3301
  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;
3376
3302
  if (hasLayoutChange) this.#applyResponsivePaneStyles();
3377
3303
  const hasLeftPaneChange = columnsChanged || opts.locale !== void 0 || opts.showAddTaskButton !== void 0;
3378
- 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;
3304
+ 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;
3379
3305
  if (!(hasLeftPaneChange || hasRightPaneChange || hasLayoutChange)) return;
3380
3306
  if (this.#rafPending && this.#rafId !== null) {
3381
3307
  cancelAnimationFrame(this.#rafId);
@@ -3418,6 +3344,7 @@ var GanttChart = class {
3418
3344
  */
3419
3345
  collapseAll() {
3420
3346
  this.#assertAlive();
3347
+ const changed = this.#buildExpandCollapseAllPayload(false);
3421
3348
  this.#expandedIds.clear();
3422
3349
  if (this.#rafPending && this.#rafId !== null) {
3423
3350
  cancelAnimationFrame(this.#rafId);
@@ -3425,6 +3352,10 @@ var GanttChart = class {
3425
3352
  this.#rafPending = false;
3426
3353
  }
3427
3354
  this.#render();
3355
+ if (changed.length > 0) this.#callbacks.onExpandCollapseAll?.({
3356
+ tasks: changed,
3357
+ instance: this
3358
+ });
3428
3359
  }
3429
3360
  /**
3430
3361
  * Expands all expandable groups in the task tree.
@@ -3433,6 +3364,7 @@ var GanttChart = class {
3433
3364
  */
3434
3365
  expandAll() {
3435
3366
  this.#assertAlive();
3367
+ const changed = this.#buildExpandCollapseAllPayload(true);
3436
3368
  this.#expandedIds.clear();
3437
3369
  if (this.#input !== null) for (const id of getExpandableTaskIds(this.#input.tasks)) this.#expandedIds.add(id);
3438
3370
  if (this.#rafPending && this.#rafId !== null) {
@@ -3441,6 +3373,23 @@ var GanttChart = class {
3441
3373
  this.#rafPending = false;
3442
3374
  }
3443
3375
  this.#render();
3376
+ if (changed.length > 0) this.#callbacks.onExpandCollapseAll?.({
3377
+ tasks: changed,
3378
+ instance: this
3379
+ });
3380
+ }
3381
+ #buildExpandCollapseAllPayload(open) {
3382
+ if (this.#input === null) return [];
3383
+ const expandableIds = getExpandableTaskIds(this.#input.tasks);
3384
+ const changed = [];
3385
+ for (const id of expandableIds) if (this.#expandedIds.has(id) !== open) {
3386
+ const task = this.#findTask(id);
3387
+ if (task !== void 0) changed.push(task.kind === "project" ? {
3388
+ ...task,
3389
+ open
3390
+ } : { ...task });
3391
+ }
3392
+ return changed;
3444
3393
  }
3445
3394
  /**
3446
3395
  * Removes the chart DOM and internal listeners, rendering the instance
@@ -3553,6 +3502,7 @@ var GanttChart = class {
3553
3502
  paddingTop,
3554
3503
  paddingBottom,
3555
3504
  showWeekends: this.#opts.showWeekends ?? true,
3505
+ showTodayMarker: this.#opts.showTodayMarker ?? true,
3556
3506
  weekendDays: this.#weekendDays,
3557
3507
  specialDaysByDate: this.#specialDaysByDate,
3558
3508
  locale: this.#locale
@@ -3576,8 +3526,20 @@ var GanttChart = class {
3576
3526
  #renderGridInternal(state) {
3577
3527
  renderLeftPane(this.#leftBody, state, {
3578
3528
  onToggle: (id) => {
3579
- if (this.#expandedIds.has(id)) this.#expandedIds.delete(id);
3580
- else this.#expandedIds.add(id);
3529
+ const expanded = !this.#expandedIds.has(id);
3530
+ if (expanded) this.#expandedIds.add(id);
3531
+ else this.#expandedIds.delete(id);
3532
+ const task = this.#findTask(id);
3533
+ if (task !== void 0) {
3534
+ const payload = task.kind === "project" ? {
3535
+ ...task,
3536
+ open: expanded
3537
+ } : { ...task };
3538
+ this.#callbacks.onExpandCollapse?.({
3539
+ task: payload,
3540
+ instance: this
3541
+ });
3542
+ }
3581
3543
  this.#scheduleRender();
3582
3544
  },
3583
3545
  onTaskClick: (id) => this.#cbs.onTaskClick?.(id),
@@ -3599,12 +3561,25 @@ var GanttChart = class {
3599
3561
  #rebuildLeftPaneHeader() {
3600
3562
  this.#columnResizeCleanup();
3601
3563
  clearChildren(this.#leftHeader);
3602
- const headerEl = buildLeftPaneHeader(this.#columns);
3564
+ const headerEl = buildLeftPaneHeader(this.#columns, this.#locale);
3565
+ this.#wireHeaderTreeControls(headerEl);
3603
3566
  this.#leftHeader.append(headerEl);
3604
3567
  this.#columnResizeCleanup = setupColumnResize(headerEl, this.#leftBody, this.#columns, (updated) => {
3605
3568
  this.#cbs.onGridColumnsChange?.(updated);
3606
3569
  });
3607
3570
  }
3571
+ #wireHeaderTreeControls(headerEl) {
3572
+ const expandBtn = headerEl.querySelector(".gantt-header-expand-btn");
3573
+ const collapseBtn = headerEl.querySelector(".gantt-header-collapse-btn");
3574
+ if (expandBtn !== null) expandBtn.addEventListener("click", (e) => {
3575
+ e.stopPropagation();
3576
+ this.expandAll();
3577
+ });
3578
+ if (collapseBtn !== null) collapseBtn.addEventListener("click", (e) => {
3579
+ e.stopPropagation();
3580
+ this.collapseAll();
3581
+ });
3582
+ }
3608
3583
  #scheduleRender() {
3609
3584
  if (this.#rafPending || this.#destroyed) return;
3610
3585
  this.#rafPending = true;
@@ -3657,7 +3632,8 @@ var GanttChart = class {
3657
3632
  zIndex: "11",
3658
3633
  background: "var(--gantt-header-bg)"
3659
3634
  });
3660
- const headerEl = buildLeftPaneHeader(this.#columns);
3635
+ const headerEl = buildLeftPaneHeader(this.#columns, this.#locale);
3636
+ this.#wireHeaderTreeControls(headerEl);
3661
3637
  leftHeader.append(headerEl);
3662
3638
  leftPane.append(leftHeader);
3663
3639
  this.#leftHeader = leftHeader;
@@ -3730,6 +3706,6 @@ var GanttChart = class {
3730
3706
  }
3731
3707
  };
3732
3708
  //#endregion
3733
- 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, 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 };
3709
+ 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 };
3734
3710
 
3735
3711
  //# sourceMappingURL=index.mjs.map