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
@@ -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,
@@ -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
  }
@@ -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 */}
@@ -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(tmpl)]);
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 */