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.
@@ -4701,11 +4701,14 @@
4701
4701
  if (!containerEl) {
4702
4702
  return null;
4703
4703
  }
4704
- const scrollValue = containerEl[property];
4705
- const maxScrollValue = property === "scrollLeft" ? containerEl.scrollWidth : containerEl.scrollHeight;
4706
- const fullValue = property === "scrollLeft" ? containerEl.clientWidth : containerEl.clientHeight;
4707
- const firstIndex = Math.floor(scrollValue / cellSize);
4708
- const lastIndex = Math.ceil((scrollValue + fullValue) / cellSize) + 10;
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
- let rafId = null;
4719
- let prevIndexes = indexes;
4720
- const handler = () => {
4721
- const nextIndexes = getStartAndEnd(
4722
- containerRef.current,
4723
- property,
4724
- cellSize
4725
- );
4726
- const isChanged = prevIndexes ? nextIndexes ? nextIndexes.some(
4727
- (value, index2) => !prevIndexes || prevIndexes[index2] !== value
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
- rafId = requestAnimationFrame(handler);
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
- if (rafId !== null) {
4738
- cancelAnimationFrame(rafId);
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, indexes, property]);
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 dateBaseX = rtl ? tickX + columnWidth : tickX;
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 + rightEdgeX,
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
- "rect",
11087
+ "circle",
11075
11088
  {
11076
- x: additionalLeftSpace + rectX,
11077
- y: rectY,
11078
- width: rotatedHeight,
11079
- height: rotatedHeight,
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 + rightEdgeX + 8,
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 mapTaskToCoordinates = React.useMemo(
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[];