gantt-renderer 0.4.0 → 0.6.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 +16 -0
- package/dist/index.d.mts +141 -89
- package/dist/index.mjs +209 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -39,17 +39,20 @@ const taskBase = {
|
|
|
39
39
|
const TaskLeafSchema = z.object({
|
|
40
40
|
...taskBase,
|
|
41
41
|
kind: z.literal("task"),
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/** ISO date: YYYY-MM-DD */
|
|
43
|
+
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
44
44
|
/** 0–100 completion percentage (integer). */
|
|
45
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"]
|
|
46
49
|
});
|
|
47
50
|
/** @internal */
|
|
48
51
|
const TaskProjectSchema = z.object({
|
|
49
52
|
...taskBase,
|
|
50
53
|
kind: z.literal("project"),
|
|
51
|
-
/**
|
|
52
|
-
|
|
54
|
+
/** ISO date: YYYY-MM-DD */
|
|
55
|
+
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/u, "Expected YYYY-MM-DD"),
|
|
53
56
|
/** 0–100 completion percentage (integer). */
|
|
54
57
|
percentComplete: z.number().int().min(0).max(100).default(0),
|
|
55
58
|
/**
|
|
@@ -59,6 +62,9 @@ const TaskProjectSchema = z.object({
|
|
|
59
62
|
* @default true
|
|
60
63
|
*/
|
|
61
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"]
|
|
62
68
|
});
|
|
63
69
|
/** @internal */
|
|
64
70
|
const TaskMilestoneSchema = z.object({
|
|
@@ -152,7 +158,7 @@ const GanttInputSchema = z.object({
|
|
|
152
158
|
* Parses raw external data.
|
|
153
159
|
*
|
|
154
160
|
* @param raw - The unvalidated input from the consumer.
|
|
155
|
-
* @returns The parsed and validated
|
|
161
|
+
* @returns The parsed and validated input with `data` typed as `Record<string, unknown>`.
|
|
156
162
|
* @throws {import('zod').ZodError} On schema validation failure.
|
|
157
163
|
*/
|
|
158
164
|
function parseGanttInput(raw) {
|
|
@@ -466,7 +472,8 @@ const EN_US_LABELS = {
|
|
|
466
472
|
ariaMilestone: "Milestone {0}",
|
|
467
473
|
addSubtaskTitle: "Add subtask",
|
|
468
474
|
columnTaskName: "Task name",
|
|
469
|
-
columnStartDate: "Start
|
|
475
|
+
columnStartDate: "Start",
|
|
476
|
+
columnEndDate: "End",
|
|
470
477
|
columnDuration: "Duration",
|
|
471
478
|
columnQuarter: "Q"
|
|
472
479
|
};
|
|
@@ -477,6 +484,134 @@ const CHART_LOCALE_EN_US = {
|
|
|
477
484
|
weekNumbering: "iso",
|
|
478
485
|
weekendDays: [0, 6]
|
|
479
486
|
};
|
|
487
|
+
const CHART_LOCALE_EN_GB = {
|
|
488
|
+
code: "en-GB",
|
|
489
|
+
labels: {
|
|
490
|
+
ariaTask: "Task {0}",
|
|
491
|
+
ariaMilestone: "Milestone {0}",
|
|
492
|
+
addSubtaskTitle: "Add subtask",
|
|
493
|
+
columnTaskName: "Task name",
|
|
494
|
+
columnStartDate: "Start",
|
|
495
|
+
columnEndDate: "End",
|
|
496
|
+
columnDuration: "Duration",
|
|
497
|
+
columnQuarter: "Q"
|
|
498
|
+
},
|
|
499
|
+
weekStartsOn: 1,
|
|
500
|
+
weekNumbering: "iso",
|
|
501
|
+
weekendDays: [0, 6]
|
|
502
|
+
};
|
|
503
|
+
const CHART_LOCALE_DE_DE = {
|
|
504
|
+
code: "de-DE",
|
|
505
|
+
labels: {
|
|
506
|
+
ariaTask: "Aufgabe {0}",
|
|
507
|
+
ariaMilestone: "Meilenstein {0}",
|
|
508
|
+
addSubtaskTitle: "Teilaufgabe hinzufügen",
|
|
509
|
+
columnTaskName: "Aufgabenname",
|
|
510
|
+
columnStartDate: "Start",
|
|
511
|
+
columnEndDate: "Ende",
|
|
512
|
+
columnDuration: "Dauer",
|
|
513
|
+
columnQuarter: "Q"
|
|
514
|
+
},
|
|
515
|
+
weekStartsOn: 1,
|
|
516
|
+
weekNumbering: "iso",
|
|
517
|
+
weekendDays: [0, 6]
|
|
518
|
+
};
|
|
519
|
+
const CHART_LOCALE_FR_FR = {
|
|
520
|
+
code: "fr-FR",
|
|
521
|
+
labels: {
|
|
522
|
+
ariaTask: "Tâche {0}",
|
|
523
|
+
ariaMilestone: "Jalon {0}",
|
|
524
|
+
addSubtaskTitle: "Ajouter une sous-tâche",
|
|
525
|
+
columnTaskName: "Nom de la tâche",
|
|
526
|
+
columnStartDate: "Début",
|
|
527
|
+
columnEndDate: "Fin",
|
|
528
|
+
columnDuration: "Durée",
|
|
529
|
+
columnQuarter: "T"
|
|
530
|
+
},
|
|
531
|
+
weekStartsOn: 1,
|
|
532
|
+
weekNumbering: "iso",
|
|
533
|
+
weekendDays: [0, 6]
|
|
534
|
+
};
|
|
535
|
+
const CHART_LOCALE_ES_ES = {
|
|
536
|
+
code: "es-ES",
|
|
537
|
+
labels: {
|
|
538
|
+
ariaTask: "Tarea {0}",
|
|
539
|
+
ariaMilestone: "Hito {0}",
|
|
540
|
+
addSubtaskTitle: "Añadir subtarea",
|
|
541
|
+
columnTaskName: "Nombre de tarea",
|
|
542
|
+
columnStartDate: "Inicio",
|
|
543
|
+
columnEndDate: "Fin",
|
|
544
|
+
columnDuration: "Duración",
|
|
545
|
+
columnQuarter: "T"
|
|
546
|
+
},
|
|
547
|
+
weekStartsOn: 1,
|
|
548
|
+
weekNumbering: "iso",
|
|
549
|
+
weekendDays: [0, 6]
|
|
550
|
+
};
|
|
551
|
+
const CHART_LOCALE_IT_IT = {
|
|
552
|
+
code: "it-IT",
|
|
553
|
+
labels: {
|
|
554
|
+
ariaTask: "Attività {0}",
|
|
555
|
+
ariaMilestone: "Pietra miliare {0}",
|
|
556
|
+
addSubtaskTitle: "Aggiungi sottoattività",
|
|
557
|
+
columnTaskName: "Nome attività",
|
|
558
|
+
columnStartDate: "Inizio",
|
|
559
|
+
columnEndDate: "Fine",
|
|
560
|
+
columnDuration: "Durata",
|
|
561
|
+
columnQuarter: "T"
|
|
562
|
+
},
|
|
563
|
+
weekStartsOn: 1,
|
|
564
|
+
weekNumbering: "iso",
|
|
565
|
+
weekendDays: [0, 6]
|
|
566
|
+
};
|
|
567
|
+
const CHART_LOCALE_PT_PT = {
|
|
568
|
+
code: "pt-PT",
|
|
569
|
+
labels: {
|
|
570
|
+
ariaTask: "Tarefa {0}",
|
|
571
|
+
ariaMilestone: "Marco {0}",
|
|
572
|
+
addSubtaskTitle: "Adicionar subtarefa",
|
|
573
|
+
columnTaskName: "Nome da tarefa",
|
|
574
|
+
columnStartDate: "Início",
|
|
575
|
+
columnEndDate: "Fim",
|
|
576
|
+
columnDuration: "Duração",
|
|
577
|
+
columnQuarter: "T"
|
|
578
|
+
},
|
|
579
|
+
weekStartsOn: 1,
|
|
580
|
+
weekNumbering: "iso",
|
|
581
|
+
weekendDays: [0, 6]
|
|
582
|
+
};
|
|
583
|
+
const CHART_LOCALE_ZH_CN = {
|
|
584
|
+
code: "zh-CN",
|
|
585
|
+
labels: {
|
|
586
|
+
ariaTask: "任务 {0}",
|
|
587
|
+
ariaMilestone: "里程碑 {0}",
|
|
588
|
+
addSubtaskTitle: "添加子任务",
|
|
589
|
+
columnTaskName: "任务名称",
|
|
590
|
+
columnStartDate: "开始",
|
|
591
|
+
columnEndDate: "结束",
|
|
592
|
+
columnDuration: "工期",
|
|
593
|
+
columnQuarter: "季"
|
|
594
|
+
},
|
|
595
|
+
weekStartsOn: 1,
|
|
596
|
+
weekNumbering: "us",
|
|
597
|
+
weekendDays: [0, 6]
|
|
598
|
+
};
|
|
599
|
+
const CHART_LOCALE_JA_JP = {
|
|
600
|
+
code: "ja-JP",
|
|
601
|
+
labels: {
|
|
602
|
+
ariaTask: "タスク {0}",
|
|
603
|
+
ariaMilestone: "マイルストーン {0}",
|
|
604
|
+
addSubtaskTitle: "サブタスクを追加",
|
|
605
|
+
columnTaskName: "タスク名",
|
|
606
|
+
columnStartDate: "開始",
|
|
607
|
+
columnEndDate: "終了",
|
|
608
|
+
columnDuration: "期間",
|
|
609
|
+
columnQuarter: "Q"
|
|
610
|
+
},
|
|
611
|
+
weekStartsOn: 0,
|
|
612
|
+
weekNumbering: "us",
|
|
613
|
+
weekendDays: [0, 6]
|
|
614
|
+
};
|
|
480
615
|
function tryGetWeekInfo(code) {
|
|
481
616
|
try {
|
|
482
617
|
if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
|
|
@@ -759,6 +894,16 @@ function formatUpperLabel(date, scale, locale) {
|
|
|
759
894
|
}
|
|
760
895
|
}
|
|
761
896
|
/**
|
|
897
|
+
* Returns the number of days in an inclusive range from `start` to `end`.
|
|
898
|
+
*
|
|
899
|
+
* @param start - The start date.
|
|
900
|
+
* @param end - The end date.
|
|
901
|
+
* @returns The number of days, inclusive.
|
|
902
|
+
*/
|
|
903
|
+
function getRangeDays(start, end) {
|
|
904
|
+
return Math.round(diffDays(start, end)) + 1;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
762
907
|
* Formats a `YYYY-MM-DD` string for display in the grid.
|
|
763
908
|
*
|
|
764
909
|
* @param dateStr - An ISO-8601 date string in `YYYY-MM-DD` format.
|
|
@@ -871,7 +1016,7 @@ function createPixelMapper(scale, viewportStart) {
|
|
|
871
1016
|
const originMs = viewportStart.getTime();
|
|
872
1017
|
const pxPerMs = columnWidth / msPerColumn;
|
|
873
1018
|
const msPerPx = msPerColumn / columnWidth;
|
|
874
|
-
const
|
|
1019
|
+
const msPerDay = 864e5;
|
|
875
1020
|
return {
|
|
876
1021
|
originMs,
|
|
877
1022
|
columnWidth,
|
|
@@ -881,11 +1026,11 @@ function createPixelMapper(scale, viewportStart) {
|
|
|
881
1026
|
toDate(x) {
|
|
882
1027
|
return new Date(originMs + x * msPerPx);
|
|
883
1028
|
},
|
|
884
|
-
|
|
885
|
-
return
|
|
1029
|
+
durationDaysToWidth(days) {
|
|
1030
|
+
return days * msPerDay * pxPerMs;
|
|
886
1031
|
},
|
|
887
|
-
|
|
888
|
-
return px * msPerPx /
|
|
1032
|
+
widthToDurationDays(px) {
|
|
1033
|
+
return px * msPerPx / msPerDay;
|
|
889
1034
|
}
|
|
890
1035
|
};
|
|
891
1036
|
}
|
|
@@ -934,7 +1079,8 @@ function computeLayout(rows, mapper) {
|
|
|
934
1079
|
});
|
|
935
1080
|
continue;
|
|
936
1081
|
}
|
|
937
|
-
const
|
|
1082
|
+
const days = getRangeDays(start, parseDate(task.endDate));
|
|
1083
|
+
const width = Math.max(mapper.durationDaysToWidth(days), 4);
|
|
938
1084
|
const progressWidth = width * Math.min(1, Math.max(0, (task.percentComplete ?? 0) / 100));
|
|
939
1085
|
result.set(task.id, {
|
|
940
1086
|
taskId: task.id,
|
|
@@ -978,7 +1124,7 @@ function deriveViewport(tasks, paddingHours = 48) {
|
|
|
978
1124
|
const start = parseDate(task.startDate);
|
|
979
1125
|
if (start.getTime() < minMs) minMs = start.getTime();
|
|
980
1126
|
if (task.kind !== "milestone") {
|
|
981
|
-
const end =
|
|
1127
|
+
const end = parseDate(task.endDate);
|
|
982
1128
|
if (end.getTime() > maxMs) maxMs = end.getTime();
|
|
983
1129
|
} else if (start.getTime() > maxMs) maxMs = start.getTime();
|
|
984
1130
|
}
|
|
@@ -1490,17 +1636,10 @@ const DEFAULT_GRID_COLUMNS = [
|
|
|
1490
1636
|
},
|
|
1491
1637
|
{
|
|
1492
1638
|
id: "startDate",
|
|
1493
|
-
header: "Start
|
|
1639
|
+
header: "Start",
|
|
1494
1640
|
width: "90px",
|
|
1495
1641
|
field: "startDate",
|
|
1496
|
-
format: (value, _task, _row, locale) => formatDisplayDate(
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
id: "durationHours",
|
|
1500
|
-
header: "Duration",
|
|
1501
|
-
width: "68px",
|
|
1502
|
-
field: "durationHours",
|
|
1503
|
-
format: (value) => value > 0 ? String(value) : "—"
|
|
1642
|
+
format: (value, _task, _row, locale) => formatDisplayDate(value, locale)
|
|
1504
1643
|
},
|
|
1505
1644
|
{
|
|
1506
1645
|
id: "actions",
|
|
@@ -1529,13 +1668,6 @@ function gridColumnDefaults(locale) {
|
|
|
1529
1668
|
field: "startDate",
|
|
1530
1669
|
format: (value, _task, _row, loc) => formatDisplayDate(String(value), loc)
|
|
1531
1670
|
},
|
|
1532
|
-
{
|
|
1533
|
-
id: "durationHours",
|
|
1534
|
-
header: locale.labels?.columnDuration ?? EN_US_LABELS.columnDuration,
|
|
1535
|
-
width: "68px",
|
|
1536
|
-
field: "durationHours",
|
|
1537
|
-
format: (value) => value > 0 ? String(value) : "—"
|
|
1538
|
-
},
|
|
1539
1671
|
{
|
|
1540
1672
|
id: "actions",
|
|
1541
1673
|
header: "",
|
|
@@ -1601,13 +1733,13 @@ function toTask$1(node) {
|
|
|
1601
1733
|
case "task": return {
|
|
1602
1734
|
...base,
|
|
1603
1735
|
kind: "task",
|
|
1604
|
-
|
|
1736
|
+
endDate: node.endDate,
|
|
1605
1737
|
percentComplete: node.percentComplete
|
|
1606
1738
|
};
|
|
1607
1739
|
case "project": return {
|
|
1608
1740
|
...base,
|
|
1609
1741
|
kind: "project",
|
|
1610
|
-
|
|
1742
|
+
endDate: node.endDate,
|
|
1611
1743
|
percentComplete: node.percentComplete,
|
|
1612
1744
|
open: node.open
|
|
1613
1745
|
};
|
|
@@ -1619,7 +1751,7 @@ function toTask$1(node) {
|
|
|
1619
1751
|
}
|
|
1620
1752
|
function getTaskField(task, field) {
|
|
1621
1753
|
switch (field) {
|
|
1622
|
-
case "
|
|
1754
|
+
case "endDate": return task.kind !== "milestone" ? task.endDate : void 0;
|
|
1623
1755
|
case "percentComplete": return task.kind !== "milestone" ? task.percentComplete : void 0;
|
|
1624
1756
|
case "open": return task.kind === "project" ? task.open : void 0;
|
|
1625
1757
|
default: return task[field];
|
|
@@ -1716,14 +1848,17 @@ function buildAddButton(row, cbs, locale) {
|
|
|
1716
1848
|
});
|
|
1717
1849
|
return btn;
|
|
1718
1850
|
}
|
|
1719
|
-
function
|
|
1851
|
+
function buildActionsPlaceholder() {
|
|
1852
|
+
return el("div");
|
|
1853
|
+
}
|
|
1854
|
+
function buildCell(column, row, expandedIds, cbs, locale, showAddTaskButton) {
|
|
1720
1855
|
switch (column.id) {
|
|
1721
1856
|
case "name": return buildTreeNameCell(row, expandedIds, cbs);
|
|
1722
|
-
case "actions": return buildAddButton(row, cbs, locale);
|
|
1857
|
+
case "actions": return showAddTaskButton ? buildAddButton(row, cbs, locale) : buildActionsPlaceholder();
|
|
1723
1858
|
default: return buildDataCell(row, column, locale);
|
|
1724
1859
|
}
|
|
1725
1860
|
}
|
|
1726
|
-
function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
1861
|
+
function buildRow(row, selectedId, expandedIds, cbs, columns, locale, showAddTaskButton) {
|
|
1727
1862
|
const selected = row.id === selectedId;
|
|
1728
1863
|
const wrapper = el("div");
|
|
1729
1864
|
wrapper.className = "gantt-row";
|
|
@@ -1755,7 +1890,7 @@ function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
|
1755
1890
|
cbs.onTaskClick(row.id);
|
|
1756
1891
|
}
|
|
1757
1892
|
});
|
|
1758
|
-
for (const column of visibleColumns(columns)) wrapper.append(buildCell(column, row, expandedIds, cbs, locale));
|
|
1893
|
+
for (const column of visibleColumns(columns)) wrapper.append(buildCell(column, row, expandedIds, cbs, locale, showAddTaskButton));
|
|
1759
1894
|
return wrapper;
|
|
1760
1895
|
}
|
|
1761
1896
|
/**
|
|
@@ -1765,8 +1900,9 @@ function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
|
1765
1900
|
* @param state - The current chart state.
|
|
1766
1901
|
* @param cbs - The left pane callbacks.
|
|
1767
1902
|
* @param columns - The grid column schema.
|
|
1903
|
+
* @param showAddTaskButton - Whether to render the add-subtask button in the actions column.
|
|
1768
1904
|
*/
|
|
1769
|
-
function renderLeftPane(container, state, cbs, columns) {
|
|
1905
|
+
function renderLeftPane(container, state, cbs, columns, showAddTaskButton = true) {
|
|
1770
1906
|
const { allRows, selectedId, expandedIds, startIndex, endIndex, paddingTop, paddingBottom, locale } = state;
|
|
1771
1907
|
const frag = document.createDocumentFragment();
|
|
1772
1908
|
if (paddingTop > 0) {
|
|
@@ -1774,7 +1910,7 @@ function renderLeftPane(container, state, cbs, columns) {
|
|
|
1774
1910
|
spacer.style.height = `${paddingTop}px`;
|
|
1775
1911
|
frag.append(spacer);
|
|
1776
1912
|
}
|
|
1777
|
-
for (const row of allRows.slice(startIndex, endIndex + 1)) frag.append(buildRow(row, selectedId, expandedIds, cbs, columns, locale));
|
|
1913
|
+
for (const row of allRows.slice(startIndex, endIndex + 1)) frag.append(buildRow(row, selectedId, expandedIds, cbs, columns, locale, showAddTaskButton));
|
|
1778
1914
|
if (paddingBottom > 0) {
|
|
1779
1915
|
const spacer = el("div");
|
|
1780
1916
|
spacer.style.height = `${paddingBottom}px`;
|
|
@@ -2067,13 +2203,13 @@ function toTask(node) {
|
|
|
2067
2203
|
case "task": return {
|
|
2068
2204
|
...base,
|
|
2069
2205
|
kind: "task",
|
|
2070
|
-
|
|
2206
|
+
endDate: node.endDate,
|
|
2071
2207
|
percentComplete: node.percentComplete
|
|
2072
2208
|
};
|
|
2073
2209
|
case "project": return {
|
|
2074
2210
|
...base,
|
|
2075
2211
|
kind: "project",
|
|
2076
|
-
|
|
2212
|
+
endDate: node.endDate,
|
|
2077
2213
|
percentComplete: node.percentComplete,
|
|
2078
2214
|
open: node.open
|
|
2079
2215
|
};
|
|
@@ -2107,22 +2243,22 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2107
2243
|
const startX = e.clientX;
|
|
2108
2244
|
const originDate = parseDate(task.startDate);
|
|
2109
2245
|
const mapper = getMapper();
|
|
2110
|
-
let
|
|
2246
|
+
let lastDays = 0;
|
|
2111
2247
|
function onMove(me) {
|
|
2112
2248
|
const dx = me.clientX - startX;
|
|
2113
|
-
|
|
2249
|
+
lastDays = Math.round(mapper.widthToDurationDays(dx));
|
|
2114
2250
|
cbs.onTaskMove?.({
|
|
2115
2251
|
id: task.id,
|
|
2116
|
-
startDate:
|
|
2252
|
+
startDate: addDays(originDate, lastDays)
|
|
2117
2253
|
});
|
|
2118
2254
|
}
|
|
2119
2255
|
function onUp() {
|
|
2120
2256
|
window.removeEventListener("pointermove", onMove);
|
|
2121
2257
|
window.removeEventListener("pointerup", onUp);
|
|
2122
2258
|
barEl.style.cursor = "grab";
|
|
2123
|
-
if (
|
|
2259
|
+
if (lastDays !== 0) cbs._onTaskMoveFinal?.({
|
|
2124
2260
|
id: task.id,
|
|
2125
|
-
startDate:
|
|
2261
|
+
startDate: addDays(originDate, lastDays)
|
|
2126
2262
|
});
|
|
2127
2263
|
}
|
|
2128
2264
|
barEl.style.cursor = "grabbing";
|
|
@@ -2137,16 +2273,16 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2137
2273
|
resizeHandleEl.setPointerCapture(e.pointerId);
|
|
2138
2274
|
} catch {}
|
|
2139
2275
|
const startX = e.clientX;
|
|
2140
|
-
|
|
2276
|
+
if (task.kind === "milestone") return;
|
|
2277
|
+
const origEnd = parseDate(task.endDate);
|
|
2141
2278
|
const mapper = getMapper();
|
|
2142
|
-
let
|
|
2279
|
+
let lastEnd = origEnd;
|
|
2143
2280
|
function onMove(me) {
|
|
2144
2281
|
const dx = me.clientX - startX;
|
|
2145
|
-
|
|
2146
|
-
lastDuration = Math.max(1, origDur + hoursDelta);
|
|
2282
|
+
lastEnd = addDays(origEnd, Math.round(mapper.widthToDurationDays(dx)));
|
|
2147
2283
|
cbs.onTaskResize?.({
|
|
2148
2284
|
id: task.id,
|
|
2149
|
-
|
|
2285
|
+
endDate: lastEnd.toISOString().slice(0, 10)
|
|
2150
2286
|
});
|
|
2151
2287
|
}
|
|
2152
2288
|
function onUp() {
|
|
@@ -2154,7 +2290,7 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2154
2290
|
window.removeEventListener("pointerup", onUp);
|
|
2155
2291
|
cbs._onTaskResizeFinal?.({
|
|
2156
2292
|
id: task.id,
|
|
2157
|
-
|
|
2293
|
+
endDate: lastEnd.toISOString().slice(0, 10)
|
|
2158
2294
|
});
|
|
2159
2295
|
}
|
|
2160
2296
|
window.addEventListener("pointermove", onMove);
|
|
@@ -2920,9 +3056,12 @@ const OVERSCAN = 4;
|
|
|
2920
3056
|
* Validates input, builds a DOM tree, and renders a full interactive chart
|
|
2921
3057
|
* inside the given container element.
|
|
2922
3058
|
*
|
|
3059
|
+
* @param TTaskData - The type of the optional `data` property on tasks. Defaults to `never`.
|
|
3060
|
+
* @param TLinkData - The type of the optional `data` property on links. Defaults to `never`.
|
|
3061
|
+
*
|
|
2923
3062
|
* @example
|
|
2924
3063
|
* ```ts
|
|
2925
|
-
* const chart = new GanttChart(document.getElementById('chart')!,
|
|
3064
|
+
* const chart = new GanttChart(document.getElementById('chart')!, {
|
|
2926
3065
|
* locale: 'de-DE',
|
|
2927
3066
|
* theme: 'dark',
|
|
2928
3067
|
* });
|
|
@@ -2948,6 +3087,7 @@ var GanttChart = class {
|
|
|
2948
3087
|
#timelineMinWidth;
|
|
2949
3088
|
#columns;
|
|
2950
3089
|
#leftPaneDefaultWidth;
|
|
3090
|
+
#showAddTaskButton;
|
|
2951
3091
|
#weekendDays;
|
|
2952
3092
|
#specialDaysByDate;
|
|
2953
3093
|
#expandedIds;
|
|
@@ -2979,6 +3119,7 @@ var GanttChart = class {
|
|
|
2979
3119
|
this.#locale = resolveChartLocale(opts.locale);
|
|
2980
3120
|
this.#columns = opts.gridColumns ?? gridColumnDefaults(this.#locale);
|
|
2981
3121
|
this.#leftPaneDefaultWidth = opts.leftPaneWidth ?? gridNaturalWidth(this.#columns);
|
|
3122
|
+
this.#showAddTaskButton = opts.showAddTaskButton ?? true;
|
|
2982
3123
|
this.#height = opts.height ?? 500;
|
|
2983
3124
|
this.#timelineMinWidth = opts.timelineMinWidth ?? 220;
|
|
2984
3125
|
this.#weekendDays = normalizeWeekendDays(opts.weekendDays ?? this.#locale.weekendDays);
|
|
@@ -3030,9 +3171,11 @@ var GanttChart = class {
|
|
|
3030
3171
|
_onTaskMoveFinal: async (payload) => {
|
|
3031
3172
|
const task = this.#findTask(payload.id);
|
|
3032
3173
|
if (task !== void 0) {
|
|
3174
|
+
const newEndDate = task.kind !== "milestone" ? parseDate(task.endDate) : payload.startDate;
|
|
3033
3175
|
const result = this.#callbacks.onTaskMove?.({
|
|
3034
3176
|
task,
|
|
3035
3177
|
newStartDate: payload.startDate,
|
|
3178
|
+
newEndDate,
|
|
3036
3179
|
instance: this
|
|
3037
3180
|
});
|
|
3038
3181
|
if (result instanceof Promise) {
|
|
@@ -3054,25 +3197,30 @@ var GanttChart = class {
|
|
|
3054
3197
|
const task = this.#input?.tasks.find((t) => t.id === payload.id);
|
|
3055
3198
|
if (task !== void 0) this.#dragOriginals.set(payload.id, task);
|
|
3056
3199
|
}
|
|
3057
|
-
this.#patchTask(payload.id, {
|
|
3200
|
+
this.#patchTask(payload.id, { endDate: payload.endDate });
|
|
3058
3201
|
this.#scheduleRender();
|
|
3059
3202
|
},
|
|
3060
3203
|
_onTaskResizeFinal: async (payload) => {
|
|
3061
3204
|
const task = this.#findTask(payload.id);
|
|
3062
|
-
if (task !== void 0) {
|
|
3205
|
+
if (task !== void 0 && task.kind !== "milestone") {
|
|
3206
|
+
const newStartDate = parseDate(task.startDate);
|
|
3207
|
+
const newEndDate = parseDate(task.endDate);
|
|
3208
|
+
const newDurationHours = diffHours(newEndDate, newStartDate);
|
|
3063
3209
|
const result = this.#callbacks.onTaskResize?.({
|
|
3064
3210
|
task,
|
|
3065
|
-
newDurationHours
|
|
3211
|
+
newDurationHours,
|
|
3212
|
+
newStartDate,
|
|
3213
|
+
newEndDate,
|
|
3066
3214
|
instance: this
|
|
3067
3215
|
});
|
|
3068
3216
|
if (result instanceof Promise) {
|
|
3069
3217
|
if (!await result) {
|
|
3070
3218
|
const original = this.#dragOriginals.get(payload.id);
|
|
3071
|
-
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, {
|
|
3219
|
+
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { endDate: original.endDate });
|
|
3072
3220
|
}
|
|
3073
3221
|
} else if (!result) {
|
|
3074
3222
|
const original = this.#dragOriginals.get(payload.id);
|
|
3075
|
-
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, {
|
|
3223
|
+
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { endDate: original.endDate });
|
|
3076
3224
|
}
|
|
3077
3225
|
}
|
|
3078
3226
|
this.#dragOriginals.clear();
|
|
@@ -3229,9 +3377,10 @@ var GanttChart = class {
|
|
|
3229
3377
|
if (opts.weekendDays !== void 0) this.#weekendDays = normalizeWeekendDays(opts.weekendDays);
|
|
3230
3378
|
if (opts.specialDays !== void 0) this.#specialDaysByDate = buildSpecialDayIndex(opts.specialDays);
|
|
3231
3379
|
if (opts.theme !== void 0) this.#applyTheme();
|
|
3380
|
+
if (opts.showAddTaskButton !== void 0) this.#showAddTaskButton = opts.showAddTaskButton;
|
|
3232
3381
|
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;
|
|
3233
3382
|
if (hasLayoutChange) this.#applyResponsivePaneStyles();
|
|
3234
|
-
const hasLeftPaneChange = columnsChanged || opts.locale !== void 0;
|
|
3383
|
+
const hasLeftPaneChange = columnsChanged || opts.locale !== void 0 || opts.showAddTaskButton !== void 0;
|
|
3235
3384
|
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;
|
|
3236
3385
|
if (!(hasLeftPaneChange || hasRightPaneChange || hasLayoutChange)) return;
|
|
3237
3386
|
if (this.#rafPending && this.#rafId !== null) {
|
|
@@ -3438,7 +3587,7 @@ var GanttChart = class {
|
|
|
3438
3587
|
},
|
|
3439
3588
|
onTaskDoubleClick: (payload) => this.#cbs.onTaskDoubleClick?.(payload),
|
|
3440
3589
|
onTaskAdd: (id) => this.#cbs.onTaskAdd?.(id)
|
|
3441
|
-
}, this.#columns);
|
|
3590
|
+
}, this.#columns, this.#showAddTaskButton);
|
|
3442
3591
|
}
|
|
3443
3592
|
#renderTimeline = () => {
|
|
3444
3593
|
this.#rafPending = false;
|
|
@@ -3582,6 +3731,6 @@ var GanttChart = class {
|
|
|
3582
3731
|
}
|
|
3583
3732
|
};
|
|
3584
3733
|
//#endregion
|
|
3585
|
-
export { BAR_HEIGHT, BAR_Y_OFFSET, CHART_LOCALE_EN_US, 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 };
|
|
3734
|
+
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 };
|
|
3586
3735
|
|
|
3587
3736
|
//# sourceMappingURL=index.mjs.map
|