funda-ui 4.7.711 → 4.7.730

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 (37) hide show
  1. package/DragDropList/index.d.ts +1 -0
  2. package/DragDropList/index.js +143 -52
  3. package/DynamicFields/index.d.ts +1 -0
  4. package/DynamicFields/index.js +108 -8
  5. package/EventCalendarTimeline/index.css +12 -1
  6. package/EventCalendarTimeline/index.d.ts +1 -0
  7. package/EventCalendarTimeline/index.js +99 -6
  8. package/MultipleSelect/index.js +162 -71
  9. package/Table/index.css +5 -1
  10. package/Table/index.js +410 -90
  11. package/Utils/useBoundedDrag.d.ts +1 -0
  12. package/Utils/useBoundedDrag.js +124 -39
  13. package/lib/cjs/DragDropList/index.d.ts +1 -0
  14. package/lib/cjs/DragDropList/index.js +143 -52
  15. package/lib/cjs/DynamicFields/index.d.ts +1 -0
  16. package/lib/cjs/DynamicFields/index.js +108 -8
  17. package/lib/cjs/EventCalendarTimeline/index.d.ts +1 -0
  18. package/lib/cjs/EventCalendarTimeline/index.js +99 -6
  19. package/lib/cjs/MultipleSelect/index.js +162 -71
  20. package/lib/cjs/Table/index.js +410 -90
  21. package/lib/cjs/Utils/useBoundedDrag.d.ts +1 -0
  22. package/lib/cjs/Utils/useBoundedDrag.js +124 -39
  23. package/lib/css/EventCalendarTimeline/index.css +12 -1
  24. package/lib/css/Table/index.css +5 -1
  25. package/lib/esm/DragDropList/index.tsx +23 -16
  26. package/lib/esm/DynamicFields/index.tsx +107 -8
  27. package/lib/esm/EventCalendarTimeline/index.scss +15 -1
  28. package/lib/esm/EventCalendarTimeline/index.tsx +130 -11
  29. package/lib/esm/ModalDialog/index.tsx +0 -1
  30. package/lib/esm/Table/Table.tsx +9 -7
  31. package/lib/esm/Table/TableRow.tsx +9 -3
  32. package/lib/esm/Table/index.scss +8 -2
  33. package/lib/esm/Table/utils/DragHandleSprite.tsx +6 -2
  34. package/lib/esm/Table/utils/func.ts +12 -1
  35. package/lib/esm/Table/utils/hooks/useTableDraggable.tsx +401 -93
  36. package/lib/esm/Utils/hooks/useBoundedDrag.tsx +142 -39
  37. package/package.json +1 -1
@@ -575,6 +575,13 @@ function initOrderProps(rootElem) {
575
575
  }
