funda-ui 4.7.723 → 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.
@@ -112,6 +112,7 @@ export interface BoundedDragOptions {
112
112
  dragHandleSelector?: string;
113
113
  onDragStart?: (index: number) => void;
114
114
  onDragOver?: (dragIndex: number | null, dropIndex: number | null) => void;
115
+ onDragUpdate?: (dragIndex: number | null, dropIndex: number | null) => void;
115
116
  onDragEnd?: (dragIndex: number | null, dropIndex: number | null) => void;
116
117
  }
117
118
  export declare const useBoundedDrag: (options?: BoundedDragOptions) => {
@@ -220,6 +220,7 @@ var useBoundedDrag = function useBoundedDrag() {
220
220
  dragHandleSelector = _options$dragHandleSe === void 0 ? '.custom-draggable-list__handle' : _options$dragHandleSe,
221
221
  onDragStart = options.onDragStart,
222
222
  onDragOver = options.onDragOver,
223
+ onDragUpdate = options.onDragUpdate,
223
224
  onDragEnd = options.onDragEnd;
224
225
  var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
225
226
  _useState2 = _slicedToArray(_useState, 2),
@@ -228,11 +229,35 @@ var useBoundedDrag = function useBoundedDrag() {
228
229
  var dragItem = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
229
230
  var dragOverItem = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
230
231
  var dragNode = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
232
+ var draggedElement = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
233
+ var boundaryElement = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
231
234
  var touchOffset = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)({
232
235
  x: 0,
233
236
  y: 0
234
237
  });
235
238
  var currentHoverItem = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
239
+ var rafId = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
240
+ var lastUpdateDragIndex = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
241
+ var lastUpdateDropIndex = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
242
+
243
+ /**
244
+ * Performance Note:
245
+ *
246
+ * Drag-over events can fire at a very high frequency, especially on touch devices
247
+ * or when dragging quickly. Directly performing DOM read/write operations in the
248
+ * event handler (e.g. `getBoundingClientRect`, `classList` changes, style updates)
249
+ * can easily cause layout thrashing and frame drops when there are many items.
250
+ *
251
+ * To mitigate this, we:
252
+ * - Collect the pointer coordinates synchronously in the event handler.
253
+ * - Schedule all DOM-intensive work inside `requestAnimationFrame`, so the browser
254
+ * batches these operations before the next paint.
255
+ * - Cancel any pending frame (`cancelAnimationFrame`) before scheduling a new one,
256
+ * ensuring there is at most one pending DOM update per frame.
257
+ *
258
+ * This keeps drag interactions smooth even with large lists.
259
+ */
260
+
236
261
  var handleDragStart = function handleDragStart(e, position) {
237
262
  var isTouch = ('touches' in e);
238
263
  var target = e.target;
@@ -285,69 +310,127 @@ var useBoundedDrag = function useBoundedDrag() {
285
310
  opacity: '0.9'
286
311
  });
287
312
  document.body.appendChild(dragNode.current);
313
+
314
+ // Keep track of the original element (acts as a placeholder inside the list)
315
+ draggedElement.current = listItem;
316
+ boundaryElement.current = boundary;
288
317
  setIsDragging(true);
289
318
  listItem.classList.add('dragging-placeholder');
290
319
  } else {
291
- // ... desktop drag logic remains the same ...
320
+ // Desktop: use native drag image, but still record dragged element / boundary
321
+ draggedElement.current = listItem;
322
+ boundaryElement.current = boundary;
323
+ setIsDragging(true);
324
+ var dragEvent = e;
325
+ if (dragEvent.dataTransfer) {
326
+ dragEvent.dataTransfer.effectAllowed = 'move';
327
+ // Optional: customize drag preview if needed
328
+ dragEvent.dataTransfer.setData('text/plain', '');
329
+ }
330
+ listItem.classList.add('dragging-placeholder');
292
331
  }
293
332
  };
