gantt-renderer 0.3.0 → 0.4.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 +5 -1
- package/dist/index.d.mts +57 -18
- package/dist/index.mjs +242 -75
- package/dist/index.mjs.map +1 -1
- package/dist/styles/gantt.css +17 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -30,6 +30,8 @@ const taskBase = {
|
|
|
30
30
|
parent: z.number().int().positive().optional(),
|
|
31
31
|
/** Optional CSS color value for the task bar. Overrides the default color assignment. */
|
|
32
32
|
color: z.string().optional(),
|
|
33
|
+
/** When `true`, the task bar cannot be dragged or resized. */
|
|
34
|
+
readonly: z.boolean().optional(),
|
|
33
35
|
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
34
36
|
data: z.record(z.string(), z.unknown()).optional()
|
|
35
37
|
};
|
|
@@ -86,6 +88,8 @@ const LinkSchema = z.object({
|
|
|
86
88
|
* @default 'FS'
|
|
87
89
|
*/
|
|
88
90
|
type: LinkTypeSchema.default("FS"),
|
|
91
|
+
/** When `true`, the link cannot be modified or deleted through the UI. */
|
|
92
|
+
readonly: z.boolean().optional(),
|
|
89
93
|
/** Optional arbitrary metadata for consumer use. Preserved in the parsed output. */
|
|
90
94
|
data: z.record(z.string(), z.unknown()).optional()
|
|
91
95
|
}).refine((l) => l.source !== l.target, {
|
|
@@ -1748,7 +1752,7 @@ function buildRow(row, selectedId, expandedIds, cbs, columns, locale) {
|
|
|
1748
1752
|
wrapper.addEventListener("keydown", (event) => {
|
|
1749
1753
|
if (event.key === "Enter" || event.key === " ") {
|
|
1750
1754
|
event.preventDefault();
|
|
1751
|
-
cbs.
|
|
1755
|
+
cbs.onTaskClick(row.id);
|
|
1752
1756
|
}
|
|
1753
1757
|
});
|
|
1754
1758
|
for (const column of visibleColumns(columns)) wrapper.append(buildCell(column, row, expandedIds, cbs, locale));
|
|
@@ -2056,6 +2060,7 @@ function toTask(node) {
|
|
|
2056
2060
|
startDate: node.startDate,
|
|
2057
2061
|
...node.parent === void 0 ? {} : { parent: node.parent },
|
|
2058
2062
|
...node.color === void 0 ? {} : { color: node.color },
|
|
2063
|
+
...node.readonly === void 0 ? {} : { readonly: node.readonly },
|
|
2059
2064
|
...node.data === void 0 ? {} : { data: node.data }
|
|
2060
2065
|
};
|
|
2061
2066
|
switch (node.kind) {
|
|
@@ -2098,7 +2103,7 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2098
2103
|
try {
|
|
2099
2104
|
barEl.setPointerCapture(e.pointerId);
|
|
2100
2105
|
} catch {}
|
|
2101
|
-
cbs.
|
|
2106
|
+
cbs.onTaskClick?.(task.id);
|
|
2102
2107
|
const startX = e.clientX;
|
|
2103
2108
|
const originDate = parseDate(task.startDate);
|
|
2104
2109
|
const mapper = getMapper();
|
|
@@ -2115,7 +2120,7 @@ function attachDrag(barEl, resizeHandleEl, task, getMapper, cbs) {
|
|
|
2115
2120
|
window.removeEventListener("pointermove", onMove);
|
|
2116
2121
|
window.removeEventListener("pointerup", onUp);
|
|
2117
2122
|
barEl.style.cursor = "grab";
|
|
2118
|
-
cbs._onTaskMoveFinal?.({
|
|
2123
|
+
if (lastHours !== 0) cbs._onTaskMoveFinal?.({
|
|
2119
2124
|
id: task.id,
|
|
2120
2125
|
startDate: addHours(originDate, lastHours)
|
|
2121
2126
|
});
|
|
@@ -2186,7 +2191,7 @@ function attachProgressDrag(progressEl, barEl, task, _getMapper, cbs) {
|
|
|
2186
2191
|
if (e.button !== 0) return;
|
|
2187
2192
|
e.preventDefault();
|
|
2188
2193
|
e.stopPropagation();
|
|
2189
|
-
cbs.
|
|
2194
|
+
cbs.onTaskClick?.(task.id);
|
|
2190
2195
|
try {
|
|
2191
2196
|
progressEl.setPointerCapture(e.pointerId);
|
|
2192
2197
|
} catch {}
|
|
@@ -2231,7 +2236,7 @@ function attachProgressDrag(progressEl, barEl, task, _getMapper, cbs) {
|
|
|
2231
2236
|
*/
|
|
2232
2237
|
function attachMilestoneClick(diamondEl, taskId, cbs) {
|
|
2233
2238
|
function onClick() {
|
|
2234
|
-
cbs.
|
|
2239
|
+
cbs.onTaskClick?.(taskId);
|
|
2235
2240
|
}
|
|
2236
2241
|
function onDoubleClick(event) {
|
|
2237
2242
|
if (event.detail === 2) {
|
|
@@ -2361,11 +2366,16 @@ function createRightPaneRefs() {
|
|
|
2361
2366
|
scrollContainer.append(stripeContainer);
|
|
2362
2367
|
scrollContainer.append(absoluteLayer);
|
|
2363
2368
|
absoluteLayer.append(svgLayer);
|
|
2369
|
+
const tooltipEl = el("div");
|
|
2370
|
+
tooltipEl.className = "gantt-tooltip";
|
|
2371
|
+
tooltipEl.style.display = "none";
|
|
2372
|
+
scrollContainer.append(tooltipEl);
|
|
2364
2373
|
return {
|
|
2365
2374
|
scrollContainer,
|
|
2366
2375
|
stripeContainer,
|
|
2367
2376
|
absoluteLayer,
|
|
2368
2377
|
svgLayer,
|
|
2378
|
+
tooltipEl,
|
|
2369
2379
|
barRegistry: /* @__PURE__ */ new Map()
|
|
2370
2380
|
};
|
|
2371
2381
|
}
|
|
@@ -2410,8 +2420,9 @@ function renderSpecialDayBackgrounds(layer, beforeNode, state, contentHeight) {
|
|
|
2410
2420
|
cur = next;
|
|
2411
2421
|
}
|
|
2412
2422
|
}
|
|
2413
|
-
function renderBar(layer, svgLayer, task, layout, selectedId, registry, state, cbs) {
|
|
2423
|
+
function renderBar(layer, svgLayer, task, layout, selectedId, registry, state, cbs, tooltipEl) {
|
|
2414
2424
|
const selected = task.id === selectedId;
|
|
2425
|
+
const readonly = task.readonly === true;
|
|
2415
2426
|
const color = BAR_COLOR[layout.kind] ?? BAR_COLOR["task"];
|
|
2416
2427
|
const bar = el("div");
|
|
2417
2428
|
bar.className = `gantt-bar${selected ? " gantt-bar--selected gantt-shape--selected" : ""}`;
|
|
@@ -2423,7 +2434,7 @@ function renderBar(layer, svgLayer, task, layout, selectedId, registry, state, c
|
|
|
2423
2434
|
height: `${layout.height}px`,
|
|
2424
2435
|
...color === void 0 ? {} : { background: color },
|
|
2425
2436
|
borderRadius: layout.kind === "project" ? "3px" : "4px",
|
|
2426
|
-
cursor: "grab",
|
|
2437
|
+
cursor: readonly ? "pointer" : "grab",
|
|
2427
2438
|
userSelect: "none",
|
|
2428
2439
|
overflow: "hidden",
|
|
2429
2440
|
zIndex: selected ? "3" : "2",
|
|
@@ -2474,30 +2485,38 @@ function renderBar(layer, svgLayer, task, layout, selectedId, registry, state, c
|
|
|
2474
2485
|
bar.setAttribute("aria-label", ariaLabel(state.locale, "ariaTask", task.text));
|
|
2475
2486
|
bar.setAttribute("aria-pressed", String(selected));
|
|
2476
2487
|
bar.dataset["taskId"] = String(task.id);
|
|
2477
|
-
bar.addEventListener("click", () => {
|
|
2478
|
-
cbs.
|
|
2488
|
+
bar.addEventListener("click", (event) => {
|
|
2489
|
+
if (event.detail === 2) cbs.onTaskDoubleClick?.({
|
|
2490
|
+
id: task.id,
|
|
2491
|
+
task: toTask(task)
|
|
2492
|
+
});
|
|
2493
|
+
else cbs.onTaskClick?.(task.id);
|
|
2479
2494
|
});
|
|
2480
2495
|
bar.addEventListener("keydown", (event) => {
|
|
2481
2496
|
if (event.key === "Enter" || event.key === " ") {
|
|
2482
2497
|
event.preventDefault();
|
|
2483
|
-
cbs.
|
|
2498
|
+
cbs.onTaskClick?.(task.id);
|
|
2484
2499
|
}
|
|
2485
2500
|
});
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
+
let handle;
|
|
2502
|
+
let cleanupDrag;
|
|
2503
|
+
if (!readonly) {
|
|
2504
|
+
handle = el("div");
|
|
2505
|
+
handle.className = "gantt-resize-handle";
|
|
2506
|
+
css(handle, {
|
|
2507
|
+
position: "absolute",
|
|
2508
|
+
right: "0",
|
|
2509
|
+
top: "0",
|
|
2510
|
+
width: "8px",
|
|
2511
|
+
height: "100%",
|
|
2512
|
+
cursor: "ew-resize",
|
|
2513
|
+
zIndex: "1",
|
|
2514
|
+
touchAction: "none"
|
|
2515
|
+
});
|
|
2516
|
+
bar.append(handle);
|
|
2517
|
+
layer.insertBefore(bar, svgLayer);
|
|
2518
|
+
cleanupDrag = attachDrag(bar, handle, task, () => state.mapper, cbs);
|
|
2519
|
+
} else layer.insertBefore(bar, svgLayer);
|
|
2501
2520
|
let cleanupLinkHandles;
|
|
2502
2521
|
if (state.linkCreationEnabled) {
|
|
2503
2522
|
const barCenterY = layout.y + layout.height / 2;
|
|
@@ -2532,17 +2551,52 @@ function renderBar(layer, svgLayer, task, layout, selectedId, registry, state, c
|
|
|
2532
2551
|
bar.removeEventListener("mouseleave", onBarLeave);
|
|
2533
2552
|
};
|
|
2534
2553
|
}
|
|
2554
|
+
const onTooltipEnter = () => {
|
|
2555
|
+
const content = cbs.onTooltipText?.({
|
|
2556
|
+
id: task.id,
|
|
2557
|
+
task: toTask(task)
|
|
2558
|
+
});
|
|
2559
|
+
if (content && content.length > 0) {
|
|
2560
|
+
tooltipEl.innerHTML = content;
|
|
2561
|
+
tooltipEl.style.display = "";
|
|
2562
|
+
} else tooltipEl.style.display = "none";
|
|
2563
|
+
};
|
|
2564
|
+
const onTooltipMove = (e) => {
|
|
2565
|
+
const offsetX = 12;
|
|
2566
|
+
const offsetY = -8;
|
|
2567
|
+
let left = e.clientX + offsetX;
|
|
2568
|
+
let top = e.clientY + offsetY;
|
|
2569
|
+
const maxLeft = window.innerWidth - tooltipEl.offsetWidth - 4;
|
|
2570
|
+
const maxTop = window.innerHeight - tooltipEl.offsetHeight - 4;
|
|
2571
|
+
left = Math.max(4, Math.min(left, maxLeft));
|
|
2572
|
+
top = Math.max(4, Math.min(top, maxTop));
|
|
2573
|
+
tooltipEl.style.left = `${left}px`;
|
|
2574
|
+
tooltipEl.style.top = `${top}px`;
|
|
2575
|
+
};
|
|
2576
|
+
const onTooltipLeave = () => {
|
|
2577
|
+
tooltipEl.style.display = "none";
|
|
2578
|
+
};
|
|
2579
|
+
bar.addEventListener("mouseenter", onTooltipEnter);
|
|
2580
|
+
bar.addEventListener("mousemove", onTooltipMove);
|
|
2581
|
+
bar.addEventListener("mouseleave", onTooltipLeave);
|
|
2582
|
+
const cleanupTooltip = () => {
|
|
2583
|
+
bar.removeEventListener("mouseenter", onTooltipEnter);
|
|
2584
|
+
bar.removeEventListener("mousemove", onTooltipMove);
|
|
2585
|
+
bar.removeEventListener("mouseleave", onTooltipLeave);
|
|
2586
|
+
};
|
|
2535
2587
|
const entry = {
|
|
2536
2588
|
bar,
|
|
2537
|
-
resizeHandle: handle
|
|
2538
|
-
cleanupDrag
|
|
2589
|
+
resizeHandle: handle ?? el("div")
|
|
2539
2590
|
};
|
|
2591
|
+
if (cleanupDrag !== void 0) entry.cleanupDrag = cleanupDrag;
|
|
2540
2592
|
if (cleanupLinkHandles !== void 0) entry.cleanupLinkHandles = cleanupLinkHandles;
|
|
2541
2593
|
if (cleanupProgressDrag !== void 0) entry.cleanupProgressDrag = cleanupProgressDrag;
|
|
2594
|
+
if (cleanupTooltip !== void 0) entry.cleanupTooltip = cleanupTooltip;
|
|
2542
2595
|
registry.set(task.id, entry);
|
|
2543
2596
|
}
|
|
2544
|
-
function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cbs, state) {
|
|
2597
|
+
function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cbs, state, tooltipEl) {
|
|
2545
2598
|
const selected = task.id === selectedId;
|
|
2599
|
+
const readonly = task.readonly === true;
|
|
2546
2600
|
const size = MILESTONE_HALF * 2;
|
|
2547
2601
|
const diamond = el("div");
|
|
2548
2602
|
diamond.className = `gantt-milestone${selected ? " gantt-shape--selected" : ""}`;
|
|
@@ -2554,7 +2608,7 @@ function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cb
|
|
|
2554
2608
|
height: `${size}px`,
|
|
2555
2609
|
background: "var(--gantt-milestone)",
|
|
2556
2610
|
transform: "rotate(45deg)",
|
|
2557
|
-
cursor: "pointer",
|
|
2611
|
+
cursor: readonly ? "default" : "pointer",
|
|
2558
2612
|
zIndex: "4"
|
|
2559
2613
|
});
|
|
2560
2614
|
diamond.tabIndex = 0;
|
|
@@ -2565,7 +2619,7 @@ function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cb
|
|
|
2565
2619
|
diamond.addEventListener("keydown", (event) => {
|
|
2566
2620
|
if (event.key === "Enter" || event.key === " ") {
|
|
2567
2621
|
event.preventDefault();
|
|
2568
|
-
cbs.
|
|
2622
|
+
cbs.onTaskClick?.(task.id);
|
|
2569
2623
|
}
|
|
2570
2624
|
});
|
|
2571
2625
|
const labelEl = el("span");
|
|
@@ -2585,7 +2639,15 @@ function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cb
|
|
|
2585
2639
|
layer.insertBefore(diamond, svgLayer);
|
|
2586
2640
|
bindMilestoneTask(diamond, task);
|
|
2587
2641
|
const dummy = el("div");
|
|
2588
|
-
|
|
2642
|
+
let cleanupDrag;
|
|
2643
|
+
if (!readonly) cleanupDrag = attachMilestoneClick(diamond, task.id, cbs);
|
|
2644
|
+
else diamond.addEventListener("click", (event) => {
|
|
2645
|
+
if (event.detail === 2) cbs.onTaskDoubleClick?.({
|
|
2646
|
+
id: task.id,
|
|
2647
|
+
task: toTask(task)
|
|
2648
|
+
});
|
|
2649
|
+
else cbs.onTaskClick?.(task.id);
|
|
2650
|
+
});
|
|
2589
2651
|
let cleanupLinkHandles;
|
|
2590
2652
|
if (state.linkCreationEnabled) {
|
|
2591
2653
|
const diamondCenterY = layout.y + layout.height / 2;
|
|
@@ -2611,12 +2673,46 @@ function renderMilestone(layer, svgLayer, task, layout, selectedId, registry, cb
|
|
|
2611
2673
|
diamond.removeEventListener("mouseleave", onDiamondLeave);
|
|
2612
2674
|
};
|
|
2613
2675
|
}
|
|
2676
|
+
const onTooltipEnter = () => {
|
|
2677
|
+
const content = cbs.onTooltipText?.({
|
|
2678
|
+
id: task.id,
|
|
2679
|
+
task: toTask(task)
|
|
2680
|
+
});
|
|
2681
|
+
if (content && content.length > 0) {
|
|
2682
|
+
tooltipEl.innerHTML = content;
|
|
2683
|
+
tooltipEl.style.display = "";
|
|
2684
|
+
} else tooltipEl.style.display = "none";
|
|
2685
|
+
};
|
|
2686
|
+
const onTooltipMove = (e) => {
|
|
2687
|
+
const offsetX = 12;
|
|
2688
|
+
const offsetY = -8;
|
|
2689
|
+
let left = e.clientX + offsetX;
|
|
2690
|
+
let top = e.clientY + offsetY;
|
|
2691
|
+
const maxLeft = window.innerWidth - tooltipEl.offsetWidth - 4;
|
|
2692
|
+
const maxTop = window.innerHeight - tooltipEl.offsetHeight - 4;
|
|
2693
|
+
left = Math.max(4, Math.min(left, maxLeft));
|
|
2694
|
+
top = Math.max(4, Math.min(top, maxTop));
|
|
2695
|
+
tooltipEl.style.left = `${left}px`;
|
|
2696
|
+
tooltipEl.style.top = `${top}px`;
|
|
2697
|
+
};
|
|
2698
|
+
const onTooltipLeave = () => {
|
|
2699
|
+
tooltipEl.style.display = "none";
|
|
2700
|
+
};
|
|
2701
|
+
diamond.addEventListener("mouseenter", onTooltipEnter);
|
|
2702
|
+
diamond.addEventListener("mousemove", onTooltipMove);
|
|
2703
|
+
diamond.addEventListener("mouseleave", onTooltipLeave);
|
|
2704
|
+
const cleanupTooltip = () => {
|
|
2705
|
+
diamond.removeEventListener("mouseenter", onTooltipEnter);
|
|
2706
|
+
diamond.removeEventListener("mousemove", onTooltipMove);
|
|
2707
|
+
diamond.removeEventListener("mouseleave", onTooltipLeave);
|
|
2708
|
+
};
|
|
2614
2709
|
const entry = {
|
|
2615
2710
|
bar: diamond,
|
|
2616
|
-
resizeHandle: dummy
|
|
2617
|
-
cleanupDrag
|
|
2711
|
+
resizeHandle: dummy
|
|
2618
2712
|
};
|
|
2713
|
+
if (cleanupDrag !== void 0) entry.cleanupDrag = cleanupDrag;
|
|
2619
2714
|
if (cleanupLinkHandles !== void 0) entry.cleanupLinkHandles = cleanupLinkHandles;
|
|
2715
|
+
if (cleanupTooltip !== void 0) entry.cleanupTooltip = cleanupTooltip;
|
|
2620
2716
|
registry.set(task.id, entry);
|
|
2621
2717
|
}
|
|
2622
2718
|
/**
|
|
@@ -2665,7 +2761,7 @@ function renderRightPane(refs, state, cbs) {
|
|
|
2665
2761
|
for (const node of toRemove) absoluteLayer.removeChild(node);
|
|
2666
2762
|
hideGhostLine(svgLayer);
|
|
2667
2763
|
for (const { cleanupDrag, cleanupLinkHandles, cleanupProgressDrag } of barRegistry.values()) {
|
|
2668
|
-
cleanupDrag();
|
|
2764
|
+
cleanupDrag?.();
|
|
2669
2765
|
cleanupLinkHandles?.();
|
|
2670
2766
|
cleanupProgressDrag?.();
|
|
2671
2767
|
}
|
|
@@ -2708,8 +2804,8 @@ function renderRightPane(refs, state, cbs) {
|
|
|
2708
2804
|
for (const task of visibleRows) {
|
|
2709
2805
|
const layout = layouts.get(task.id);
|
|
2710
2806
|
if (layout === void 0) continue;
|
|
2711
|
-
if (layout.kind === "milestone") renderMilestone(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, cbs, state);
|
|
2712
|
-
else renderBar(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, state, cbs);
|
|
2807
|
+
if (layout.kind === "milestone") renderMilestone(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, cbs, state, refs.tooltipEl);
|
|
2808
|
+
else renderBar(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, state, cbs, refs.tooltipEl);
|
|
2713
2809
|
}
|
|
2714
2810
|
updateDependencyLayer(svgLayer, links.filter((link) => visibleTaskIds.has(link.sourceTaskId) && visibleTaskIds.has(link.targetTaskId)), totalWidth, contentHeight, selectedId, highlightLinkedDependenciesOnSelect, cbs);
|
|
2715
2811
|
}
|
|
@@ -2869,15 +2965,16 @@ var GanttChart = class {
|
|
|
2869
2965
|
/**
|
|
2870
2966
|
* Constructs a new chart, builds the DOM, and wires internal event handling.
|
|
2871
2967
|
* Data must be loaded via {@link update} before the chart renders.
|
|
2968
|
+
* Callbacks must be set via {@link setCallbacks} before user interactions are handled.
|
|
2872
2969
|
*
|
|
2873
2970
|
* @param container - The host `HTMLElement` the chart will be appended to.
|
|
2874
|
-
* @param opts - Configuration
|
|
2971
|
+
* @param opts - Configuration options.
|
|
2875
2972
|
*/
|
|
2876
|
-
constructor(container, opts = {}
|
|
2973
|
+
constructor(container, opts = {}) {
|
|
2877
2974
|
this.#container = container;
|
|
2878
2975
|
this.#scale = opts.scale ?? "day";
|
|
2879
2976
|
this.#opts = opts;
|
|
2880
|
-
this.#callbacks =
|
|
2977
|
+
this.#callbacks = {};
|
|
2881
2978
|
this.#taskIndex = /* @__PURE__ */ new Map();
|
|
2882
2979
|
this.#locale = resolveChartLocale(opts.locale);
|
|
2883
2980
|
this.#columns = opts.gridColumns ?? gridColumnDefaults(this.#locale);
|
|
@@ -2887,21 +2984,39 @@ var GanttChart = class {
|
|
|
2887
2984
|
this.#weekendDays = normalizeWeekendDays(opts.weekendDays ?? this.#locale.weekendDays);
|
|
2888
2985
|
this.#specialDaysByDate = buildSpecialDayIndex(opts.specialDays ?? []);
|
|
2889
2986
|
this.#expandedIds = /* @__PURE__ */ new Set();
|
|
2890
|
-
this.#cbs =
|
|
2891
|
-
|
|
2987
|
+
this.#cbs = this.#buildCallbackAdapter();
|
|
2988
|
+
this.#buildDom();
|
|
2989
|
+
this.#wireEvents();
|
|
2990
|
+
container.append(this.#root);
|
|
2991
|
+
this.#applyTheme();
|
|
2992
|
+
this.#applyResponsivePaneStyles();
|
|
2993
|
+
this.#setupResizeObserver();
|
|
2994
|
+
}
|
|
2995
|
+
#buildCallbackAdapter() {
|
|
2996
|
+
return {
|
|
2997
|
+
onTaskClick: (id) => {
|
|
2892
2998
|
if (this.#selectedId === id) return;
|
|
2893
2999
|
this.#selectedId = id;
|
|
2894
3000
|
if (this.#selectedId !== null) {
|
|
2895
3001
|
const task = this.#findTask(this.#selectedId);
|
|
2896
|
-
if (task !== void 0) this.#callbacks.
|
|
3002
|
+
if (task !== void 0) this.#callbacks.onTaskClick?.({
|
|
3003
|
+
task,
|
|
3004
|
+
instance: this
|
|
3005
|
+
});
|
|
2897
3006
|
}
|
|
2898
3007
|
this.#scheduleRender();
|
|
2899
3008
|
},
|
|
2900
3009
|
onTaskDoubleClick: (payload) => {
|
|
2901
|
-
this.#callbacks.onTaskDoubleClick?.({
|
|
3010
|
+
this.#callbacks.onTaskDoubleClick?.({
|
|
3011
|
+
task: payload.task,
|
|
3012
|
+
instance: this
|
|
3013
|
+
});
|
|
2902
3014
|
},
|
|
2903
3015
|
onTaskEditIntent: (payload) => {
|
|
2904
|
-
this.#callbacks.onTaskDoubleClick?.({
|
|
3016
|
+
this.#callbacks.onTaskDoubleClick?.({
|
|
3017
|
+
task: payload.task,
|
|
3018
|
+
instance: this
|
|
3019
|
+
});
|
|
2905
3020
|
},
|
|
2906
3021
|
onTaskMove: (payload) => {
|
|
2907
3022
|
if (!this.#dragOriginals.has(payload.id)) {
|
|
@@ -2912,13 +3027,20 @@ var GanttChart = class {
|
|
|
2912
3027
|
this.#patchTask(payload.id, { startDate: iso });
|
|
2913
3028
|
this.#scheduleRender();
|
|
2914
3029
|
},
|
|
2915
|
-
_onTaskMoveFinal: (payload) => {
|
|
3030
|
+
_onTaskMoveFinal: async (payload) => {
|
|
2916
3031
|
const task = this.#findTask(payload.id);
|
|
2917
3032
|
if (task !== void 0) {
|
|
2918
|
-
|
|
3033
|
+
const result = this.#callbacks.onTaskMove?.({
|
|
2919
3034
|
task,
|
|
2920
|
-
newStartDate: payload.startDate
|
|
2921
|
-
|
|
3035
|
+
newStartDate: payload.startDate,
|
|
3036
|
+
instance: this
|
|
3037
|
+
});
|
|
3038
|
+
if (result instanceof Promise) {
|
|
3039
|
+
if (!await result) {
|
|
3040
|
+
const original = this.#dragOriginals.get(payload.id);
|
|
3041
|
+
if (original !== void 0) this.#patchTask(payload.id, { startDate: original.startDate });
|
|
3042
|
+
}
|
|
3043
|
+
} else if (!result) {
|
|
2922
3044
|
const original = this.#dragOriginals.get(payload.id);
|
|
2923
3045
|
if (original !== void 0) this.#patchTask(payload.id, { startDate: original.startDate });
|
|
2924
3046
|
}
|
|
@@ -2935,13 +3057,20 @@ var GanttChart = class {
|
|
|
2935
3057
|
this.#patchTask(payload.id, { durationHours: payload.durationHours });
|
|
2936
3058
|
this.#scheduleRender();
|
|
2937
3059
|
},
|
|
2938
|
-
_onTaskResizeFinal: (payload) => {
|
|
3060
|
+
_onTaskResizeFinal: async (payload) => {
|
|
2939
3061
|
const task = this.#findTask(payload.id);
|
|
2940
3062
|
if (task !== void 0) {
|
|
2941
|
-
|
|
3063
|
+
const result = this.#callbacks.onTaskResize?.({
|
|
2942
3064
|
task,
|
|
2943
|
-
newDurationHours: payload.durationHours
|
|
2944
|
-
|
|
3065
|
+
newDurationHours: payload.durationHours,
|
|
3066
|
+
instance: this
|
|
3067
|
+
});
|
|
3068
|
+
if (result instanceof Promise) {
|
|
3069
|
+
if (!await result) {
|
|
3070
|
+
const original = this.#dragOriginals.get(payload.id);
|
|
3071
|
+
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { durationHours: original.durationHours });
|
|
3072
|
+
}
|
|
3073
|
+
} else if (!result) {
|
|
2945
3074
|
const original = this.#dragOriginals.get(payload.id);
|
|
2946
3075
|
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { durationHours: original.durationHours });
|
|
2947
3076
|
}
|
|
@@ -2958,13 +3087,20 @@ var GanttChart = class {
|
|
|
2958
3087
|
this.#patchTask(payload.id, { percentComplete: payload.percentComplete });
|
|
2959
3088
|
this.#scheduleRender();
|
|
2960
3089
|
},
|
|
2961
|
-
_onTaskProgressDragFinal: (payload) => {
|
|
3090
|
+
_onTaskProgressDragFinal: async (payload) => {
|
|
2962
3091
|
const task = this.#findTask(payload.id);
|
|
2963
3092
|
if (task !== void 0) {
|
|
2964
|
-
|
|
3093
|
+
const result = this.#callbacks.onProgressChange?.({
|
|
2965
3094
|
task,
|
|
2966
|
-
newPercentComplete: payload.percentComplete
|
|
2967
|
-
|
|
3095
|
+
newPercentComplete: payload.percentComplete,
|
|
3096
|
+
instance: this
|
|
3097
|
+
});
|
|
3098
|
+
if (result instanceof Promise) {
|
|
3099
|
+
if (!await result) {
|
|
3100
|
+
const original = this.#dragOriginals.get(payload.id);
|
|
3101
|
+
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { percentComplete: original.percentComplete });
|
|
3102
|
+
}
|
|
3103
|
+
} else if (!result) {
|
|
2968
3104
|
const original = this.#dragOriginals.get(payload.id);
|
|
2969
3105
|
if (original !== void 0 && original.kind !== "milestone") this.#patchTask(payload.id, { percentComplete: original.percentComplete });
|
|
2970
3106
|
}
|
|
@@ -2975,13 +3111,22 @@ var GanttChart = class {
|
|
|
2975
3111
|
},
|
|
2976
3112
|
onTaskAdd: (parentId) => {
|
|
2977
3113
|
const parentTask = this.#findTask(parentId);
|
|
2978
|
-
if (parentTask !== void 0) this.#callbacks.onTaskAdd?.({
|
|
3114
|
+
if (parentTask !== void 0) this.#callbacks.onTaskAdd?.({
|
|
3115
|
+
parentTask,
|
|
3116
|
+
instance: this
|
|
3117
|
+
});
|
|
2979
3118
|
},
|
|
2980
3119
|
onLeftPaneWidthChange: (width) => {
|
|
2981
|
-
this.#callbacks.onLeftPaneWidthChange?.(
|
|
3120
|
+
this.#callbacks.onLeftPaneWidthChange?.({
|
|
3121
|
+
width,
|
|
3122
|
+
instance: this
|
|
3123
|
+
});
|
|
2982
3124
|
},
|
|
2983
3125
|
onGridColumnsChange: (updatedColumns) => {
|
|
2984
|
-
this.#callbacks.onGridColumnsChange?.(
|
|
3126
|
+
this.#callbacks.onGridColumnsChange?.({
|
|
3127
|
+
columns: updatedColumns,
|
|
3128
|
+
instance: this
|
|
3129
|
+
});
|
|
2985
3130
|
},
|
|
2986
3131
|
onLinkCreate: (payload) => {
|
|
2987
3132
|
const sourceTask = this.#findTask(payload.sourceTaskId);
|
|
@@ -2989,22 +3134,39 @@ var GanttChart = class {
|
|
|
2989
3134
|
if (sourceTask !== void 0 && targetTask !== void 0) this.#callbacks.onLinkCreate?.({
|
|
2990
3135
|
type: "FS",
|
|
2991
3136
|
sourceTask,
|
|
2992
|
-
targetTask
|
|
3137
|
+
targetTask,
|
|
3138
|
+
instance: this
|
|
2993
3139
|
});
|
|
2994
3140
|
},
|
|
2995
3141
|
onLinkClick: (payload) => {
|
|
2996
|
-
this.#callbacks.onLinkClick?.({
|
|
3142
|
+
this.#callbacks.onLinkClick?.({
|
|
3143
|
+
link: payload,
|
|
3144
|
+
instance: this
|
|
3145
|
+
});
|
|
2997
3146
|
},
|
|
2998
3147
|
onLinkDblClick: (payload) => {
|
|
2999
|
-
this.#callbacks.onLinkDblClick?.({
|
|
3000
|
-
|
|
3148
|
+
this.#callbacks.onLinkDblClick?.({
|
|
3149
|
+
link: payload,
|
|
3150
|
+
instance: this
|
|
3151
|
+
});
|
|
3152
|
+
},
|
|
3153
|
+
onTooltipText: (payload) => this.#callbacks.onTooltipText?.({
|
|
3154
|
+
task: payload.task,
|
|
3155
|
+
instance: this
|
|
3156
|
+
}) ?? null
|
|
3001
3157
|
};
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3158
|
+
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Sets or replaces the chart's user-facing callbacks.
|
|
3161
|
+
* Does not trigger a re-render.
|
|
3162
|
+
*
|
|
3163
|
+
* @param cbs - The {@link GanttCallbacks} to register.
|
|
3164
|
+
* @throws {GanttError} When the instance has been destroyed.
|
|
3165
|
+
*/
|
|
3166
|
+
setCallbacks(cbs) {
|
|
3167
|
+
this.#assertAlive();
|
|
3168
|
+
this.#callbacks = cbs;
|
|
3169
|
+
this.#cbs = this.#buildCallbackAdapter();
|
|
3008
3170
|
}
|
|
3009
3171
|
/**
|
|
3010
3172
|
* Replaces the full dataset and re-renders.
|
|
@@ -3085,14 +3247,18 @@ var GanttChart = class {
|
|
|
3085
3247
|
* Programmatically selects or deselects a task.
|
|
3086
3248
|
*
|
|
3087
3249
|
* @param id - The task ID to select, or `null` to clear the selection.
|
|
3250
|
+
* @param fireCallback - Whether to fire the `onTaskClick` callback. Default `true`.
|
|
3088
3251
|
* @throws {GanttError} When the instance has been destroyed.
|
|
3089
3252
|
*/
|
|
3090
|
-
select(id) {
|
|
3253
|
+
select(id, fireCallback = true) {
|
|
3091
3254
|
this.#assertAlive();
|
|
3092
3255
|
if (id === null) this.#selectedId = null;
|
|
3093
3256
|
else {
|
|
3094
3257
|
const task = this.#input?.tasks.find((t) => t.id === id);
|
|
3095
|
-
if (task !== void 0) this.#callbacks.
|
|
3258
|
+
if (task !== void 0 && fireCallback) this.#callbacks.onTaskClick?.({
|
|
3259
|
+
task,
|
|
3260
|
+
instance: this
|
|
3261
|
+
});
|
|
3096
3262
|
this.#selectedId = id;
|
|
3097
3263
|
}
|
|
3098
3264
|
if (this.#rafPending && this.#rafId !== null) {
|
|
@@ -3145,10 +3311,11 @@ var GanttChart = class {
|
|
|
3145
3311
|
else window.removeEventListener("resize", this.#applyResponsivePaneStyles);
|
|
3146
3312
|
if (this.#rafId !== null) cancelAnimationFrame(this.#rafId);
|
|
3147
3313
|
this.#columnResizeCleanup();
|
|
3148
|
-
for (const { cleanupDrag, cleanupLinkHandles, cleanupProgressDrag } of this.#rightPaneRefs.barRegistry.values()) {
|
|
3149
|
-
cleanupDrag();
|
|
3314
|
+
for (const { cleanupDrag, cleanupLinkHandles, cleanupProgressDrag, cleanupTooltip } of this.#rightPaneRefs.barRegistry.values()) {
|
|
3315
|
+
cleanupDrag?.();
|
|
3150
3316
|
cleanupLinkHandles?.();
|
|
3151
3317
|
cleanupProgressDrag?.();
|
|
3318
|
+
cleanupTooltip?.();
|
|
3152
3319
|
}
|
|
3153
3320
|
clearChildren(this.#container);
|
|
3154
3321
|
}
|
|
@@ -3181,7 +3348,7 @@ var GanttChart = class {
|
|
|
3181
3348
|
id: payload.id,
|
|
3182
3349
|
atMs: now
|
|
3183
3350
|
};
|
|
3184
|
-
this.#cbs.
|
|
3351
|
+
this.#cbs.onTaskClick?.(payload.id);
|
|
3185
3352
|
};
|
|
3186
3353
|
#onScroll = () => {
|
|
3187
3354
|
({scrollTop: this.#scrollTop} = this.#scrollEl);
|
|
@@ -3265,7 +3432,7 @@ var GanttChart = class {
|
|
|
3265
3432
|
else this.#expandedIds.add(id);
|
|
3266
3433
|
this.#scheduleRender();
|
|
3267
3434
|
},
|
|
3268
|
-
|
|
3435
|
+
onTaskClick: (id) => this.#cbs.onTaskClick?.(id),
|
|
3269
3436
|
onRowClick: (payload) => {
|
|
3270
3437
|
this.#handleGridClick(payload);
|
|
3271
3438
|
},
|