576
576
  function initRowColProps(rootElem) {
577
577
  if (rootElem === null) return;
578
+
579
+ // !!! Important, performance optimization for large data renderings
580
+ // With this protection, it is only performed once
581
+ if (typeof rootElem.dataset.rowColPropsInit !== 'undefined') return;
582
+ rootElem.dataset.rowColPropsInit = '1';
583
+
584
+ //
578
585
  var _allRows = allRows(rootElem);
579
586
  var _allHeadRows = allHeadRows(rootElem);
580
587
 
@@ -662,7 +669,10 @@ function cellMark(row, col) {
662
669
  }
663
670
  function removeCellFocusClassName(root) {
664
671
  if (root) {
665
- [].slice.call(root.querySelectorAll('td, th')).forEach(function (el) {
672
+ // !!! Important, performance optimization for large data renderings
673
+ // Only query elements with cell-focus classes
674
+ var focusedCells = root.querySelectorAll('td.cell-focus, th.cell-focus');
675
+ focusedCells.forEach(function (el) {
666
676
  el.classList.remove('cell-focus');
667
677
  });
668
678
  }
@@ -800,6 +810,32 @@ const App = () => {
800
810
 
801
811
  */
802
812
 
813
+ /**
814
+ * Performance Optimizations for Large Data Sets:
815
+ *
816
+ * This hook has been optimized to handle large datasets (1000+ rows) efficiently.
817
+ * Key optimizations include:
818
+ *
819
+ * 1. RequestAnimationFrame for DOM Updates
820
+ * - DOM operations are batched within requestAnimationFrame callbacks
821
+ * - Browser executes updates before next frame render, reducing visual lag
822
+ * - Pending RAF callbacks are cancelled to prevent accumulation
823
+ *
824
+ * 2. Caching Strategy
825
+ * - tbodyRef: Cached to avoid repeated DOM queries
826
+ * - colCount: Cached to eliminate repeated queries in placeholderGenerator
827
+ * - allRowsCache: Cached with time-based invalidation (100ms)
828
+ *
829
+ * 3. Redundant Operation Prevention
830
+ * - Tracks last hovered row order (lastOverOrder)
831
+ * - Skips placeholder operations when hovering over the same row
832
+ * - Reduces unnecessary DOM manipulations during drag
833
+ *
834
+ * 4. Batch DOM Operations
835
+ * - removePlaceholder: Uses cached tbodyRef and batch removal
836
+ * - handleDragEnd: Uses DocumentFragment for batch DOM updates
837
+ * - Map-based lookups instead of repeated querySelector calls
838
+ */
803
839
 
804
840
 
805
841
  function useTableDraggable(_ref, deps) {
@@ -812,43 +848,126 @@ function useTableDraggable(_ref, deps) {
812
848
  _useState2 = _slicedToArray(_useState, 2),
813
849
  sortData = _useState2[0],
814
850
  setSortData = _useState2[1];
851
+ var _useState3 = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(false),
852
+ _useState4 = _slicedToArray(_useState3, 2),
853
+ isDragging = _useState4[0],
854
+ setIsDragging = _useState4[1];
855
+
856
+ // Performance optimization: cache for drag operations
857
+ var dragCacheRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)({
858
+ draggedObj: null,
859
+ overObj: null,
860
+ allRowsCache: null,
861
+ lastUpdateTime: 0,
862
+ tbodyRef: null,
863
+ colCount: 0,
864
+ lastOverOrder: null,
865
+ rafId: null
866
+ });
815
867
 
816
868
  // ================================================================
817
869
  // drag & drop
818
870
  // ================================================================
819
871
  var draggedObj = null;
820
872
  var overObj = null;
873
+
874
+ // Helper function to filter out cloned elements and get only real rows
875
+ var getRealRows = function getRealRows(rows) {
876
+ return rows.filter(function (row) {
877
+ return !row.classList.contains('row-obj-clonelast') && !row.classList.contains('row-obj-lastplaceholder');
878
+ });
879
+ };
821
880
  var placeholderGenerator = function placeholderGenerator(trHeight) {
822
- var tbodyRef = getTbody(spyElement);
823
- if (tbodyRef === null || tbodyRef.querySelector('tr') === null) return;
881
+ // Use cached tbodyRef and colCount for better performance
882
+ var tbodyRef = dragCacheRef.current.tbodyRef;
883
+ if (!tbodyRef) {
884
+ tbodyRef = getTbody(spyElement);
885
+ dragCacheRef.current.tbodyRef = tbodyRef;
886
+ }
887
+ if (tbodyRef === null) return null;
888
+
889
+ // Cache colCount to avoid repeated queries
890
+ var colCount = dragCacheRef.current.colCount;
891
+ if (colCount === 0) {
892
+ var firstRow = tbodyRef.querySelector('tr');
893
+ if (firstRow === null) return null;
894
+ colCount = firstRow.children.length;
895
+ dragCacheRef.current.colCount = colCount;
896
+ }
824
897
 
825
898
  // Insert a row at the "index" of the table
826
899
  var newRow = document.createElement('tr');
827
900
  newRow.className = 'row-placeholder';
828
901
  newRow.dataset.placeholder = 'true';
829
902
  newRow.style.height = trHeight + 'px';
903
+ newRow.style.minHeight = trHeight + 'px'; // Ensure minimum height
830
904
 
831
905
  // Insert a cell in the row at index
832
906
  var newCell = newRow.insertCell(0);
833
- newCell.colSpan = tbodyRef.querySelector('tr').children.length;
907
+ newCell.colSpan = colCount;
908
+ newCell.style.minHeight = trHeight + 'px'; // Ensure cell has minimum height
834
909
 
835
- // Append a text node to the cell
836
- var newText = document.createTextNode(' ');
837
- newCell.appendChild(newText);
910
+ // Use non-breaking space to ensure proper height rendering
911
+ // Multiple spaces or a placeholder element helps maintain consistent height
912
+ newCell.innerHTML = ' '; // Use   instead of regular space for better height consistency
913
+
914
+ return newRow;
915
+ };
916
+ var lastPlaceholderGenerator = function lastPlaceholderGenerator(trHeight) {
917
+ // Use cached tbodyRef and colCount for better performance
918
+ var tbodyRef = dragCacheRef.current.tbodyRef;
919
+ if (!tbodyRef) {
920
+ tbodyRef = getTbody(spyElement);
921
+ dragCacheRef.current.tbodyRef = tbodyRef;
922
+ }
923
+ if (tbodyRef === null) return null;
924
+ var curEl = tbodyRef.querySelector('.row-obj-lastplaceholder');
925
+ if (curEl !== null) return;
926
+
927
+ // Cache colCount to avoid repeated queries
928
+ var colCount = dragCacheRef.current.colCount;
929
+ if (colCount === 0) {
930
+ var firstRow = tbodyRef.querySelector('tr');
931
+ if (firstRow === null) return null;
932
+ colCount = firstRow.children.length;
933
+ dragCacheRef.current.colCount = colCount;
934
+ }
935
+
936
+ // Create a dedicated last placeholder row that is kept in DOM but hidden by default
937
+ var newRow = document.createElement('tr');
938
+ newRow.className = 'row-obj row-obj-lastplaceholder';
939
+ // NOTE: Do NOT set data-placeholder here, otherwise it will be removed by removePlaceholder
940
+ newRow.style.height = trHeight + 'px';
941
+ newRow.style.minHeight = trHeight + 'px';
942
+ newRow.style.display = 'none';
943
+ var newCell = newRow.insertCell(0);
944
+ newCell.colSpan = colCount;
945
+ newCell.style.minHeight = trHeight + 'px';
946
+ newCell.innerHTML = ' ';
947
+
948
+ // Insert after the last real row (excluding cloned rows)
949
+ var rows = getRealRows(allRows(spyElement));
950
+ var lastRealRow = rows.length > 0 ? rows[rows.length - 1] : null;
951
+ if (lastRealRow && lastRealRow.parentNode === tbodyRef) {
952
+ insertAfter(newRow, lastRealRow);
953
+ } else {
954
+ tbodyRef.appendChild(newRow);
955
+ }
838
956
  return newRow;
839
957
  };
958
+
959
+ // An invisible HELPER element used to trigger the touch of the last element
840
960
  var lastRowGenerator = function lastRowGenerator(trHeight) {
841
961
  var tbodyRef = getTbody(spyElement);
842
962
  if (tbodyRef === null || tbodyRef.querySelector('tr') === null) return;
843
- var cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
844
- if (cloneEl !== null) return;
963
+ var curEl = tbodyRef.querySelector('.row-obj-clonelast');
964
+ if (curEl !== null) return;
845
965
 
846
966
  // Insert a row at the "index" of the table
847
967
  var newRow = document.createElement('tr');
848
968
  newRow.className = 'row-obj row-obj-clonelast';
849
969
  newRow.dataset.order = allRows(spyElement).length.toString();
850
970
  newRow.style.height = trHeight + 'px';
851
- newRow.style.display = 'none';
852
971
 
853
972
  // Insert a cell in the row at index
854
973
  var newCell = newRow.insertCell(0);
@@ -857,17 +976,30 @@ function useTableDraggable(_ref, deps) {
857
976
  // Append a text node to the cell
858
977
  var newText = document.createTextNode(' ');
859
978
  newCell.appendChild(newText);
979
+
980
+ //
981
+ lastPlaceholderGenerator(trHeight);
860
982
  return newRow;
861
983
  };
862
984
  var removePlaceholder = function removePlaceholder() {
863
- var tbodyRef = getTbody(spyElement);
985
+ // Use cached tbodyRef
986
+ var tbodyRef = dragCacheRef.current.tbodyRef;
987
+ if (!tbodyRef) {
988
+ tbodyRef = getTbody(spyElement);
989
+ dragCacheRef.current.tbodyRef = tbodyRef;
990
+ }
864
991
  if (tbodyRef === null) return;
865
992
 
866
- // Delete row at the "index" of the table
867
- var placeholder = [].slice.call(tbodyRef.querySelectorAll("[data-placeholder]"));
868
- placeholder.forEach(function (node) {
869
- tbodyRef.removeChild(node);
870
- });
993
+ // Optimize: use querySelectorAll and remove in batch
994
+ var placeholders = tbodyRef.querySelectorAll("[data-placeholder]");
995
+ if (placeholders.length > 0) {
996
+ // Use DocumentFragment for batch removal (though in this case direct removal is fine)
997
+ placeholders.forEach(function (node) {
998
+ if (node.parentNode) {
999
+ node.parentNode.removeChild(node);
1000
+ }
1001
+ });
1002
+ }
871
1003
  };
872
1004
 
873
1005
  // Initialize drag & drop data
@@ -904,114 +1036,289 @@ function useTableDraggable(_ref, deps) {
904
1036
  };
905
1037
 
906
1038
  // events fired on the drop targets
1039
+ // Optimized with requestAnimationFrame, throttling and caching
907
1040
  var handledragOver = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useCallback)(function (e) {
908
- var tbodyRef = getTbody(spyElement);
909
- if (tbodyRef === null) return;
1041
+ // Always prevent default in sync code
910
1042
  e.preventDefault();
911
- if (draggedObj === null) return;
912
- draggedObj.style.display = 'none';
1043
+
1044
+ // Use cached draggedObj and tbodyRef
1045
+ var currentDraggedObj = dragCacheRef.current.draggedObj || draggedObj;
1046
+ if (currentDraggedObj === null) return;
1047
+ var tbodyRef = dragCacheRef.current.tbodyRef;
1048
+ if (!tbodyRef) {
1049
+ tbodyRef = getTbody(spyElement);
1050
+ if (tbodyRef === null) return;
1051
+ dragCacheRef.current.tbodyRef = tbodyRef;
1052
+ }
1053
+
1054
+ // Early return for placeholder targets
913
1055
  if (e.target.classList.contains('row-placeholder')) return;
914
1056
  var itemsWrapper = e.target.parentNode;
915
- if (itemsWrapper.classList.contains('row-obj')) {
916
- overObj = itemsWrapper;
1057
+ if (!itemsWrapper || !itemsWrapper.classList || !itemsWrapper.classList.contains('row-obj')) {
1058
+ return;
1059
+ }
1060
+
1061
+ // Skip cloned elements - they should not be valid drop targets
1062
+ if (itemsWrapper.classList.contains('row-obj-lastplaceholder')) {
1063
+ return;
1064
+ }
1065
+
1066
+ // Check if we're still over the same row (avoid unnecessary operations)
1067
+ var currentOrder = Number(itemsWrapper.dataset.order);
1068
+ if (dragCacheRef.current.lastOverOrder === currentOrder) {
1069
+ return; // Same target, skip
1070
+ }
1071
+
1072
+ // console.log(' --> overObj: ', itemsWrapper);
1073
+
1074
+ // Use requestAnimationFrame for smoother DOM updates
1075
+ // Cancel previous frame if pending
1076
+ if (dragCacheRef.current.rafId !== null) {
1077
+ cancelAnimationFrame(dragCacheRef.current.rafId);
1078
+ }
1079
+
1080
+ // Store references for use in RAF callback
1081
+ var targetWrapper = itemsWrapper;
1082
+ var targetOrder = currentOrder;
1083
+ dragCacheRef.current.rafId = requestAnimationFrame(function () {
1084
+ overObj = targetWrapper;
1085
+ dragCacheRef.current.overObj = targetWrapper;
1086
+ dragCacheRef.current.lastOverOrder = targetOrder;
1087
+ currentDraggedObj.style.display = 'none';
917
1088
  removePlaceholder();
918
- if (Number(overObj.dataset.order) === allRows(spyElement).length - 1) {
919
- tbodyRef.insertBefore(placeholderGenerator(allRows(spyElement).at(-2).clientHeight), overObj);
920
- } else {
921
- tbodyRef.insertBefore(placeholderGenerator(overObj.clientHeight), overObj);
1089
+
1090
+ // Cache allRows result to avoid multiple queries
1091
+ var cachedRows = dragCacheRef.current.allRowsCache;
1092
+ var now = Date.now();
1093
+ if (!cachedRows || now - dragCacheRef.current.lastUpdateTime > 100) {
1094
+ cachedRows = allRows(spyElement);
1095
+ dragCacheRef.current.allRowsCache = cachedRows;
1096
+ dragCacheRef.current.lastUpdateTime = now;
922
1097
  }
923
- }
924
- }, [sortData]);
1098
+
1099
+ // Filter out cloned elements to get real rows count
1100
+ var realRows = getRealRows(cachedRows);
1101
+ var totalRows = realRows.length;
1102
+ var overOrder = Number(overObj.dataset.order);
1103
+
1104
+ // When hovering over the last real row, use its height for placeholder
1105
+ // Otherwise use the overObj's height
1106
+ var isOverLastRow = overOrder === totalRows - 1 && realRows.length > 0 && realRows[totalRows - 1];
1107
+ var placeholderHeight = isOverLastRow ? realRows[totalRows - 1].clientHeight : overObj.clientHeight;
1108
+ var placeholder = placeholderGenerator(placeholderHeight);
1109
+ if (placeholder) {
1110
+ var draggedOrder = Number(currentDraggedObj.dataset.order);
1111
+ //console.log(' --> drag index list: ', draggedOrder, overOrder, totalRows - 1);
1112
+ tbodyRef.insertBefore(placeholder, overObj);
1113
+ }
1114
+ dragCacheRef.current.rafId = null;
1115
+ });
1116
+ }, [sortData, spyElement]);
925
1117
  var handleDragStart = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useCallback)(function (e) {
926
1118
  var tbodyRef = getTbody(spyElement);
927
1119
  if (tbodyRef === null) return;
1120
+ setIsDragging(true);
928
1121
  draggedObj = e.currentTarget;
1122
+ // Cache draggedObj and tbodyRef for performance
1123
+ dragCacheRef.current.draggedObj = draggedObj;
1124
+ dragCacheRef.current.tbodyRef = tbodyRef;
1125
+ dragCacheRef.current.lastOverOrder = null; // Reset
1126
+
929
1127
  e.dataTransfer.effectAllowed = 'move';
930
1128
  e.dataTransfer.setData('text/html', draggedObj);
931
1129
  draggedObj.classList.add('dragging');
932
- allRows(spyElement).at(-1).style.setProperty('display', 'table-row', "important");
1130
+
1131
+ // Cache allRows and use cached result
1132
+ var cachedRows = allRows(spyElement);
1133
+ dragCacheRef.current.allRowsCache = cachedRows;
1134
+ dragCacheRef.current.lastUpdateTime = Date.now();
1135
+
1136
+ // Cache colCount if not already cached
1137
+ if (dragCacheRef.current.colCount === 0) {
1138
+ var firstRow = tbodyRef.querySelector('tr');
1139
+ if (firstRow) {
1140
+ dragCacheRef.current.colCount = firstRow.children.length;
1141
+ }
1142
+ }
1143
+ var lastRow = cachedRows[cachedRows.length - 1];
1144
+ if (lastRow && !lastRow.classList.contains('row-obj-lastplaceholder')) {
1145
+ lastRow.style.setProperty('display', 'table-row', "important");
1146
+ }
933
1147
 
934
1148
  // callback
935
1149
  var dragStart = function dragStart(callback) {
936
1150
  callback.call(null, draggedObj, sortData, sortDataByIndex(sortData, data));
937
1151
  };
938
1152
  onRowDrag === null || onRowDrag === void 0 ? void 0 : onRowDrag(dragStart, null);
939
-
940
- // init clone <tr>
941
- // !!! It needs to be put at the end of the code to fix the location of the clone element
942
- var cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
943
- if (cloneEl !== null) {
944
- cloneEl.style.display = 'none';
945
- }
946
- }, [handledragOver]);
1153
+ }, [handledragOver, sortData, data, spyElement, onRowDrag]);
947
1154
  var handleDragEnd = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useCallback)(function (e) {
948
1155
  var tbodyRef = getTbody(spyElement);
949
1156
  if (tbodyRef === null) return;
950
- draggedObj.style.display = 'table-row';
1157
+ setIsDragging(false);
1158
+
1159
+ // Use cached draggedObj if available
1160
+ var currentDraggedObj = dragCacheRef.current.draggedObj || draggedObj;
1161
+ var currentOverObj = dragCacheRef.current.overObj || overObj;
1162
+ if (currentDraggedObj) {
1163
+ currentDraggedObj.style.display = 'table-row';
1164
+ currentDraggedObj.classList.remove('dragging');
1165
+ }
951
1166
  removePlaceholder();
952
- draggedObj.classList.remove('dragging');
953
1167
  tbodyRef === null || tbodyRef === void 0 ? void 0 : tbodyRef.classList.remove('drag-trigger-mousedown');
954
- if (overObj === null) return;
1168
+
1169
+ // Cancel any pending animation frame
1170
+ if (dragCacheRef.current.rafId !== null) {
1171
+ cancelAnimationFrame(dragCacheRef.current.rafId);
1172
+ dragCacheRef.current.rafId = null;
1173
+ }
1174
+ if (currentOverObj === null) {
1175
+ // Reset cache
1176
+ dragCacheRef.current.draggedObj = null;
1177
+ dragCacheRef.current.overObj = null;
1178
+ dragCacheRef.current.allRowsCache = null;
1179
+ dragCacheRef.current.lastOverOrder = null;
1180
+ return;
1181
+ }
955
1182
 
956
1183
  // update state
957
1184
  var curData = [];
958
1185
  curData = JSON.parse(JSON.stringify(sortData));
959
- var from = Number(draggedObj.dataset.order);
960
- var to = Number(overObj.dataset.order);
961
- if (from < to) to--;
1186
+ var from = Number(currentDraggedObj.dataset.order);
1187
+ var to = Number(currentOverObj.dataset.order);
1188
+
1189
+ // Get real rows to determine the actual last row index
1190
+ var allRowsForLastIndex = allRows(spyElement);
1191
+ var realRows = getRealRows(allRowsForLastIndex);
1192
+ var actualLastRowIndex = realRows.length - 1;
1193
+
1194
+ // Standard drag-and-drop logic:
1195
+ // When dragging from a lower index to a higher index, we need to decrement 'to'
1196
+ // because removing the element at 'from' causes all subsequent elements to shift left by 1
1197
+ // However, when dragging to the last position, we want to swap with the last element
1198
+ // After removing 'from', if we want to swap with the last element, we should insert
1199
+ // at the position that will result in the dragged element being at the last position
1200
+ if (from < to) {
1201
+ // Special case: dragging to the last position
1202
+ // We want to swap with the last element, so after removing 'from',
1203
+ // we should insert at the new last position (which is curData.length - 1)
1204
+ // Since 'to' is the original last index, and we're removing 'from' (which is < 'to'),
1205
+ // the new last position after removal is still 'to' (no shift because 'from' is before 'to')
1206
+ // Wait, that's not right. If we remove 'from', elements from 'from+1' to 'to' shift left by 1
1207
+ // So 'to' becomes 'to-1'. But we want to insert at the last position, which is 'to-1'
1208
+ // So we should decrement 'to' as normal. But then the element will be at 'to-1', not 'to'
1209
+ //
1210
+ // Actually, the issue is: when dragging to the last element, we want to SWAP with it
1211
+ // So the dragged element should end up at the last position, and the last element should
1212
+ // end up at the dragged element's original position
1213
+ //
1214
+ // Let's think step by step with an example: [A, B, C, D, E], from=1 (B), to=4 (E)
1215
+ // We want result: [A, C, D, E, B] (B and E swapped)
1216
+ // Step 1: Remove B -> [A, C, D, E] (indices 0-3)
1217
+ // Step 2: Insert B at position 4 -> [A, C, D, E, B] ✓
1218
+ // So 'to' should be 4 (not decremented) to get the correct result
1219
+ if (to === actualLastRowIndex) {
1220
+ // Don't decrement 'to' when dragging to the last position
1221
+ // This ensures the element is inserted at the last position after removal
1222
+ } else {
1223
+ // Normal case: dragging forward but not to the last position
1224
+ to--;
1225
+ }
1226
+ }
1227
+ // If from >= to, no adjustment needed (dragging backward)
962
1228
 
963
- //sort data
964
- var newData = [];
1229
+ // Optimize: simplify the sorting logic (the nested loop was inefficient)
1230
+ curData.splice(to, 0, curData.splice(from, 1)[0]);
1231
+ var newData = useTableDraggable_toConsumableArray(curData); // Direct copy instead of nested loop
965
1232
 
966
- // console.log('--> data1:', curData);
1233
+ setSortData(newData);
967
1234
 
968
- curData.splice(to, 0, curData.splice(from, 1)[0]);
969
- for (var i = 0; i < curData.length; i++) {
970
- for (var j = 0; j < curData.length; j++) {
971
- if (curData[i] === curData[j]) {
972
- newData.push(curData[j]);
1235
+ // Performance optimization: batch DOM updates using a map
1236
+ var table = spyElement.querySelector('table');
1237
+ if (!table) return;
1238
+ var tbody = table.querySelector('tbody');
1239
+ if (!tbody) return;
1240
+
1241
+ // Get all rows once and create a map for faster lookups
1242
+ // Support both data-key attribute (user-provided) and data-order fallback
1243
+ var allRowsElements = Array.from(allRows(spyElement));
1244
+
1245
+ // Create a map: original index (from sortData) -> row element
1246
+ var rowMap = new Map();
1247
+ allRowsElements.forEach(function (row) {
1248
+ // First try to use data-key attribute (if user provided it)
1249
+ var dataKey = row.getAttribute('data-key');
1250
+ if (dataKey) {
1251
+ var match = dataKey.match(/row-(\d+)/);
1252
+ if (match) {
1253
+ var index = Number(match[1]);
1254
+ rowMap.set(index, row);
1255
+ return;
973
1256
  }
974
1257
  }
975
- }
976
1258
 
977
- // console.log("--> data2: ", newData);
978
- setSortData(newData);
1259
+ // Fallback: use data-order to match with sortData indices
1260
+ var currentOrder = Number(row.dataset.order);
1261
+ if (sortData && !isNaN(currentOrder) && currentOrder >= 0 && currentOrder < sortData.length) {
1262
+ var originalIndex = sortData[currentOrder];
1263
+ if (originalIndex !== undefined) {
1264
+ rowMap.set(originalIndex, row);
1265
+ }
1266
+ }
1267
+ });
979
1268
 
980
- // reset data-id in order to sort data
1269
+ // Update order attributes using the map (batch operation)
981
1270
  newData.forEach(function (curId, order) {
982
- var _el = spyElement.querySelector('table').querySelector("tbody [data-key=\"row-".concat(curId, "\"]"));
983
- if (_el !== null) _el.dataset.order = order;
1271
+ var _el = rowMap.get(curId);
1272
+ if (_el !== null && _el !== undefined) {
1273
+ _el.dataset.order = order.toString();
1274
+ }
984
1275
  });
985
1276
 
986
- // sort elements
987
- var categoryItemsArray = allRows(spyElement);
1277
+ // Performance optimization: Use DocumentFragment to batch DOM updates
1278
+ // NOTE: Keep the special last placeholder row (`row-obj-lastplaceholder`)
1279
+ // out of the main sort, otherwise it may jump to the top after each drag.
1280
+ var lastPlaceholderRow = allRowsElements.find(function (row) {
1281
+ return row.classList && row.classList.contains('row-obj-lastplaceholder');
1282
+ });
1283
+ var rowsToSort = lastPlaceholderRow ? allRowsElements.filter(function (row) {
1284
+ return row !== lastPlaceholderRow;
1285
+ }) : allRowsElements;
988
1286
  var sorter = function sorter(a, b) {
989
- var txt1 = Number(a.dataset.order),
990
- txt2 = Number(b.dataset.order);
1287
+ var txt1 = Number(a.dataset.order);
1288
+ var txt2 = Number(b.dataset.order);
991
1289
  return txt2 < txt1 ? -1 : txt2 > txt1 ? 1 : 0;
992
1290
  };
993
- var sorted = categoryItemsArray.sort(sorter).reverse();
1291
+ var sorted = useTableDraggable_toConsumableArray(rowsToSort).sort(sorter).reverse();
1292
+
1293
+ // Ensure the last placeholder row always stays at the bottom
1294
+ if (lastPlaceholderRow) {
1295
+ sorted.push(lastPlaceholderRow);
1296
+ }
1297
+
1298
+ // Use DocumentFragment to minimize reflows
1299
+ var fragment = document.createDocumentFragment();
994
1300
  sorted.forEach(function (e) {
995
- return spyElement.querySelector('table').querySelector('tbody').appendChild(e);
1301
+ return fragment.appendChild(e);
996
1302
  });
1303
+ tbody.appendChild(fragment);
997
1304
 
998
1305
  // callback
999
1306
  var dragEnd = function dragEnd(callback) {
1000
- callback.call(null, draggedObj, newData, sortDataByIndex(newData, data));
1307
+ callback.call(null, currentDraggedObj, newData, sortDataByIndex(newData, data));
1001
1308
  };
1002
1309
  onRowDrag === null || onRowDrag === void 0 ? void 0 : onRowDrag(null, dragEnd);
1003
1310
 
1004
1311
  // init clone <tr>
1005
1312
  // !!! It needs to be put at the end of the code to fix the location of the clone element
1006
1313
  var _allRows = allRows(spyElement);
1007
- var cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
1008
- if (cloneEl !== null) {
1009
- if (typeof _allRows.at(-1) !== 'undefined') {
1010
- insertAfter(cloneEl, _allRows.at(-1));
1011
- cloneEl.style.display = 'none';
1012
- }
1013
- }
1014
- }, [sortData]);
1314
+ dragCacheRef.current.allRowsCache = _allRows;
1315
+ dragCacheRef.current.lastUpdateTime = Date.now();
1316
+
1317
+ // Reset cache
1318
+ dragCacheRef.current.draggedObj = null;
1319
+ dragCacheRef.current.overObj = null;
1320
+ dragCacheRef.current.lastOverOrder = null;
1321
+ }, [sortData, spyElement, data, onRowDrag]);
1015
1322
  (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function () {
1016
1323
  if (enabled) {
1017
1324
  if (Array.isArray(data) && data.length > 0) {
@@ -1030,9 +1337,12 @@ function useTableDraggable(_ref, deps) {
1030
1337
  }
1031
1338
  }, [data, enabled, spyElement].concat(useTableDraggable_toConsumableArray(deps)));
1032
1339
  return {
1033
- handleDragStart: handleDragStart,
1034
- handleDragEnd: handleDragEnd,
1035
- handledragOver: handledragOver,
1340
+ isDragging: isDragging,
1341
+ dragHandlers: {
1342
+ handleDragStart: handleDragStart,
1343
+ handleDragOver: handledragOver,
1344
+ handleDragEnd: handleDragEnd
1345
+ },
1036
1346
  handleTbodyEnter: handleTbodyEnter,
1037
1347
  handleTbodyLeave: handleTbodyLeave
1038
1348
  };
@@ -1361,10 +1671,10 @@ var Table = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_react_a
1361
1671
  spyElement: rootRef.current,
1362
1672
  onRowDrag: onRowDrag
1363
1673
  }, [data, rootRef]),
1364
- handleDragStart = _useTableDraggable.handleDragStart,
1365
- handleDragEnd = _useTableDraggable.handleDragEnd,
1366
- handledragOver = _useTableDraggable.handledragOver,
1367
- handleTbodyEnter = _useTableDraggable.handleTbodyEnter;
1674
+ isDragging = _useTableDraggable.isDragging,
1675
+ dragHandlers = _useTableDraggable.dragHandlers,
1676
+ handleTbodyEnter = _useTableDraggable.handleTbodyEnter,
1677
+ handleTbodyLeave = _useTableDraggable.handleTbodyLeave;
1368
1678
  var tableKeyPress = hooks_useTableKeyPress({
1369
1679
  enabled: keyboardFocusable,
1370
1680
  data: data,
@@ -1462,9 +1772,10 @@ var Table = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_react_a
1462
1772
  onColSort: onColSort,
1463
1773
  // drag & drop
1464
1774
  rowDraggable: rowDraggable,
1465
- handleDragStart: handleDragStart,
1466
- handleDragEnd: handleDragEnd,
1467
- handledragOver: handledragOver,
1775
+ isRowDragging: isDragging,
1776
+ handleDragStart: dragHandlers.handleDragStart,
1777
+ handleDragEnd: dragHandlers.handleDragEnd,
1778
+ handledragOver: dragHandlers.handleDragOver,
1468
1779
  handleTbodyEnter: handleTbodyEnter,
1469
1780
  // filter
1470
1781
  filterFields: filterFields,
@@ -1695,11 +2006,17 @@ var TableRow = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_reac
1695
2006
  // selection
1696
2007
  // ================================================================
1697
2008
  var _res = convertMapToArr(selectedItems);
2009
+ // Performance optimization: stringify itemData only once instead of N times
2010
+ var itemDataStr = itemData ? JSON.stringify(itemData) : '';
1698
2011
  var filteredSelectedItems = _res.map(function (v) {
1699
2012
  return Number(v);
1700
2013
  }).map(function (rowNum) {
1701
- if (JSON.stringify(itemData) === JSON.stringify(originData[rowNum])) {
1702
- return originData[rowNum];
2014
+ var originItem = originData === null || originData === void 0 ? void 0 : originData[rowNum];
2015
+ // Fast path: reference equality
2016
+ if (itemData === originItem) return originItem;
2017
+ // Fallback: JSON comparison (itemDataStr is cached)
2018
+ if (itemDataStr && itemDataStr === JSON.stringify(originItem)) {
2019
+ return originItem;
1703
2020
  }
1704
2021
  }).filter(Boolean);
1705
2022
  var selectedClassName = activeClassName || 'active';
@@ -1718,9 +2035,7 @@ var TableRow = /*#__PURE__*/(0,external_root_React_commonjs2_react_commonjs_reac
1718
2035
  var _item$s, _itemData$s;
1719
2036
  return !((_item$s = item[s]) !== null && _item$s !== void 0 && _item$s.toLowerCase().includes((_itemData$s = itemData[s]) === null || _itemData$s === void 0 ? void 0 : _itemData$s.toLowerCase()));
1720
2037
  });
1721
- }) ? 'd-none' : '' : '', " ").concat(itemData && originData ? filteredSelectedItems.some(function (item) {
1722
- return JSON.stringify(itemData) === JSON.stringify(item);
1723
- }) ? selectedClassName : '' : '')
2038
+ }) ? 'd-none' : '' : '', " ").concat(itemData && originData && filteredSelectedItems.length > 0 ? selectedClassName : '')
1724
2039
  }), children));
