gantt-renderer 0.5.0 → 0.7.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 +10 -0
- package/dist/index.d.mts +155 -88
- package/dist/index.mjs +213 -66
- 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({
|
|
@@ -151,8 +157,12 @@ const GanttInputSchema = z.object({
|
|
|
151
157
|
/**
|
|
152
158
|
* Parses raw external data.
|
|
153
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
|
+
*
|
|
154
164
|
* @param raw - The unvalidated input from the consumer.
|
|
155
|
-
* @returns The parsed and validated
|
|
165
|
+
* @returns The parsed and validated input with `data` typed per the type parameters.
|
|
156
166
|
* @throws {import('zod').ZodError} On schema validation failure.
|
|
157
167
|
*/
|
|
158
168
|
function parseGanttInput(raw) {
|
|
@@ -466,7 +476,8 @@ const EN_US_LABELS = {
|
|
|
466
476
|
ariaMilestone: "Milestone {0}",
|
|
467
477
|
addSubtaskTitle: "Add subtask",
|
|
468
478
|
columnTaskName: "Task name",
|
|
469
|
-
columnStartDate: "Start
|
|
479
|
+
columnStartDate: "Start",
|
|
480
|
+
columnEndDate: "End",
|
|
470
481
|
columnDuration: "Duration",
|
|
471
482
|
columnQuarter: "Q"
|
|
472
483
|
};
|
|
@@ -477,6 +488,134 @@ const CHART_LOCALE_EN_US = {
|
|
|
477
488
|
weekNumbering: "iso",
|
|
478
489
|
weekendDays: [0, 6]
|
|
479
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
|
+
};
|
|
480
619
|
function tryGetWeekInfo(code) {
|
|
481
620
|
try {
|
|
482
621
|
if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
|
|
@@ -759,6 +898,16 @@ function formatUpperLabel(date, scale, locale) {
|
|
|
759
898
|
}
|
|
760
899
|
}
|
|
761
900
|
/**
|
|
901
|
+
* Returns the number of days in an inclusive range from `start` to `end`.
|
|
902
|
+
*
|
|
903
|
+
* @param start - The start date.
|
|
904
|
+
* @param end - The end date.
|
|
905
|
+
* @returns The number of days, inclusive.
|
|
906
|
+
*/
|
|
907
|
+
function getRangeDays(start, end) {
|
|
908
|
+
return Math.round(diffDays(start, end)) + 1;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
762
911
|
* Formats a `YYYY-MM-DD` string for display in the grid.
|
|
763
912
|
*
|
|
764
913
|
* @param dateStr - An ISO-8601 date string in `YYYY-MM-DD` format.
|
|
@@ -871,7 +1020,7 @@ function createPixelMapper(scale, viewportStart) {
|
|
|
871
1020
|
const originMs = viewportStart.getTime();
|
|
872
1021
|
const pxPerMs = columnWidth / msPerColumn;
|
|
873
1022
|
const msPerPx = msPerColumn / columnWidth;
|
|
874
|
-
const
|
|
1023
|
+
const msPerDay = 864e5;
|
|
875
1024
|
return {
|
|
876
1025
|
originMs,
|
|
877
1026
|
columnWidth,
|
|
@@ -881,11 +1030,11 @@ function createPixelMapper(scale, viewportStart) {
|
|
|
881
1030
|
toDate(x) {
|
|
882
1031
|
return new Date(originMs + x * msPerPx);
|
|
883
1032
|
},
|
|
884
|
-
|
|
885
|
-
return
|
|
1033
|
+
durationDaysToWidth(days) {
|
|
1034
|
+
return days * msPerDay * pxPerMs;
|
|
886
1035
|
},
|
|
887
|
-
|
|
888
|
-
return px * msPerPx /
|
|
1036
|
+
widthToDurationDays(px) {
|
|
1037
|
+
return px * msPerPx / msPerDay;
|
|
889
1038
|
}
|
|
890
1039
|
};
|
|
891
1040
|
}
|
|
@@ -934,7 +1083,8 @@ function computeLayout(rows, mapper) {
|
|
|
934
1083
|
});
|
|
935
1084
|
continue;
|
|
936
1085
|
}
|
|
937
|
-
const
|
|
1086
|
+
const days = getRangeDays(start, parseDate(task.endDate));
|
|
1087
|
+
const width = Math.max(mapper.durationDaysToWidth(days), 4);
|
|
938
1088
|
const progressWidth = width * Math.min(1, Math.max(0, (task.percentComplete ?? 0) / 100));
|
|
939
1089
|
result.set(task.id, {
|
|
940
1090
|
taskId: task.id,
|
|
@@ -978,7 +1128,7 @@ function deriveViewport(tasks, paddingHours = 48) {
|
|
|
978
1128
|
const start = parseDate(task.startDate);
|
|
979
1129
|
if (start.getTime() < minMs) minMs = start.getTime();
|
|
980
1130
|
if (task.kind !== "milestone") {
|
|
981
|
-
const end =
|
|
1131
|
+
const end = parseDate(task.endDate);
|
|
982
1132
|
if (end.getTime() > maxMs) maxMs = end.getTime();
|
|
983
1133
|
} else if (start.getTime() > maxMs) maxMs = start.getTime();
|
|
984
1134
|
}
|
|
@@ -1490,17 +1640,10 @@ const DEFAULT_GRID_COLUMNS = [
|
|
|
1490
1640
|
},
|
|
1491
1641
|
{
|
|
1492
1642
|
id: "startDate",
|
|
1493
|
-
header: "Start
|
|
1643
|
+
header: "Start",
|
|
1494
1644
|
width: "90px",
|
|
1495
1645
|
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) : "—"
|
|
1646
|
+
format: (value, _task, _row, locale) => formatDisplayDate(value, locale)
|
|
1504
1647
|
},
|
|
1505
1648
|
{
|
|
1506
1649
|
id: "actions",
|
|
@@ -1529,13 +1672,6 @@ function gridColumnDefaults(locale) {
|
|
|
1529
1672
|
field: "startDate",
|
|
1530
1673
|
format: (value, _task, _row, loc) => formatDisplayDate(String(value), loc)
|
|
1531
1674
|
},
|
|
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
1675
|
{
|
|
1540
1676
|
id: "actions",
|
|
1541
1677
|
header: "",
|
|
@@ -1601,13 +1737,13 @@ function toTask$1(node) {
|
|
|
1601
1737
|
case "task": return {
|
|
1602
1738
|
...base,
|
|
1603
1739
|
kind: "task",
|
|
1604
|
-
|
|
1740
|
+
endDate: node.endDate,
|
|
1605
1741
|
percentComplete: node.percentComplete
|
|
1606
1742
|
};
|
|
1607
1743
|
case "project": return {
|
|
1608
1744
|
...base,
|
|
1609
1745
|
kind: "project",
|
|
1610
|
-
|
|
1746
|
+
endDate: node.endDate,
|
|
1611
1747
|
percentComplete: node.percentComplete,
|
|
1612
1748
|
open: node.open
|
|
1613
1749
|
};
|
|
@@ -1619,7 +1755,7 @@ function toTask$1(node) {
|
|
|
1619
1755
|
}
|
|
1620
1756
|
function getTaskField(task, field) {
|
|
1621
1757
|
switch (field) {
|
|
1622
|
-
case "
|
|
1758
|
+
case "endDate": return task.kind !== "milestone" ? task.endDate : void 0;
|
|
1623
1759
|
case "percentComplete": return task.kind !== "milestone" ? task.percentComplete : void 0;
|
|
1624
1760
|
case "open": return task.kind === "project" ? task.open : void 0;
|
|
1625
1761
|
default: return task[field];
|
|
@@ -1716,14 +1852,17 @@ function buildAddButton(row, cbs, locale) {
|
|
|
1716
1852
|
});
|
|
1717
1853
|
return btn;
|
|
1718
1854
|
}
|
|
1719
|
-
function
|
|
1855
|
+
function buildActionsPlaceholder() {
|
|
1856
|
+
return el("div");
|
|
1857
|
+
}
|
|
1858
|
+
function buildCell(column, row, expandedIds, cbs, locale, showAddTaskButton) {
|
|
1720
1859
|
switch (column.id) {
|
|
1721
1860
|
case "name": return buildTreeNameCell(row, expandedIds, cbs);
|
|
1722
|
-
case "actions": return buildAddButton(row, cbs, locale);
|
|
1861
|
+
case "actions": return showAddTaskButton ? buildAddButton(row, cbs, locale) : buildActionsPlaceholder();
|
|
1723
1862
|
default: return buildDataCell(row, column, locale);
|
|
1724
1863
|
}
|
|
1725
1864
|
}
|
|
1726
|
-
function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
1865
|
+
function buildRow(row, selectedId, expandedIds, cbs, columns, locale, showAddTaskButton) {
|
|
1727
1866
|
const selected = row.id === selectedId;
|
|
1728
1867
|
const wrapper = el("div");
|
|
1729
1868
|
wrapper.className = "gantt-row";
|
|
@@ -1755,7 +1894,7 @@ function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
|
1755
1894
|
cbs.onTaskClick(row.id);
|
|
1756
1895
|
}
|
|
1757
1896
|
});
|
|
1758
|
-
for (const column of visibleColumns(columns)) wrapper.append(buildCell(column, row, expandedIds, cbs, locale));
|
|
1897
|
+
for (const column of visibleColumns(columns)) wrapper.append(buildCell(column, row, expandedIds, cbs, locale, showAddTaskButton));
|
|
1759
1898
|
return wrapper;
|
|
1760
1899
|
}
|
|
1761
1900
|
/**
|
|
@@ -1765,8 +1904,9 @@ function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
|
1765
1904
|
* @param state - The current chart state.
|
|
1766
1905
|
* @param cbs - The left pane callbacks.
|
|
1767
1906
|
* @param columns - The grid column schema.
|
|
1907
|
+
* @param showAddTaskButton - Whether to render the add-subtask button in the actions column.
|
|
1768
1908
|
*/
|
|
1769
|
-
function renderLeftPane(container, state, cbs, columns) {
|
|
1909
|
+
function renderLeftPane(container, state, cbs, columns, showAddTaskButton = true) {
|
|
1770
1910
|
const { allRows, selectedId, expandedIds, startIndex, endIndex, paddingTop, paddingBottom, locale } = state;
|
|
1771
1911
|
const frag = document.createDocumentFragment();
|
|
1772
1912
|
if (paddingTop > 0) {
|
|
@@ -1774,7 +1914,7 @@ function renderLeftPane(container, state, cbs, columns) {
|
|
|
1774
1914
|
spacer.style.height = `${paddingTop}px`;
|
|
1775
1915
|
frag.append(spacer);
|
|
1776
1916
|
}
|
|
1777
|
-
for (const row of allRows.slice(startIndex, endIndex + 1)) frag.append(buildRow(row, selectedId, expandedIds, cbs, columns, locale));
|
|
1917
|
+
for (const row of allRows.slice(startIndex, endIndex + 1)) frag.append(buildRow(row, selectedId, expandedIds, cbs, columns, locale, showAddTaskButton));
|
|
1778
1918
|
if (paddingBottom > 0) {
|
|
1779
1919
|
const spacer = el("div");
|
|
1780
1920
|
spacer.style.height = `${paddingBottom}px`;
|
|
@@ -2067,13 +2207,13 @@ function toTask(node) {
|
|
|
2067
2207
|
case "task": return {
|
|
2068
2208
|
...base,
|
|
2069
2209
|
kind: "task",
|
|
2070
|
-
|
|
2210
|
+
endDate: node.endDate,
|
|
2071
2211
|
percentComplete: node.percentComplete
|
|
2072
2212
|
};
|
|
2073
2213
|
case "project": return {
|
|
2074
2214
|
...base,
|
|
2075
2215
|
kind: "project",
|
|
2076
|
-
|
|
2216
|
+
endDate: node.endDate,
|
|
2077
2217
|
percentComplete: node.percentComplete,
|
|
2078
2218
|
open: node.open
|
|
2079
2219
|
};
|
|
@@ -2107,22 +2247,22 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2107
2247
|
const startX = e.clientX;
|
|
2108
2248
|
const originDate = parseDate(task.startDate);
|
|
2109
2249
|
const mapper = getMapper();
|
|
2110
|
-
let
|
|
2250
|
+
let lastDays = 0;
|
|
2111
2251
|
function onMove(me) {
|
|
2112
2252
|
const dx = me.clientX - startX;
|
|
2113
|
-
|
|
2253
|
+
lastDays = Math.round(mapper.widthToDurationDays(dx));
|
|
2114
2254
|
cbs.onTaskMove?.({
|
|
2115
2255
|
id: task.id,
|
|
2116
|
-
startDate:
|
|
2256
|
+
startDate: addDays(originDate, lastDays)
|
|
2117
2257
|
});
|
|
2118
2258
|
}
|
|
2119
2259
|
function onUp() {
|
|
2120
2260
|
window.removeEventListener("pointermove", onMove);
|
|
2121
2261
|
window.removeEventListener("pointerup", onUp);
|
|
2122
2262
|
barEl.style.cursor = "grab";
|
|
2123
|
-
if (
|
|
2263
|
+
if (lastDays !== 0) cbs._onTaskMoveFinal?.({
|
|
2124
2264
|
id: task.id,
|
|
2125
|
-
startDate:
|
|
2265
|
+
startDate: addDays(originDate, lastDays)
|
|
2126
2266
|
});
|
|
2127
2267
|
}
|
|
2128
2268
|
barEl.style.cursor = "grabbing";
|
|
@@ -2137,16 +2277,16 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2137
2277
|
resizeHandleEl.setPointerCapture(e.pointerId);
|
|
2138
2278
|
} catch {}
|
|
2139
2279
|
const startX = e.clientX;
|
|
2140
|
-
|
|
2280
|
+
if (task.kind === "milestone") return;
|
|
2281
|
+
const origEnd = parseDate(task.endDate);
|
|
2141
2282
|
const mapper = getMapper();
|
|
2142
|
-
let
|
|
2283
|
+
let lastEnd = origEnd;
|
|
2143
2284
|
function onMove(me) {
|
|
2144
2285
|
const dx = me.clientX - startX;
|
|
2145
|
-
|
|
2146
|
-
lastDuration = Math.max(1, origDur + hoursDelta);
|
|
2286
|
+
lastEnd = addDays(origEnd, Math.round(mapper.widthToDurationDays(dx)));
|
|
2147
2287
|
cbs.onTaskResize?.({
|
|
2148
2288
|
id: task.id,
|
|
2149
|
-
|
|
2289
|
+
endDate: lastEnd.toISOString().slice(0, 10)
|
|
2150
2290
|
});
|
|
2151
2291
|
}
|
|
2152
2292
|
function onUp() {
|
|
@@ -2154,7 +2294,7 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2154
2294
|
window.removeEventListener("pointerup", onUp);
|
|
2155
2295
|
cbs._onTaskResizeFinal?.({
|
|
2156
2296
|
id: task.id,
|
|
2157
|
-
|
|
2297
|
+
endDate: lastEnd.toISOString().slice(0, 10)
|
|
2158
2298
|
});
|
|
2159
2299
|
}
|
|
2160
2300
|
window.addEventListener("pointermove", onMove);
|
|
@@ -2920,9 +3060,12 @@ const OVERSCAN = 4;
|
|
|
2920
3060
|
* Validates input, builds a DOM tree, and renders a full interactive chart
|
|
2921
3061
|
* inside the given container element.
|
|
2922
3062
|
*
|
|
3063
|
+
* @param TTaskData - The type of the optional `data` property on tasks. Defaults to `never`.
|
|
3064
|
+
* @param TLinkData - The type of the optional `data` property on links. Defaults to `never`.
|
|
3065
|
+
*
|
|
2923
3066
|
* @example
|
|
2924
3067
|
* ```ts
|
|
2925
|
-
* const chart = new GanttChart(document.getElementById('chart')!,
|
|
3068
|
+
* const chart = new GanttChart(document.getElementById('chart')!, {
|
|
2926
3069
|
* locale: 'de-DE',
|
|
2927
3070
|
* theme: 'dark',
|
|
2928
3071
|
* });
|
|
@@ -2948,6 +3091,7 @@ var GanttChart = class {
|
|
|
2948
3091
|
#timelineMinWidth;
|
|
2949
3092
|
#columns;
|
|
2950
3093
|
#leftPaneDefaultWidth;
|
|
3094
|
+
#showAddTaskButton;
|
|
2951
3095
|
#weekendDays;
|
|
2952
3096
|
#specialDaysByDate;
|
|
2953
3097
|
#expandedIds;
|
|
@@ -2979,6 +3123,7 @@ var GanttChart = class {
|
|
|
2979
3123
|
this.#locale = resolveChartLocale(opts.locale);
|
|
2980
3124
|
this.#columns = opts.gridColumns ?? gridColumnDefaults(this.#locale);
|
|
2981
3125
|
this.#leftPaneDefaultWidth = opts.leftPaneWidth ?? gridNaturalWidth(this.#columns);
|
|
3126
|
+
this.#showAddTaskButton = opts.showAddTaskButton ?? true;
|
|
2982
3127
|
this.#height = opts.height ?? 500;
|
|
2983
3128
|
this.#timelineMinWidth = opts.timelineMinWidth ?? 220;
|
|
2984
3129
|
this.#weekendDays = normalizeWeekendDays(opts.weekendDays ?? this.#locale.weekendDays);
|
|
@@ -3030,8 +3175,7 @@ var GanttChart = class {
|
|
|
3030
3175
|
_onTaskMoveFinal: async (payload) => {
|
|
3031
3176
|
const task = this.#findTask(payload.id);
|
|
3032
3177
|
if (task !== void 0) {
|
|
3033
|
-
const
|
|
3034
|
-
const newEndDate = addHours(payload.startDate, durationHours);
|
|
3178
|
+
const newEndDate = task.kind !== "milestone" ? parseDate(task.endDate) : payload.startDate;
|
|
3035
3179
|
const result = this.#callbacks.onTaskMove?.({
|
|
3036
3180
|
task,
|
|
3037
3181
|
newStartDate: payload.startDate,
|
|
@@ -3057,17 +3201,18 @@ var GanttChart = class {
|
|
|
3057
3201
|
const task = this.#input?.tasks.find((t) => t.id === payload.id);
|
|
3058
3202
|
if (task !== void 0) this.#dragOriginals.set(payload.id, task);
|
|
3059
3203
|
}
|
|
3060
|
-
this.#patchTask(payload.id, {
|
|
3204
|
+
this.#patchTask(payload.id, { endDate: payload.endDate });
|
|
3061
3205
|
this.#scheduleRender();
|
|
3062
3206
|
},
|
|
3063
3207
|
_onTaskResizeFinal: async (payload) => {
|
|
3064
3208
|
const task = this.#findTask(payload.id);
|
|
3065
|
-
if (task !== void 0) {
|
|
3209
|
+
if (task !== void 0 && task.kind !== "milestone") {
|
|
3066
3210
|
const newStartDate = parseDate(task.startDate);
|
|
3067
|
-
const newEndDate =
|
|
3211
|
+
const newEndDate = parseDate(task.endDate);
|
|
3212
|
+
const newDurationHours = diffHours(newEndDate, newStartDate);
|
|
3068
3213
|
const result = this.#callbacks.onTaskResize?.({
|
|
3069
3214
|
task,
|
|
3070
|
-
newDurationHours
|
|
3215
|
+
newDurationHours,
|
|
3071
3216
|
newStartDate,
|
|
3072
3217
|
newEndDate,
|
|
3073
3218
|
instance: this
|
|
@@ -3075,11 +3220,11 @@ var GanttChart = class {
|
|
|
3075
3220
|
if (result instanceof Promise) {
|
|
3076
3221
|
if (!await result) {
|
|
3077
3222
|
const original = this.#dragOriginals.get(payload.id);
|
|
3078
|
-
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 });
|
|
3079
3224
|
}
|
|
3080
3225
|
} else if (!result) {
|
|
3081
3226
|
const original = this.#dragOriginals.get(payload.id);
|
|
3082
|
-
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, {
|
|
3227
|
+
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { endDate: original.endDate });
|
|
3083
3228
|
}
|
|
3084
3229
|
}
|
|
3085
3230
|
this.#dragOriginals.clear();
|
|
@@ -3183,9 +3328,10 @@ var GanttChart = class {
|
|
|
3183
3328
|
*/
|
|
3184
3329
|
update(newInput) {
|
|
3185
3330
|
this.#assertAlive();
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3331
|
+
const input = newInput;
|
|
3332
|
+
validateLinkRefs(input.tasks, input.links);
|
|
3333
|
+
detectCycles(input.tasks, input.links);
|
|
3334
|
+
this.#input = structuredClone(input);
|
|
3189
3335
|
this.#taskIndex = buildTaskIndex(this.#input.tasks);
|
|
3190
3336
|
this.#expandedIds = getInitialExpandedIds(this.#input.tasks);
|
|
3191
3337
|
if (this.#rafPending && this.#rafId !== null) {
|
|
@@ -3236,9 +3382,10 @@ var GanttChart = class {
|
|
|
3236
3382
|
if (opts.weekendDays !== void 0) this.#weekendDays = normalizeWeekendDays(opts.weekendDays);
|
|
3237
3383
|
if (opts.specialDays !== void 0) this.#specialDaysByDate = buildSpecialDayIndex(opts.specialDays);
|
|
3238
3384
|
if (opts.theme !== void 0) this.#applyTheme();
|
|
3385
|
+
if (opts.showAddTaskButton !== void 0) this.#showAddTaskButton = opts.showAddTaskButton;
|
|
3239
3386
|
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;
|
|
3240
3387
|
if (hasLayoutChange) this.#applyResponsivePaneStyles();
|
|
3241
|
-
const hasLeftPaneChange = columnsChanged || opts.locale !== void 0;
|
|
3388
|
+
const hasLeftPaneChange = columnsChanged || opts.locale !== void 0 || opts.showAddTaskButton !== void 0;
|
|
3242
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;
|
|
3243
3390
|
if (!(hasLeftPaneChange || hasRightPaneChange || hasLayoutChange)) return;
|
|
3244
3391
|
if (this.#rafPending && this.#rafId !== null) {
|
|
@@ -3445,7 +3592,7 @@ var GanttChart = class {
|
|
|
3445
3592
|
},
|
|
3446
3593
|
onTaskDoubleClick: (payload) => this.#cbs.onTaskDoubleClick?.(payload),
|
|
3447
3594
|
onTaskAdd: (id) => this.#cbs.onTaskAdd?.(id)
|
|
3448
|
-
}, this.#columns);
|
|
3595
|
+
}, this.#columns, this.#showAddTaskButton);
|
|
3449
3596
|
}
|
|
3450
3597
|
#renderTimeline = () => {
|
|
3451
3598
|
this.#rafPending = false;
|
|
@@ -3589,6 +3736,6 @@ var GanttChart = class {
|
|
|
3589
3736
|
}
|
|
3590
3737
|
};
|
|
3591
3738
|
//#endregion
|
|
3592
|
-
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 };
|
|
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 };
|
|
3593
3740
|
|
|
3594
3741
|
//# sourceMappingURL=index.mjs.map
|