294
333
  var handleDragOver = function handleDragOver(e) {
334
+ // Always prevent default synchronously
295
335
  e.preventDefault();
296
336
  var isTouch = ('touches' in e);
297
337
  if (!isTouch) {
298
338
  e.dataTransfer.dropEffect = 'move';
299
339
  }
300
340
 
301
- // Get the current pointer/touch position
302
- var point = isTouch ? e.touches[0] : {
303
- clientX: e.clientX,
304
- clientY: e.clientY
305
- };
341
+ // Extract primitive coordinates synchronously to avoid using pooled events in async callbacks
342
+ var clientX;
343
+ var clientY;
344
+ if (isTouch) {
345
+ var touch = e.touches[0];
346
+ clientX = touch.clientX;
347
+ clientY = touch.clientY;
348
+ } else {
349
+ clientX = e.clientX;
350
+ clientY = e.clientY;
351
+ }
306
352
 
307
- // Update dragged element position for touch events
308
- if (isTouch && isDragging && dragNode.current) {
309
- dragNode.current.style.left = "".concat(point.clientX - touchOffset.current.x, "px");
310
- dragNode.current.style.top = "".concat(point.clientY - touchOffset.current.y, "px");
353
+ // Cancel any pending frame to avoid stacking DOM operations
354
+ if (rafId.current !== null) {
355
+ cancelAnimationFrame(rafId.current);
311
356
  }
357
+ rafId.current = requestAnimationFrame(function () {
358
+ // Update dragged element position for touch events
359
+ if (isTouch && isDragging && dragNode.current) {
360
+ dragNode.current.style.left = "".concat(clientX - touchOffset.current.x, "px");
361
+ dragNode.current.style.top = "".concat(clientY - touchOffset.current.y, "px");
362
+ }
312
363
 
313
- // Find the element below the pointer/touch
314
- var elemBelow = document.elementFromPoint(point.clientX, point.clientY);
315
- if (!elemBelow) return;
364
+ // Find the element below the pointer/touch
365
+ var elemBelow = document.elementFromPoint(clientX, clientY);
366
+ if (!elemBelow) return;
316
367
 
317
- // Find the closest list item
318
- var listItem = elemBelow.closest(itemSelector);
319
- if (!listItem || listItem === currentHoverItem.current) return;
368
+ // Find the closest list item
369
+ var listItem = elemBelow.closest(itemSelector);
370
+ if (!listItem) return;
320
371
 
321
- // Check boundary
322
- var boundary = listItem.closest(boundarySelector);
323
- if (!boundary) return;
372
+ // Check boundary
373
+ var boundary = boundaryElement.current || listItem.closest(boundarySelector);
374
+ if (!boundary) return;
324
375
 
325
- // Update hover states
326
- if (currentHoverItem.current) {
327
- currentHoverItem.current.classList.remove('drag-over', 'drag-over-top', 'drag-over-bottom');
328
- }
329
- currentHoverItem.current = listItem;
330
- listItem.classList.add('drag-over');
331
-
332
- // Calculate position in list
333
- var position = Array.from(listItem.parentNode.children).indexOf(listItem);
334
- dragOverItem.current = position;
335
-
336
- // Determine drop position (top/bottom)
337
- var rect = listItem.getBoundingClientRect();
338
- var middleY = rect.top + rect.height / 2;
339
- if (point.clientY < middleY) {
340
- listItem.classList.add('drag-over-top');
341
- } else {
342
- listItem.classList.add('drag-over-bottom');
343
- }
344
- onDragOver === null || onDragOver === void 0 ? void 0 : onDragOver(dragItem.current, dragOverItem.current);
376
+ // Update hover states
377
+ if (currentHoverItem.current && currentHoverItem.current !== listItem) {
378
+ currentHoverItem.current.classList.remove('drag-over', 'drag-over-top', 'drag-over-bottom');
379
+ }
380
+ currentHoverItem.current = listItem;
381
+ listItem.classList.add('drag-over');
382
+ var dragEl = draggedElement.current;
383
+ if (!dragEl || !dragEl.parentNode) return;
384
+ var container = boundary;
385
+
386
+ // Collect current ordered items in the container
387
+ var children = Array.from(container.querySelectorAll(itemSelector));
388
+ var currentIndex = children.indexOf(dragEl);
389
+ var targetIndex = children.indexOf(listItem);
390
+ if (currentIndex === -1 || targetIndex === -1) return;
391
+
392
+ // Determine drop position (top/bottom)
393
+ var rect = listItem.getBoundingClientRect();
394
+ var middleY = rect.top + rect.height / 2;
395
+ listItem.classList.remove('drag-over-top', 'drag-over-bottom');
396
+ var insertBefore = clientY < middleY ? listItem : listItem.nextElementSibling;
397
+ if (clientY < middleY) {
398
+ listItem.classList.add('drag-over-top');
399
+ } else {
400
+ listItem.classList.add('drag-over-bottom');
401
+ }
402
+
403
+ // Only move in DOM when the effective position changes
404
+ if (insertBefore !== dragEl && container.contains(dragEl)) {
405
+ container.insertBefore(dragEl, insertBefore);
406
+ }
407
+
408
+ // Recompute index after DOM move
409
+ var reorderedChildren = Array.from(container.querySelectorAll(itemSelector));
410
+ var newIndex = reorderedChildren.indexOf(dragEl);
411
+ dragOverItem.current = newIndex;
412
+ onDragOver === null || onDragOver === void 0 ? void 0 : onDragOver(dragItem.current, dragOverItem.current);
413
+
414
+ // Only fire onDragUpdate when the (dragIndex, dropIndex) pair actually changes.
415
+ if (onDragUpdate && (dragItem.current !== lastUpdateDragIndex.current || dragOverItem.current !== lastUpdateDropIndex.current)) {
416
+ lastUpdateDragIndex.current = dragItem.current;
417
+ lastUpdateDropIndex.current = dragOverItem.current;
418
+ onDragUpdate(dragItem.current, dragOverItem.current);
419
+ }
420
+ rafId.current = null;
421
+ });
345
422
  };
346
423
  var handleDragEnd = function handleDragEnd(e) {
347
424
  var isTouch = ('touches' in e);
348
425
  if (isTouch && !isDragging) return;
349
426
  onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(dragItem.current, dragOverItem.current);
350
427
 
428
+ // Cancel any pending animation frame
429
+ if (rafId.current !== null) {
430
+ cancelAnimationFrame(rafId.current);
431
+ rafId.current = null;
432
+ }
433
+
351
434
  // Cleanup
352
435
  if (dragNode.current) {
353
436
  dragNode.current.remove();
@@ -361,6 +444,8 @@ var useBoundedDrag = function useBoundedDrag() {
361
444
  currentHoverItem.current = null;
362
445
  dragItem.current = null;
363
446
  dragOverItem.current = null;
447
+ draggedElement.current = null;
448
+ boundaryElement.current = null;
364
449
  };
365
450
  return {
366
451
  isDragging: isDragging,
@@ -7,7 +7,7 @@
7
7
  --syntable-row-active-bg: #f0f8ff;
8
8
  --syntable-scrollbar-color: rgba(0, 0, 0, 0.2);
9
9
  --syntable-scrollbar-track: rgba(0, 0, 0, 0);
10
- --syntable-scrollbar-h: 3px;
10
+ --syntable-scrollbar-h: 10px;
11
11
  --syntable-scrollbar-w: 10px;
12
12
  --syntable-padding-x: 1rem;
13
13
  --syntable-padding-y: 0.5rem;
@@ -414,10 +414,13 @@
414
414
  }
415
415
  .syntable__wrapper.allow-dragdrop tbody .row-obj-clonelast {
416
416
  height: 0 !important;
417
+ border-color: transparent !important;
418
+ padding: 0 !important;
417
419
  }
418
420
  .syntable__wrapper.allow-dragdrop tbody .row-obj-clonelast td {
419
421
  border: none;
420
422
  box-shadow: none;
423
+ padding: 0 !important;
421
424
  }
422
425
  .syntable__wrapper.allow-dragdrop tbody td,
423
426
  .syntable__wrapper.allow-dragdrop tbody th {
@@ -428,6 +431,7 @@
428
431
  .syntable__wrapper.allow-dragdrop tbody.drag-trigger-mousedown th {
429
432
  pointer-events: auto;
430
433
  }
434
+ .syntable__wrapper.allow-dragdrop .row-obj-lastplaceholder,
431
435
  .syntable__wrapper.allow-dragdrop .row-placeholder {
432
436
  border: 2px dotted #b5ba91;
433
437
  background-color: #e4e9c3;
@@ -8,7 +8,6 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
8
8
  import useBoundedDrag from 'funda-utils/dist/cjs/useBoundedDrag';
9
9
 
10
10
 
11
-
12
11
  export interface ListItem {
13
12
  id: number;
14
13
  parentId?: number;
@@ -19,6 +18,7 @@ export interface ListItem {
19
18
  depth?: number;
20
19
  children?: ListItem[];
21
20
  disabled?: boolean;
21
+ itemDraggable?: boolean;
22
22
  appendControl?: React.ReactNode;
23
23
  [key: string]: any;
24
24
  }
@@ -222,6 +222,10 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
222
222
  onDragOver: (dragIndex: number | null, dropIndex: number | null) => {
223
223
  // Additional drag over logic if needed
224
224
  },
225
+ onDragUpdate: (dragIndex: number | null, dropIndex: number | null) => {
226
+ // console.log(dragIndex, dropIndex);
227
+
228
+ },
225
229
  onDragEnd: (dragIndex: number | null, dropIndex: number | null) => {
226
230
  if (dragIndex !== null && dropIndex !== null && dragIndex !== dropIndex) {
227
231
  // Handle item movement
@@ -251,12 +255,12 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
251
255
  });
252
256
 
253
257
  // Calculate new insert position
254
- let insertIndex = dropIndex;
255
- if (dropIndex > dragIndex) {
256
- insertIndex -= itemsToMove.length;
257
- }
258
+ // Directly use dropIndex as the insertion position to avoid items snapping back
259
+ // when dragging an item from above to directly below its neighbor.
260
+ const insertIndex = dropIndex;
258
261
 
259
- // Insert all items
262
+ // Insert all items (remove first, then insert at the target index;
263
+ // JavaScript's splice will handle index shifting automatically).
260
264
  newItems.splice(insertIndex, 0, ...itemsBeingMoved);
261
265
 
262
266
  // Rebuild tree structure
@@ -397,6 +401,9 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
397
401
  // If the item should be hidden, the rendering is skipped
398
402
  if (!shouldShowItem(item)) return null;
399
403
 
404
+ // Item level draggable control, default true when not specified
405
+ const isItemDraggable = draggable && item.itemDraggable !== false;
406
+
400
407
  // collapse
401
408
  const hasChildItems = hasChildren(item.id);
402
409
  const isCollapsed = collapsedItems.has(item.id);
@@ -415,7 +422,7 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
415
422
  clsWrite(dragMode, 'handle'),
416
423
  {
417
424
  'disabled': item.disabled,
418
- 'draggable': draggable,
425
+ 'draggable': isItemDraggable,
419
426
  'editing': editingItem === item.id,
420
427
 
421
428
  // collapse
@@ -423,13 +430,13 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
423
430
  'collapsed': isCollapsed
424
431
  }
425
432
  )}
426
- draggable={!draggable ? undefined : editingItem !== item.id && "true"}
427
- onDragStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
428
- onDragOver={!draggable ? undefined : dragHandlers.handleDragOver}
429
- onDragEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
430
- onTouchStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
431
- onTouchMove={!draggable ? undefined : dragHandlers.handleDragOver}
432
- onTouchEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
433
+ draggable={!isItemDraggable ? undefined : editingItem !== item.id && "true"}
434
+ onDragStart={!isItemDraggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
435
+ onDragOver={!isItemDraggable ? undefined : dragHandlers.handleDragOver}
436
+ onDragEnd={!isItemDraggable ? undefined : dragHandlers.handleDragEnd}
437
+ onTouchStart={!isItemDraggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
438
+ onTouchMove={!isItemDraggable ? undefined : dragHandlers.handleDragOver}
439
+ onTouchEnd={!isItemDraggable ? undefined : dragHandlers.handleDragEnd}
433
440
  style={itemStyle}
434
441
  onDoubleClick={() => handleDoubleClick(item)}
435
442
  >
@@ -437,14 +444,14 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
437
444
  {renderOption ? (
438
445
  renderOption(
439
446
  item,
440
- `${prefix}-draggable-list__handle`,
447
+ isItemDraggable ? `${prefix}-draggable-list__handle` : '',
441
448
  index
442
449
  )
443
450
  ) : (
444
451
  <>
445
452
  {/** DRAG HANDLE */}
446
453
  {/* Fix the problem that mobile terminals cannot be touched, DO NOT USE "<svg>" */}
447
- {draggable && !handleHide ? <span className={`${prefix}-draggable-list__handle ${handlePos ?? 'left'}`} draggable={dragMode === 'handle'} dangerouslySetInnerHTML={{
454
+ {isItemDraggable && !handleHide ? <span className={`${prefix}-draggable-list__handle ${handlePos ?? 'left'}`} draggable={dragMode === 'handle'} dangerouslySetInnerHTML={{
448
455
  __html: `${handleIcon}`
449
456
  }}></span> : null}
450
457
  {/** /DRAG HANDLE */}
@@ -5,6 +5,7 @@ import useComId from 'funda-utils/dist/cjs/useComId';
5
5
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
6
6
 
7
7
 
8
+
8
9
  import { TableProvider } from './TableContext';
9
10
  import useTableResponsive from './utils/hooks/useTableResponsive';
10
11
  import useTableDraggable from './utils/hooks/useTableDraggable';
@@ -148,10 +149,10 @@ const Table = forwardRef<HTMLDivElement, TableProps>((
148
149
 
149
150
  // initialize drag & drop data
150
151
  const {
151
- handleDragStart,
152
- handleDragEnd,
153
- handledragOver,
154
- handleTbodyEnter
152
+ isDragging,
153
+ dragHandlers,
154
+ handleTbodyEnter,
155
+ handleTbodyLeave
155
156
  } = useTableDraggable({
156
157
  enabled: rowDraggable && rootRef.current,
157
158
  data: data,
@@ -268,9 +269,10 @@ const Table = forwardRef<HTMLDivElement, TableProps>((
268
269
 
269
270
  // drag & drop
270
271
  rowDraggable,
271
- handleDragStart,
272
- handleDragEnd,
273
- handledragOver,
272
+ isRowDragging: isDragging,
273
+ handleDragStart: dragHandlers.handleDragStart,
274
+ handleDragEnd: dragHandlers.handleDragEnd,
275
+ handledragOver: dragHandlers.handleDragOver,
274
276
  handleTbodyEnter,
275
277
 
276
278
 
@@ -44,9 +44,15 @@ const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>((
44
44
  // selection
45
45
  // ================================================================
46
46
  const _res = convertMapToArr(selectedItems);
47
+ // Performance optimization: stringify itemData only once instead of N times
48
+ const itemDataStr = itemData ? JSON.stringify(itemData) : '';
47
49
  const filteredSelectedItems = _res.map((v: any) => Number(v)).map((rowNum: number) => {
48
- if (JSON.stringify(itemData) === JSON.stringify(originData[rowNum])) {
49
- return originData[rowNum];
50
+ const originItem = originData?.[rowNum];
51
+ // Fast path: reference equality
52
+ if (itemData === originItem) return originItem;
53
+ // Fallback: JSON comparison (itemDataStr is cached)
54
+ if (itemDataStr && itemDataStr === JSON.stringify(originItem)) {
55
+ return originItem;
50
56
  }
51
57
  }).filter(Boolean);
52
58
  const selectedClassName = activeClassName || 'active';
@@ -64,7 +70,7 @@ const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>((
64
70
  onDragEnd={rowDraggable ? handleDragEnd : () => void(0)}
65
71
  data-row-data={`${itemData && typeof itemData === 'object' ? JSON.stringify(itemData) : itemData}`}
66
72
 
67
- className={`row-obj ${className || ''} ${active ? selectedClassName : ''} ${itemData && originData ? (filteredData.every((item: any) => filterFieldsData.some((s: string) => !item[s]?.toLowerCase().includes(itemData[s]?.toLowerCase())) ) ? 'd-none' : '') : ''} ${itemData && originData ? (filteredSelectedItems.some((item: any) => JSON.stringify(itemData) === JSON.stringify(item) ) ? selectedClassName : '') : ''}`}
73
+ className={`row-obj ${className || ''} ${active ? selectedClassName : ''} ${itemData && originData ? (filteredData.every((item: any) => filterFieldsData.some((s: string) => !item[s]?.toLowerCase().includes(itemData[s]?.toLowerCase())) ) ? 'd-none' : '') : ''} ${itemData && originData && filteredSelectedItems.length > 0 ? selectedClassName : ''}`}
68
74
  >
69
75
  {children}
70
76
  </tr>
@@ -1,3 +1,5 @@
1
+
2
+
1
3
  /* ======================================================
2
4
  <!-- Table (Synthetic) -->
3
5
  /* ====================================================== */
@@ -7,7 +9,7 @@
7
9
  --syntable-row-active-bg: #f0f8ff;
8
10
  --syntable-scrollbar-color: rgba(0, 0, 0, 0.2);
9
11
  --syntable-scrollbar-track: rgba(0, 0, 0, 0);
10
- --syntable-scrollbar-h: 3px;
12
+ --syntable-scrollbar-h: 10px;
11
13
  --syntable-scrollbar-w: 10px;
12
14
  --syntable-padding-x: 1rem;
13
15
  --syntable-padding-y: 0.5rem;
@@ -165,11 +167,13 @@
165
167
 
166
168
  .row-obj-clonelast {
167
169
  height: 0 !important;
168
-
170
+ border-color: transparent !important;
171
+ padding: 0 !important;
169
172
 
170
173
  td {
171
174
  border: none;
172
175
  box-shadow: none;
176
+ padding: 0 !important;
173
177
  }
174
178
  }
175
179
 
@@ -190,11 +194,13 @@
190
194
  }
191
195
 
192
196
  /* placeholder */
197
+ .row-obj-lastplaceholder,
193
198
  .row-placeholder {
194
199
  border: 2px dotted #b5ba91;
195
200
  background-color: #e4e9c3;
196
201
  }
197
202
 
203
+
198
204
  /* trigger */
199
205
  .drag-trigger {
200
206
  display: inline-block;
@@ -15,7 +15,8 @@ const DragHandleSprite = forwardRef((props: DragHandleSpriteProps, externalRef:
15
15
 
16
16
  const {
17
17
  rowDraggable,
18
- handleTbodyEnter
18
+ handleTbodyEnter,
19
+ handleTbodyLeave
19
20
  } = useContext(TableContext);
20
21
 
21
22
  return (
@@ -23,7 +24,10 @@ const DragHandleSprite = forwardRef((props: DragHandleSpriteProps, externalRef:
23
24
  {rowDraggable ? <span
24
25
  ref={externalRef}
25
26
  className={className || 'drag-trigger'}
26
- onMouseEnter={handleTbodyEnter}
27
+ // Only when mousedown happens on this handle will we allow row dragging.
28
+ onMouseDown={handleTbodyEnter}
29
+ onMouseUp={handleTbodyLeave}
30
+ onMouseLeave={handleTbodyLeave}
27
31
  >
28
32
  {icon || <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none">
29
33
  <g>
@@ -63,6 +63,14 @@ export function initOrderProps(rootElem: any) {
63
63
 
64
64
  export function initRowColProps(rootElem: any) {
65
65
  if (rootElem === null) return;
66
+
67
+ // !!! Important, performance optimization for large data renderings
68
+ // With this protection, it is only performed once
69
+ if (typeof rootElem.dataset.rowColPropsInit !== 'undefined') return;
70
+ rootElem.dataset.rowColPropsInit = '1';
71
+
72
+
73
+ //
66
74
  const _allRows = allRows(rootElem);
67
75
  const _allHeadRows = allHeadRows(rootElem);
68
76
 
@@ -167,7 +175,10 @@ export function cellMark(row: number | string | undefined, col: number | string
167
175
 
168
176
  export function removeCellFocusClassName(root: any) {
169
177
  if (root) {
170
- [].slice.call(root.querySelectorAll('td, th')).forEach((el: HTMLTableElement) => {
178
+ // !!! Important, performance optimization for large data renderings
179
+ // Only query elements with cell-focus classes
180
+ const focusedCells = root.querySelectorAll('td.cell-focus, th.cell-focus');
181
+ focusedCells.forEach((el: HTMLElement) => {
171
182
  el.classList.remove('cell-focus');
172
183
  });
173
184
  }