1725
2040
  });
1726
2041
  /* harmony default export */ const src_TableRow = (TableRow);
@@ -2027,11 +2342,16 @@ var DragHandleSprite = /*#__PURE__*/(0,external_root_React_commonjs2_react_commo
2027
2342
  icon = props.icon;
2028
2343
  var _useContext = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useContext)(TableContext),
2029
2344
  rowDraggable = _useContext.rowDraggable,
2030
- handleTbodyEnter = _useContext.handleTbodyEnter;
2345
+ handleTbodyEnter = _useContext.handleTbodyEnter,
2346
+ handleTbodyLeave = _useContext.handleTbodyLeave;
2031
2347
  return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement((external_root_React_commonjs2_react_commonjs_react_amd_react_default()).Fragment, null, rowDraggable ? /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("span", {
2032
2348
  ref: externalRef,
2033
- className: className || 'drag-trigger',
2034
- onMouseEnter: handleTbodyEnter
2349
+ className: className || 'drag-trigger'
2350
+ // Only when mousedown happens on this handle will we allow row dragging.
2351
+ ,
2352
+ onMouseDown: handleTbodyEnter,
2353
+ onMouseUp: handleTbodyLeave,
2354
+ onMouseLeave: handleTbodyLeave
2035
2355
  }, icon || /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("svg", {
2036
2356
  width: "1em",
2037
2357
  height: "1em",