gantt-task-react-v 1.2.14 → 1.3.2
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/dist/gantt-task-react.es.js +383 -50
- package/dist/gantt-task-react.umd.js +383 -50
- package/dist/helpers/use-incremental-coordinates.d.ts +17 -0
- package/dist/helpers/use-memory-management.d.ts +122 -0
- package/dist/helpers/use-optimized-data-structures.d.ts +24 -0
- package/dist/helpers/use-optimized-svg-rendering.d.ts +117 -0
- package/dist/helpers/use-performance-monitor.d.ts +106 -0
- package/dist/helpers/use-progressive-loading.d.ts +63 -0
- package/dist/helpers/use-spatial-dependency-map.d.ts +49 -0
- package/dist/helpers/use-web-worker.d.ts +50 -0
- package/package.json +7 -5
|
@@ -4701,11 +4701,14 @@
|
|
|
4701
4701
|
if (!containerEl) {
|
|
4702
4702
|
return null;
|
|
4703
4703
|
}
|
|
4704
|
-
const
|
|
4705
|
-
const
|
|
4706
|
-
const
|
|
4707
|
-
const
|
|
4708
|
-
const
|
|
4704
|
+
const el = containerEl;
|
|
4705
|
+
const scrollValue = property === "scrollLeft" ? el.scrollLeft : el.scrollTop;
|
|
4706
|
+
const maxScrollValue = property === "scrollLeft" ? el.scrollWidth : el.scrollHeight;
|
|
4707
|
+
const fullValue = property === "scrollLeft" ? el.clientWidth : el.clientHeight;
|
|
4708
|
+
const firstIndex = Math.max(0, Math.floor(scrollValue / cellSize));
|
|
4709
|
+
const visibleCount = Math.max(1, Math.ceil(fullValue / cellSize));
|
|
4710
|
+
const overscan = Math.min(100, Math.max(10, Math.ceil(visibleCount * 0.5)));
|
|
4711
|
+
const lastIndex = Math.floor((scrollValue + fullValue) / cellSize) + overscan;
|
|
4709
4712
|
const isStartOfScroll = scrollValue < DELTA;
|
|
4710
4713
|
const isEndOfScroll = scrollValue + fullValue > maxScrollValue - DELTA;
|
|
4711
4714
|
return [firstIndex, lastIndex, isStartOfScroll, isEndOfScroll, fullValue];
|
|
@@ -4714,31 +4717,49 @@
|
|
|
4714
4717
|
const [indexes, setIndexes] = React.useState(
|
|
4715
4718
|
() => getStartAndEnd(containerRef.current, property, cellSize)
|
|
4716
4719
|
);
|
|
4720
|
+
const rafRef = React.useRef(null);
|
|
4721
|
+
const pendingRef = React.useRef(false);
|
|
4722
|
+
const update = React.useMemo(() => {
|
|
4723
|
+
const fn = () => {
|
|
4724
|
+
pendingRef.current = false;
|
|
4725
|
+
const next = getStartAndEnd(containerRef.current, property, cellSize);
|
|
4726
|
+
setIndexes((prev) => {
|
|
4727
|
+
const changed = !prev || !next || next.some((v, i) => prev ? prev[i] !== v : true);
|
|
4728
|
+
return changed ? next : prev;
|
|
4729
|
+
});
|
|
4730
|
+
rafRef.current = null;
|
|
4731
|
+
};
|
|
4732
|
+
return fn;
|
|
4733
|
+
}, [cellSize, containerRef, property]);
|
|
4717
4734
|
React.useEffect(() => {
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
) : true : Boolean(nextIndexes);
|
|
4729
|
-
if (isChanged) {
|
|
4730
|
-
prevIndexes = nextIndexes;
|
|
4731
|
-
setIndexes(nextIndexes);
|
|
4732
|
-
}
|
|
4733
|
-
rafId = requestAnimationFrame(handler);
|
|
4735
|
+
const el = containerRef.current;
|
|
4736
|
+
if (!el) {
|
|
4737
|
+
return void 0;
|
|
4738
|
+
}
|
|
4739
|
+
setIndexes(getStartAndEnd(el, property, cellSize));
|
|
4740
|
+
const onScroll = () => {
|
|
4741
|
+
if (pendingRef.current)
|
|
4742
|
+
return;
|
|
4743
|
+
pendingRef.current = true;
|
|
4744
|
+
rafRef.current = requestAnimationFrame(update);
|
|
4734
4745
|
};
|
|
4735
|
-
|
|
4746
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
4747
|
+
if (pendingRef.current)
|
|
4748
|
+
return;
|
|
4749
|
+
pendingRef.current = true;
|
|
4750
|
+
rafRef.current = requestAnimationFrame(update);
|
|
4751
|
+
});
|
|
4752
|
+
resizeObserver.observe(el);
|
|
4753
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
4736
4754
|
return () => {
|
|
4737
|
-
|
|
4738
|
-
|
|
4755
|
+
el.removeEventListener("scroll", onScroll);
|
|
4756
|
+
resizeObserver.disconnect();
|
|
4757
|
+
if (rafRef.current !== null) {
|
|
4758
|
+
cancelAnimationFrame(rafRef.current);
|
|
4739
4759
|
}
|
|
4760
|
+
pendingRef.current = false;
|
|
4740
4761
|
};
|
|
4741
|
-
}, [cellSize, containerRef,
|
|
4762
|
+
}, [cellSize, containerRef, property, update]);
|
|
4742
4763
|
return indexes;
|
|
4743
4764
|
};
|
|
4744
4765
|
const taskListNameWrapper = "_taskListNameWrapper_16z6n_1";
|
|
@@ -10928,7 +10949,7 @@
|
|
|
10928
10949
|
};
|
|
10929
10950
|
const GanttTodayInner = ({
|
|
10930
10951
|
additionalLeftSpace,
|
|
10931
|
-
distances,
|
|
10952
|
+
distances: { columnWidth },
|
|
10932
10953
|
ganttFullHeight,
|
|
10933
10954
|
isUnknownDates,
|
|
10934
10955
|
rtl,
|
|
@@ -10942,7 +10963,6 @@
|
|
|
10942
10963
|
todayLabel = "Today",
|
|
10943
10964
|
dataDateLabel = "Data Date"
|
|
10944
10965
|
}) => {
|
|
10945
|
-
const { columnWidth, rowHeight, barFill } = distances;
|
|
10946
10966
|
const todayElement = React.useMemo(() => {
|
|
10947
10967
|
if (isUnknownDates || !showTodayLine) {
|
|
10948
10968
|
return null;
|
|
@@ -11049,20 +11069,13 @@
|
|
|
11049
11069
|
}
|
|
11050
11070
|
};
|
|
11051
11071
|
const tickX = dataIndex * columnWidth * extraMultiplier();
|
|
11052
|
-
const
|
|
11072
|
+
const x = rtl ? tickX + columnWidth : tickX;
|
|
11053
11073
|
const color = dataDateColor || "var(--gantt-calendar-today-color)";
|
|
11054
|
-
const taskHeight = rowHeight * barFill / 100;
|
|
11055
|
-
const rotatedHeight = taskHeight / Math.SQRT2;
|
|
11056
|
-
const centerX = dateBaseX;
|
|
11057
|
-
const centerY = 6;
|
|
11058
|
-
const rectX = centerX - rotatedHeight / 2;
|
|
11059
|
-
const rectY = centerY - rotatedHeight / 2;
|
|
11060
|
-
const rightEdgeX = centerX + taskHeight / 2;
|
|
11061
11074
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11062
11075
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11063
11076
|
"rect",
|
|
11064
11077
|
{
|
|
11065
|
-
x: additionalLeftSpace +
|
|
11078
|
+
x: additionalLeftSpace + x,
|
|
11066
11079
|
y: 0,
|
|
11067
11080
|
width: 2,
|
|
11068
11081
|
height: ganttFullHeight,
|
|
@@ -11071,22 +11084,19 @@
|
|
|
11071
11084
|
}
|
|
11072
11085
|
),
|
|
11073
11086
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11074
|
-
"
|
|
11087
|
+
"circle",
|
|
11075
11088
|
{
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
fill: color
|
|
11081
|
-
transform: `rotate(45 ${additionalLeftSpace + centerX} ${centerY})`,
|
|
11082
|
-
rx: 2,
|
|
11083
|
-
ry: 2
|
|
11089
|
+
className: styles$c.ganttTodayCircle,
|
|
11090
|
+
cx: x + 1,
|
|
11091
|
+
cy: 6,
|
|
11092
|
+
r: 6,
|
|
11093
|
+
fill: color
|
|
11084
11094
|
}
|
|
11085
11095
|
),
|
|
11086
11096
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11087
11097
|
"text",
|
|
11088
11098
|
{
|
|
11089
|
-
x: additionalLeftSpace +
|
|
11099
|
+
x: additionalLeftSpace + x + 8,
|
|
11090
11100
|
y: 10,
|
|
11091
11101
|
fill: color,
|
|
11092
11102
|
fontSize: 12,
|
|
@@ -11105,9 +11115,7 @@
|
|
|
11105
11115
|
showDataDateLine,
|
|
11106
11116
|
dataDate,
|
|
11107
11117
|
dataDateColor,
|
|
11108
|
-
dataDateLabel
|
|
11109
|
-
rowHeight,
|
|
11110
|
-
barFill
|
|
11118
|
+
dataDateLabel
|
|
11111
11119
|
]);
|
|
11112
11120
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "today", children: [
|
|
11113
11121
|
dataDateElement,
|
|
@@ -17495,6 +17503,308 @@
|
|
|
17495
17503
|
scrollToRightStep
|
|
17496
17504
|
];
|
|
17497
17505
|
};
|
|
17506
|
+
const usePerformanceMonitor = (enabled = true) => {
|
|
17507
|
+
const metricsRef = React.useRef([]);
|
|
17508
|
+
const benchmarksRef = React.useRef(/* @__PURE__ */ new Map());
|
|
17509
|
+
const frameTimesRef = React.useRef([]);
|
|
17510
|
+
const lastFrameTimeRef = React.useRef(performance.now());
|
|
17511
|
+
const updateFPS = React.useCallback(() => {
|
|
17512
|
+
if (!enabled)
|
|
17513
|
+
return;
|
|
17514
|
+
const now = performance.now();
|
|
17515
|
+
const deltaTime = now - lastFrameTimeRef.current;
|
|
17516
|
+
lastFrameTimeRef.current = now;
|
|
17517
|
+
frameTimesRef.current.push(deltaTime);
|
|
17518
|
+
if (frameTimesRef.current.length > 60) {
|
|
17519
|
+
frameTimesRef.current.shift();
|
|
17520
|
+
}
|
|
17521
|
+
}, [enabled]);
|
|
17522
|
+
const getFPS = React.useCallback(() => {
|
|
17523
|
+
if (frameTimesRef.current.length === 0)
|
|
17524
|
+
return 0;
|
|
17525
|
+
const avgFrameTime = frameTimesRef.current.reduce((sum, time) => sum + time, 0) / frameTimesRef.current.length;
|
|
17526
|
+
return Math.round(1e3 / avgFrameTime);
|
|
17527
|
+
}, []);
|
|
17528
|
+
const startMeasurement = React.useCallback(
|
|
17529
|
+
(name, metadata) => {
|
|
17530
|
+
if (!enabled)
|
|
17531
|
+
return;
|
|
17532
|
+
performance.mark(`${name}-start`);
|
|
17533
|
+
benchmarksRef.current.set(name, {
|
|
17534
|
+
name,
|
|
17535
|
+
startTime: performance.now(),
|
|
17536
|
+
metadata
|
|
17537
|
+
});
|
|
17538
|
+
},
|
|
17539
|
+
[enabled]
|
|
17540
|
+
);
|
|
17541
|
+
const endMeasurement = React.useCallback(
|
|
17542
|
+
(name) => {
|
|
17543
|
+
if (!enabled)
|
|
17544
|
+
return 0;
|
|
17545
|
+
const benchmark = benchmarksRef.current.get(name);
|
|
17546
|
+
if (!benchmark) {
|
|
17547
|
+
console.warn(`No benchmark found for ${name}`);
|
|
17548
|
+
return 0;
|
|
17549
|
+
}
|
|
17550
|
+
const endTime = performance.now();
|
|
17551
|
+
const duration = endTime - benchmark.startTime;
|
|
17552
|
+
performance.mark(`${name}-end`);
|
|
17553
|
+
performance.measure(name, `${name}-start`, `${name}-end`);
|
|
17554
|
+
benchmark.endTime = endTime;
|
|
17555
|
+
benchmark.duration = duration;
|
|
17556
|
+
return duration;
|
|
17557
|
+
},
|
|
17558
|
+
[enabled]
|
|
17559
|
+
);
|
|
17560
|
+
const measureFunction = React.useCallback(
|
|
17561
|
+
(name, fn, metadata) => {
|
|
17562
|
+
return (...args) => {
|
|
17563
|
+
if (!enabled)
|
|
17564
|
+
return fn(...args);
|
|
17565
|
+
startMeasurement(name, metadata);
|
|
17566
|
+
try {
|
|
17567
|
+
const result = fn(...args);
|
|
17568
|
+
return result;
|
|
17569
|
+
} finally {
|
|
17570
|
+
endMeasurement(name);
|
|
17571
|
+
}
|
|
17572
|
+
};
|
|
17573
|
+
},
|
|
17574
|
+
[enabled, startMeasurement, endMeasurement]
|
|
17575
|
+
);
|
|
17576
|
+
const measureAsyncFunction = React.useCallback(
|
|
17577
|
+
(name, fn, metadata) => {
|
|
17578
|
+
return async (...args) => {
|
|
17579
|
+
if (!enabled)
|
|
17580
|
+
return fn(...args);
|
|
17581
|
+
startMeasurement(name, metadata);
|
|
17582
|
+
try {
|
|
17583
|
+
const result = await fn(...args);
|
|
17584
|
+
return result;
|
|
17585
|
+
} finally {
|
|
17586
|
+
endMeasurement(name);
|
|
17587
|
+
}
|
|
17588
|
+
};
|
|
17589
|
+
},
|
|
17590
|
+
[enabled, startMeasurement, endMeasurement]
|
|
17591
|
+
);
|
|
17592
|
+
const getPerformanceReport = React.useCallback(() => {
|
|
17593
|
+
const entries = performance.getEntriesByType(
|
|
17594
|
+
"measure"
|
|
17595
|
+
);
|
|
17596
|
+
const report = {};
|
|
17597
|
+
entries.forEach((entry) => {
|
|
17598
|
+
if (!report[entry.name]) {
|
|
17599
|
+
report[entry.name] = {
|
|
17600
|
+
count: 0,
|
|
17601
|
+
totalTime: 0,
|
|
17602
|
+
avgTime: 0,
|
|
17603
|
+
maxTime: 0,
|
|
17604
|
+
minTime: Infinity
|
|
17605
|
+
};
|
|
17606
|
+
}
|
|
17607
|
+
const stat = report[entry.name];
|
|
17608
|
+
stat.count++;
|
|
17609
|
+
stat.totalTime += entry.duration;
|
|
17610
|
+
stat.maxTime = Math.max(stat.maxTime, entry.duration);
|
|
17611
|
+
stat.minTime = Math.min(stat.minTime, entry.duration);
|
|
17612
|
+
stat.avgTime = stat.totalTime / stat.count;
|
|
17613
|
+
});
|
|
17614
|
+
return {
|
|
17615
|
+
measurements: report,
|
|
17616
|
+
fps: getFPS(),
|
|
17617
|
+
currentMetrics: metricsRef.current[metricsRef.current.length - 1],
|
|
17618
|
+
allMetrics: [...metricsRef.current]
|
|
17619
|
+
};
|
|
17620
|
+
}, [getFPS]);
|
|
17621
|
+
const addMetrics = React.useCallback(
|
|
17622
|
+
(metrics) => {
|
|
17623
|
+
if (!enabled)
|
|
17624
|
+
return;
|
|
17625
|
+
const fullMetrics = {
|
|
17626
|
+
...metrics,
|
|
17627
|
+
timestamp: Date.now()
|
|
17628
|
+
};
|
|
17629
|
+
metricsRef.current.push(fullMetrics);
|
|
17630
|
+
if (metricsRef.current.length > 100) {
|
|
17631
|
+
metricsRef.current.shift();
|
|
17632
|
+
}
|
|
17633
|
+
},
|
|
17634
|
+
[enabled]
|
|
17635
|
+
);
|
|
17636
|
+
const clearMeasurements = React.useCallback(() => {
|
|
17637
|
+
performance.clearMarks();
|
|
17638
|
+
performance.clearMeasures();
|
|
17639
|
+
benchmarksRef.current.clear();
|
|
17640
|
+
metricsRef.current.length = 0;
|
|
17641
|
+
frameTimesRef.current.length = 0;
|
|
17642
|
+
}, []);
|
|
17643
|
+
const getMemoryUsage = React.useCallback(() => {
|
|
17644
|
+
if ("memory" in performance) {
|
|
17645
|
+
const memory = performance.memory;
|
|
17646
|
+
return memory ? memory.usedJSHeapSize : 0;
|
|
17647
|
+
}
|
|
17648
|
+
return 0;
|
|
17649
|
+
}, []);
|
|
17650
|
+
return {
|
|
17651
|
+
startMeasurement,
|
|
17652
|
+
endMeasurement,
|
|
17653
|
+
measureFunction,
|
|
17654
|
+
measureAsyncFunction,
|
|
17655
|
+
getPerformanceReport,
|
|
17656
|
+
addMetrics,
|
|
17657
|
+
clearMeasurements,
|
|
17658
|
+
updateFPS,
|
|
17659
|
+
getFPS,
|
|
17660
|
+
getMemoryUsage,
|
|
17661
|
+
enabled
|
|
17662
|
+
};
|
|
17663
|
+
};
|
|
17664
|
+
const useAdaptivePerformance = (thresholds) => {
|
|
17665
|
+
const { getFPS, getPerformanceReport } = usePerformanceMonitor();
|
|
17666
|
+
const performanceLevelRef = React.useRef("high");
|
|
17667
|
+
const updatePerformanceLevel = React.useCallback(() => {
|
|
17668
|
+
var _a;
|
|
17669
|
+
const fps = getFPS();
|
|
17670
|
+
const report = getPerformanceReport();
|
|
17671
|
+
const renderTime = ((_a = report.measurements["render"]) == null ? void 0 : _a.avgTime) || 0;
|
|
17672
|
+
if (fps < thresholds.fpsThreshold * 0.5 || renderTime > thresholds.renderTimeThreshold * 2) {
|
|
17673
|
+
performanceLevelRef.current = "low";
|
|
17674
|
+
} else if (fps < thresholds.fpsThreshold || renderTime > thresholds.renderTimeThreshold) {
|
|
17675
|
+
performanceLevelRef.current = "medium";
|
|
17676
|
+
} else {
|
|
17677
|
+
performanceLevelRef.current = "high";
|
|
17678
|
+
}
|
|
17679
|
+
return performanceLevelRef.current;
|
|
17680
|
+
}, [getFPS, getPerformanceReport, thresholds]);
|
|
17681
|
+
const getQualitySettings = React.useCallback(() => {
|
|
17682
|
+
const level = performanceLevelRef.current;
|
|
17683
|
+
switch (level) {
|
|
17684
|
+
case "low":
|
|
17685
|
+
return {
|
|
17686
|
+
enableAnimations: false,
|
|
17687
|
+
renderQuality: "low",
|
|
17688
|
+
maxVisibleTasks: 100,
|
|
17689
|
+
updateFrequency: 500
|
|
17690
|
+
// ms
|
|
17691
|
+
};
|
|
17692
|
+
case "medium":
|
|
17693
|
+
return {
|
|
17694
|
+
enableAnimations: true,
|
|
17695
|
+
renderQuality: "medium",
|
|
17696
|
+
maxVisibleTasks: 500,
|
|
17697
|
+
updateFrequency: 100
|
|
17698
|
+
};
|
|
17699
|
+
case "high":
|
|
17700
|
+
default:
|
|
17701
|
+
return {
|
|
17702
|
+
enableAnimations: true,
|
|
17703
|
+
renderQuality: "high",
|
|
17704
|
+
maxVisibleTasks: 1e3,
|
|
17705
|
+
updateFrequency: 16
|
|
17706
|
+
// 60fps
|
|
17707
|
+
};
|
|
17708
|
+
}
|
|
17709
|
+
}, []);
|
|
17710
|
+
return {
|
|
17711
|
+
performanceLevel: performanceLevelRef.current,
|
|
17712
|
+
updatePerformanceLevel,
|
|
17713
|
+
getQualitySettings
|
|
17714
|
+
};
|
|
17715
|
+
};
|
|
17716
|
+
const useIncrementalCoordinates = (visibleTasks, visibleTasksMirror, taskToRowIndexMap, startDate, viewMode, rtl, fullRowHeight, taskHeight, taskYOffset, distances, svgWidth, renderedRowIndexes, renderedColumnIndexes) => {
|
|
17717
|
+
const cacheRef = React.useRef({
|
|
17718
|
+
coordinates: /* @__PURE__ */ new Map(),
|
|
17719
|
+
lastUpdateParams: null
|
|
17720
|
+
});
|
|
17721
|
+
return React.useMemo(() => {
|
|
17722
|
+
const cache = cacheRef.current;
|
|
17723
|
+
const currentParams = {
|
|
17724
|
+
startDate,
|
|
17725
|
+
viewMode,
|
|
17726
|
+
rtl,
|
|
17727
|
+
fullRowHeight,
|
|
17728
|
+
taskHeight,
|
|
17729
|
+
taskYOffset,
|
|
17730
|
+
svgWidth,
|
|
17731
|
+
distances
|
|
17732
|
+
};
|
|
17733
|
+
const needsFullRecalc = !cache.lastUpdateParams || cache.lastUpdateParams.startDate.getTime() !== startDate.getTime() || cache.lastUpdateParams.viewMode !== viewMode || cache.lastUpdateParams.rtl !== rtl || cache.lastUpdateParams.fullRowHeight !== fullRowHeight || cache.lastUpdateParams.taskHeight !== taskHeight || cache.lastUpdateParams.taskYOffset !== taskYOffset || cache.lastUpdateParams.svgWidth !== svgWidth || JSON.stringify(cache.lastUpdateParams.distances) !== JSON.stringify(distances);
|
|
17734
|
+
if (needsFullRecalc) {
|
|
17735
|
+
cache.coordinates.clear();
|
|
17736
|
+
}
|
|
17737
|
+
const tasksToCalculate = /* @__PURE__ */ new Set();
|
|
17738
|
+
if (renderedRowIndexes && renderedColumnIndexes) {
|
|
17739
|
+
const [startRow, endRow] = renderedRowIndexes;
|
|
17740
|
+
const rowBuffer = Math.max(5, Math.ceil((endRow - startRow) * 0.5));
|
|
17741
|
+
const bufferedStartRow = Math.max(0, startRow - rowBuffer);
|
|
17742
|
+
const bufferedEndRow = endRow + rowBuffer;
|
|
17743
|
+
visibleTasks.forEach((task, index2) => {
|
|
17744
|
+
if (task.type === "empty" || !visibleTasksMirror[task.id])
|
|
17745
|
+
return;
|
|
17746
|
+
if (index2 >= bufferedStartRow && index2 <= bufferedEndRow) {
|
|
17747
|
+
tasksToCalculate.add(task.id);
|
|
17748
|
+
}
|
|
17749
|
+
});
|
|
17750
|
+
} else {
|
|
17751
|
+
visibleTasks.forEach((task) => {
|
|
17752
|
+
if (task.type !== "empty" && visibleTasksMirror[task.id]) {
|
|
17753
|
+
tasksToCalculate.add(task.id);
|
|
17754
|
+
}
|
|
17755
|
+
});
|
|
17756
|
+
}
|
|
17757
|
+
if (!needsFullRecalc) {
|
|
17758
|
+
for (const [taskId] of cache.coordinates) {
|
|
17759
|
+
if (!tasksToCalculate.has(taskId)) {
|
|
17760
|
+
cache.coordinates.delete(taskId);
|
|
17761
|
+
}
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
const res = /* @__PURE__ */ new Map();
|
|
17765
|
+
visibleTasks.forEach((task) => {
|
|
17766
|
+
if (task.type === "empty" || !tasksToCalculate.has(task.id))
|
|
17767
|
+
return;
|
|
17768
|
+
const { id, comparisonLevel = 1 } = task;
|
|
17769
|
+
const cacheKey = `${id}_${comparisonLevel}`;
|
|
17770
|
+
let taskCoordinates = cache.coordinates.get(cacheKey);
|
|
17771
|
+
if (!taskCoordinates || needsFullRecalc) {
|
|
17772
|
+
taskCoordinates = countTaskCoordinates(
|
|
17773
|
+
task,
|
|
17774
|
+
taskToRowIndexMap,
|
|
17775
|
+
startDate,
|
|
17776
|
+
viewMode,
|
|
17777
|
+
rtl,
|
|
17778
|
+
fullRowHeight,
|
|
17779
|
+
taskHeight,
|
|
17780
|
+
taskYOffset,
|
|
17781
|
+
distances,
|
|
17782
|
+
svgWidth
|
|
17783
|
+
);
|
|
17784
|
+
cache.coordinates.set(cacheKey, taskCoordinates);
|
|
17785
|
+
}
|
|
17786
|
+
const resByLevel = res.get(comparisonLevel) || /* @__PURE__ */ new Map();
|
|
17787
|
+
resByLevel.set(id, taskCoordinates);
|
|
17788
|
+
res.set(comparisonLevel, resByLevel);
|
|
17789
|
+
});
|
|
17790
|
+
cache.lastUpdateParams = currentParams;
|
|
17791
|
+
return res;
|
|
17792
|
+
}, [
|
|
17793
|
+
visibleTasks,
|
|
17794
|
+
visibleTasksMirror,
|
|
17795
|
+
taskToRowIndexMap,
|
|
17796
|
+
startDate,
|
|
17797
|
+
viewMode,
|
|
17798
|
+
rtl,
|
|
17799
|
+
fullRowHeight,
|
|
17800
|
+
taskHeight,
|
|
17801
|
+
taskYOffset,
|
|
17802
|
+
distances,
|
|
17803
|
+
svgWidth,
|
|
17804
|
+
renderedRowIndexes,
|
|
17805
|
+
renderedColumnIndexes
|
|
17806
|
+
]);
|
|
17807
|
+
};
|
|
17498
17808
|
const fillMinAndMaxChildsMap = (resOnLevel, task, childTasksOnLevel) => {
|
|
17499
17809
|
const childs = childTasksOnLevel.get(task.id);
|
|
17500
17810
|
if (!childs || childs.length === 0) {
|
|
@@ -19114,6 +19424,11 @@
|
|
|
19114
19424
|
() => getChildsAndRoots(sortedTasks, null),
|
|
19115
19425
|
[sortedTasks]
|
|
19116
19426
|
);
|
|
19427
|
+
const adaptiveSettings = useAdaptivePerformance({
|
|
19428
|
+
fpsThreshold: 30,
|
|
19429
|
+
renderTimeThreshold: 16
|
|
19430
|
+
// 60fps = 16ms per frame
|
|
19431
|
+
});
|
|
19117
19432
|
const minAndMaxChildsMap = React.useMemo(
|
|
19118
19433
|
() => getMinAndMaxChildsMap(rootTasksMap, childTasksMap),
|
|
19119
19434
|
[rootTasksMap, childTasksMap]
|
|
@@ -19287,7 +19602,7 @@
|
|
|
19287
19602
|
svgWidth
|
|
19288
19603
|
]
|
|
19289
19604
|
);
|
|
19290
|
-
const
|
|
19605
|
+
const originalMapTaskToCoordinates = React.useMemo(
|
|
19291
19606
|
() => getMapTaskToCoordinates(
|
|
19292
19607
|
sortedTasks,
|
|
19293
19608
|
visibleTasksMirror,
|
|
@@ -19315,6 +19630,24 @@
|
|
|
19315
19630
|
visibleTasksMirror
|
|
19316
19631
|
]
|
|
19317
19632
|
);
|
|
19633
|
+
const optimizedMapTaskToCoordinates = useIncrementalCoordinates(
|
|
19634
|
+
visibleTasks,
|
|
19635
|
+
visibleTasksMirror,
|
|
19636
|
+
taskToRowIndexMap,
|
|
19637
|
+
startDate,
|
|
19638
|
+
viewMode,
|
|
19639
|
+
rtl,
|
|
19640
|
+
fullRowHeight,
|
|
19641
|
+
taskHeight,
|
|
19642
|
+
taskYOffset,
|
|
19643
|
+
distances,
|
|
19644
|
+
svgWidth,
|
|
19645
|
+
null,
|
|
19646
|
+
// renderedRowIndexes - can be integrated later with virtualization
|
|
19647
|
+
null
|
|
19648
|
+
// renderedColumnIndexes - can be integrated later with virtualization
|
|
19649
|
+
);
|
|
19650
|
+
const mapTaskToCoordinates = adaptiveSettings.performanceLevel === "high" ? optimizedMapTaskToCoordinates : originalMapTaskToCoordinates;
|
|
19318
19651
|
const scrollToTask = React.useCallback(
|
|
19319
19652
|
(task) => {
|
|
19320
19653
|
const { x1 } = getTaskCoordinates(task, mapTaskToCoordinates);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Distances, MapTaskToCoordinates, TaskCoordinates, RenderTask, TaskToRowIndexMap, ViewMode } from "../types";
|
|
2
|
+
import type { OptimizedListParams } from "./use-optimized-list";
|
|
3
|
+
/**
|
|
4
|
+
* Optimized hook for calculating task coordinates incrementally
|
|
5
|
+
* Only recalculates coordinates for visible tasks and caches results
|
|
6
|
+
*/
|
|
7
|
+
export declare const useIncrementalCoordinates: (visibleTasks: readonly RenderTask[], visibleTasksMirror: Readonly<Record<string, true>>, taskToRowIndexMap: TaskToRowIndexMap, startDate: Date, viewMode: ViewMode, rtl: boolean, fullRowHeight: number, taskHeight: number, taskYOffset: number, distances: Distances, svgWidth: number, renderedRowIndexes: OptimizedListParams | null, renderedColumnIndexes: OptimizedListParams | null) => MapTaskToCoordinates;
|
|
8
|
+
/**
|
|
9
|
+
* Hook for getting coordinates of tasks in a specific range only
|
|
10
|
+
* Useful for virtual scrolling scenarios
|
|
11
|
+
*/
|
|
12
|
+
export declare const useRangedCoordinates: (tasks: readonly RenderTask[], taskToRowIndexMap: TaskToRowIndexMap, startDate: Date, viewMode: ViewMode, rtl: boolean, fullRowHeight: number, taskHeight: number, taskYOffset: number, distances: Distances, svgWidth: number, startIndex: number, endIndex: number, comparisonLevel?: number) => Map<string, TaskCoordinates>;
|
|
13
|
+
/**
|
|
14
|
+
* Hook for smart coordinate invalidation
|
|
15
|
+
* Tracks which tasks need coordinate recalculation based on their dependencies
|
|
16
|
+
*/
|
|
17
|
+
export declare const useCoordinateInvalidation: (tasks: readonly RenderTask[], changedTaskIds: Set<string>) => Set<string>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { TaskCoordinates } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Generic object pool for reusing objects to reduce garbage collection
|
|
4
|
+
*/
|
|
5
|
+
declare class ObjectPool<T> {
|
|
6
|
+
private pool;
|
|
7
|
+
private createFn;
|
|
8
|
+
private resetFn?;
|
|
9
|
+
private maxSize;
|
|
10
|
+
constructor(createFn: () => T, resetFn?: (obj: T) => void, maxSize?: number);
|
|
11
|
+
acquire(): T;
|
|
12
|
+
release(obj: T): void;
|
|
13
|
+
clear(): void;
|
|
14
|
+
get size(): number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Task coordinates object pool for reducing memory allocations
|
|
18
|
+
*/
|
|
19
|
+
export declare const useTaskCoordinatesPool: (maxSize?: number) => {
|
|
20
|
+
acquire: () => TaskCoordinates;
|
|
21
|
+
release: (coords: TaskCoordinates) => void;
|
|
22
|
+
clear: () => void;
|
|
23
|
+
pool: ObjectPool<TaskCoordinates>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Array pool for reusing arrays to reduce allocations
|
|
27
|
+
*/
|
|
28
|
+
export declare const useArrayPool: <T>(maxSize?: number) => {
|
|
29
|
+
acquire: () => T[];
|
|
30
|
+
release: (arr: T[]) => void;
|
|
31
|
+
clear: () => void;
|
|
32
|
+
pool: ObjectPool<T[]>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Map pool for reusing Map objects
|
|
36
|
+
*/
|
|
37
|
+
export declare const useMapPool: <K, V>(maxSize?: number) => {
|
|
38
|
+
acquire: () => Map<K, V>;
|
|
39
|
+
release: (map: Map<K, V>) => void;
|
|
40
|
+
clear: () => void;
|
|
41
|
+
pool: ObjectPool<Map<K, V>>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Set pool for reusing Set objects
|
|
45
|
+
*/
|
|
46
|
+
export declare const useSetPool: <T>(maxSize?: number) => {
|
|
47
|
+
acquire: () => Set<T>;
|
|
48
|
+
release: (set: Set<T>) => void;
|
|
49
|
+
clear: () => void;
|
|
50
|
+
pool: ObjectPool<Set<T>>;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Memory manager that coordinates multiple object pools
|
|
54
|
+
*/
|
|
55
|
+
export declare const useMemoryManager: () => {
|
|
56
|
+
coordinatesPool: {
|
|
57
|
+
acquire: () => TaskCoordinates;
|
|
58
|
+
release: (coords: TaskCoordinates) => void;
|
|
59
|
+
clear: () => void;
|
|
60
|
+
pool: ObjectPool<TaskCoordinates>;
|
|
61
|
+
};
|
|
62
|
+
arrayPool: {
|
|
63
|
+
acquire: () => unknown[];
|
|
64
|
+
release: (arr: unknown[]) => void;
|
|
65
|
+
clear: () => void;
|
|
66
|
+
pool: ObjectPool<unknown[]>;
|
|
67
|
+
};
|
|
68
|
+
mapPool: {
|
|
69
|
+
acquire: () => Map<unknown, unknown>;
|
|
70
|
+
release: (map: Map<unknown, unknown>) => void;
|
|
71
|
+
clear: () => void;
|
|
72
|
+
pool: ObjectPool<Map<unknown, unknown>>;
|
|
73
|
+
};
|
|
74
|
+
setPool: {
|
|
75
|
+
acquire: () => Set<unknown>;
|
|
76
|
+
release: (set: Set<unknown>) => void;
|
|
77
|
+
clear: () => void;
|
|
78
|
+
pool: ObjectPool<Set<unknown>>;
|
|
79
|
+
};
|
|
80
|
+
memoryStats: {
|
|
81
|
+
coordinatesPoolSize: number;
|
|
82
|
+
arrayPoolSize: number;
|
|
83
|
+
mapPoolSize: number;
|
|
84
|
+
setPoolSize: number;
|
|
85
|
+
};
|
|
86
|
+
clearAllPools: () => void;
|
|
87
|
+
forceGarbageCollection: () => void;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Memory-efficient batch processor that reuses objects
|
|
91
|
+
*/
|
|
92
|
+
export declare const useBatchProcessor: <T, R>(batchSize?: number) => {
|
|
93
|
+
processBatch: (items: T[], processor: (batch: T[]) => Promise<R[]> | R[], onProgress?: (processed: number, total: number) => void) => Promise<R[]>;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Weak reference manager for preventing memory leaks
|
|
97
|
+
*/
|
|
98
|
+
export declare const useWeakReferenceManager: () => {
|
|
99
|
+
addWeakRef: (obj: object) => WeakRef<object>;
|
|
100
|
+
cleanupDeadRefs: () => number;
|
|
101
|
+
getAliveRefCount: () => number;
|
|
102
|
+
totalRefs: number;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Memory monitoring hook for tracking memory usage
|
|
106
|
+
*/
|
|
107
|
+
export declare const useMemoryMonitor: (intervalMs?: number) => {
|
|
108
|
+
memoryInfo: {
|
|
109
|
+
usedJSHeapSize: number;
|
|
110
|
+
totalJSHeapSize: number;
|
|
111
|
+
jsHeapSizeLimit: number;
|
|
112
|
+
};
|
|
113
|
+
updateMemoryInfo: () => void;
|
|
114
|
+
startMonitoring: () => () => void;
|
|
115
|
+
getMemoryPressure: () => number;
|
|
116
|
+
};
|
|
117
|
+
declare global {
|
|
118
|
+
interface Window {
|
|
119
|
+
gc?: () => void;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RenderTask, TaskMapByLevel, ChildByLevelMap, RootMapByLevel } from "../types";
|
|
2
|
+
export interface OptimizedDataStructures {
|
|
3
|
+
tasksByLevel: Map<number, RenderTask[]>;
|
|
4
|
+
tasksMap: TaskMapByLevel;
|
|
5
|
+
childTasksMap: ChildByLevelMap;
|
|
6
|
+
rootTasksMap: RootMapByLevel;
|
|
7
|
+
taskIndicesByLevel: Map<number, Map<string, number>>;
|
|
8
|
+
visibleTasksAtLevel: Map<number, RenderTask[]>;
|
|
9
|
+
taskParentMap: Map<string, string | null>;
|
|
10
|
+
taskDepthMap: Map<string, number>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Pre-computes optimized data structures for faster lookups
|
|
14
|
+
* Reduces O(n) operations to O(1) for common queries
|
|
15
|
+
*/
|
|
16
|
+
export declare const useOptimizedDataStructures: (tasks: readonly RenderTask[]) => OptimizedDataStructures;
|
|
17
|
+
/**
|
|
18
|
+
* Hook for getting tasks at specific level with O(1) access
|
|
19
|
+
*/
|
|
20
|
+
export declare const useTasksAtLevel: (optimizedData: OptimizedDataStructures, level: number) => RenderTask[];
|
|
21
|
+
/**
|
|
22
|
+
* Hook for getting visible tasks in a specific range for virtualization
|
|
23
|
+
*/
|
|
24
|
+
export declare const useVisibleTasksInRange: (optimizedData: OptimizedDataStructures, level: number, startIndex: number, endIndex: number) => RenderTask[];
|