pixel-react 1.16.3 → 1.16.4

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.
Files changed (61) hide show
  1. package/README.md +75 -75
  2. package/lib/components/Charts/BarChart/BarChart.js +8 -8
  3. package/lib/components/Charts/DashboardDonutChart/DashboardDonutChart.js +6 -6
  4. package/lib/components/Charts/IconRadialChart/IconRadialChart.js +4 -4
  5. package/lib/components/Charts/PieChart/PieChart.js +5 -5
  6. package/lib/components/Comments/Comments.js +14 -14
  7. package/lib/components/Comments/childComment/ChildComment.js +14 -14
  8. package/lib/components/EditTextField/EditTextField.js +2 -2
  9. package/lib/components/LabelEditTextField/LabelEditTextField.js +4 -4
  10. package/lib/components/StatusIndicator/StatusIndicator.js +5 -5
  11. package/lib/components/TreeV3/Components/TableBody.d.ts +3 -0
  12. package/lib/components/TreeV3/Components/TableBody.js +17 -0
  13. package/lib/components/TreeV3/Components/TableBody.js.map +1 -0
  14. package/lib/components/TreeV3/Components/TableCell.d.ts +4 -0
  15. package/lib/components/TreeV3/Components/TableCell.js +89 -0
  16. package/lib/components/TreeV3/Components/TableCell.js.map +1 -0
  17. package/lib/components/TreeV3/Components/TableHead.d.ts +4 -0
  18. package/lib/components/TreeV3/Components/TableHead.js +53 -0
  19. package/lib/components/TreeV3/Components/TableHead.js.map +1 -0
  20. package/lib/components/TreeV3/Components/TableRow.d.ts +4 -0
  21. package/lib/components/TreeV3/Components/TableRow.js +14 -0
  22. package/lib/components/TreeV3/Components/TableRow.js.map +1 -0
  23. package/lib/components/TreeV3/TableTreeFn.d.ts +5 -0
  24. package/lib/components/TreeV3/TableTreeFn.js +724 -0
  25. package/lib/components/TreeV3/TableTreeFn.js.map +1 -0
  26. package/lib/components/TreeV3/Utils/TableCell.d.ts +1 -0
  27. package/lib/components/TreeV3/Utils/TableCell.js +24 -0
  28. package/lib/components/TreeV3/Utils/TableCell.js.map +1 -0
  29. package/lib/components/TreeV3/Utils/addLastChild.d.ts +2 -0
  30. package/lib/components/TreeV3/Utils/addLastChild.js +24 -0
  31. package/lib/components/TreeV3/Utils/addLastChild.js.map +1 -0
  32. package/lib/components/TreeV3/Utils/addNewRow.d.ts +14 -0
  33. package/lib/components/TreeV3/Utils/addNewRow.js +80 -0
  34. package/lib/components/TreeV3/Utils/addNewRow.js.map +1 -0
  35. package/lib/components/TreeV3/Utils/formatDataCell.d.ts +2 -0
  36. package/lib/components/TreeV3/Utils/formatDataCell.js +15 -0
  37. package/lib/components/TreeV3/Utils/formatDataCell.js.map +1 -0
  38. package/lib/components/TreeV3/Utils/getAllChildIds.d.ts +2 -0
  39. package/lib/components/TreeV3/Utils/getAllChildIds.js +12 -0
  40. package/lib/components/TreeV3/Utils/getAllChildIds.js.map +1 -0
  41. package/lib/components/TreeV3/Utils/renderSpaces.d.ts +0 -0
  42. package/lib/components/TreeV3/Utils/renderSpaces.js +2 -0
  43. package/lib/components/TreeV3/Utils/renderSpaces.js.map +1 -0
  44. package/lib/components/TreeV3/Utils/updateParentSibling.d.ts +3 -0
  45. package/lib/components/TreeV3/Utils/updateParentSibling.js +65 -0
  46. package/lib/components/TreeV3/Utils/updateParentSibling.js.map +1 -0
  47. package/lib/components/TreeV3/data.d.ts +247 -0
  48. package/lib/components/TreeV3/data.js +529 -0
  49. package/lib/components/TreeV3/data.js.map +1 -0
  50. package/lib/components/TreeV3/index.d.ts +3 -0
  51. package/lib/components/TreeV3/index.js +4 -0
  52. package/lib/components/TreeV3/index.js.map +1 -0
  53. package/lib/components/TreeV3/types.d.ts +170 -0
  54. package/lib/components/TreeV3/types.js +2 -0
  55. package/lib/components/TreeV3/types.js.map +1 -0
  56. package/lib/index.js +7 -7
  57. package/lib/index.js.map +1 -1
  58. package/lib/styles.css +1 -1
  59. package/lib/styles.css.map +1 -1
  60. package/lib/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +106 -106
