funda-ui 4.5.666 → 4.5.671
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/ColorPicker/index.js +3 -1
- package/Date/index.js +14 -1
- package/DragDropList/index.css +188 -0
- package/DragDropList/index.d.ts +44 -0
- package/DragDropList/index.js +1587 -0
- package/Input/index.d.ts +2 -0
- package/Input/index.js +14 -1
- package/LICENSE +21 -0
- package/MultipleSelect/index.css +237 -144
- package/MultipleSelect/index.d.ts +24 -10
- package/MultipleSelect/index.js +2240 -1225
- package/README.md +3 -1
- package/RangeSlider/index.js +14 -1
- package/Textarea/index.d.ts +2 -0
- package/Textarea/index.js +14 -1
- package/Tree/index.d.ts +1 -0
- package/Tree/index.js +29 -0
- package/Utils/useBoundedDrag.d.ts +125 -0
- package/Utils/useBoundedDrag.js +380 -0
- package/Utils/useDragDropPosition.d.ts +169 -0
- package/Utils/useDragDropPosition.js +456 -0
- package/Utils/useIsMobile.d.ts +2 -0
- package/Utils/useIsMobile.js +168 -0
- package/all.d.ts +1 -0
- package/all.js +1 -1
- package/lib/cjs/ColorPicker/index.js +3 -1
- package/lib/cjs/Date/index.js +14 -1
- package/lib/cjs/DragDropList/index.d.ts +44 -0
- package/lib/cjs/DragDropList/index.js +1587 -0
- package/lib/cjs/Input/index.d.ts +2 -0
- package/lib/cjs/Input/index.js +14 -1
- package/lib/cjs/MultipleSelect/index.d.ts +24 -10
- package/lib/cjs/MultipleSelect/index.js +2240 -1225
- package/lib/cjs/RangeSlider/index.js +14 -1
- package/lib/cjs/Textarea/index.d.ts +2 -0
- package/lib/cjs/Textarea/index.js +14 -1
- package/lib/cjs/Tree/index.d.ts +1 -0
- package/lib/cjs/Tree/index.js +29 -0
- package/lib/cjs/Utils/useBoundedDrag.d.ts +125 -0
- package/lib/cjs/Utils/useBoundedDrag.js +380 -0
- package/lib/cjs/Utils/useDragDropPosition.d.ts +169 -0
- package/lib/cjs/Utils/useDragDropPosition.js +456 -0
- package/lib/cjs/Utils/useIsMobile.d.ts +2 -0
- package/lib/cjs/Utils/useIsMobile.js +168 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -1
- package/lib/css/DragDropList/index.css +188 -0
- package/lib/css/MultipleSelect/index.css +237 -144
- package/lib/esm/ColorPicker/index.tsx +53 -49
- package/lib/esm/DragDropList/index.scss +245 -0
- package/lib/esm/DragDropList/index.tsx +494 -0
- package/lib/esm/Input/index.tsx +17 -3
- package/lib/esm/MultipleSelect/index.scss +288 -183
- package/lib/esm/MultipleSelect/index.tsx +305 -166
- package/lib/esm/MultipleSelect/utils/func.ts +21 -1
- package/lib/esm/Tabs/Tabs.tsx +1 -1
- package/lib/esm/Textarea/index.tsx +18 -1
- package/lib/esm/Tree/TreeList.tsx +32 -0
- package/lib/esm/Tree/index.tsx +3 -0
- package/lib/esm/Utils/hooks/useBoundedDrag.tsx +301 -0
- package/lib/esm/Utils/hooks/useDragDropPosition.tsx +420 -0
- package/lib/esm/Utils/hooks/useIsMobile.tsx +56 -0
- package/lib/esm/index.js +1 -0
- package/package.json +1 -1
- package/lib/esm/MultipleSelect/ItemList.tsx +0 -323
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
convertTree,
|
|
5
|
+
addTreeDepth,
|
|
6
|
+
} from 'funda-utils/dist/cjs/tree';
|
|
7
|
+
import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
8
|
+
import useBoundedDrag from 'funda-utils/dist/cjs/useBoundedDrag';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export interface ListItem {
|
|
12
|
+
id: number;
|
|
13
|
+
parentId?: number;
|
|
14
|
+
label: string;
|
|
15
|
+
listItemLabel: string;
|
|
16
|
+
value: string;
|
|
17
|
+
queryString: string;
|
|
18
|
+
depth?: number;
|
|
19
|
+
children?: ListItem[];
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
appendControl?: React.ReactNode;
|
|
22
|
+
[key: string]: any;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export interface DragDropListProps {
|
|
27
|
+
wrapperClassName?: string;
|
|
28
|
+
prefix?: string;
|
|
29
|
+
data?: ListItem[];
|
|
30
|
+
draggable?: boolean;
|
|
31
|
+
handleHide?: boolean;
|
|
32
|
+
handleIcon?: string;
|
|
33
|
+
handlePos?: 'left' | 'right';
|
|
34
|
+
dragMode?: 'handle' | 'block';
|
|
35
|
+
editable?: boolean;
|
|
36
|
+
itemStyle?: React.CSSProperties;
|
|
37
|
+
hierarchical?: boolean;
|
|
38
|
+
indentation?: string;
|
|
39
|
+
doubleIndent?: boolean;
|
|
40
|
+
alternateCollapse?: boolean;
|
|
41
|
+
arrow?: React.ReactNode;
|
|
42
|
+
onUpdate?: (items: ListItem[], curId: number) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface EditValue {
|
|
46
|
+
[propName: string]: string | number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TouchOffset {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
}
|
|
53
|
+
export interface OptionConfig {
|
|
54
|
+
[propName: string]: string | number | boolean | Function | any[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const DragDropList = forwardRef((props: DragDropListProps, externalRef: any) => {
|
|
58
|
+
const {
|
|
59
|
+
wrapperClassName,
|
|
60
|
+
prefix = 'custom',
|
|
61
|
+
data,
|
|
62
|
+
draggable = true,
|
|
63
|
+
handleHide = false,
|
|
64
|
+
handleIcon = '☰',
|
|
65
|
+
handlePos = 'left',
|
|
66
|
+
dragMode = 'handle',
|
|
67
|
+
editable = false,
|
|
68
|
+
itemStyle,
|
|
69
|
+
hierarchical = true,
|
|
70
|
+
indentation,
|
|
71
|
+
doubleIndent,
|
|
72
|
+
alternateCollapse,
|
|
73
|
+
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></>,
|
|
74
|
+
onUpdate,
|
|
75
|
+
...attributes
|
|
76
|
+
} = props;
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
const INDENT_PLACEHOLDER = doubleIndent ? ` ` : ` `;
|
|
80
|
+
const INDENT_LAST_PLACEHOLDER = `${typeof indentation !== 'undefined' && indentation !== '' ? `${indentation} ` : ''}`;
|
|
81
|
+
|
|
82
|
+
const rootRef = useRef<any>(null);
|
|
83
|
+
const [items, setItems] = useState<ListItem[]>([]);
|
|
84
|
+
const [editingItem, setEditingItem] = useState<number | null>(null);
|
|
85
|
+
|
|
86
|
+
const dragHandle = useRef<HTMLSpanElement | null>(null);
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
// Edit
|
|
90
|
+
const [editValue, setEditValue] = useState<Record<string, string | number>>({});
|
|
91
|
+
|
|
92
|
+
// Collapse/Expand
|
|
93
|
+
const [collapsedItems, setCollapsedItems] = useState<Set<number>>(new Set());
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
// Get editable field list
|
|
97
|
+
const getEditableFields = (item: ListItem): string[] => {
|
|
98
|
+
// Exclude fields that don't need to be edited
|
|
99
|
+
const excludeFields = ['id', 'parentId', 'depth', 'children', 'disabled', 'appendControl', 'parentItem'];
|
|
100
|
+
return Object.keys(item).filter(key => !excludeFields.includes(key));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// ================================================================
|
|
105
|
+
// General
|
|
106
|
+
// ================================================================
|
|
107
|
+
const deepCloneWithReactNode = (obj: any): any => {
|
|
108
|
+
if (obj === null || typeof obj !== 'object') {
|
|
109
|
+
return obj;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Handle array
|
|
113
|
+
if (Array.isArray(obj)) {
|
|
114
|
+
return obj.map(item => deepCloneWithReactNode(item));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle object
|
|
118
|
+
const clonedObj: any = {};
|
|
119
|
+
for (const key in obj) {
|
|
120
|
+
if (key === 'appendControl') {
|
|
121
|
+
clonedObj[key] = obj[key];
|
|
122
|
+
} else {
|
|
123
|
+
clonedObj[key] = deepCloneWithReactNode(obj[key]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return clonedObj;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
const getItemWithChildrenIndices = (items: ListItem[], startIndex: number): number[] => {
|
|
131
|
+
const indices = [startIndex];
|
|
132
|
+
const startItem = items[startIndex];
|
|
133
|
+
const startDepth = startItem.depth || 0;
|
|
134
|
+
|
|
135
|
+
// Check if subsequent items are child items
|
|
136
|
+
for (let i = startIndex + 1; i < items.length; i++) {
|
|
137
|
+
const currentItem = items[i];
|
|
138
|
+
const currentDepth = currentItem.depth || 0;
|
|
139
|
+
if (currentDepth > startDepth) {
|
|
140
|
+
indices.push(i);
|
|
141
|
+
} else {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return indices;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
const getIndentStr = (item: ListItem): number | string => {
|
|
151
|
+
|
|
152
|
+
// Add indent placeholder
|
|
153
|
+
let indent = '';
|
|
154
|
+
const depthData = item.depth;
|
|
155
|
+
if (depthData) {
|
|
156
|
+
Array(depthData).fill(0).forEach((k, i) => {
|
|
157
|
+
indent += INDENT_PLACEHOLDER;
|
|
158
|
+
if (i === depthData - 1) {
|
|
159
|
+
indent += INDENT_LAST_PLACEHOLDER;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return indent;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
// ================================================================
|
|
170
|
+
// Collapse/Expand
|
|
171
|
+
// ================================================================
|
|
172
|
+
// Add collapse/expand handler function
|
|
173
|
+
const handleCollapse = (itemId: number, e: React.MouseEvent) => {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
e.stopPropagation();
|
|
176
|
+
|
|
177
|
+
setCollapsedItems(prev => {
|
|
178
|
+
const newCollapsed = new Set(prev);
|
|
179
|
+
if (newCollapsed.has(itemId)) {
|
|
180
|
+
newCollapsed.delete(itemId);
|
|
181
|
+
} else {
|
|
182
|
+
newCollapsed.add(itemId);
|
|
183
|
+
}
|
|
184
|
+
return newCollapsed;
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Helper function to determine if an item should be displayed
|
|
189
|
+
const shouldShowItem = (item: ListItem): boolean => {
|
|
190
|
+
if (!alternateCollapse) return true;
|
|
191
|
+
|
|
192
|
+
let currentId = item.parentId;
|
|
193
|
+
while (currentId) {
|
|
194
|
+
if (collapsedItems.has(currentId)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
const parentItem = items.find(i => i.id === currentId);
|
|
198
|
+
currentId = parentItem?.parentId;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const hasChildren = (itemId: number): boolean => {
|
|
204
|
+
return items.some(item => item.parentId === itemId);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
// ================================================================
|
|
211
|
+
// Drag & Drop Handlers (Desktop & Touch)
|
|
212
|
+
// ================================================================
|
|
213
|
+
const { isDragging, dragHandlers } = useBoundedDrag({
|
|
214
|
+
dragMode,
|
|
215
|
+
boundarySelector: `.${prefix}-draggable-list`,
|
|
216
|
+
itemSelector: `.${prefix}-draggable-list__item`,
|
|
217
|
+
dragHandleSelector: `.${prefix}-draggable-list__handle`,
|
|
218
|
+
onDragStart: (index: number) => {
|
|
219
|
+
// Additional drag start logic if needed
|
|
220
|
+
},
|
|
221
|
+
onDragOver: (dragIndex: number | null, dropIndex: number | null) => {
|
|
222
|
+
// Additional drag over logic if needed
|
|
223
|
+
},
|
|
224
|
+
onDragEnd: (dragIndex: number | null, dropIndex: number | null) => {
|
|
225
|
+
if (dragIndex !== null && dropIndex !== null && dragIndex !== dropIndex) {
|
|
226
|
+
// Handle item movement
|
|
227
|
+
const newItems = deepCloneWithReactNode(items);
|
|
228
|
+
const itemsToMove = getItemWithChildrenIndices(newItems, dragIndex);
|
|
229
|
+
const itemsBeingMoved = itemsToMove.map(index => newItems[index]);
|
|
230
|
+
|
|
231
|
+
// ... rest of your existing drag end logic ...
|
|
232
|
+
|
|
233
|
+
const _targetId = newItems[dragIndex]?.id;
|
|
234
|
+
|
|
235
|
+
// Calculate depth difference
|
|
236
|
+
const draggedDepth = newItems[dragIndex]?.depth || 0;
|
|
237
|
+
const dropDepth = newItems[dropIndex]?.depth || 0;
|
|
238
|
+
const depthDiff = dropDepth - draggedDepth;
|
|
239
|
+
|
|
240
|
+
// Adjust depth for all moving items
|
|
241
|
+
itemsBeingMoved.forEach(item => {
|
|
242
|
+
if (item.depth !== undefined) {
|
|
243
|
+
item.depth += depthDiff;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Remove all items from their original location (from back to front to keep indexing correct)
|
|
248
|
+
itemsToMove.reverse().forEach(index => {
|
|
249
|
+
newItems.splice(index, 1);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Calculate new insert position
|
|
253
|
+
let insertIndex = dropIndex;
|
|
254
|
+
if (dropIndex > dragIndex) {
|
|
255
|
+
insertIndex -= itemsToMove.length;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Insert all items
|
|
259
|
+
newItems.splice(insertIndex, 0, ...itemsBeingMoved);
|
|
260
|
+
|
|
261
|
+
// Rebuild tree structure
|
|
262
|
+
const tree = hierarchical ? convertTree(newItems, '', 'id', 'parentId') : newItems;
|
|
263
|
+
const updatedItems = hierarchical ? addTreeDepth(tree) : tree;
|
|
264
|
+
|
|
265
|
+
setItems(updatedItems);
|
|
266
|
+
onUpdate?.(updatedItems, _targetId);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
// ================================================================
|
|
274
|
+
// Editable
|
|
275
|
+
// ================================================================
|
|
276
|
+
|
|
277
|
+
const handleDoubleClick = (item: ListItem) => {
|
|
278
|
+
if (!editable) return;
|
|
279
|
+
|
|
280
|
+
setEditingItem(item.id);
|
|
281
|
+
// Only editable fields are copied
|
|
282
|
+
const editableFields = getEditableFields(item);
|
|
283
|
+
const editableValues = editableFields.reduce((acc, field) => ({
|
|
284
|
+
...acc,
|
|
285
|
+
[field]: item[field as keyof ListItem]
|
|
286
|
+
}), {});
|
|
287
|
+
|
|
288
|
+
setEditValue(editableValues);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const handleEditCancel = () => {
|
|
292
|
+
setEditingItem(null);
|
|
293
|
+
setEditValue({});
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const handleEditSave = (itemId: number) => {
|
|
297
|
+
const newItems: ListItem[] = items.map(item => {
|
|
298
|
+
if (item.id === itemId) {
|
|
299
|
+
return {
|
|
300
|
+
...item,
|
|
301
|
+
...editValue
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return item;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
setItems(newItems);
|
|
308
|
+
onUpdate?.(newItems, itemId);
|
|
309
|
+
setEditingItem(null);
|
|
310
|
+
setEditValue({});
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const handleInputChange = (field: string, value: any) => {
|
|
314
|
+
setEditValue(prev => ({
|
|
315
|
+
...prev,
|
|
316
|
+
[field]: value
|
|
317
|
+
}));
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
const handleKeyDown = (e: any, itemId: number) => {
|
|
322
|
+
if (e.key === 'Enter') {
|
|
323
|
+
handleEditSave(itemId);
|
|
324
|
+
} else if (e.key === 'Escape') {
|
|
325
|
+
handleEditCancel();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
const renderEditForm = (item: ListItem) => {
|
|
332
|
+
const editableFields = getEditableFields(item);
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div className={`${prefix}-draggable-list__edit-form`}>
|
|
336
|
+
{editableFields.map((field) => (
|
|
337
|
+
<div key={field} className={`${prefix}-draggable-list__edit-field`}>
|
|
338
|
+
<label>{field}:</label>
|
|
339
|
+
<input
|
|
340
|
+
type="text"
|
|
341
|
+
value={editValue[field] || ''}
|
|
342
|
+
onChange={(e) => handleInputChange(field, e.target.value)}
|
|
343
|
+
onKeyDown={(e) => handleKeyDown(e, item.id)}
|
|
344
|
+
placeholder={field}
|
|
345
|
+
autoFocus={field === editableFields[0]}
|
|
346
|
+
/>
|
|
347
|
+
</div>
|
|
348
|
+
))}
|
|
349
|
+
|
|
350
|
+
<div className={`${prefix}-draggable-list__edit-buttons`}>
|
|
351
|
+
<button onClick={() => handleEditSave(item.id)}>✓</button>
|
|
352
|
+
<button onClick={handleEditCancel}>✕</button>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
|
|
360
|
+
// data init
|
|
361
|
+
//--------------
|
|
362
|
+
if (typeof data !== 'undefined' && Array.isArray(data)) {
|
|
363
|
+
const tree = hierarchical ? convertTree(data, '', 'id', 'parentId') : data;
|
|
364
|
+
const _ORGIN_DATA = hierarchical ? addTreeDepth(tree) : tree;
|
|
365
|
+
setItems(_ORGIN_DATA);
|
|
366
|
+
}
|
|
367
|
+
}, [data]);
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<ul
|
|
373
|
+
{...attributes}
|
|
374
|
+
ref={(node) => {
|
|
375
|
+
rootRef.current = node;
|
|
376
|
+
if (typeof externalRef === 'function') {
|
|
377
|
+
externalRef(node);
|
|
378
|
+
} else if (externalRef) {
|
|
379
|
+
externalRef.current = node;
|
|
380
|
+
}
|
|
381
|
+
}}
|
|
382
|
+
className={combinedCls(
|
|
383
|
+
`${prefix}-draggable-list`,
|
|
384
|
+
clsWrite(wrapperClassName, 'mb-3'),
|
|
385
|
+
clsWrite(dragMode, 'handle'),
|
|
386
|
+
`handle-pos-${handlePos ?? 'left'}`,
|
|
387
|
+
{
|
|
388
|
+
'draggable': draggable,
|
|
389
|
+
'icon-hide': handleHide,
|
|
390
|
+
'alternate-collapse': alternateCollapse
|
|
391
|
+
}
|
|
392
|
+
)}
|
|
393
|
+
>
|
|
394
|
+
{items.map((item: ListItem, index: number) => {
|
|
395
|
+
|
|
396
|
+
// If the item should be hidden, the rendering is skipped
|
|
397
|
+
if (!shouldShowItem(item)) return null;
|
|
398
|
+
|
|
399
|
+
// collapse
|
|
400
|
+
const hasChildItems = hasChildren(item.id);
|
|
401
|
+
const isCollapsed = collapsedItems.has(item.id);
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
return <li
|
|
405
|
+
key={item.id}
|
|
406
|
+
data-index={index}
|
|
407
|
+
data-id={item.id}
|
|
408
|
+
data-parent-id={item.parentId}
|
|
409
|
+
data-value={item.value}
|
|
410
|
+
data-label={item.label}
|
|
411
|
+
data-listitemlabel={item.listItemLabel}
|
|
412
|
+
className={combinedCls(
|
|
413
|
+
`${prefix}-draggable-list__item`,
|
|
414
|
+
clsWrite(dragMode, 'handle'),
|
|
415
|
+
{
|
|
416
|
+
'disabled': item.disabled,
|
|
417
|
+
'draggable': draggable,
|
|
418
|
+
'editing': editingItem === item.id,
|
|
419
|
+
|
|
420
|
+
// collapse
|
|
421
|
+
'has-children': hasChildItems,
|
|
422
|
+
'collapsed': isCollapsed
|
|
423
|
+
}
|
|
424
|
+
)}
|
|
425
|
+
draggable={!draggable ? undefined : editingItem !== item.id && "true"}
|
|
426
|
+
onDragStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
|
|
427
|
+
onDragOver={!draggable ? undefined : dragHandlers.handleDragOver}
|
|
428
|
+
onDragEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
|
|
429
|
+
onTouchStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
|
|
430
|
+
onTouchMove={!draggable ? undefined : dragHandlers.handleDragOver}
|
|
431
|
+
onTouchEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
|
|
432
|
+
style={itemStyle}
|
|
433
|
+
onDoubleClick={() => handleDoubleClick(item)}
|
|
434
|
+
>
|
|
435
|
+
<div className={`${prefix}-draggable-list__itemcontent`}>
|
|
436
|
+
|
|
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 */}
|
|
443
|
+
|
|
444
|
+
{editingItem === item.id ? (
|
|
445
|
+
renderEditForm(item)
|
|
446
|
+
) : (
|
|
447
|
+
<div className={`${prefix}-draggable-list__itemcontent-inner`}>
|
|
448
|
+
|
|
449
|
+
<div className={`${prefix}-draggable-list__itemlabel`}>
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
{/** LABEL */}
|
|
453
|
+
|
|
454
|
+
<span dangerouslySetInnerHTML={{
|
|
455
|
+
__html: `${getIndentStr(item)}${typeof item.listItemLabel === 'undefined' ? item.label : item.listItemLabel}`
|
|
456
|
+
}} />
|
|
457
|
+
{/** /LABEL */}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
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 */}
|
|
472
|
+
|
|
473
|
+
</div>
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
{/** EXTENDS */}
|
|
477
|
+
{item.appendControl ? <>
|
|
478
|
+
<div className={`${prefix}-draggable-list__itemext`} id={`${prefix}-draggable-list__itemext-${item.value}`}>
|
|
479
|
+
{item.appendControl}
|
|
480
|
+
</div>
|
|
481
|
+
</> : null}
|
|
482
|
+
{/** /EXTENDS */}
|
|
483
|
+
|
|
484
|
+
</div>
|
|
485
|
+
)}
|
|
486
|
+
</div>
|
|
487
|
+
</li>
|
|
488
|
+
})}
|
|
489
|
+
</ul>
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
export default DragDropList;
|
package/lib/esm/Input/index.tsx
CHANGED
|
@@ -6,8 +6,6 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
|
6
6
|
import { actualPropertyValue, getTextTop } from 'funda-utils/dist/cjs/inputsCalculation';
|
|
7
7
|
import useDebounce from 'funda-utils/dist/cjs/useDebounce';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
9
|
export type InputProps = {
|
|
12
10
|
contentRef?: React.ForwardedRef<any>;
|
|
13
11
|
wrapperClassName?: string;
|
|
@@ -61,6 +59,8 @@ export type InputProps = {
|
|
|
61
59
|
onBlur?: (e: any, param: any, el: any) => void;
|
|
62
60
|
onFocus?: (e: any, param: any, el: any) => void;
|
|
63
61
|
onPressEnter?: (e: any, el: any) => void;
|
|
62
|
+
onKeyDown?: (e: any, el: any) => void;
|
|
63
|
+
onKeyUp?: (e: any, el: any) => void;
|
|
64
64
|
|
|
65
65
|
};
|
|
66
66
|
|
|
@@ -117,6 +117,8 @@ const Input = forwardRef((props: InputProps, externalRef: any) => {
|
|
|
117
117
|
onBlur,
|
|
118
118
|
onFocus,
|
|
119
119
|
onPressEnter,
|
|
120
|
+
onKeyDown,
|
|
121
|
+
onKeyUp,
|
|
120
122
|
...attributes
|
|
121
123
|
} = props;
|
|
122
124
|
|
|
@@ -312,7 +314,12 @@ const Input = forwardRef((props: InputProps, externalRef: any) => {
|
|
|
312
314
|
set: (value: string, cb?: any) => {
|
|
313
315
|
setChangedVal(`${value}`);
|
|
314
316
|
cb?.();
|
|
315
|
-
}
|
|
317
|
+
},
|
|
318
|
+
aiPredictReset: () => {
|
|
319
|
+
setTimeout(() => { // Avoid conflicts with other asynchronous states, resulting in invalid clearing
|
|
320
|
+
setCurrentSuggestion('');
|
|
321
|
+
}, 0);
|
|
322
|
+
},
|
|
316
323
|
}),
|
|
317
324
|
[contentRef],
|
|
318
325
|
);
|
|
@@ -383,6 +390,9 @@ const Input = forwardRef((props: InputProps, externalRef: any) => {
|
|
|
383
390
|
|
|
384
391
|
|
|
385
392
|
function handleKeyPressed(event: KeyboardEvent<HTMLInputElement>) {
|
|
393
|
+
|
|
394
|
+
onKeyDown?.(event, valRef.current);
|
|
395
|
+
|
|
386
396
|
if (typeof (onKeyPressedCallback) === 'function') {
|
|
387
397
|
const newData: any = onKeyPressedCallback(event, valRef.current);
|
|
388
398
|
if (newData) setChangedVal(newData); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
|
|
@@ -440,6 +450,9 @@ const Input = forwardRef((props: InputProps, externalRef: any) => {
|
|
|
440
450
|
|
|
441
451
|
}
|
|
442
452
|
|
|
453
|
+
function handleKeyUp(event: KeyboardEvent<HTMLInputElement>) {
|
|
454
|
+
onKeyUp?.(event, valRef.current);
|
|
455
|
+
}
|
|
443
456
|
|
|
444
457
|
useEffect(() => {
|
|
445
458
|
|
|
@@ -544,6 +557,7 @@ const Input = forwardRef((props: InputProps, externalRef: any) => {
|
|
|
544
557
|
|
|
545
558
|
}}
|
|
546
559
|
onKeyDown={handleKeyPressed}
|
|
560
|
+
onKeyUp={handleKeyUp}
|
|
547
561
|
onCompositionStart={handleComposition}
|
|
548
562
|
onCompositionUpdate={handleComposition}
|
|
549
563
|
onCompositionEnd={handleComposition}
|