funda-ui 4.7.197 → 4.7.202

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.
@@ -1,11 +1,10 @@
1
- import React, { useState, useRef } from 'react';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
2
 
3
3
  import Item from './AccordionItem';
4
4
 
5
5
  import animateStyles from 'funda-utils/dist/cjs/anim';
6
6
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
7
7
 
8
-
9
8
  // Adapt the easing parameters of TweenMax
10
9
  export enum EasingList {
11
10
  linear = 'linear',
@@ -15,23 +14,23 @@ export enum EasingList {
15
14
  }
16
15
 
17
16
 
18
- export type AccordionOptionChangeFnType = (arg1: any, arg2: any) => void;
17
+ export type AccordionOptionChangeFnType = (element: HTMLDivElement, index: number) => void;
19
18
 
20
19
 
21
20
  export type AccordionProps = {
22
21
  wrapperClassName?: string;
23
- /** One event type, such as `click` or `mouseover` */
24
- triggerType?: string;
25
- /** Display the only first item of a list */
26
- displayTheFirstItem?: boolean;
27
- /** Display all items */
28
- displayAllItems?: boolean;
22
+ /** The index of the item to be displayed by default. Set to -1 to display none, or an array of indices to display multiple items */
23
+ defaultActiveIndex?: number | number[];
24
+ /** Whether to expand all items by default */
25
+ defaultActiveAll?: boolean;
29
26
  /** The number of milliseconds(ms) each iteration of the animation takes to complete */
30
27
  duration?: number;
31
28
  /** Types of easing animation */
32
29
  easing?: string;
33
30
  /** Mutually exclusive alternate expansion between the levels */
34
31
  alternateCollapse?: boolean;
32
+ /** Only allow arrow to trigger the accordion */
33
+ arrowOnly?: boolean;
35
34
  /** This function is called whenever the data is updated.
36
35
  * Exposes the JSON format data about the option as an argument.
37
36
  */
@@ -43,142 +42,184 @@ export type AccordionProps = {
43
42
  const Accordion = (props: AccordionProps) => {
44
43
  const {
45
44
  wrapperClassName,
46
- triggerType,
47
- displayTheFirstItem,
48
- displayAllItems,
45
+ defaultActiveIndex,
46
+ defaultActiveAll = false,
49
47
  duration,
50
48
  easing,
51
- alternateCollapse,
49
+ alternateCollapse = true,
50
+ arrowOnly = false,
52
51
  onChange,
53
52
  children
54
53
  } = props;
55
54
 
56
-
55
+ const animSpeed = duration || 200;
57
56
  const easeType: string = typeof alternateCollapse === 'undefined' ? EasingList['linear'] : EasingList[easing as never];
58
- const ALTER = typeof alternateCollapse === 'undefined' ? true : alternateCollapse;
59
57
  const rootRef = useRef<any>(null);
60
58
  const [animOK, setAnimOK] = useState<boolean>(false);
61
59
  const [heightObserver, setHeightObserver] = useState<number>(-1);
60
+ const [expandedItems, setExpandedItems] = useState<Set<number>>(new Set()); // Keep track of all expanded items
61
+ const animationInProgress = useRef<boolean>(false);
62
62
 
63
63
 
64
64
  function handleClickItem(e: React.MouseEvent) {
65
+ if (animationInProgress.current) return;
65
66
  if ((e.target as any).closest('.custom-accordion-header') === null) return;
66
- if ( animOK ) return;
67
-
68
- // DO NOT place it before the above code, otherwise it will cause the checkbox and radio controls to fail.
69
- e.preventDefault();
70
-
71
- //Prevents further propagation of the current event in the capturing and bubbling phases(if use `e.target`).
72
- e.stopPropagation();
73
-
74
-
75
- //
76
- const reactDomEl: any = e.currentTarget;
77
- const curIndex: any = reactDomEl.dataset.index;
78
- const reactDomWrapperEl: HTMLElement = rootRef.current;
79
- const animSpeed = duration || 200;
80
- const $li = reactDomWrapperEl.querySelectorAll( '.custom-accordion-item' );
81
- const $allContent = reactDomWrapperEl.querySelectorAll( '.custom-accordion-content__wrapper' );
82
- const $curContent = reactDomEl.querySelector( '.custom-accordion-content__wrapper' );
83
-
84
-
85
- if ( reactDomEl.getAttribute( 'aria-expanded' ) === 'false' || reactDomEl.getAttribute( 'aria-expanded' ) === null ) {
86
-
67
+ if (animOK) return;
68
+
69
+ animationInProgress.current = true;
70
+
71
+ const reactDomEl: any = arrowOnly ? e.currentTarget.closest('.custom-accordion-item') : e.currentTarget;
72
+ const curIndex: number = parseInt(reactDomEl.dataset.index);
73
+ const reactDomWrapperEl: HTMLElement = rootRef.current;
74
+ const $li = reactDomWrapperEl.querySelectorAll('.custom-accordion-item');
75
+ const $allContent = reactDomWrapperEl.querySelectorAll('.custom-accordion-content__wrapper');
76
+ const $curContent = reactDomEl.querySelector('.custom-accordion-content__wrapper');
77
+ const $trigger = reactDomEl.querySelector('.custom-accordion-trigger');
78
+
79
+ if (reactDomEl.getAttribute('aria-expanded') === 'false' || reactDomEl.getAttribute('aria-expanded') === null) {
87
80
  setAnimOK(true);
88
- setTimeout( () => {
81
+ setTimeout(() => {
89
82
  setAnimOK(false);
90
- }, animSpeed);
83
+ }, animSpeed);
91
84
 
92
- if (ALTER) {
93
- //Hide other all sibling <dt> of the selected element
85
+ if (alternateCollapse) {
86
+ // Hide other all sibling content
94
87
  Array.prototype.forEach.call($allContent, (node) => {
95
- if ( node.clientHeight > 0 ) {
88
+ if (node.clientHeight > 0) {
96
89
  animateStyles(node, {
97
- startHeight : node.scrollHeight,
98
- endHeight : 0,
99
- speed : animSpeed
100
- } as never, easeType);
90
+ startHeight: node.scrollHeight,
91
+ endHeight: 0,
92
+ speed: animSpeed
93
+ } as never, easeType, () => {
94
+ animationInProgress.current = false;
95
+ });
101
96
  }
102
-
103
97
  });
104
98
 
105
- //to open
99
+ // Update all items to collapsed state
106
100
  Array.prototype.forEach.call($li, (node) => {
107
- node.classList.remove('active');
108
- node.querySelector('.custom-accordion-trigger')?.classList.remove('active');
109
- node.querySelector('.custom-accordion-trigger')?.classList.add('collapsed');
110
- node.setAttribute( 'aria-expanded', false );
101
+ node.setAttribute('aria-expanded', 'false');
111
102
  });
112
103
 
104
+ // Update expanded items state
105
+ setExpandedItems(new Set([curIndex]));
106
+ } else {
107
+ // Add current item to expanded items
108
+ setExpandedItems(prev => new Set([...(prev as never), curIndex]));
109
+ }
110
+
111
+ reactDomEl.setAttribute('aria-expanded', 'true');
113
112
 
113
+ // Call onTriggerChange if it exists in the child props
114
+ const childProps = (children as any[])[curIndex]?.props;
115
+ if (typeof childProps?.onTriggerChange === 'function' && $trigger) {
116
+ childProps.onTriggerChange($trigger, true);
114
117
  }
115
118
 
116
- reactDomEl.classList.add('active');
117
- reactDomEl.querySelector('.custom-accordion-trigger')?.classList.add('active');
118
- reactDomEl.querySelector('.custom-accordion-trigger')?.classList.remove('collapsed');
119
- reactDomEl.setAttribute( 'aria-expanded', true );
120
- // When the height of the element is 0, the value of `offsetHeight` and `clientHeight` will be 0
121
- animateStyles($curContent, {
122
- startHeight : 0,
123
- endHeight : $curContent.scrollHeight,
124
- speed : animSpeed
125
- } as never, easeType, () => {
126
- // content height observer
119
+ animateStyles($curContent, {
120
+ startHeight: 0,
121
+ endHeight: $curContent.scrollHeight,
122
+ speed: animSpeed
123
+ } as never, easeType, () => {
127
124
  setHeightObserver(curIndex);
125
+ animationInProgress.current = false;
128
126
  });
129
-
130
- } else {
131
-
132
- if ( e.type == 'click' ) {
133
-
134
- //to close
135
- reactDomEl.classList.remove('active');
136
- reactDomEl.querySelector('.custom-accordion-trigger')?.classList.remove('active');
137
- reactDomEl.querySelector('.custom-accordion-trigger')?.classList.add('collapsed');
138
- reactDomEl.setAttribute( 'aria-expanded', false );
139
- animateStyles($curContent, {
140
- startHeight : $curContent.scrollHeight,
141
- endHeight : 0,
142
- speed : animSpeed
143
- } as never, easeType);
144
- }
145
-
146
- }
147
-
148
- if (typeof (onChange) === 'function') {
149
- onChange(reactDomEl, Number(curIndex));
150
- }
127
+ } else {
128
+ reactDomEl.setAttribute('aria-expanded', 'false');
151
129
 
130
+ // Call onTriggerChange if it exists in the child props
131
+ const childProps = (children as any[])[curIndex]?.props;
132
+ if (typeof childProps?.onTriggerChange === 'function' && $trigger) {
133
+ childProps.onTriggerChange($trigger, false);
134
+ }
152
135
 
153
- }
136
+ // Remove current item from expanded items
137
+ setExpandedItems(prev => {
138
+ const newSet = new Set(prev);
139
+ newSet.delete(curIndex);
140
+ return newSet;
141
+ });
154
142
 
143
+ animateStyles($curContent, {
144
+ startHeight: $curContent.scrollHeight,
145
+ endHeight: 0,
146
+ speed: animSpeed
147
+ } as never, easeType, () => {
148
+ animationInProgress.current = false;
149
+ });
150
+ }
151
+
152
+ onChange?.(reactDomEl, curIndex);
153
+ }
154
+
155
+
156
+ // Initialize expanded items based on defaultActiveIndex or defaultActiveAll
157
+ useEffect(() => {
158
+ if (defaultActiveAll && children && rootRef.current) {
159
+ const allIndices = Array.from({ length: (children as any[]).length }, (_, i) => i);
160
+ setExpandedItems(new Set(allIndices));
161
+
162
+ // Actually expand all items without animation
163
+ const $allItems = rootRef.current.querySelectorAll('.custom-accordion-item');
164
+
165
+ Array.prototype.forEach.call($allItems, (node, index) => {
166
+ node.setAttribute('aria-expanded', 'true');
167
+ const $curContent = node.querySelector('.custom-accordion-content__wrapper');
168
+ const $trigger = node.querySelector('.custom-accordion-trigger');
169
+
170
+ // Call onTriggerChange if it exists in the child props
171
+ const childProps = (children as any[])[index]?.props;
172
+ if (typeof childProps?.onTriggerChange === 'function' && $trigger) {
173
+ childProps.onTriggerChange($trigger, true);
174
+ }
175
+
176
+ // Directly set height without animation
177
+ if ($curContent) {
178
+ $curContent.style.height = `${$curContent.scrollHeight}px`;
179
+ }
180
+ });
181
+ } else if (defaultActiveIndex !== undefined) {
182
+ const initialExpanded = new Set<number>();
183
+ if (Array.isArray(defaultActiveIndex)) {
184
+ defaultActiveIndex.forEach(index => initialExpanded.add(index));
185
+ } else if (typeof defaultActiveIndex === 'number') {
186
+ initialExpanded.add(defaultActiveIndex);
187
+ }
188
+ setExpandedItems(initialExpanded);
189
+ }
190
+ }, [defaultActiveIndex, defaultActiveAll, children]);
191
+
155
192
  return (
156
193
  <>
157
-
158
194
  <div className={combinedCls(
159
195
  'custom-accordion-item',
160
196
  clsWrite(wrapperClassName, 'accordion')
161
197
  )} role="tablist" ref={rootRef}>
162
198
  {(children != null) ? (children as any[]).map((item, i) => {
163
199
  const childProps = { ...item.props };
164
- const _defaultActive = i === 0 && displayTheFirstItem ? true : false;
200
+ let _defaultActive = false;
201
+
202
+ if (Array.isArray(defaultActiveIndex)) {
203
+ _defaultActive = defaultActiveIndex.includes(i);
204
+ } else if (typeof defaultActiveIndex === 'number') {
205
+ _defaultActive = defaultActiveIndex === i;
206
+ }
207
+
165
208
  return <Item
166
209
  key={"item" + i}
167
210
  index={i}
211
+ animSpeed={animSpeed}
212
+ arrowOnly={arrowOnly}
168
213
  heightObserver={heightObserver}
169
- defaultActive={typeof displayAllItems === 'undefined' ? _defaultActive : displayAllItems}
170
- triggerType={triggerType || 'click'}
214
+ defaultActive={_defaultActive}
171
215
  onToggleEv={handleClickItem}
216
+ isExpanded={expandedItems.has(i)} // Both controlled and uncontrolled modes are implemented
172
217
  {...childProps}
173
218
  />;
174
-
175
219
  }) : null}
176
-
177
220
  </div>
178
-
179
-
180
221
  </>
181
- )
222
+ );
182
223
  };
183
224
 
184
225
  export default Accordion;
@@ -5,6 +5,9 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
5
5
  export type AccordionItemProps = {
6
6
  heightObserver?: number;
7
7
  index?: number;
8
+ arrowOnly?: boolean;
9
+ animSpeed?: number;
10
+ easeType?: string;
8
11
  /** Class of items */
9
12
  itemClassName?: string;
10
13
  itemContentWrapperClassName?: string;
@@ -21,8 +24,10 @@ export type AccordionItemProps = {
21
24
  onToggleEv?: React.MouseEventHandler<HTMLElement>;
22
25
  /** Handling events when the animation execution is complete */
23
26
  onTransitionEnd?: React.TransitionEventHandler<HTMLElement>;
24
- /** One event type, such as `click` or `mouseover` */
25
- triggerType?: string;
27
+ /** Callback when trigger state changes, provides trigger element and expanded state */
28
+ onItemCollapse?: (trigger: HTMLElement, icon: HTMLElement, isExpanded: boolean) => void;
29
+ /** Control expanded state from parent */
30
+ isExpanded?: boolean;
26
31
  /** -- */
27
32
  children: React.ReactNode;
28
33
  };
@@ -31,10 +36,12 @@ export type AccordionItemProps = {
31
36
 
32
37
 
33
38
  const AccordionItem = (props: AccordionItemProps) => {
34
-
35
39
  const {
36
40
  heightObserver,
37
41
  index,
42
+ animSpeed,
43
+ easeType,
44
+ arrowOnly,
38
45
  itemClassName,
39
46
  itemContentWrapperClassName,
40
47
  itemContentClassName,
@@ -46,18 +53,60 @@ const AccordionItem = (props: AccordionItemProps) => {
46
53
  title,
47
54
  onToggleEv,
48
55
  onTransitionEnd,
49
- triggerType,
56
+ onItemCollapse,
57
+ isExpanded: controlledExpanded,
50
58
  children,
51
59
  ...attributes
52
60
  } = props;
53
61
 
62
+ const [internalExpanded, setInternalExpanded] = useState<boolean>(false);
63
+ const isFirstRender = useRef<boolean>(true);
64
+ const initialHeightSet = useRef<boolean>(false);
65
+
66
+ // Use controlled or uncontrolled expanded state
67
+ const isExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
54
68
 
55
-
56
- const activedClassName = typeof(defaultActive) !== 'undefined' && defaultActive !== false ? ' active' : '';
57
69
  const observer = useRef<ResizeObserver | null>(null);
58
70
  const contentWrapperRef = useRef<HTMLDivElement | null>(null);
59
71
  const contentRef = useRef<HTMLDivElement | null>(null);
60
-
72
+ const triggerRef = useRef<HTMLDivElement | HTMLButtonElement | null>(null);
73
+ const iconRef = useRef<HTMLSpanElement | null>(null);
74
+
75
+ const handleToggle = (e: React.MouseEvent<HTMLElement>) => {
76
+ e.preventDefault();
77
+ e.stopPropagation();
78
+
79
+ if (controlledExpanded === undefined) {
80
+ setInternalExpanded(prev => !prev);
81
+ }
82
+ onToggleEv?.(e);
83
+ };
84
+
85
+ useEffect(() => {
86
+ if (triggerRef.current && typeof onItemCollapse === 'function') {
87
+ if (isFirstRender.current) {
88
+ isFirstRender.current = false;
89
+ return;
90
+ }
91
+ onItemCollapse(triggerRef.current, iconRef.current as HTMLElement, isExpanded);
92
+ }
93
+ }, [isExpanded, onItemCollapse]);
94
+
95
+ useEffect(() => {
96
+ if (contentWrapperRef.current && !initialHeightSet.current) {
97
+ initialHeightSet.current = true;
98
+ const shouldBeExpanded = typeof defaultActive !== 'undefined' && defaultActive !== false;
99
+ if (controlledExpanded === undefined) {
100
+ setInternalExpanded(shouldBeExpanded);
101
+ }
102
+
103
+ // Set initial height when defaultActive is true
104
+ if (shouldBeExpanded && contentRef.current) {
105
+ const contentHeight = contentRef.current.offsetHeight;
106
+ contentWrapperRef.current.style.height = `${contentHeight}px`;
107
+ }
108
+ }
109
+ }, [defaultActive, controlledExpanded]);
61
110
 
62
111
  useEffect(() => {
63
112
 
@@ -88,6 +137,7 @@ const AccordionItem = (props: AccordionItemProps) => {
88
137
 
89
138
  }, [heightObserver]);
90
139
 
140
+
91
141
  return (
92
142
  <>
93
143
 
@@ -97,36 +147,72 @@ const AccordionItem = (props: AccordionItemProps) => {
97
147
  className={combinedCls(
98
148
  'custom-accordion-item',
99
149
  clsWrite(itemClassName, 'accordion-item'),
100
- activedClassName
150
+ isExpanded ? ' active' : ''
101
151
  )}
102
- onClick={triggerType === 'click' ? onToggleEv : () => {}}
103
- onMouseOver={triggerType === 'click' ? () => {} : onToggleEv}
104
- onTransitionEnd={typeof onTransitionEnd === 'function' ? onTransitionEnd : () => {}}
105
- aria-expanded={defaultActive ? 'true' : 'false'}
152
+ onClick={arrowOnly ? undefined : handleToggle}
153
+ onTransitionEnd={typeof onTransitionEnd === 'function' ? onTransitionEnd : undefined}
154
+ aria-expanded={isExpanded ? 'true' : 'false'}
106
155
  style={typeof itemStyle !== 'undefined' ? itemStyle : {}}
156
+ >
157
+
158
+ <div
159
+ className={combinedCls(
160
+ 'custom-accordion-header',
161
+ clsWrite(itemHeaderClassName, 'accordion-header position-relative')
162
+ )}
163
+ role="presentation"
107
164
  >
108
165
 
109
- <div className={combinedCls(
110
- 'custom-accordion-header',
111
- clsWrite(itemHeaderClassName, 'accordion-header position-relative')
112
- )} role="presentation">
113
- <button tabIndex={-1} className={combinedCls(
114
- 'custom-accordion-trigger',
115
- clsWrite(itemTriggerClassName, 'accordion-button'),
116
- activedClassName === '' ? 'collapsed' : 'active'
117
- )} type="button">
118
- {title}
119
- </button>
120
-
121
- {itemTriggerIcon}
166
+ {arrowOnly ? (
167
+ <div
168
+ ref={triggerRef as React.RefObject<HTMLDivElement>}
169
+ tabIndex={-1}
170
+ className={combinedCls(
171
+ 'custom-accordion-trigger',
172
+ clsWrite(itemTriggerClassName, 'accordion-button'),
173
+ isExpanded ? 'active' : 'collapsed'
174
+ )}
175
+ >
176
+ {title}
177
+ </div>
178
+ ) : (
179
+ <button
180
+ ref={triggerRef as React.RefObject<HTMLButtonElement>}
181
+ tabIndex={-1}
182
+ className={combinedCls(
183
+ 'custom-accordion-trigger',
184
+ clsWrite(itemTriggerClassName, 'accordion-button'),
185
+ isExpanded ? 'active' : 'collapsed'
186
+ )}
187
+ type="button"
188
+ >
189
+ {title}
190
+ </button>
191
+ )}
192
+
193
+
194
+
195
+ <span
196
+ ref={iconRef}
197
+ onClick={!arrowOnly ? undefined : handleToggle}
198
+ className="custom-accordion-trigger__icon"
199
+ style={!arrowOnly ? {pointerEvents: 'none'} : {cursor: 'pointer'}}
200
+ >
201
+ {itemTriggerIcon}
202
+ </span>
203
+
204
+
122
205
  </div>
123
- <div ref={contentWrapperRef} className={combinedCls(
124
- 'custom-accordion-content__wrapper w-100',
125
- clsWrite(itemContentWrapperClassName, 'accordion-collapse')
126
- )}
127
- role="tabpanel" style={{
128
- height: defaultActive ? 'auto' : '0px',
129
- overflow: 'hidden' // “overflow” affects the width, so add `w-100` to `custom-accordion-content__wrapper`
206
+ <div
207
+ ref={contentWrapperRef}
208
+ className={combinedCls(
209
+ 'custom-accordion-content__wrapper w-100',
210
+ clsWrite(itemContentWrapperClassName, 'accordion-collapse')
211
+ )}
212
+ role="tabpanel"
213
+ style={{
214
+ height: '0',
215
+ overflow: 'hidden' // "overflow" affects the width, so add `w-100` to `custom-accordion-content__wrapper`
130
216
  }}>
131
217
  <div className={combinedCls(
132
218
  'custom-accordion-content',
@@ -136,10 +222,8 @@ const AccordionItem = (props: AccordionItemProps) => {
136
222
  </div>
137
223
  </div>
138
224
  </div>
139
-
140
225
  </>
141
226
  )
142
-
143
227
  }
144
228
 
145
229
  export default AccordionItem;
@@ -8,6 +8,7 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
8
8
  import useBoundedDrag from 'funda-utils/dist/cjs/useBoundedDrag';
9
9
 
10
10
 
11
+
11
12
  export interface ListItem {
12
13
  id: number;
13
14
  parentId?: number;
@@ -39,6 +40,7 @@ export interface DragDropListProps {
39
40
  doubleIndent?: boolean;
40
41
  alternateCollapse?: boolean;
41
42
  arrow?: React.ReactNode;
43
+ renderOption?: (item: ListItem, dragHandleClassName: string, index: number) => React.ReactNode;
42
44
  onUpdate?: (items: ListItem[], curId: number) => void;
43
45
  }
44
46
 
@@ -71,6 +73,7 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
71
73
  doubleIndent,
72
74
  alternateCollapse,
73
75
  arrow = <><svg viewBox="0 0 22 22" width="8px"><path d="m345.44 248.29l-194.29 194.28c-12.359 12.365-32.397 12.365-44.75 0-12.354-12.354-12.354-32.391 0-44.744l171.91-171.91-171.91-171.9c-12.354-12.359-12.354-32.394 0-44.748 12.354-12.359 32.391-12.359 44.75 0l194.29 194.28c6.177 6.18 9.262 14.271 9.262 22.366 0 8.099-3.091 16.196-9.267 22.373" transform="matrix(.03541-.00013.00013.03541 2.98 3.02)" fill="#a5a5a5" /></svg></>,
76
+ renderOption,
74
77
  onUpdate,
75
78
  ...attributes
76
79
  } = props;
@@ -83,8 +86,6 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
83
86
  const [items, setItems] = useState<ListItem[]>([]);
84
87
  const [editingItem, setEditingItem] = useState<number | null>(null);
85
88
 
86
- const dragHandle = useRef<HTMLSpanElement | null>(null);
87
-
88
89
 
89
90
  // Edit
90
91
  const [editValue, setEditValue] = useState<Record<string, string | number>>({});
@@ -433,55 +434,64 @@ const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) =>
433
434
  onDoubleClick={() => handleDoubleClick(item)}
434
435
  >
435
436
  <div className={`${prefix}-draggable-list__itemcontent`}>
437
+ {renderOption ? (
438
+ renderOption(
439
+ item,
440
+ `${prefix}-draggable-list__handle`,
441
+ index
442
+ )
443
+ ) : (
444
+ <>
445
+ {/** DRAG HANDLE */}
446
+ {/* 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={{
448
+ __html: `${handleIcon}`
449
+ }}></span> : null}
450
+ {/** /DRAG HANDLE */}
436
451
 
437
- {/** DRAG HANDLE */}
438
- {/* Fix the problem that mobile terminals cannot be touched, DO NOT USE "<svg>" */}
439
- {draggable && !handleHide ? <span ref={dragHandle} className={`${prefix}-draggable-list__handle ${handlePos ?? 'left'}`} draggable={dragMode === 'handle'} dangerouslySetInnerHTML={{
440
- __html: `${handleIcon}`
441
- }}></span> : null}
442
- {/** /DRAG HANDLE */}
452
+ {editingItem === item.id ? (
453
+ renderEditForm(item)
454
+ ) : (
455
+ <div className={`${prefix}-draggable-list__itemcontent-inner`}>
443
456
 
444
- {editingItem === item.id ? (
445
- renderEditForm(item)
446
- ) : (
447
- <div className={`${prefix}-draggable-list__itemcontent-inner`}>
457
+ <div className={`${prefix}-draggable-list__itemlabel`}>
448
458
 
449
- <div className={`${prefix}-draggable-list__itemlabel`}>
450
459
 
460
+ {/** LABEL */}
451
461
 
452
- {/** LABEL */}
462
+ <span dangerouslySetInnerHTML={{
463
+ __html: `${getIndentStr(item)}${typeof item.listItemLabel === 'undefined' ? item.label : item.listItemLabel}`
464
+ }} />
465
+ {/** /LABEL */}
453
466
 
454
- <span dangerouslySetInnerHTML={{
455
- __html: `${getIndentStr(item)}${typeof item.listItemLabel === 'undefined' ? item.label : item.listItemLabel}`
456
- }} />
457
- {/** /LABEL */}
458
467
 
459
468
 
460
469
 
470
+ {/** COLLOPSE */}
471
+ {alternateCollapse && hasChildItems && (
472
+ <span
473
+ className={`${prefix}-draggable-list__collapse-arrow`}
474
+ onClick={(e) => handleCollapse(item.id, e)}
475
+ >
476
+ {arrow || (isCollapsed ? '▶' : '▼')}
477
+ </span>
478
+ )}
479
+ {/** /COLLOPSE */}
461
480
 
462
- {/** COLLOPSE */}
463
- {alternateCollapse && hasChildItems && (
464
- <span
465
- className={`${prefix}-draggable-list__collapse-arrow`}
466
- onClick={(e) => handleCollapse(item.id, e)}
467
- >
468
- {arrow || (isCollapsed ? '▶' : '▼')}
469
- </span>
470
- )}
471
- {/** /COLLOPSE */}
481
+ </div>
472
482
 
473
- </div>
474
483
 
484
+ {/** EXTENDS */}
485
+ {item.appendControl ? <>
486
+ <div className={`${prefix}-draggable-list__itemext`} id={`${prefix}-draggable-list__itemext-${item.value}`}>
487
+ {item.appendControl}
488
+ </div>
489
+ </> : null}
490
+ {/** /EXTENDS */}
475
491
 
476
- {/** EXTENDS */}
477
- {item.appendControl ? <>
478
- <div className={`${prefix}-draggable-list__itemext`} id={`${prefix}-draggable-list__itemext-${item.value}`}>
479
- {item.appendControl}
480
492
  </div>
481
- </> : null}
482
- {/** /EXTENDS */}
483
-
484
- </div>
493
+ )}
494
+ </>
485
495
  )}
486
496
  </div>
487
497
  </li>