@@ -0,0 +1,724 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable max-lines */
3
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, forwardRef, } from 'react';
4
+ import './TableTree.scss';
5
+ import TableBody from './Components/TableBody';
6
+ import TableHead from './Components/TableHead';
7
+ import { debounce } from '../../utils/debounce/debounce';
8
+ import { checkEmpty } from '../../utils/checkEmpty/checkEmpty';
9
+ const DEFAULT_COLUMN_WIDTH = 400;
10
+ const ROW_HEIGHT = 32;
11
+ const OVERSCAN = 6;
12
+ const ROOT_PARENT_ID = '__root__';
13
+ const insertNewNode = (treeData, newNode, rootNode) => {
14
+ if (!newNode?.sourceId || !newNode.action)
15
+ return treeData;
16
+ const { sourceId, action, value = '', error = '', label, type, options, selectedOption, confirmIconTooltip, cancelIconTooltip, payloadSourceId, } = newNode;
17
+ const fallbackParentId = newNode
18
+ ?.parentId;
19
+ const fallbackHierarchy = newNode
20
+ ?.hierarchy;
21
+ const convertedOptions = options?.map((option) => ({
22
+ label: option,
23
+ value: option,
24
+ }));
25
+ const convertedSelectedOption = selectedOption
26
+ ? { label: selectedOption, value: selectedOption }
27
+ : undefined;
28
+ const nodeMap = new Map();
29
+ treeData.forEach((node) => nodeMap.set(node.key, node));
30
+ if (rootNode) {
31
+ nodeMap.set(rootNode.key, rootNode);
32
+ }
33
+ const sourceNode = nodeMap.get(sourceId);
34
+ let payloadSourceNode;
35
+ if (payloadSourceId) {
36
+ payloadSourceNode = nodeMap.get(payloadSourceId);
37
+ }
38
+ // For add-inside flows, backend can return navigateTo as a parent key or a
39
+ // key not present in the current page. Fallback to payload source when possible.
40
+ let effectiveSourceNode = sourceNode ?? (action === 'addInside' ? payloadSourceNode : undefined);
41
+ if (!effectiveSourceNode && action === 'addInside' && fallbackParentId) {
42
+ effectiveSourceNode = nodeMap.get(fallbackParentId);
43
+ }
44
+ const fallbackInsertAtEnd = action === 'addInside' && !effectiveSourceNode && treeData.length >= 0;
45
+ if (!effectiveSourceNode && !fallbackInsertAtEnd)
46
+ return treeData;
47
+ const updatedTreeData = [...treeData];
48
+ const sourceIndex = effectiveSourceNode
49
+ ? treeData.findIndex((node) => node.key === effectiveSourceNode.key)
50
+ : -1;
51
+ let newNodeParentId = null;
52
+ let newNodeHierarchy = 0;
53
+ let insertionIndex = sourceIndex + 1;
54
+ switch (action) {
55
+ case 'addAbove': {
56
+ const sourceNodeForInsert = effectiveSourceNode;
57
+ if (!sourceNodeForInsert)
58
+ return treeData;
59
+ newNodeParentId = sourceNodeForInsert.parentId;
60
+ newNodeHierarchy = sourceNodeForInsert.hierarchy;
61
+ break;
62
+ }
63
+ case 'addBelow': {
64
+ const sourceNodeForInsert = effectiveSourceNode;
65
+ if (!sourceNodeForInsert)
66
+ return treeData;
67
+ newNodeParentId =
68
+ payloadSourceNode?.parentId ?? sourceNodeForInsert.parentId;
69
+ newNodeHierarchy =
70
+ payloadSourceNode?.hierarchy ?? sourceNodeForInsert.hierarchy;
71
+ break;
72
+ }
73
+ case 'addInside': {
74
+ if (fallbackInsertAtEnd) {
75
+ newNodeParentId =
76
+ fallbackParentId ?? payloadSourceId ?? sourceId ?? null;
77
+ newNodeHierarchy = fallbackHierarchy ?? 1;
78
+ insertionIndex = updatedTreeData.length;
79
+ break;
80
+ }
81
+ const parentNode = payloadSourceNode ?? effectiveSourceNode;
82
+ if (!parentNode)
83
+ return treeData;
84
+ newNodeParentId = parentNode.key;
85
+ newNodeHierarchy = (parentNode.hierarchy ?? 0) + 1;
86
+ // Keep add-inside input at the end of the current parent subtree.
87
+ const parentIndex = updatedTreeData.findIndex((node) => node.key === parentNode.key);
88
+ if (parentIndex !== -1) {
89
+ insertionIndex = parentIndex + 1;
90
+ while (insertionIndex < updatedTreeData.length &&
91
+ (updatedTreeData[insertionIndex]?.hierarchy ?? 0) >
92
+ (parentNode.hierarchy ?? 0)) {
93
+ insertionIndex++;
94
+ }
95
+ }
96
+ break;
97
+ }
98
+ }
99
+ const newNodeBase = {
100
+ hierarchy: newNodeHierarchy,
101
+ parentId: newNodeParentId,
102
+ sourceId: effectiveSourceNode?.key ?? sourceId ?? payloadSourceId ?? 'new-node',
103
+ isNewNode: true,
104
+ key: 'new-node',
105
+ value,
106
+ error,
107
+ label,
108
+ type,
109
+ options: convertedOptions,
110
+ selectedOption: convertedSelectedOption,
111
+ confirmIconTooltip,
112
+ cancelIconTooltip,
113
+ };
114
+ switch (action) {
115
+ case 'addAbove':
116
+ updatedTreeData.splice(sourceIndex, 0, newNodeBase);
117
+ break;
118
+ case 'addBelow':
119
+ updatedTreeData.splice(sourceIndex + 1, 0, newNodeBase);
120
+ break;
121
+ case 'addInside':
122
+ updatedTreeData.splice(Math.max(0, insertionIndex), 0, newNodeBase);
123
+ break;
124
+ }
125
+ return updatedTreeData;
126
+ };
127
+ const prepareTreeRows = (treeData, rootNode) => {
128
+ if (!treeData?.length)
129
+ return [];
130
+ const nodeById = new Map();
131
+ const childrenByParent = new Map();
132
+ const siblingIndex = new Map();
133
+ const loadedCountsByParent = new Map();
134
+ if (rootNode && rootNode.key) {
135
+ nodeById.set(rootNode.key, rootNode);
136
+ }
137
+ treeData.forEach((node) => {
138
+ nodeById.set(node.key, node);
139
+ const parentKey = node.parentId ?? ROOT_PARENT_ID;
140
+ if (!childrenByParent.has(parentKey)) {
141
+ childrenByParent.set(parentKey, []);
142
+ loadedCountsByParent.set(parentKey, { container: 0, resource: 0 });
143
+ }
144
+ childrenByParent.get(parentKey)?.push(node);
145
+ const counts = loadedCountsByParent.get(parentKey);
146
+ if (counts) {
147
+ if (node.container) {
148
+ counts.container++;
149
+ }
150
+ else {
151
+ counts.resource++;
152
+ }
153
+ }
154
+ });
155
+ childrenByParent.forEach((children) => {
156
+ children.forEach((child, index) => {
157
+ siblingIndex.set(child.key, index);
158
+ });
159
+ });
160
+ const hasNextSibling = (node) => {
161
+ const parentKey = node.parentId ?? ROOT_PARENT_ID;
162
+ const siblings = childrenByParent.get(parentKey);
163
+ if (!siblings || siblings.length === 0)
164
+ return false;
165
+ const index = siblingIndex.get(node.key) ?? 0;
166
+ if (index < siblings.length - 1)
167
+ return true;
168
+ const parent = nodeById.get(parentKey);
169
+ const loadedCounts = loadedCountsByParent.get(parentKey);
170
+ if (parent && loadedCounts) {
171
+ const siblingContainerResources = siblings
172
+ .filter((node) => node.container)
173
+ .reduce((sum, node) => sum + (node.totalResourceCount ?? 0), 0);
174
+ const siblingDirectResourcesCount = siblings.filter((node) => !node.container).length;
175
+ const calculatedDirectResources = Math.max(0, (parent.totalResourceCount ?? 0) -
176
+ (siblingContainerResources + siblingDirectResourcesCount));
177
+ if (node.container) {
178
+ const hasMoreContainers = loadedCounts.container < (parent.subContainerCount ?? 0);
179
+ const hasResourcesFollowing = calculatedDirectResources > 0;
180
+ return hasMoreContainers || hasResourcesFollowing;
181
+ }
182
+ else {
183
+ return loadedCounts.resource < calculatedDirectResources;
184
+ }
185
+ }
186
+ return false;
187
+ };
188
+ const buildParentSiblings = (node) => {
189
+ const hierarchy = node.hierarchy ?? 0;
190
+ if (hierarchy <= 0)
191
+ return [];
192
+ const path = [];
193
+ let current = node;
194
+ while (current) {
195
+ path.unshift(current);
196
+ if (!current.parentId)
197
+ break;
198
+ const parent = nodeById.get(current.parentId);
199
+ if (!parent)
200
+ break;
201
+ current = parent;
202
+ }
203
+ const flags = path.map((ancestor) => hasNextSibling(ancestor));
204
+ if (flags.length === hierarchy)
205
+ return flags;
206
+ if (flags.length > hierarchy)
207
+ return flags.slice(flags.length - hierarchy);
208
+ const padding = new Array(hierarchy - flags.length).fill(false);
209
+ return padding.concat(flags);
210
+ };
211
+ const resolveSelectedStatus = (node) => {
212
+ if (node.selectedStatus !== undefined)
213
+ return node.selectedStatus;
214
+ let current = node;
215
+ while (current?.parentId) {
216
+ const parent = nodeById.get(current.parentId);
217
+ if (!parent)
218
+ break;
219
+ if (parent.selectedStatus === 'completely')
220
+ return 'completely';
221
+ if (parent.selectedStatus === 'none')
222
+ return 'none';
223
+ current = parent;
224
+ }
225
+ if (rootNode?.selectedStatus === 'completely')
226
+ return 'completely';
227
+ if (rootNode?.selectedStatus === 'none')
228
+ return 'none';
229
+ return node.selectedStatus;
230
+ };
231
+ return treeData.map((node) => {
232
+ const nodeWithSiblings = node;
233
+ const finalParentSiblings = nodeWithSiblings.parentSiblings &&
234
+ nodeWithSiblings.parentSiblings.length > 0
235
+ ? nodeWithSiblings.parentSiblings
236
+ : buildParentSiblings(node);
237
+ return {
238
+ ...node,
239
+ lastChild: !hasNextSibling(node),
240
+ parentSiblings: finalParentSiblings,
241
+ selectedStatus: resolveSelectedStatus(node),
242
+ };
243
+ });
244
+ };
245
+ const TableTreeFn = forwardRef(({ treeData, columnsData, selected = [], select = null, onChange = () => { }, onClick = () => { }, onExpand = () => { }, loadMore = () => { }, tableBorder, height = 'calc(100vh - 134px)', newNode, onAddConfirm = () => { }, onAddCancel = () => { }, handleEditFieldError, loading = false, rootNode, pagination = true, selectedNode, tableHeaderBgColor = 'var(--border-color)', hideOnDisable = false, freezeColumns, scriptLengthTruncate = 25, addModuleInputWidth = 150, addModuleSelectWidth, onScroll, onScrollEnd, disableEditLabelConfirmIcon = false, transparentHeader = false, navigateTreeNode = null, handleRemoveNavigateTreeNode = () => { }, scrollThreshold = 128, showHeader = true, }, ref) => {
246
+ const [expanding, setExpanding] = useState(null);
247
+ const [scrollDirection, setScrollDirection] = useState(null);
248
+ const [virtualRange, setVirtualRange] = useState({ start: 0, end: 20 });
249
+ const [newNodeEditable, setNewNodeEditable] = useState(false);
250
+ const containerRef = useRef(null);
251
+ const headerRef = useRef(null);
252
+ const headerHeightRef = useRef(0);
253
+ const loadingRef = useRef(loading);
254
+ const scrollRafRef = useRef(null);
255
+ const scrollPositionRef = useRef({ lastScrollTop: 0 });
256
+ const previousFirstKeyRef = useRef(null);
257
+ const suppressScrollCancelUntilRef = useRef(0);
258
+ const debouncedScrollEndRef = useRef(null);
259
+ const expandTargetRef = useRef(null);
260
+ const expandingFallbackTimerRef = useRef(null);
261
+ const navigateAttemptsRef = useRef({ key: null, attempts: 0 });
262
+ const navigateClearTimerRef = useRef(null);
263
+ const getWidthInfo = (width, defaultWidth) => {
264
+ const w = width ?? defaultWidth;
265
+ const str = String(w);
266
+ if (str.endsWith('%')) {
267
+ return { value: parseFloat(str), unit: '%' };
268
+ }
269
+ return { value: parseFloat(str) || 0, unit: 'px' };
270
+ };
271
+ const columnMeta = useMemo(() => {
272
+ let stickyLeftPx = 0;
273
+ let stickyLeftPct = 0;
274
+ return columnsData.map((col, index) => {
275
+ const { value, unit } = getWidthInfo(col.width, DEFAULT_COLUMN_WIDTH);
276
+ const sticky = !!freezeColumns && index < freezeColumns;
277
+ const widthCss = unit === '%' ? `${value}%` : `${value}px`;
278
+ const leftCss = `calc(${stickyLeftPx}px + ${stickyLeftPct}%)`;
279
+ const meta = {
280
+ width: widthCss,
281
+ sticky,
282
+ left: leftCss,
283
+ zIndex: sticky ? 100 + (freezeColumns ?? 0) - index : 1,
284
+ };
285
+ if (sticky) {
286
+ if (unit === '%') {
287
+ stickyLeftPct += value;
288
+ }
289
+ else {
290
+ stickyLeftPx += value;
291
+ }
292
+ }
293
+ return meta;
294
+ });
295
+ }, [columnsData, freezeColumns]);
296
+ const totalWidth = useMemo(() => {
297
+ let totalPx = 0;
298
+ let totalPct = 0;
299
+ columnsData.forEach((col) => {
300
+ const { value, unit } = getWidthInfo(col.width, DEFAULT_COLUMN_WIDTH);
301
+ if (unit === '%') {
302
+ totalPct += value;
303
+ }
304
+ else {
305
+ totalPx += value;
306
+ }
307
+ });
308
+ return `calc(${totalPx}px + ${totalPct}%)`;
309
+ }, [columnsData]);
310
+ const frozenColumnWidth = useMemo(() => {
311
+ if (!freezeColumns)
312
+ return '0px';
313
+ let totalPx = 0;
314
+ let totalPct = 0;
315
+ const frozenCols = columnMeta.slice(0, freezeColumns);
316
+ frozenCols.forEach((col) => {
317
+ const val = parseFloat(col.width) || 0;
318
+ if (col.width.endsWith('%')) {
319
+ totalPct += val;
320
+ }
321
+ else {
322
+ totalPx += val;
323
+ }
324
+ });
325
+ return `calc(${totalPx}px + ${totalPct}%)`;
326
+ }, [columnMeta, freezeColumns]);
327
+ const treeDataWithNewNode = useMemo(() => insertNewNode(treeData, newNode, rootNode?.node), [treeData, newNode, rootNode]);
328
+ const preparedRows = useMemo(() => prepareTreeRows(treeDataWithNewNode, rootNode?.node), [treeDataWithNewNode, rootNode?.node]);
329
+ const visiblePreparedRows = useMemo(() => preparedRows.filter((node) => !node.hide), [preparedRows]);
330
+ const preparedRowsByKey = useMemo(() => {
331
+ const map = new Map();
332
+ preparedRows.forEach((node) => map.set(node.key, node));
333
+ return map;
334
+ }, [preparedRows]);
335
+ const keyIndexMap = useMemo(() => {
336
+ const map = new Map();
337
+ visiblePreparedRows.forEach((node, index) => map.set(node.key, index));
338
+ return map;
339
+ }, [visiblePreparedRows]);
340
+ const clearExpanding = useCallback((targetKey) => {
341
+ setExpanding((current) => {
342
+ if (!current)
343
+ return current;
344
+ if (targetKey && current !== targetKey)
345
+ return current;
346
+ return null;
347
+ });
348
+ if (!targetKey || expandTargetRef.current?.key === targetKey) {
349
+ expandTargetRef.current = null;
350
+ }
351
+ if (expandingFallbackTimerRef.current) {
352
+ clearTimeout(expandingFallbackTimerRef.current);
353
+ expandingFallbackTimerRef.current = null;
354
+ }
355
+ }, []);
356
+ useEffect(() => {
357
+ loadingRef.current = loading;
358
+ }, [loading]);
359
+ const updateVirtualRange = useCallback(() => {
360
+ const container = containerRef.current;
361
+ if (!container)
362
+ return;
363
+ const headerHeight = headerHeightRef.current || 0;
364
+ const rowScrollTop = Math.max(0, container.scrollTop - headerHeight);
365
+ const viewportHeight = container.clientHeight - headerHeight;
366
+ let start = Math.floor(rowScrollTop / ROW_HEIGHT) - OVERSCAN;
367
+ let end = Math.ceil((rowScrollTop + viewportHeight) / ROW_HEIGHT) + OVERSCAN;
368
+ start = Math.max(0, start);
369
+ end = Math.min(visiblePreparedRows.length, end);
370
+ setVirtualRange((prev) => {
371
+ if (prev.start === start && prev.end === end)
372
+ return prev;
373
+ return { start, end };
374
+ });
375
+ }, [visiblePreparedRows.length]);
376
+ const scheduleRangeUpdate = useCallback(() => {
377
+ if (scrollRafRef.current !== null)
378
+ return;
379
+ scrollRafRef.current = requestAnimationFrame(() => {
380
+ scrollRafRef.current = null;
381
+ updateVirtualRange();
382
+ });
383
+ }, [updateVirtualRange]);
384
+ // Handle scroll position safety when searching/filtering
385
+ useLayoutEffect(() => {
386
+ const container = containerRef.current;
387
+ if (!container)
388
+ return;
389
+ const totalContentHeight = visiblePreparedRows.length * ROW_HEIGHT;
390
+ const maxScrollTop = Math.max(0, totalContentHeight - (container.clientHeight - headerHeightRef.current));
391
+ if (scrollPositionRef.current.lastScrollTop > maxScrollTop &&
392
+ visiblePreparedRows.length > 0) {
393
+ container.scrollTop = 0;
394
+ scrollPositionRef.current.lastScrollTop = 0;
395
+ }
396
+ updateVirtualRange();
397
+ }, [visiblePreparedRows.length, updateVirtualRange]);
398
+ const handleScroll = useCallback(() => {
399
+ const container = containerRef.current;
400
+ if (!container)
401
+ return;
402
+ const currentScrollTop = container.scrollTop;
403
+ const direction = currentScrollTop > scrollPositionRef.current.lastScrollTop
404
+ ? 'down'
405
+ : 'up';
406
+ scrollPositionRef.current.lastScrollTop = currentScrollTop;
407
+ scheduleRangeUpdate();
408
+ onScroll?.();
409
+ debouncedScrollEndRef.current?.();
410
+ if (!pagination || loading || scrollDirection || checkEmpty(treeData))
411
+ return;
412
+ const scrollHeight = container.scrollHeight;
413
+ const clientHeight = container.clientHeight;
414
+ const nearBottom = scrollHeight - (currentScrollTop + clientHeight) < scrollThreshold;
415
+ const nearTop = currentScrollTop < scrollThreshold + headerHeightRef.current;
416
+ if (direction === 'down' &&
417
+ nearBottom &&
418
+ !treeData[treeData.length - 1]?.lastResource) {
419
+ setScrollDirection('below');
420
+ loadMore('below');
421
+ }
422
+ if (direction === 'up' && nearTop && !treeData[0]?.lastResource) {
423
+ setScrollDirection('above');
424
+ previousFirstKeyRef.current = treeData[0]?.key ?? null;
425
+ loadMore('above');
426
+ }
427
+ }, [
428
+ treeData,
429
+ pagination,
430
+ loading,
431
+ scrollDirection,
432
+ scrollThreshold,
433
+ scheduleRangeUpdate,
434
+ onScroll,
435
+ loadMore,
436
+ ]);
437
+ useEffect(() => {
438
+ if (loading || !pagination || checkEmpty(treeData))
439
+ return;
440
+ const firstNode = treeData[0];
441
+ const hasMoreAbove = !firstNode?.lastResource;
442
+ const { totalSubContainerCount = 0, totalResourceCount = 0 } = rootNode?.node || {};
443
+ const totalItems = totalSubContainerCount + totalResourceCount;
444
+ const isListTooShort = visiblePreparedRows.length < 10 &&
445
+ visiblePreparedRows.length < totalItems;
446
+ if (hasMoreAbove && isListTooShort) {
447
+ previousFirstKeyRef.current = firstNode?.key ?? null;
448
+ setScrollDirection('above');
449
+ loadMore('above');
450
+ }
451
+ }, [
452
+ loading,
453
+ pagination,
454
+ treeData,
455
+ visiblePreparedRows.length,
456
+ rootNode,
457
+ loadMore,
458
+ ]);
459
+ useLayoutEffect(() => {
460
+ if (!loading && scrollDirection === 'above') {
461
+ const container = containerRef.current;
462
+ const previousKey = previousFirstKeyRef.current;
463
+ if (container && previousKey) {
464
+ const newIndex = keyIndexMap.get(previousKey);
465
+ if (newIndex !== undefined && newIndex > 0) {
466
+ container.scrollTop += newIndex * ROW_HEIGHT;
467
+ }
468
+ }
469
+ previousFirstKeyRef.current = null;
470
+ setScrollDirection(null);
471
+ }
472
+ else if (!loading && scrollDirection === 'below') {
473
+ setScrollDirection(null);
474
+ }
475
+ }, [loading, scrollDirection, keyIndexMap]);
476
+ useEffect(() => {
477
+ const container = containerRef.current;
478
+ if (!container)
479
+ return;
480
+ debouncedScrollEndRef.current = onScrollEnd
481
+ ? debounce(() => {
482
+ onScrollEnd?.();
483
+ }, 150)
484
+ : null;
485
+ container.addEventListener('scroll', handleScroll, { passive: true });
486
+ return () => {
487
+ container.removeEventListener('scroll', handleScroll);
488
+ debouncedScrollEndRef.current?.cancel();
489
+ };
490
+ }, [handleScroll, onScrollEnd]);
491
+ useEffect(() => {
492
+ const container = containerRef.current;
493
+ if (!container)
494
+ return;
495
+ const handleScrollCapture = (event) => {
496
+ if (Date.now() < suppressScrollCancelUntilRef.current) {
497
+ event.stopImmediatePropagation();
498
+ }
499
+ };
500
+ container.addEventListener('scroll', handleScrollCapture, {
501
+ passive: true,
502
+ capture: true,
503
+ });
504
+ return () => {
505
+ container.removeEventListener('scroll', handleScrollCapture, true);
506
+ };
507
+ }, []);
508
+ useEffect(() => {
509
+ const headerNode = headerRef.current;
510
+ if (!headerNode)
511
+ return;
512
+ const updateHeight = () => {
513
+ headerHeightRef.current = headerNode.getBoundingClientRect().height;
514
+ updateVirtualRange();
515
+ };
516
+ const resizeObserver = new ResizeObserver(updateHeight);
517
+ resizeObserver.observe(headerNode);
518
+ return () => resizeObserver.disconnect();
519
+ }, [updateVirtualRange]);
520
+ useLayoutEffect(() => {
521
+ if (!newNode || !containerRef.current)
522
+ return;
523
+ const container = containerRef.current;
524
+ const newNodeIndex = keyIndexMap.get('new-node');
525
+ if (newNodeIndex === undefined)
526
+ return;
527
+ const headerHeight = headerHeightRef.current || 0;
528
+ const viewportHeight = container.clientHeight - headerHeight;
529
+ if (viewportHeight <= 0)
530
+ return;
531
+ const rowTop = newNodeIndex * ROW_HEIGHT;
532
+ const rowBottom = rowTop + ROW_HEIGHT;
533
+ const rowScrollTop = Math.max(0, container.scrollTop - headerHeight);
534
+ const viewTop = rowScrollTop;
535
+ const viewBottom = rowScrollTop + viewportHeight;
536
+ let nextRowScrollTop = rowScrollTop;
537
+ if (rowTop < viewTop) {
538
+ nextRowScrollTop = rowTop;
539
+ }
540
+ else if (rowBottom > viewBottom) {
541
+ nextRowScrollTop = Math.max(0, rowBottom - viewportHeight);
542
+ }
543
+ if (nextRowScrollTop !== rowScrollTop) {
544
+ suppressScrollCancelUntilRef.current = Date.now() + 300;
545
+ container.scrollTop = nextRowScrollTop + headerHeight;
546
+ updateVirtualRange();
547
+ }
548
+ const rafId = requestAnimationFrame(() => {
549
+ setNewNodeEditable(true);
550
+ });
551
+ return () => cancelAnimationFrame(rafId);
552
+ }, [newNode, keyIndexMap, updateVirtualRange]);
553
+ useEffect(() => {
554
+ if (!newNode) {
555
+ setNewNodeEditable(false);
556
+ }
557
+ }, [newNode]);
558
+ useLayoutEffect(() => {
559
+ if (!expanding || loading)
560
+ return;
561
+ const expected = expandTargetRef.current;
562
+ if (!expected || expected.key !== expanding) {
563
+ const rafId = requestAnimationFrame(() => {
564
+ clearExpanding(expanding);
565
+ });
566
+ return () => cancelAnimationFrame(rafId);
567
+ }
568
+ const updatedNode = preparedRowsByKey.get(expected.key);
569
+ if (!updatedNode) {
570
+ const rafId = requestAnimationFrame(() => {
571
+ clearExpanding(expected.key);
572
+ });
573
+ return () => cancelAnimationFrame(rafId);
574
+ }
575
+ if (Boolean(updatedNode.expanded) !== expected.expanded)
576
+ return;
577
+ const rafId = requestAnimationFrame(() => {
578
+ clearExpanding(expected.key);
579
+ });
580
+ return () => cancelAnimationFrame(rafId);
581
+ }, [preparedRowsByKey, expanding, loading, clearExpanding]);
582
+ useEffect(() => {
583
+ if (navigateAttemptsRef.current.key !== navigateTreeNode) {
584
+ navigateAttemptsRef.current = {
585
+ key: navigateTreeNode,
586
+ attempts: 0,
587
+ };
588
+ }
589
+ if (!navigateTreeNode && navigateClearTimerRef.current) {
590
+ clearTimeout(navigateClearTimerRef.current);
591
+ navigateClearTimerRef.current = null;
592
+ }
593
+ }, [navigateTreeNode]);
594
+ useLayoutEffect(() => {
595
+ if (!navigateTreeNode || newNode)
596
+ return;
597
+ const container = containerRef.current;
598
+ if (!container)
599
+ return;
600
+ const rowIndex = keyIndexMap.get(navigateTreeNode);
601
+ if (rowIndex !== undefined) {
602
+ navigateAttemptsRef.current = {
603
+ key: navigateTreeNode,
604
+ attempts: 0,
605
+ };
606
+ const headerHeight = headerHeightRef.current || 0;
607
+ const viewportHeight = Math.max(ROW_HEIGHT, container.clientHeight - headerHeight);
608
+ const rowCenter = rowIndex * ROW_HEIGHT + ROW_HEIGHT / 2;
609
+ const targetTop = Math.max(0, rowCenter - viewportHeight / 2) + headerHeight;
610
+ suppressScrollCancelUntilRef.current = Date.now() + 400;
611
+ container.scrollTo({
612
+ top: targetTop,
613
+ behavior: 'smooth',
614
+ });
615
+ scheduleRangeUpdate();
616
+ if (navigateClearTimerRef.current) {
617
+ clearTimeout(navigateClearTimerRef.current);
618
+ }
619
+ navigateClearTimerRef.current = setTimeout(() => {
620
+ handleRemoveNavigateTreeNode?.();
621
+ }, 350);
622
+ return;
623
+ }
624
+ if (loading || scrollDirection)
625
+ return;
626
+ const retryState = navigateAttemptsRef.current;
627
+ if (retryState.key !== navigateTreeNode) {
628
+ retryState.key = navigateTreeNode;
629
+ retryState.attempts = 0;
630
+ }
631
+ retryState.attempts += 1;
632
+ if (retryState.attempts > 30) {
633
+ handleRemoveNavigateTreeNode?.();
634
+ return;
635
+ }
636
+ if (pagination && treeData[0]?.lastResource !== true) {
637
+ previousFirstKeyRef.current = null;
638
+ setScrollDirection('above');
639
+ loadMore('above');
640
+ return;
641
+ }
642
+ if (retryState.attempts > 2) {
643
+ handleRemoveNavigateTreeNode?.();
644
+ }
645
+ }, [
646
+ navigateTreeNode,
647
+ newNode,
648
+ keyIndexMap,
649
+ loading,
650
+ scrollDirection,
651
+ pagination,
652
+ treeData,
653
+ loadMore,
654
+ scheduleRangeUpdate,
655
+ handleRemoveNavigateTreeNode,
656
+ ]);
657
+ const handleToggleExpand = useCallback((node) => {
658
+ if (expanding)
659
+ return;
660
+ const nextExpandedState = !node?.expanded;
661
+ setExpanding(node.key);
662
+ expandTargetRef.current = {
663
+ key: node.key,
664
+ expanded: nextExpandedState,
665
+ };
666
+ if (expandingFallbackTimerRef.current) {
667
+ clearTimeout(expandingFallbackTimerRef.current);
668
+ }
669
+ const fallbackStartedAt = Date.now();
670
+ const MAX_FALLBACK_WAIT_MS = 2000;
671
+ const FALLBACK_CHECK_INTERVAL_MS = 1000;
672
+ const ensureExpandCleanup = () => {
673
+ if (expandTargetRef.current?.key !== node.key)
674
+ return;
675
+ const isStillLoading = loadingRef.current;
676
+ const didTimeout = Date.now() - fallbackStartedAt >= MAX_FALLBACK_WAIT_MS;
677
+ if (isStillLoading && !didTimeout) {
678
+ expandingFallbackTimerRef.current = setTimeout(ensureExpandCleanup, FALLBACK_CHECK_INTERVAL_MS);
679
+ return;
680
+ }
681
+ clearExpanding(node.key);
682
+ };
683
+ expandingFallbackTimerRef.current = setTimeout(ensureExpandCleanup, FALLBACK_CHECK_INTERVAL_MS);
684
+ onExpand?.(node);
685
+ }, [expanding, onExpand, clearExpanding]);
686
+ const handleCheckBoxChange = useCallback((e, node) => {
687
+ if (expanding)
688
+ return;
689
+ onChange?.(e, node);
690
+ }, [onChange, expanding]);
691
+ const handleRowClick = useCallback((e, node) => {
692
+ if (expanding)
693
+ return;
694
+ onClick?.(e, node);
695
+ }, [onClick, expanding]);
696
+ useEffect(() => {
697
+ return () => {
698
+ if (scrollRafRef.current !== null) {
699
+ cancelAnimationFrame(scrollRafRef.current);
700
+ scrollRafRef.current = null;
701
+ }
702
+ if (expandingFallbackTimerRef.current) {
703
+ clearTimeout(expandingFallbackTimerRef.current);
704
+ }
705
+ if (navigateClearTimerRef.current) {
706
+ clearTimeout(navigateClearTimerRef.current);
707
+ }
708
+ };
709
+ }, []);
710
+ const totalHeight = visiblePreparedRows.length * ROW_HEIGHT;
711
+ const visibleRows = visiblePreparedRows.slice(virtualRange.start, virtualRange.end);
712
+ return (_jsx("div", { className: "tree-table-wrapper-container table-tree-fn", children: _jsx("div", { className: "tree-table-wrap", ref: ref, children: _jsx("div", { className: `table-scrollable ${visiblePreparedRows.length ? '' : 'table-empty'}`, ref: containerRef, style: {
713
+ '--table-height': visiblePreparedRows.length ? height : 'auto',
714
+ '--frozen-column-width': frozenColumnWidth,
715
+ border: tableBorder,
716
+ }, children: _jsxs("div", { className: "tree-table", style: { width: totalWidth }, children: [_jsx("div", { ref: headerRef, style: { position: 'sticky', top: 0, zIndex: 98 }, children: _jsx(TableHead, { columnsData: columnsData, columnMeta: columnMeta, totalWidth: totalWidth, rootNode: rootNode, onCheckBoxChange: handleCheckBoxChange, selected: selected, selectedNode: selectedNode, tableHeaderBgColor: tableHeaderBgColor, hideOnDisable: hideOnDisable, transparentHeader: transparentHeader, scriptLengthTruncate: scriptLengthTruncate, showHeader: showHeader }) }), _jsx(TableBody, { flattenedTreeData: visibleRows, columnsData: columnsData, columnMeta: columnMeta, totalWidth: totalWidth, rowHeight: ROW_HEIGHT, startIndex: virtualRange.start, totalHeight: totalHeight, selected: selected, select: select, onRowClick: handleRowClick, onToggleExpand: handleToggleExpand, onCheckBoxChange: handleCheckBoxChange, onAddConfirm: onAddConfirm, onAddCancel: onAddCancel, handleEditFieldError: handleEditFieldError, expanding: expanding, selectedNode: selectedNode, hideOnDisable: hideOnDisable, scriptLengthTruncate: scriptLengthTruncate, addModuleInputWidth: addModuleInputWidth, addModuleSelectWidth: addModuleSelectWidth, disableEditLabelConfirmIcon: disableEditLabelConfirmIcon, isEditable: newNode ? newNodeEditable : undefined, setIsEditable: newNode
717
+ ? (id) => {
718
+ setNewNodeEditable(Boolean(id));
719
+ }
720
+ : undefined })] }) }) }) }));
721
+ });
722
+ TableTreeFn.displayName = 'TableTreeFn';
723
+ export default TableTreeFn;
724
+ //# sourceMappingURL=TableTreeFn.js.map