abdul-react 0.1.4 → 0.1.6
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/lib/components/AddResourceButton/AddResourceButton.js +19 -14
- package/lib/components/AddResourceButton/AddResourceButton.js.map +1 -1
- package/lib/components/AttachmentButton/AttachmentButton.js +2 -0
- package/lib/components/AttachmentButton/AttachmentButton.js.map +1 -1
- package/lib/components/Charts/DonutChart/DonutChart.js +24 -16
- package/lib/components/Charts/DonutChart/DonutChart.js.map +1 -1
- package/lib/components/Charts/DonutChart/type.d.ts +4 -1
- package/lib/components/ConditionalDropdown/ConditionalDropdown.js +2 -2
- package/lib/components/ConditionalDropdown/ConditionalDropdown.js.map +1 -1
- package/lib/components/EditLabel/EditLabel.d.ts +1 -1
- package/lib/components/EditLabel/EditLabel.js +2 -2
- package/lib/components/EditLabel/EditLabel.js.map +1 -1
- package/lib/components/EditLabel/types.d.ts +1 -0
- package/lib/components/Editor/Editor.js +1 -1
- package/lib/components/Editor/Editor.js.map +1 -1
- package/lib/components/Editor/constants.js +121 -40
- package/lib/components/Editor/constants.js.map +1 -1
- package/lib/components/Excel/ColorBarSelector/ColorBarSelector.d.ts +1 -1
- package/lib/components/Excel/ColorBarSelector/ColorBarSelector.js +5 -3
- package/lib/components/Excel/ColorBarSelector/ColorBarSelector.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/ActiveCell.js +72 -5
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/ActiveCell.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/EditableCell.js +9 -0
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/EditableCell.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/Spreadsheet.js +8 -2
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/Spreadsheet.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/actions.d.ts +8 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/actions.js +7 -0
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/actions.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducer.js +18 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducer.js.map +1 -1
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducerFunctions.js +10 -3
- package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducerFunctions.js.map +1 -1
- package/lib/components/Excel/ExcelToolBar/ExcelToolBar.js +8 -2
- package/lib/components/Excel/ExcelToolBar/ExcelToolBar.js.map +1 -1
- package/lib/components/Icon/iconList.js +28 -0
- package/lib/components/Icon/iconList.js.map +1 -1
- package/lib/components/MachineInputField/MachineInputField.js +1 -0
- package/lib/components/MachineInputField/MachineInputField.js.map +1 -1
- package/lib/components/MediaPreview/MediaPreview.js +1 -1
- package/lib/components/MediaPreview/MediaPreview.js.map +1 -1
- package/lib/components/MediaViewerModal/MediaViewerModal.js +1 -1
- package/lib/components/MediaViewerModal/MediaViewerModal.js.map +1 -1
- package/lib/components/MediaViewerModal/type.d.ts +1 -1
- package/lib/components/MultiSelect/Dropdown.js +2 -1
- package/lib/components/MultiSelect/Dropdown.js.map +1 -1
- package/lib/components/NLPInput/components/ChipsFolder/ChipsAccordion.js +1 -1
- package/lib/components/NLPInput/components/ChipsFolder/ChipsAccordion.js.map +1 -1
- package/lib/components/PrePostTable/components/DraggableTableRow.js +4 -1
- package/lib/components/PrePostTable/components/DraggableTableRow.js.map +1 -1
- package/lib/components/PrePostTable/components/PrePostStepAccordions.js +14 -3
- package/lib/components/PrePostTable/components/PrePostStepAccordions.js.map +1 -1
- package/lib/components/PromptContainer/PromptContainer.js +1 -1
- package/lib/components/PromptContainer/PromptContainer.js.map +1 -1
- package/lib/components/RadioButton/RadioButton.js +2 -2
- package/lib/components/RadioButton/RadioButton.js.map +1 -1
- package/lib/components/RadioButton/radioButtonTypes.d.ts +1 -0
- package/lib/components/RadioGroup/RadioGroup.js +2 -2
- package/lib/components/RadioGroup/RadioGroup.js.map +1 -1
- package/lib/components/RadioGroup/radioGroupTypes.d.ts +1 -0
- package/lib/components/Select/Select.js +5 -5
- package/lib/components/Select/Select.js.map +1 -1
- package/lib/components/Select/components/Dropdown.js +100 -17
- package/lib/components/Select/components/Dropdown.js.map +1 -1
- package/lib/components/Select/components/types.d.ts +1 -0
- package/lib/components/Select/components/types.js.map +1 -1
- package/lib/components/Select/types.d.ts +4 -0
- package/lib/components/SelectionSwitcher/SelectionSwitcher.d.ts +5 -0
- package/lib/components/SelectionSwitcher/SelectionSwitcher.js +16 -0
- package/lib/components/SelectionSwitcher/SelectionSwitcher.js.map +1 -0
- package/lib/components/SelectionSwitcher/index.d.ts +1 -0
- package/lib/components/SelectionSwitcher/index.js +2 -0
- package/lib/components/SelectionSwitcher/index.js.map +1 -0
- package/lib/components/SelectionSwitcher/types.d.ts +13 -0
- package/lib/components/SelectionSwitcher/types.js +2 -0
- package/lib/components/SelectionSwitcher/types.js.map +1 -0
- package/lib/components/SequentialConnectingBranch/types.d.ts +1 -1
- package/lib/components/SessionDropdown/SessionDropdown.js +4 -4
- package/lib/components/SessionDropdown/SessionDropdown.js.map +1 -1
- package/lib/components/SessionDropdown/type.d.ts +2 -0
- package/lib/components/SessionManager/SessionManager.js +3 -3
- package/lib/components/SessionManager/SessionManager.js.map +1 -1
- package/lib/components/StatusBadge/StatusBadge.d.ts +2 -1
- package/lib/components/StatusBadge/StatusBadge.js +3 -3
- package/lib/components/StatusBadge/StatusBadge.js.map +1 -1
- package/lib/components/StatusButton/StatusButton.d.ts +1 -1
- package/lib/components/StatusButton/StatusButton.js +3 -2
- package/lib/components/StatusButton/StatusButton.js.map +1 -1
- package/lib/components/StatusButton/types.d.ts +2 -1
- package/lib/components/StepsLandingTable/Components/StepGroupAccordions.js +8 -5
- package/lib/components/StepsLandingTable/Components/StepGroupAccordions.js.map +1 -1
- package/lib/components/StepsLandingTable/Components/StepInnerTable.d.ts +1 -1
- package/lib/components/StepsLandingTable/Components/StepInnerTable.js +2 -2
- package/lib/components/StepsLandingTable/Components/StepInnerTable.js.map +1 -1
- package/lib/components/StepsLandingTable/Components/StepResultStats.js +21 -2
- package/lib/components/StepsLandingTable/Components/StepResultStats.js.map +1 -1
- package/lib/components/StepsLandingTable/Components/StepTableMainRow.js +11 -5
- package/lib/components/StepsLandingTable/Components/StepTableMainRow.js.map +1 -1
- package/lib/components/StepsLandingTable/Components/StepsTitle.js +20 -3
- package/lib/components/StepsLandingTable/Components/StepsTitle.js.map +1 -1
- package/lib/components/StepsLandingTable/Components/Types.d.ts +11 -6
- package/lib/components/StepsLandingTable/StepLandingTable.js +2 -2
- package/lib/components/StepsLandingTable/StepLandingTable.js.map +1 -1
- package/lib/components/StepsLandingTable/types.d.ts +2 -0
- package/lib/components/StorageUsageBar/StorageUsageBar.d.ts +4 -0
- package/lib/components/StorageUsageBar/StorageUsageBar.js +9 -0
- package/lib/components/StorageUsageBar/StorageUsageBar.js.map +1 -0
- package/lib/components/StorageUsageBar/index.d.ts +1 -0
- package/lib/components/StorageUsageBar/index.js +2 -0
- package/lib/components/StorageUsageBar/index.js.map +1 -0
- package/lib/components/StorageUsageBar/types.d.ts +7 -0
- package/lib/components/StorageUsageBar/types.js +2 -0
- package/lib/components/StorageUsageBar/types.js.map +1 -0
- package/lib/components/Table/Table.js +2 -2
- package/lib/components/Table/Table.js.map +1 -1
- package/lib/components/Table/Types.d.ts +2 -0
- package/lib/components/Table/components/VirtualizedRows.d.ts +1 -1
- package/lib/components/Table/components/VirtualizedRows.js +6 -2
- package/lib/components/Table/components/VirtualizedRows.js.map +1 -1
- package/lib/components/TableTreeFn/Components/TableBody.d.ts +1 -1
- package/lib/components/TableTreeFn/Components/TableBody.js +12 -7
- package/lib/components/TableTreeFn/Components/TableBody.js.map +1 -1
- package/lib/components/TableTreeFn/Components/TableCell.d.ts +1 -1
- package/lib/components/TableTreeFn/Components/TableCell.js +25 -17
- package/lib/components/TableTreeFn/Components/TableCell.js.map +1 -1
- package/lib/components/TableTreeFn/Components/TableHead.d.ts +1 -1
- package/lib/components/TableTreeFn/Components/TableHead.js +19 -17
- package/lib/components/TableTreeFn/Components/TableHead.js.map +1 -1
- package/lib/components/TableTreeFn/Components/TableRow.d.ts +1 -1
- package/lib/components/TableTreeFn/Components/TableRow.js +7 -2
- package/lib/components/TableTreeFn/Components/TableRow.js.map +1 -1
- package/lib/components/TableTreeFn/TableTreeFn.d.ts +1 -1
- package/lib/components/TableTreeFn/TableTreeFn.js +707 -246
- package/lib/components/TableTreeFn/TableTreeFn.js.map +1 -1
- package/lib/components/TableTreeFn/Utils/TableCell.d.ts +1 -0
- package/lib/components/TableTreeFn/Utils/TableCell.js +24 -0
- package/lib/components/TableTreeFn/Utils/TableCell.js.map +1 -0
- package/lib/components/TableTreeFn/Utils/addLastChild.d.ts +1 -1
- package/lib/components/TableTreeFn/Utils/addLastChild.js +2 -2
- package/lib/components/TableTreeFn/Utils/addLastChild.js.map +1 -1
- package/lib/components/TableTreeFn/Utils/addNewRow.js +6 -5
- package/lib/components/TableTreeFn/Utils/addNewRow.js.map +1 -1
- package/lib/components/TableTreeFn/Utils/formatDataCell.js +1 -1
- package/lib/components/TableTreeFn/Utils/formatDataCell.js.map +1 -1
- package/lib/components/TableTreeFn/Utils/updateParentSibling.d.ts +1 -1
- package/lib/components/TableTreeFn/Utils/updateParentSibling.js +7 -7
- package/lib/components/TableTreeFn/Utils/updateParentSibling.js.map +1 -1
- package/lib/components/TableTreeFn/index.d.ts +2 -0
- package/lib/components/TableTreeFn/index.js +2 -0
- package/lib/components/TableTreeFn/index.js.map +1 -1
- package/lib/components/TableTreeFn/types.d.ts +30 -7
- package/lib/components/Tooltip/Tooltip.js +2 -2
- package/lib/components/Tooltip/Tooltip.js.map +1 -1
- package/lib/components/Tooltip/types.d.ts +1 -0
- package/lib/components/ZoomControl/ZoomControl.d.ts +5 -0
- package/lib/components/ZoomControl/ZoomControl.js +22 -0
- package/lib/components/ZoomControl/ZoomControl.js.map +1 -0
- package/lib/components/ZoomControl/index.d.ts +1 -0
- package/lib/components/ZoomControl/index.js +2 -0
- package/lib/components/ZoomControl/index.js.map +1 -0
- package/lib/components/ZoomControl/types.d.ts +8 -0
- package/lib/components/ZoomControl/types.js +2 -0
- package/lib/components/ZoomControl/types.js.map +1 -0
- package/lib/index.d.ts +62 -8
- package/lib/index.js +9 -9
- package/lib/index.js.map +1 -1
- package/lib/styles.css +1 -1
- package/lib/styles.css.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/AddStepGroup/AddStepGroup.js +5 -2
- package/lib/utils/AddStepGroup/AddStepGroup.js.map +1 -1
- package/lib/utils/converToCamelCase.d.ts +1 -0
- package/lib/utils/converToCamelCase.js +6 -0
- package/lib/utils/converToCamelCase.js.map +1 -0
- package/lib/utils/getTopVisibleNodeKey/getTopVisibleNodeKey.js +8 -9
- package/lib/utils/getTopVisibleNodeKey/getTopVisibleNodeKey.js.map +1 -1
- package/lib/utils/getTreeDetails/getTreeDetails.js +71 -12
- package/lib/utils/getTreeDetails/getTreeDetails.js.map +1 -1
- package/lib/utils/handleTreeExpandAllCollapseAll/handleTreeExpandAllCollapseAll.js +1 -0
- package/lib/utils/handleTreeExpandAllCollapseAll/handleTreeExpandAllCollapseAll.js.map +1 -1
- package/package.json +22 -14
|
@@ -1,242 +1,740 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
import '
|
|
4
|
-
import
|
|
2
|
+
/* eslint-disable max-lines */
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, forwardRef, } from 'react';
|
|
4
|
+
import './TableTreeFn.scss';
|
|
5
5
|
import TableBody from './Components/TableBody';
|
|
6
|
-
import
|
|
7
|
-
import { checkEmpty } from '../../utils/checkEmpty/checkEmpty';
|
|
6
|
+
import TableHead from './Components/TableHead';
|
|
8
7
|
import { debounce } from '../../utils/debounce/debounce';
|
|
9
|
-
|
|
10
|
-
|
|
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 === sourceId);
|
|
88
|
+
if (parentIndex !== -1) {
|
|
89
|
+
insertionIndex = parentIndex + 1;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const newNodeBase = {
|
|
95
|
+
hierarchy: newNodeHierarchy,
|
|
96
|
+
parentId: newNodeParentId,
|
|
97
|
+
sourceId: effectiveSourceNode?.key ?? sourceId ?? payloadSourceId ?? 'new-node',
|
|
98
|
+
isNewNode: true,
|
|
99
|
+
key: 'new-node',
|
|
100
|
+
value,
|
|
101
|
+
error,
|
|
102
|
+
label,
|
|
103
|
+
type,
|
|
104
|
+
options: convertedOptions,
|
|
105
|
+
selectedOption: convertedSelectedOption,
|
|
106
|
+
confirmIconTooltip,
|
|
107
|
+
cancelIconTooltip,
|
|
108
|
+
};
|
|
109
|
+
switch (action) {
|
|
110
|
+
case 'addAbove':
|
|
111
|
+
updatedTreeData.splice(sourceIndex, 0, newNodeBase);
|
|
112
|
+
break;
|
|
113
|
+
case 'addBelow':
|
|
114
|
+
updatedTreeData.splice(sourceIndex + 1, 0, newNodeBase);
|
|
115
|
+
break;
|
|
116
|
+
case 'addInside':
|
|
117
|
+
updatedTreeData.splice(Math.max(0, insertionIndex), 0, newNodeBase);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
return updatedTreeData;
|
|
121
|
+
};
|
|
122
|
+
const prepareTreeRows = (treeData, rootNode) => {
|
|
123
|
+
if (!treeData?.length)
|
|
124
|
+
return [];
|
|
125
|
+
const nodeById = new Map();
|
|
126
|
+
const childrenByParent = new Map();
|
|
127
|
+
const siblingIndex = new Map();
|
|
128
|
+
const loadedCountsByParent = new Map();
|
|
129
|
+
if (rootNode && rootNode.key) {
|
|
130
|
+
nodeById.set(rootNode.key, rootNode);
|
|
131
|
+
}
|
|
132
|
+
treeData.forEach((node) => {
|
|
133
|
+
nodeById.set(node.key, node);
|
|
134
|
+
const parentKey = node.parentId ?? ROOT_PARENT_ID;
|
|
135
|
+
if (!childrenByParent.has(parentKey)) {
|
|
136
|
+
childrenByParent.set(parentKey, []);
|
|
137
|
+
loadedCountsByParent.set(parentKey, { container: 0, resource: 0 });
|
|
138
|
+
}
|
|
139
|
+
childrenByParent.get(parentKey)?.push(node);
|
|
140
|
+
const counts = loadedCountsByParent.get(parentKey);
|
|
141
|
+
if (counts) {
|
|
142
|
+
if (node.container) {
|
|
143
|
+
counts.container++;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
counts.resource++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
childrenByParent.forEach((children) => {
|
|
151
|
+
children.forEach((child, index) => {
|
|
152
|
+
siblingIndex.set(child.key, index);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
const hasNextSibling = (node) => {
|
|
156
|
+
const parentKey = node.parentId ?? ROOT_PARENT_ID;
|
|
157
|
+
const siblings = childrenByParent.get(parentKey);
|
|
158
|
+
if (!siblings || siblings.length === 0)
|
|
159
|
+
return false;
|
|
160
|
+
const index = siblingIndex.get(node.key) ?? 0;
|
|
161
|
+
if (index < siblings.length - 1)
|
|
162
|
+
return true;
|
|
163
|
+
const parent = nodeById.get(parentKey);
|
|
164
|
+
const loadedCounts = loadedCountsByParent.get(parentKey);
|
|
165
|
+
if (parent && loadedCounts) {
|
|
166
|
+
const siblingContainerResources = siblings
|
|
167
|
+
.filter((node) => node.container)
|
|
168
|
+
.reduce((sum, node) => sum + (node.totalResourceCount ?? 0), 0);
|
|
169
|
+
const siblingDirectResourcesCount = siblings.filter((node) => !node.container).length;
|
|
170
|
+
const calculatedDirectResources = Math.max(0, (parent.totalResourceCount ?? 0) -
|
|
171
|
+
(siblingContainerResources + siblingDirectResourcesCount));
|
|
172
|
+
if (node.container) {
|
|
173
|
+
const hasMoreContainers = loadedCounts.container < (parent.subContainerCount ?? 0);
|
|
174
|
+
const hasResourcesFollowing = calculatedDirectResources > 0;
|
|
175
|
+
return hasMoreContainers || hasResourcesFollowing;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
return loadedCounts.resource < calculatedDirectResources;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
};
|
|
183
|
+
const buildParentSiblings = (node) => {
|
|
184
|
+
const hierarchy = node.hierarchy ?? 0;
|
|
185
|
+
if (hierarchy <= 0)
|
|
186
|
+
return [];
|
|
187
|
+
const path = [];
|
|
188
|
+
let current = node;
|
|
189
|
+
while (current) {
|
|
190
|
+
path.unshift(current);
|
|
191
|
+
if (!current.parentId)
|
|
192
|
+
break;
|
|
193
|
+
const parent = nodeById.get(current.parentId);
|
|
194
|
+
if (!parent)
|
|
195
|
+
break;
|
|
196
|
+
current = parent;
|
|
197
|
+
}
|
|
198
|
+
const flags = path.map((ancestor) => hasNextSibling(ancestor));
|
|
199
|
+
if (flags.length === hierarchy)
|
|
200
|
+
return flags;
|
|
201
|
+
if (flags.length > hierarchy)
|
|
202
|
+
return flags.slice(flags.length - hierarchy);
|
|
203
|
+
const padding = new Array(hierarchy - flags.length).fill(false);
|
|
204
|
+
return padding.concat(flags);
|
|
205
|
+
};
|
|
206
|
+
const resolveSelectedStatus = (node) => {
|
|
207
|
+
if (node.selectedStatus !== undefined)
|
|
208
|
+
return node.selectedStatus;
|
|
209
|
+
let current = node;
|
|
210
|
+
while (current?.parentId) {
|
|
211
|
+
const parent = nodeById.get(current.parentId);
|
|
212
|
+
if (!parent)
|
|
213
|
+
break;
|
|
214
|
+
if (parent.selectedStatus === 'completely')
|
|
215
|
+
return 'completely';
|
|
216
|
+
if (parent.selectedStatus === 'none')
|
|
217
|
+
return 'none';
|
|
218
|
+
current = parent;
|
|
219
|
+
}
|
|
220
|
+
if (rootNode?.selectedStatus === 'completely')
|
|
221
|
+
return 'completely';
|
|
222
|
+
if (rootNode?.selectedStatus === 'none')
|
|
223
|
+
return 'none';
|
|
224
|
+
return node.selectedStatus;
|
|
225
|
+
};
|
|
226
|
+
return treeData.map((node) => {
|
|
227
|
+
const nodeWithSiblings = node;
|
|
228
|
+
const finalParentSiblings = nodeWithSiblings.parentSiblings &&
|
|
229
|
+
nodeWithSiblings.parentSiblings.length > 0
|
|
230
|
+
? nodeWithSiblings.parentSiblings
|
|
231
|
+
: buildParentSiblings(node);
|
|
232
|
+
return {
|
|
233
|
+
...node,
|
|
234
|
+
lastChild: !hasNextSibling(node),
|
|
235
|
+
parentSiblings: finalParentSiblings,
|
|
236
|
+
selectedStatus: resolveSelectedStatus(node),
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
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) => {
|
|
11
241
|
const [expanding, setExpanding] = useState(null);
|
|
12
|
-
const [scrollDirection, setScrollDirection] = useState(null);
|
|
13
|
-
const [
|
|
14
|
-
const [
|
|
15
|
-
const [maintainScrollPosition, setMaintainScrollPosition] = useState(null);
|
|
242
|
+
const [scrollDirection, setScrollDirection] = useState(null);
|
|
243
|
+
const [virtualRange, setVirtualRange] = useState({ start: 0, end: 20 });
|
|
244
|
+
const [newNodeEditable, setNewNodeEditable] = useState(false);
|
|
16
245
|
const containerRef = useRef(null);
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
246
|
+
const headerRef = useRef(null);
|
|
247
|
+
const headerHeightRef = useRef(0);
|
|
248
|
+
const loadingRef = useRef(loading);
|
|
249
|
+
const scrollDirectionRef = useRef(null);
|
|
250
|
+
const scrollRafRef = useRef(null);
|
|
251
|
+
const scrollPositionRef = useRef({ lastScrollTop: 0 });
|
|
252
|
+
const previousFirstKeyRef = useRef(null);
|
|
253
|
+
const suppressScrollCancelUntilRef = useRef(0);
|
|
254
|
+
const debouncedScrollEndRef = useRef(null);
|
|
255
|
+
const expandTargetRef = useRef(null);
|
|
256
|
+
const expandingFallbackTimerRef = useRef(null);
|
|
257
|
+
const navigateAttemptsRef = useRef({ key: null, attempts: 0 });
|
|
258
|
+
const navigateClearTimerRef = useRef(null);
|
|
259
|
+
const getWidthInfo = (width, defaultWidth) => {
|
|
260
|
+
const w = width ?? defaultWidth;
|
|
261
|
+
const str = String(w);
|
|
262
|
+
if (str.endsWith('%')) {
|
|
263
|
+
return { value: parseFloat(str), unit: '%' };
|
|
264
|
+
}
|
|
265
|
+
return { value: parseFloat(str) || 0, unit: 'px' };
|
|
266
|
+
};
|
|
267
|
+
const columnMeta = useMemo(() => {
|
|
268
|
+
let stickyLeftPx = 0;
|
|
269
|
+
let stickyLeftPct = 0;
|
|
270
|
+
return columnsData.map((col, index) => {
|
|
271
|
+
const { value, unit } = getWidthInfo(col.width, DEFAULT_COLUMN_WIDTH);
|
|
272
|
+
const sticky = !!freezeColumns && index < freezeColumns;
|
|
273
|
+
const widthCss = unit === '%' ? `${value}%` : `${value}px`;
|
|
274
|
+
const leftCss = `calc(${stickyLeftPx}px + ${stickyLeftPct}%)`;
|
|
275
|
+
const meta = {
|
|
276
|
+
width: widthCss,
|
|
277
|
+
sticky,
|
|
278
|
+
left: leftCss,
|
|
279
|
+
zIndex: sticky ? 100 + (freezeColumns ?? 0) - index : 1,
|
|
280
|
+
};
|
|
281
|
+
if (sticky) {
|
|
282
|
+
if (unit === '%') {
|
|
283
|
+
stickyLeftPct += value;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
stickyLeftPx += value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return meta;
|
|
290
|
+
});
|
|
291
|
+
}, [columnsData, freezeColumns]);
|
|
292
|
+
const totalWidth = useMemo(() => {
|
|
293
|
+
let totalPx = 0;
|
|
294
|
+
let totalPct = 0;
|
|
295
|
+
columnsData.forEach((col) => {
|
|
296
|
+
const { value, unit } = getWidthInfo(col.width, DEFAULT_COLUMN_WIDTH);
|
|
297
|
+
if (unit === '%') {
|
|
298
|
+
totalPct += value;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
totalPx += value;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
return `calc(${totalPx}px + ${totalPct}%)`;
|
|
305
|
+
}, [columnsData]);
|
|
306
|
+
const frozenColumnWidth = useMemo(() => {
|
|
307
|
+
if (!freezeColumns)
|
|
308
|
+
return '0px';
|
|
309
|
+
let totalPx = 0;
|
|
310
|
+
let totalPct = 0;
|
|
311
|
+
const frozenCols = columnMeta.slice(0, freezeColumns);
|
|
312
|
+
frozenCols.forEach((col) => {
|
|
313
|
+
const val = parseFloat(col.width) || 0;
|
|
314
|
+
if (col.width.endsWith('%')) {
|
|
315
|
+
totalPct += val;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
totalPx += val;
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
return `calc(${totalPx}px + ${totalPct}%)`;
|
|
322
|
+
}, [columnMeta, freezeColumns]);
|
|
323
|
+
const treeDataWithNewNode = useMemo(() => insertNewNode(treeData, newNode, rootNode?.node), [treeData, newNode, rootNode]);
|
|
324
|
+
const preparedRows = useMemo(() => prepareTreeRows(treeDataWithNewNode, rootNode?.node), [treeDataWithNewNode, rootNode?.node]);
|
|
325
|
+
const visiblePreparedRows = useMemo(() => preparedRows.filter((node) => !node.hide), [preparedRows]);
|
|
326
|
+
const preparedRowsByKey = useMemo(() => {
|
|
327
|
+
const map = new Map();
|
|
328
|
+
preparedRows.forEach((node) => map.set(node.key, node));
|
|
329
|
+
return map;
|
|
330
|
+
}, [preparedRows]);
|
|
331
|
+
const keyIndexMap = useMemo(() => {
|
|
332
|
+
const map = new Map();
|
|
333
|
+
visiblePreparedRows.forEach((node, index) => map.set(node.key, index));
|
|
334
|
+
return map;
|
|
335
|
+
}, [visiblePreparedRows]);
|
|
336
|
+
const clearExpanding = useCallback((targetKey) => {
|
|
337
|
+
setExpanding((current) => {
|
|
338
|
+
if (!current)
|
|
339
|
+
return current;
|
|
340
|
+
if (targetKey && current !== targetKey)
|
|
341
|
+
return current;
|
|
342
|
+
return null;
|
|
343
|
+
});
|
|
344
|
+
if (!targetKey || expandTargetRef.current?.key === targetKey) {
|
|
345
|
+
expandTargetRef.current = null;
|
|
346
|
+
}
|
|
347
|
+
if (expandingFallbackTimerRef.current) {
|
|
348
|
+
clearTimeout(expandingFallbackTimerRef.current);
|
|
349
|
+
expandingFallbackTimerRef.current = null;
|
|
350
|
+
}
|
|
351
|
+
}, []);
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
loadingRef.current = loading;
|
|
354
|
+
}, [loading]);
|
|
355
|
+
useEffect(() => {
|
|
356
|
+
scrollDirectionRef.current = scrollDirection;
|
|
357
|
+
}, [scrollDirection]);
|
|
358
|
+
const updateScrollDirection = useCallback((direction) => {
|
|
359
|
+
scrollDirectionRef.current = direction;
|
|
360
|
+
setScrollDirection(direction);
|
|
361
|
+
}, []);
|
|
362
|
+
const updateVirtualRange = useCallback(() => {
|
|
363
|
+
const container = containerRef.current;
|
|
364
|
+
if (!container)
|
|
27
365
|
return;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
366
|
+
const headerHeight = headerHeightRef.current || 0;
|
|
367
|
+
const rowScrollTop = Math.max(0, container.scrollTop - headerHeight);
|
|
368
|
+
const viewportHeight = container.clientHeight - headerHeight;
|
|
369
|
+
let start = Math.floor(rowScrollTop / ROW_HEIGHT) - OVERSCAN;
|
|
370
|
+
let end = Math.ceil((rowScrollTop + viewportHeight) / ROW_HEIGHT) + OVERSCAN;
|
|
371
|
+
start = Math.max(0, start);
|
|
372
|
+
end = Math.min(visiblePreparedRows.length, end);
|
|
373
|
+
setVirtualRange((prev) => {
|
|
374
|
+
if (prev.start === start && prev.end === end)
|
|
375
|
+
return prev;
|
|
376
|
+
return { start, end };
|
|
377
|
+
});
|
|
378
|
+
}, [visiblePreparedRows.length]);
|
|
379
|
+
const scheduleRangeUpdate = useCallback(() => {
|
|
380
|
+
if (scrollRafRef.current !== null)
|
|
381
|
+
return;
|
|
382
|
+
scrollRafRef.current = requestAnimationFrame(() => {
|
|
383
|
+
scrollRafRef.current = null;
|
|
384
|
+
updateVirtualRange();
|
|
385
|
+
});
|
|
386
|
+
}, [updateVirtualRange]);
|
|
387
|
+
// Handle scroll position safety when searching/filtering/collapsing
|
|
388
|
+
useLayoutEffect(() => {
|
|
389
|
+
const container = containerRef.current;
|
|
390
|
+
if (!container)
|
|
35
391
|
return;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
392
|
+
const totalContentHeight = visiblePreparedRows.length * ROW_HEIGHT;
|
|
393
|
+
const maxScrollTop = Math.max(0, totalContentHeight - (container.clientHeight - headerHeightRef.current));
|
|
394
|
+
if (scrollPositionRef.current.lastScrollTop > maxScrollTop &&
|
|
395
|
+
visiblePreparedRows.length > 0) {
|
|
396
|
+
// 1. EXPLICIT NAVIGATION: Create or Clone (if Redux provided the target)
|
|
397
|
+
if (newNode || navigateTreeNode) {
|
|
398
|
+
container.scrollTop = 0;
|
|
399
|
+
scrollPositionRef.current.lastScrollTop = 0;
|
|
400
|
+
}
|
|
401
|
+
// 2. EXPLICIT COLLAPSE: The table knows you manually clicked the collapse arrow
|
|
402
|
+
else if (expanding) {
|
|
403
|
+
container.scrollTop = maxScrollTop;
|
|
404
|
+
scrollPositionRef.current.lastScrollTop = maxScrollTop;
|
|
405
|
+
}
|
|
406
|
+
// 3. MASSIVE DATA RESET: Clone (without navigation), Refresh, or Search
|
|
407
|
+
// If the list height drops drastically (by more than one full screen), reset to top
|
|
408
|
+
else if (scrollPositionRef.current.lastScrollTop - maxScrollTop >
|
|
409
|
+
container.clientHeight) {
|
|
410
|
+
container.scrollTop = 0;
|
|
411
|
+
scrollPositionRef.current.lastScrollTop = 0;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
updateVirtualRange();
|
|
415
|
+
}, [
|
|
416
|
+
visiblePreparedRows.length,
|
|
417
|
+
updateVirtualRange,
|
|
418
|
+
newNode,
|
|
419
|
+
navigateTreeNode,
|
|
420
|
+
expanding,
|
|
421
|
+
]);
|
|
40
422
|
const handleScroll = useCallback(() => {
|
|
41
423
|
const container = containerRef.current;
|
|
42
|
-
if (!container
|
|
424
|
+
if (!container)
|
|
43
425
|
return;
|
|
44
|
-
const now = Date.now();
|
|
45
426
|
const currentScrollTop = container.scrollTop;
|
|
46
|
-
const scrollHeight = container.scrollHeight;
|
|
47
|
-
const clientHeight = container.clientHeight;
|
|
48
|
-
// Scroll direction calculation
|
|
49
427
|
const direction = currentScrollTop > scrollPositionRef.current.lastScrollTop
|
|
50
428
|
? 'down'
|
|
51
429
|
: 'up';
|
|
52
|
-
scrollPositionRef.current =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
430
|
+
scrollPositionRef.current.lastScrollTop = currentScrollTop;
|
|
431
|
+
scheduleRangeUpdate();
|
|
432
|
+
onScroll?.();
|
|
433
|
+
debouncedScrollEndRef.current?.();
|
|
434
|
+
if (!pagination ||
|
|
435
|
+
loading ||
|
|
436
|
+
scrollDirectionRef.current ||
|
|
437
|
+
checkEmpty(treeData))
|
|
438
|
+
return;
|
|
439
|
+
const scrollHeight = container.scrollHeight;
|
|
440
|
+
const clientHeight = container.clientHeight;
|
|
62
441
|
const nearBottom = scrollHeight - (currentScrollTop + clientHeight) < scrollThreshold;
|
|
63
|
-
const nearTop = currentScrollTop < scrollThreshold;
|
|
442
|
+
const nearTop = currentScrollTop < scrollThreshold + headerHeightRef.current;
|
|
64
443
|
if (direction === 'down' &&
|
|
65
444
|
nearBottom &&
|
|
66
|
-
!
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
scrollDebounceRef.current = setTimeout(() => {
|
|
70
|
-
loadMoreBelow();
|
|
71
|
-
}, 150);
|
|
445
|
+
!treeData[treeData.length - 1]?.lastResource) {
|
|
446
|
+
updateScrollDirection('below');
|
|
447
|
+
loadMore('below');
|
|
72
448
|
}
|
|
73
|
-
if (direction === 'up' &&
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
treeData[0]?.lastResource !== true) {
|
|
78
|
-
scrollDebounceRef.current = setTimeout(() => {
|
|
79
|
-
loadMoreAbove();
|
|
80
|
-
}, 150);
|
|
81
|
-
}
|
|
82
|
-
// Scroll Restoration After Loading Above
|
|
83
|
-
if (!loading &&
|
|
84
|
-
scrollDirection === 'above' &&
|
|
85
|
-
previousTreeDataRef.current.length > 0 &&
|
|
86
|
-
prevScrollTop !== null) {
|
|
87
|
-
const previousTreeData = previousTreeDataRef.current;
|
|
88
|
-
let addedRowsCount = 0;
|
|
89
|
-
for (let i = 0; i < treeData.length; i++) {
|
|
90
|
-
if (previousTreeData[0] === treeData[i])
|
|
91
|
-
break;
|
|
92
|
-
addedRowsCount++;
|
|
93
|
-
}
|
|
94
|
-
let retries = 0;
|
|
95
|
-
const maxRetries = 30;
|
|
96
|
-
// this function will try to restore the scroll position after loading more data specially when loading more data above
|
|
97
|
-
const tryRestoreScroll = () => {
|
|
98
|
-
const allRows = Array.from(container.querySelectorAll('.ff-table-tree-row'));
|
|
99
|
-
let totalAddedHeight = 0;
|
|
100
|
-
for (let i = 0; i < addedRowsCount; i++) {
|
|
101
|
-
const height = allRows[i]?.getBoundingClientRect().height || 0;
|
|
102
|
-
totalAddedHeight += height;
|
|
103
|
-
}
|
|
104
|
-
const canScroll = container.scrollHeight > container.clientHeight;
|
|
105
|
-
const validHeights = totalAddedHeight > 0;
|
|
106
|
-
if (validHeights && canScroll) {
|
|
107
|
-
container.scrollTop = prevScrollTop + totalAddedHeight;
|
|
108
|
-
previousTreeDataRef.current = treeData;
|
|
109
|
-
}
|
|
110
|
-
else if (retries < maxRetries) {
|
|
111
|
-
retries++;
|
|
112
|
-
requestAnimationFrame(tryRestoreScroll);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
previousTreeDataRef.current = treeData;
|
|
116
|
-
console.warn('Failed to restore scroll position after max retries');
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
requestAnimationFrame(tryRestoreScroll);
|
|
449
|
+
if (direction === 'up' && nearTop && !treeData[0]?.lastResource) {
|
|
450
|
+
updateScrollDirection('above');
|
|
451
|
+
previousFirstKeyRef.current = treeData[0]?.key ?? null;
|
|
452
|
+
loadMore('above');
|
|
120
453
|
}
|
|
121
454
|
}, [
|
|
455
|
+
treeData,
|
|
456
|
+
pagination,
|
|
122
457
|
loading,
|
|
123
458
|
scrollDirection,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
loadMoreBelow,
|
|
127
|
-
prevScrollTop,
|
|
459
|
+
scrollThreshold,
|
|
460
|
+
scheduleRangeUpdate,
|
|
128
461
|
onScroll,
|
|
462
|
+
loadMore,
|
|
463
|
+
updateScrollDirection,
|
|
129
464
|
]);
|
|
130
|
-
// Attach scroll event listener for updated first node when we scroll
|
|
131
465
|
useEffect(() => {
|
|
132
|
-
|
|
133
|
-
if (!scrollDiv)
|
|
466
|
+
if (loading || !pagination || checkEmpty(treeData))
|
|
134
467
|
return;
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
468
|
+
const firstNode = treeData[0];
|
|
469
|
+
const hasMoreAbove = !firstNode?.lastResource;
|
|
470
|
+
const { totalSubContainerCount = 0, totalResourceCount = 0 } = rootNode?.node || {};
|
|
471
|
+
const totalItems = totalSubContainerCount + totalResourceCount;
|
|
472
|
+
const isListTooShort = visiblePreparedRows.length < 10 &&
|
|
473
|
+
visiblePreparedRows.length < totalItems;
|
|
474
|
+
if (hasMoreAbove && isListTooShort) {
|
|
475
|
+
previousFirstKeyRef.current = firstNode?.key ?? null;
|
|
476
|
+
updateScrollDirection('above');
|
|
477
|
+
loadMore('above');
|
|
478
|
+
}
|
|
479
|
+
}, [
|
|
480
|
+
loading,
|
|
481
|
+
pagination,
|
|
482
|
+
treeData,
|
|
483
|
+
visiblePreparedRows.length,
|
|
484
|
+
rootNode,
|
|
485
|
+
loadMore,
|
|
486
|
+
updateScrollDirection,
|
|
487
|
+
]);
|
|
488
|
+
useLayoutEffect(() => {
|
|
489
|
+
if (!loading && scrollDirection === 'above') {
|
|
490
|
+
const container = containerRef.current;
|
|
491
|
+
const previousKey = previousFirstKeyRef.current;
|
|
492
|
+
if (container && previousKey) {
|
|
493
|
+
const newIndex = keyIndexMap.get(previousKey);
|
|
494
|
+
if (newIndex !== undefined && newIndex > 0) {
|
|
495
|
+
container.scrollTop += newIndex * ROW_HEIGHT;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
previousFirstKeyRef.current = null;
|
|
499
|
+
updateScrollDirection(null);
|
|
500
|
+
}
|
|
501
|
+
else if (!loading && scrollDirection === 'below') {
|
|
502
|
+
updateScrollDirection(null);
|
|
503
|
+
}
|
|
504
|
+
}, [loading, scrollDirection, keyIndexMap, updateScrollDirection]);
|
|
505
|
+
useEffect(() => {
|
|
506
|
+
const container = containerRef.current;
|
|
507
|
+
if (!container)
|
|
508
|
+
return;
|
|
509
|
+
const handleWheelAtTop = (event) => {
|
|
510
|
+
if (event.deltaY >= 0)
|
|
511
|
+
return;
|
|
512
|
+
if (!pagination || checkEmpty(treeData) || loadingRef.current)
|
|
513
|
+
return;
|
|
514
|
+
if (scrollDirectionRef.current)
|
|
515
|
+
return;
|
|
516
|
+
if (container.scrollTop > 1 || treeData[0]?.lastResource)
|
|
517
|
+
return;
|
|
518
|
+
previousFirstKeyRef.current = treeData[0]?.key ?? null;
|
|
519
|
+
updateScrollDirection('above');
|
|
520
|
+
loadMore('above');
|
|
141
521
|
};
|
|
142
|
-
|
|
522
|
+
container.addEventListener('wheel', handleWheelAtTop, { passive: true });
|
|
143
523
|
return () => {
|
|
144
|
-
|
|
145
|
-
debouncedOnScrollEnd.cancel();
|
|
524
|
+
container.removeEventListener('wheel', handleWheelAtTop);
|
|
146
525
|
};
|
|
147
|
-
}, [
|
|
526
|
+
}, [pagination, treeData, loadMore, updateScrollDirection]);
|
|
148
527
|
useEffect(() => {
|
|
528
|
+
const container = containerRef.current;
|
|
529
|
+
if (!container)
|
|
530
|
+
return;
|
|
531
|
+
debouncedScrollEndRef.current = onScrollEnd
|
|
532
|
+
? debounce(() => {
|
|
533
|
+
onScrollEnd?.();
|
|
534
|
+
}, 150)
|
|
535
|
+
: null;
|
|
536
|
+
container.addEventListener('scroll', handleScroll, { passive: true });
|
|
149
537
|
return () => {
|
|
150
|
-
|
|
151
|
-
|
|
538
|
+
container.removeEventListener('scroll', handleScroll);
|
|
539
|
+
debouncedScrollEndRef.current?.cancel();
|
|
540
|
+
};
|
|
541
|
+
}, [handleScroll, onScrollEnd]);
|
|
542
|
+
useEffect(() => {
|
|
543
|
+
const container = containerRef.current;
|
|
544
|
+
if (!container)
|
|
545
|
+
return;
|
|
546
|
+
const handleScrollCapture = (event) => {
|
|
547
|
+
if (Date.now() < suppressScrollCancelUntilRef.current) {
|
|
548
|
+
event.stopImmediatePropagation();
|
|
152
549
|
}
|
|
153
550
|
};
|
|
551
|
+
container.addEventListener('scroll', handleScrollCapture, {
|
|
552
|
+
passive: true,
|
|
553
|
+
capture: true,
|
|
554
|
+
});
|
|
555
|
+
return () => {
|
|
556
|
+
container.removeEventListener('scroll', handleScrollCapture, true);
|
|
557
|
+
};
|
|
154
558
|
}, []);
|
|
155
|
-
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
const headerNode = headerRef.current;
|
|
561
|
+
if (!headerNode)
|
|
562
|
+
return;
|
|
563
|
+
const updateHeight = () => {
|
|
564
|
+
headerHeightRef.current = headerNode.getBoundingClientRect().height;
|
|
565
|
+
updateVirtualRange();
|
|
566
|
+
};
|
|
567
|
+
const resizeObserver = new ResizeObserver(updateHeight);
|
|
568
|
+
resizeObserver.observe(headerNode);
|
|
569
|
+
return () => resizeObserver.disconnect();
|
|
570
|
+
}, [updateVirtualRange]);
|
|
156
571
|
useLayoutEffect(() => {
|
|
157
|
-
if (!
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
572
|
+
if (!newNode || !containerRef.current)
|
|
573
|
+
return;
|
|
574
|
+
const container = containerRef.current;
|
|
575
|
+
const newNodeIndex = keyIndexMap.get('new-node');
|
|
576
|
+
if (newNodeIndex === undefined)
|
|
577
|
+
return;
|
|
578
|
+
const headerHeight = headerHeightRef.current || 0;
|
|
579
|
+
const viewportHeight = container.clientHeight - headerHeight;
|
|
580
|
+
if (viewportHeight <= 0)
|
|
581
|
+
return;
|
|
582
|
+
const rowTop = newNodeIndex * ROW_HEIGHT;
|
|
583
|
+
const rowBottom = rowTop + ROW_HEIGHT;
|
|
584
|
+
const rowScrollTop = Math.max(0, container.scrollTop - headerHeight);
|
|
585
|
+
const viewTop = rowScrollTop;
|
|
586
|
+
const viewBottom = rowScrollTop + viewportHeight;
|
|
587
|
+
let nextRowScrollTop = rowScrollTop;
|
|
588
|
+
if (rowTop < viewTop) {
|
|
589
|
+
nextRowScrollTop = rowTop;
|
|
171
590
|
}
|
|
172
|
-
else if (
|
|
173
|
-
|
|
591
|
+
else if (rowBottom > viewBottom) {
|
|
592
|
+
nextRowScrollTop = Math.max(0, rowBottom - viewportHeight);
|
|
174
593
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const node = document.getElementById(navigateTreeNode);
|
|
180
|
-
const container = containerRef.current;
|
|
181
|
-
if (node && container) {
|
|
182
|
-
// Store current scroll position
|
|
183
|
-
setMaintainScrollPosition(container.scrollTop);
|
|
184
|
-
// Calculate scroll position
|
|
185
|
-
const nodeRect = node.getBoundingClientRect();
|
|
186
|
-
const containerRect = container.getBoundingClientRect();
|
|
187
|
-
const scrollTop = container.scrollTop;
|
|
188
|
-
const nodeTop = nodeRect.top - containerRect.top + scrollTop;
|
|
189
|
-
const containerHeight = containerRect.height;
|
|
190
|
-
// Scroll to center the node
|
|
191
|
-
container.scrollTo({
|
|
192
|
-
top: nodeTop - containerHeight / 2 + nodeRect.height / 2,
|
|
193
|
-
behavior: 'smooth',
|
|
194
|
-
});
|
|
195
|
-
}
|
|
594
|
+
if (nextRowScrollTop !== rowScrollTop) {
|
|
595
|
+
suppressScrollCancelUntilRef.current = Date.now() + 300;
|
|
596
|
+
container.scrollTop = nextRowScrollTop + headerHeight;
|
|
597
|
+
updateVirtualRange();
|
|
196
598
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
|
|
599
|
+
const rafId = requestAnimationFrame(() => {
|
|
600
|
+
setNewNodeEditable(true);
|
|
601
|
+
});
|
|
602
|
+
return () => cancelAnimationFrame(rafId);
|
|
603
|
+
}, [newNode, keyIndexMap, updateVirtualRange]);
|
|
202
604
|
useEffect(() => {
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
setMaintainScrollPosition(null);
|
|
605
|
+
if (!newNode) {
|
|
606
|
+
setNewNodeEditable(false);
|
|
206
607
|
}
|
|
207
|
-
}, [
|
|
208
|
-
|
|
608
|
+
}, [newNode]);
|
|
609
|
+
useLayoutEffect(() => {
|
|
610
|
+
if (!expanding || loading)
|
|
611
|
+
return;
|
|
612
|
+
const expected = expandTargetRef.current;
|
|
613
|
+
if (!expected || expected.key !== expanding) {
|
|
614
|
+
const rafId = requestAnimationFrame(() => {
|
|
615
|
+
clearExpanding(expanding);
|
|
616
|
+
});
|
|
617
|
+
return () => cancelAnimationFrame(rafId);
|
|
618
|
+
}
|
|
619
|
+
const updatedNode = preparedRowsByKey.get(expected.key);
|
|
620
|
+
if (!updatedNode) {
|
|
621
|
+
const rafId = requestAnimationFrame(() => {
|
|
622
|
+
clearExpanding(expected.key);
|
|
623
|
+
});
|
|
624
|
+
return () => cancelAnimationFrame(rafId);
|
|
625
|
+
}
|
|
626
|
+
if (Boolean(updatedNode.expanded) !== expected.expanded)
|
|
627
|
+
return;
|
|
628
|
+
const rafId = requestAnimationFrame(() => {
|
|
629
|
+
clearExpanding(expected.key);
|
|
630
|
+
});
|
|
631
|
+
return () => cancelAnimationFrame(rafId);
|
|
632
|
+
}, [preparedRowsByKey, expanding, loading, clearExpanding]);
|
|
209
633
|
useEffect(() => {
|
|
634
|
+
if (navigateAttemptsRef.current.key !== navigateTreeNode) {
|
|
635
|
+
navigateAttemptsRef.current = {
|
|
636
|
+
key: navigateTreeNode,
|
|
637
|
+
attempts: 0,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
if (!navigateTreeNode && navigateClearTimerRef.current) {
|
|
641
|
+
clearTimeout(navigateClearTimerRef.current);
|
|
642
|
+
navigateClearTimerRef.current = null;
|
|
643
|
+
}
|
|
644
|
+
}, [navigateTreeNode]);
|
|
645
|
+
useLayoutEffect(() => {
|
|
646
|
+
if (!navigateTreeNode || newNode)
|
|
647
|
+
return;
|
|
210
648
|
const container = containerRef.current;
|
|
211
|
-
if (!container
|
|
649
|
+
if (!container)
|
|
212
650
|
return;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
651
|
+
const rowIndex = keyIndexMap.get(navigateTreeNode);
|
|
652
|
+
if (rowIndex !== undefined) {
|
|
653
|
+
navigateAttemptsRef.current = {
|
|
654
|
+
key: navigateTreeNode,
|
|
655
|
+
attempts: 0,
|
|
656
|
+
};
|
|
657
|
+
const headerHeight = headerHeightRef.current || 0;
|
|
658
|
+
const viewportHeight = Math.max(ROW_HEIGHT, container.clientHeight - headerHeight);
|
|
659
|
+
const rowCenter = rowIndex * ROW_HEIGHT + ROW_HEIGHT / 2;
|
|
660
|
+
const targetTop = Math.max(0, rowCenter - viewportHeight / 2) + headerHeight;
|
|
661
|
+
suppressScrollCancelUntilRef.current = Date.now() + 400;
|
|
662
|
+
container.scrollTo({
|
|
663
|
+
top: targetTop,
|
|
664
|
+
behavior: 'smooth',
|
|
665
|
+
});
|
|
666
|
+
scheduleRangeUpdate();
|
|
667
|
+
if (navigateClearTimerRef.current) {
|
|
668
|
+
clearTimeout(navigateClearTimerRef.current);
|
|
220
669
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (treeData.length > 0) {
|
|
226
|
-
previousTreeDataRef.current = treeData;
|
|
670
|
+
navigateClearTimerRef.current = setTimeout(() => {
|
|
671
|
+
handleRemoveNavigateTreeNode?.();
|
|
672
|
+
}, 350);
|
|
673
|
+
return;
|
|
227
674
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
675
|
+
if (loading || scrollDirection)
|
|
676
|
+
return;
|
|
677
|
+
const retryState = navigateAttemptsRef.current;
|
|
678
|
+
if (retryState.key !== navigateTreeNode) {
|
|
679
|
+
retryState.key = navigateTreeNode;
|
|
680
|
+
retryState.attempts = 0;
|
|
232
681
|
}
|
|
233
|
-
|
|
682
|
+
retryState.attempts += 1;
|
|
683
|
+
if (retryState.attempts > 30) {
|
|
684
|
+
handleRemoveNavigateTreeNode?.();
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (pagination && treeData[0]?.lastResource !== true) {
|
|
688
|
+
previousFirstKeyRef.current = null;
|
|
689
|
+
updateScrollDirection('above');
|
|
690
|
+
loadMore('above');
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (retryState.attempts > 2) {
|
|
694
|
+
handleRemoveNavigateTreeNode?.();
|
|
695
|
+
}
|
|
696
|
+
}, [
|
|
697
|
+
navigateTreeNode,
|
|
698
|
+
newNode,
|
|
699
|
+
keyIndexMap,
|
|
700
|
+
loading,
|
|
701
|
+
scrollDirection,
|
|
702
|
+
pagination,
|
|
703
|
+
treeData,
|
|
704
|
+
loadMore,
|
|
705
|
+
scheduleRangeUpdate,
|
|
706
|
+
handleRemoveNavigateTreeNode,
|
|
707
|
+
updateScrollDirection,
|
|
708
|
+
]);
|
|
234
709
|
const handleToggleExpand = useCallback((node) => {
|
|
235
710
|
if (expanding)
|
|
236
711
|
return;
|
|
712
|
+
const nextExpandedState = !node?.expanded;
|
|
237
713
|
setExpanding(node.key);
|
|
714
|
+
expandTargetRef.current = {
|
|
715
|
+
key: node.key,
|
|
716
|
+
expanded: nextExpandedState,
|
|
717
|
+
};
|
|
718
|
+
if (expandingFallbackTimerRef.current) {
|
|
719
|
+
clearTimeout(expandingFallbackTimerRef.current);
|
|
720
|
+
}
|
|
721
|
+
const fallbackStartedAt = Date.now();
|
|
722
|
+
const MAX_FALLBACK_WAIT_MS = 2000;
|
|
723
|
+
const FALLBACK_CHECK_INTERVAL_MS = 1000;
|
|
724
|
+
const ensureExpandCleanup = () => {
|
|
725
|
+
if (expandTargetRef.current?.key !== node.key)
|
|
726
|
+
return;
|
|
727
|
+
const isStillLoading = loadingRef.current;
|
|
728
|
+
const didTimeout = Date.now() - fallbackStartedAt >= MAX_FALLBACK_WAIT_MS;
|
|
729
|
+
if (isStillLoading && !didTimeout) {
|
|
730
|
+
expandingFallbackTimerRef.current = setTimeout(ensureExpandCleanup, FALLBACK_CHECK_INTERVAL_MS);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
clearExpanding(node.key);
|
|
734
|
+
};
|
|
735
|
+
expandingFallbackTimerRef.current = setTimeout(ensureExpandCleanup, FALLBACK_CHECK_INTERVAL_MS);
|
|
238
736
|
onExpand?.(node);
|
|
239
|
-
}, [onExpand,
|
|
737
|
+
}, [expanding, onExpand, clearExpanding]);
|
|
240
738
|
const handleCheckBoxChange = useCallback((e, node) => {
|
|
241
739
|
if (expanding)
|
|
242
740
|
return;
|
|
@@ -247,69 +745,32 @@ showHeader = true, }, ref) => {
|
|
|
247
745
|
return;
|
|
248
746
|
onClick?.(e, node);
|
|
249
747
|
}, [onClick, expanding]);
|
|
250
|
-
const DEFAULT_COLUMN_WIDTH = 400;
|
|
251
|
-
const calculateFrozenWidth = (columnData, freezeColumns) => {
|
|
252
|
-
return columnData
|
|
253
|
-
.slice(0, freezeColumns)
|
|
254
|
-
.reduce((acc, col) => acc + parseInt(col.width || `${DEFAULT_COLUMN_WIDTH}`, 10), 0);
|
|
255
|
-
};
|
|
256
|
-
let frozenWidth;
|
|
257
|
-
if (freezeColumns) {
|
|
258
|
-
frozenWidth = calculateFrozenWidth(columnsData, freezeColumns);
|
|
259
|
-
}
|
|
260
|
-
// half visible, click on upward arrow functionality, page auto scrolling
|
|
261
748
|
useEffect(() => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
newNode.sourceId === treeData[0]?.key) {
|
|
267
|
-
container.scrollTo({ top: 0, behavior: 'smooth' });
|
|
268
|
-
}
|
|
269
|
-
else if (newNode.action === 'addAbove' &&
|
|
270
|
-
newNode.sourceId === newNode.firstNodeKey) {
|
|
271
|
-
const newScrollTop = container.scrollTop - 64;
|
|
272
|
-
container.scrollTo({ top: newScrollTop, behavior: 'smooth' });
|
|
273
|
-
}
|
|
274
|
-
}, [newNode]);
|
|
275
|
-
useLayoutEffect(() => {
|
|
276
|
-
if (!navigateTreeNode)
|
|
277
|
-
return;
|
|
278
|
-
const container = containerRef.current;
|
|
279
|
-
if (!container)
|
|
280
|
-
return;
|
|
281
|
-
let retryCount = 0;
|
|
282
|
-
const maxRetries = 30;
|
|
283
|
-
const scrollToNode = () => {
|
|
284
|
-
const element = document.getElementById(navigateTreeNode);
|
|
285
|
-
if (element) {
|
|
286
|
-
setTimeout(() => {
|
|
287
|
-
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
288
|
-
handleRemoveNavigateTreeNode();
|
|
289
|
-
}, 200);
|
|
749
|
+
return () => {
|
|
750
|
+
if (scrollRafRef.current !== null) {
|
|
751
|
+
cancelAnimationFrame(scrollRafRef.current);
|
|
752
|
+
scrollRafRef.current = null;
|
|
290
753
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
retryCount++;
|
|
297
|
-
requestAnimationFrame(scrollToNode);
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
handleRemoveNavigateTreeNode();
|
|
301
|
-
}
|
|
754
|
+
if (expandingFallbackTimerRef.current) {
|
|
755
|
+
clearTimeout(expandingFallbackTimerRef.current);
|
|
756
|
+
}
|
|
757
|
+
if (navigateClearTimerRef.current) {
|
|
758
|
+
clearTimeout(navigateClearTimerRef.current);
|
|
302
759
|
}
|
|
303
760
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
'--
|
|
309
|
-
|
|
310
|
-
: '0px',
|
|
761
|
+
}, []);
|
|
762
|
+
const totalHeight = visiblePreparedRows.length * ROW_HEIGHT;
|
|
763
|
+
const visibleRows = visiblePreparedRows.slice(virtualRange.start, virtualRange.end);
|
|
764
|
+
return (_jsx("div", { className: "tree-table-wrapper-container table-tree-fn", children: _jsx("div", { className: "tree-table-fn-wrap", ref: ref, children: _jsx("div", { className: `table-fn-scrollable ${visiblePreparedRows.length ? '' : 'table-fn-empty'}`, ref: containerRef, style: {
|
|
765
|
+
'--table-height': visiblePreparedRows.length ? height : 'auto',
|
|
766
|
+
'--frozen-column-width': frozenColumnWidth,
|
|
311
767
|
border: tableBorder,
|
|
312
|
-
}, children: _jsxs("
|
|
768
|
+
}, children: _jsxs("div", { className: "tree-table-fn", 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
|
|
769
|
+
? (id) => {
|
|
770
|
+
setNewNodeEditable(Boolean(id));
|
|
771
|
+
}
|
|
772
|
+
: undefined })] }) }) }) }));
|
|
313
773
|
});
|
|
774
|
+
TableTreeFn.displayName = 'TableTreeFn';
|
|
314
775
|
export default TableTreeFn;
|
|
315
776
|
//# sourceMappingURL=TableTreeFn.js.map
|