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.
- package/DragDropList/index.d.ts +1 -0
- package/DragDropList/index.js +143 -52
- package/DynamicFields/index.d.ts +1 -0
- package/DynamicFields/index.js +108 -8
- package/EventCalendarTimeline/index.css +12 -1
- package/EventCalendarTimeline/index.d.ts +1 -0
- package/EventCalendarTimeline/index.js +99 -6
- package/MultipleSelect/index.js +162 -71
- package/Table/index.css +5 -1
- package/Table/index.js +410 -90
- package/Utils/useBoundedDrag.d.ts +1 -0
- package/Utils/useBoundedDrag.js +124 -39
- package/lib/cjs/DragDropList/index.d.ts +1 -0
- package/lib/cjs/DragDropList/index.js +143 -52
- package/lib/cjs/DynamicFields/index.d.ts +1 -0
- package/lib/cjs/DynamicFields/index.js +108 -8
- package/lib/cjs/EventCalendarTimeline/index.d.ts +1 -0
- package/lib/cjs/EventCalendarTimeline/index.js +99 -6
- package/lib/cjs/MultipleSelect/index.js +162 -71
- package/lib/cjs/Table/index.js +410 -90
- package/lib/cjs/Utils/useBoundedDrag.d.ts +1 -0
- package/lib/cjs/Utils/useBoundedDrag.js +124 -39
- package/lib/css/EventCalendarTimeline/index.css +12 -1
- package/lib/css/Table/index.css +5 -1
- package/lib/esm/DragDropList/index.tsx +23 -16
- package/lib/esm/DynamicFields/index.tsx +107 -8
- package/lib/esm/EventCalendarTimeline/index.scss +15 -1
- package/lib/esm/EventCalendarTimeline/index.tsx +130 -11
- package/lib/esm/ModalDialog/index.tsx +0 -1
- package/lib/esm/Table/Table.tsx +9 -7
- package/lib/esm/Table/TableRow.tsx +9 -3
- package/lib/esm/Table/index.scss +8 -2
- package/lib/esm/Table/utils/DragHandleSprite.tsx +6 -2
- package/lib/esm/Table/utils/func.ts +12 -1
- package/lib/esm/Table/utils/hooks/useTableDraggable.tsx +401 -93
- package/lib/esm/Utils/hooks/useBoundedDrag.tsx +142 -39
- package/package.json +1 -1
|
@@ -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
|
-
//
|
|
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
|
-
//
|
|
302
|
-
var
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
//
|
|
308
|
-
if (
|
|
309
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
364
|
+
// Find the element below the pointer/touch
|
|
365
|
+
var elemBelow = document.elementFromPoint(clientX, clientY);
|
|
366
|
+
if (!elemBelow) return;
|
|
316
367
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
368
|
+
// Find the closest list item
|
|
369
|
+
var listItem = elemBelow.closest(itemSelector);
|
|
370
|
+
if (!listItem) return;
|
|
320
371
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
372
|
+
// Check boundary
|
|
373
|
+
var boundary = boundaryElement.current || listItem.closest(boundarySelector);
|
|
374
|
+
if (!boundary) return;
|
|
324
375
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
listItem.
|
|
343
|
-
|
|
344
|
-
|
|
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,
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
}
|
|
236
236
|
*/
|
|
237
237
|
/* cell */
|
|
238
|
-
/* header */
|
|
238
|
+
/* The width of the left column header */
|
|
239
239
|
/* event container */
|
|
240
240
|
/* days container */
|
|
241
241
|
/* remove empty cells */
|
|
@@ -466,10 +466,21 @@
|
|
|
466
466
|
background-color: var(--custom-event-tl-table-divider-bg);
|
|
467
467
|
border-left: var(--custom-event-tl-table-divider-border);
|
|
468
468
|
border-right: var(--custom-event-tl-table-divider-border);
|
|
469
|
+
position: relative;
|
|
469
470
|
}
|
|
470
471
|
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider > div {
|
|
471
472
|
width: var(--custom-event-tl-table-divider-w);
|
|
472
473
|
}
|
|
474
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable {
|
|
475
|
+
cursor: col-resize;
|
|
476
|
+
user-select: none;
|
|
477
|
+
}
|
|
478
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable:hover {
|
|
479
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
480
|
+
}
|
|
481
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable:active {
|
|
482
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
483
|
+
}
|
|
473
484
|
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__cell-cushion {
|
|
474
485
|
padding: var(--custom-event-tl-table-cell-padding);
|
|
475
486
|
}
|
package/lib/css/Table/index.css
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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':
|
|
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={!
|
|
427
|
-
onDragStart={!
|
|
428
|
-
onDragOver={!
|
|
429
|
-
onDragEnd={!
|
|
430
|
-
onTouchStart={!
|
|
431
|
-
onTouchMove={!
|
|
432
|
-
onTouchEnd={!
|
|
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
|
-
{
|
|
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 */}
|
|
@@ -20,6 +20,7 @@ export type DynamicFieldsProps = {
|
|
|
20
20
|
label?: React.ReactNode | string;
|
|
21
21
|
data: DynamicFieldsValueProps | null;
|
|
22
22
|
maxFields?: any;
|
|
23
|
+
defaultRows?: number;
|
|
23
24
|
confirmText?: string;
|
|
24
25
|
doNotRemoveDom?: boolean;
|
|
25
26
|
iconAddBefore?: React.ReactNode | string;
|
|
@@ -55,6 +56,7 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
55
56
|
label,
|
|
56
57
|
data,
|
|
57
58
|
maxFields,
|
|
59
|
+
defaultRows,
|
|
58
60
|
iconAddBefore,
|
|
59
61
|
iconAddAfter,
|
|
60
62
|
iconAdd,
|
|
@@ -96,6 +98,10 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
96
98
|
const [tmpl, setTmpl] = useState<React.ReactNode>([]);
|
|
97
99
|
const addBtnIdRef = useRef<string>('');
|
|
98
100
|
addBtnIdRef.current = `dynamic-fields-add-${idRes}`;
|
|
101
|
+
const defaultRowsInitializedRef = useRef<boolean>(false);
|
|
102
|
+
const isInitializingDefaultRowsRef = useRef<boolean>(false);
|
|
103
|
+
const rafIdRef = useRef<number | null>(null);
|
|
104
|
+
const timeoutIdsRef = useRef<ReturnType<typeof setTimeout>[]>([]);
|
|
99
105
|
|
|
100
106
|
// exposes the following methods
|
|
101
107
|
useImperativeHandle(
|
|
@@ -169,17 +175,12 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
function handleClickAdd(event: any = null) {
|
|
174
|
-
if (event !== null) {
|
|
175
|
-
if (typeof event !== 'undefined') event.preventDefault();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
+
function addRowWithTemplate(template: React.ReactNode) {
|
|
178
179
|
//button status
|
|
179
180
|
checkMaxStatus();
|
|
180
181
|
|
|
181
182
|
//
|
|
182
|
-
setVal((prevState: any[]) => [...prevState, ...generateGroup(
|
|
183
|
+
setVal((prevState: any[]) => [...prevState, ...generateGroup(template)]);
|
|
183
184
|
|
|
184
185
|
|
|
185
186
|
//
|
|
@@ -209,6 +210,14 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
209
210
|
}, 0);
|
|
210
211
|
}
|
|
211
212
|
|
|
213
|
+
function handleClickAdd(event: any = null) {
|
|
214
|
+
if (event !== null) {
|
|
215
|
+
if (typeof event !== 'undefined') event.preventDefault();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
addRowWithTemplate(tmpl);
|
|
219
|
+
}
|
|
220
|
+
|
|
212
221
|
|
|
213
222
|
function handleClickRemove(e: React.MouseEvent) {
|
|
214
223
|
if (e !== null && typeof e !== 'undefined') e.preventDefault();
|
|
@@ -331,6 +340,96 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
331
340
|
|
|
332
341
|
//
|
|
333
342
|
onLoad?.(addBtnIdRef.current, rootRef.current, PER_ROW_DOM_STRING);
|
|
343
|
+
|
|
344
|
+
// Reset initialization flag when data becomes null
|
|
345
|
+
if (!data) {
|
|
346
|
+
defaultRowsInitializedRef.current = false;
|
|
347
|
+
isInitializingDefaultRowsRef.current = false;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Add default rows if specified (only initialize once per data setup)
|
|
351
|
+
if (!defaultRowsInitializedRef.current && typeof defaultRows === 'number' && defaultRows > 0 && data && data.tmpl) {
|
|
352
|
+
// Get the initial data length
|
|
353
|
+
const initDataLength = Array.isArray(data.init) ? data.init.length : 0;
|
|
354
|
+
|
|
355
|
+
// Calculate how many rows need to be added
|
|
356
|
+
const maxRows = typeof maxFields !== 'undefined' ? parseFloat(maxFields) : Infinity;
|
|
357
|
+
const targetRows = Math.min(defaultRows, maxRows);
|
|
358
|
+
const rowsToAdd = Math.max(0, targetRows - initDataLength);
|
|
359
|
+
|
|
360
|
+
// Mark as initialized immediately to prevent re-adding
|
|
361
|
+
defaultRowsInitializedRef.current = true;
|
|
362
|
+
|
|
363
|
+
// Only add rows if needed
|
|
364
|
+
if (rowsToAdd > 0) {
|
|
365
|
+
// Mark that we're initializing default rows
|
|
366
|
+
isInitializingDefaultRowsRef.current = true;
|
|
367
|
+
|
|
368
|
+
// Clear any existing timeouts
|
|
369
|
+
timeoutIdsRef.current.forEach(id => clearTimeout(id));
|
|
370
|
+
timeoutIdsRef.current = [];
|
|
371
|
+
|
|
372
|
+
// Use requestAnimationFrame to ensure DOM is ready and state is updated
|
|
373
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
374
|
+
// Check if component is still mounted
|
|
375
|
+
if (!rootRef.current) {
|
|
376
|
+
isInitializingDefaultRowsRef.current = false;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const timeoutId1 = setTimeout(() => {
|
|
381
|
+
// Check if component is still mounted
|
|
382
|
+
if (!rootRef.current) {
|
|
383
|
+
isInitializingDefaultRowsRef.current = false;
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Add rows one by one with a small delay to ensure state updates
|
|
388
|
+
let addedCount = 0;
|
|
389
|
+
const addNextRow = () => {
|
|
390
|
+
// Check if component is still mounted before each operation
|
|
391
|
+
if (addedCount < rowsToAdd && rootRef.current) {
|
|
392
|
+
// Use data.tmpl directly to avoid dependency on state
|
|
393
|
+
addRowWithTemplate(data.tmpl);
|
|
394
|
+
addedCount++;
|
|
395
|
+
// Wait a bit before adding the next row to ensure state updates
|
|
396
|
+
if (addedCount < rowsToAdd) {
|
|
397
|
+
const timeoutId2 = setTimeout(addNextRow, 10);
|
|
398
|
+
timeoutIdsRef.current.push(timeoutId2);
|
|
399
|
+
} else {
|
|
400
|
+
// All rows added, mark initialization as complete
|
|
401
|
+
isInitializingDefaultRowsRef.current = false;
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
// No more rows to add or component unmounted
|
|
405
|
+
isInitializingDefaultRowsRef.current = false;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
addNextRow();
|
|
409
|
+
}, 50);
|
|
410
|
+
timeoutIdsRef.current.push(timeoutId1);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Cleanup function to cancel requestAnimationFrame and timeouts
|
|
416
|
+
return () => {
|
|
417
|
+
// Don't cleanup if we're in the middle of initializing defaultRows
|
|
418
|
+
// This prevents the cleanup from canceling the async operations before they complete
|
|
419
|
+
if (isInitializingDefaultRowsRef.current) {
|
|
420
|
+
// Allow defaultRows initialization to complete
|
|
421
|
+
// The cleanup will happen naturally when initialization finishes
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Normal cleanup for other cases
|
|
426
|
+
if (rafIdRef.current !== null) {
|
|
427
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
428
|
+
rafIdRef.current = null;
|
|
429
|
+
}
|
|
430
|
+
timeoutIdsRef.current.forEach(id => clearTimeout(id));
|
|
431
|
+
timeoutIdsRef.current = [];
|
|
432
|
+
};
|
|
334
433
|
}, [data]);
|
|
335
434
|
|
|
336
435
|
|
|
@@ -406,4 +505,4 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
406
505
|
)
|
|
407
506
|
};
|
|
408
507
|
|
|
409
|
-
export default DynamicFields;
|
|
508
|
+
export default DynamicFields;
|
|
@@ -470,7 +470,7 @@
|
|
|
470
470
|
word-break: break-word;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
/* header */
|
|
473
|
+
/* The width of the left column header */
|
|
474
474
|
.custom-event-tl-table__cell-cushion-headertitle {
|
|
475
475
|
width: var(--custom-event-tl-table-header-w);
|
|
476
476
|
}
|
|
@@ -614,10 +614,24 @@
|
|
|
614
614
|
background-color: var(--custom-event-tl-table-divider-bg);
|
|
615
615
|
border-left: var(--custom-event-tl-table-divider-border);
|
|
616
616
|
border-right: var(--custom-event-tl-table-divider-border);
|
|
617
|
+
position: relative;
|
|
617
618
|
|
|
618
619
|
> div {
|
|
619
620
|
width: var(--custom-event-tl-table-divider-w);
|
|
620
621
|
}
|
|
622
|
+
|
|
623
|
+
&--resizable {
|
|
624
|
+
cursor: col-resize;
|
|
625
|
+
user-select: none;
|
|
626
|
+
|
|
627
|
+
&:hover {
|
|
628
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
&:active {
|
|
632
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
621
635
|
}
|
|
622
636
|
|
|
623
637
|
/* cell */
|