funda-ui 4.5.680 → 4.5.682

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 (35) hide show
  1. package/DragDropList/index.css +188 -0
  2. package/DragDropList/index.d.ts +43 -0
  3. package/DragDropList/index.js +1589 -0
  4. package/MultipleSelect/index.css +237 -144
  5. package/MultipleSelect/index.d.ts +23 -10
  6. package/MultipleSelect/index.js +2242 -1225
  7. package/README.md +3 -1
  8. package/Utils/useBoundedDrag.d.ts +127 -0
  9. package/Utils/useBoundedDrag.js +382 -0
  10. package/Utils/useDragDropPosition.d.ts +169 -0
  11. package/Utils/useDragDropPosition.js +456 -0
  12. package/all.d.ts +1 -0
  13. package/all.js +1 -0
  14. package/lib/cjs/DragDropList/index.d.ts +43 -0
  15. package/lib/cjs/DragDropList/index.js +1589 -0
  16. package/lib/cjs/MultipleSelect/index.d.ts +23 -10
  17. package/lib/cjs/MultipleSelect/index.js +2242 -1225
  18. package/lib/cjs/Utils/useBoundedDrag.d.ts +127 -0
  19. package/lib/cjs/Utils/useBoundedDrag.js +382 -0
  20. package/lib/cjs/Utils/useDragDropPosition.d.ts +169 -0
  21. package/lib/cjs/Utils/useDragDropPosition.js +456 -0
  22. package/lib/cjs/index.d.ts +1 -0
  23. package/lib/cjs/index.js +1 -0
  24. package/lib/css/DragDropList/index.css +188 -0
  25. package/lib/css/MultipleSelect/index.css +237 -144
  26. package/lib/esm/DragDropList/index.scss +245 -0
  27. package/lib/esm/DragDropList/index.tsx +493 -0
  28. package/lib/esm/MultipleSelect/index.scss +288 -183
  29. package/lib/esm/MultipleSelect/index.tsx +304 -166
  30. package/lib/esm/MultipleSelect/utils/func.ts +21 -1
  31. package/lib/esm/Utils/hooks/useBoundedDrag.tsx +303 -0
  32. package/lib/esm/Utils/hooks/useDragDropPosition.tsx +420 -0
  33. package/lib/esm/index.js +1 -0
  34. package/package.json +1 -1
  35. package/lib/esm/MultipleSelect/ItemList.tsx +0 -323
@@ -34,8 +34,28 @@ export function multiSelControlOptionExist(arr: any[], val: any) {
34
34
  * @returns
35
35
  */
36
36
  export function uniqueArr(arr: any[]) {
37
- return arr.filter((item: any, index: number, self: any[]) => index === self.findIndex((t) => (t.value === item.value)));
37
+ return arr.filter((item: any, index: number, self: any[]) => index === self.findIndex((t) => (t.id == item.id)));
38
38
  }
39
39
 
40
+ /**
41
+ * Remove Duplicate objects from Options
42
+ * @param {Array} arr
43
+ * @returns
44
+ */
45
+ export function uniqueOpt(arr: any[]) {
46
+ return arr.flat().filter((item: any, index: number, self: any[]) => index === self.findIndex((t) => (t.id === item.id)));
47
+ }
40
48
 
41
49
 
50
+ /**
51
+ * Sort JSON arrays according to the order of the numeric arrays
52
+ * @param {Array<Number>|Array<String>} orderArray
53
+ * @param {Array<*>} arr
54
+ * @param {String} field
55
+ * @returns
56
+ */
57
+ export function sortedJsonArray(orderArray: number[] | string[], arr: any[], field: string = 'value') {
58
+ return orderArray.map((orderId: number | string) =>
59
+ arr.find(item => item[field] === orderId)
60
+ );
61
+ }
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Bounded Drag
3
+ *
4
+ * @usage:
5
+
6
+
7
+ const App = () => {
8
+ const [items, setItems] = useState<ListItem[]>([]);
9
+ // ... other states and refs
10
+
11
+ const deepCloneWithReactNode = (obj: any): any => {
12
+ if (obj === null || typeof obj !== 'object') {
13
+ return obj;
14
+ }
15
+
16
+ // Handle array
17
+ if (Array.isArray(obj)) {
18
+ return obj.map(item => deepCloneWithReactNode(item));
19
+ }
20
+
21
+ // Handle object
22
+ const clonedObj: any = {};
23
+ for (const key in obj) {
24
+ if (key === 'appendControl') {
25
+ clonedObj[key] = obj[key];
26
+ } else {
27
+ clonedObj[key] = deepCloneWithReactNode(obj[key]);
28
+ }
29
+ }
30
+ return clonedObj;
31
+ };
32
+
33
+
34
+ const getItemWithChildrenIndices = (items: ListItem[], startIndex: number): number[] => {
35
+ const indices = [startIndex];
36
+ const startItem = items[startIndex];
37
+ const startDepth = startItem.depth || 0;
38
+
39
+ // Check if subsequent items are child items
40
+ for (let i = startIndex + 1; i < items.length; i++) {
41
+ const currentItem = items[i];
42
+ const currentDepth = currentItem.depth || 0;
43
+ if (currentDepth > startDepth) {
44
+ indices.push(i);
45
+ } else {
46
+ break;
47
+ }
48
+ }
49
+
50
+ return indices;
51
+ };
52
+
53
+
54
+ const { isDragging, dragHandlers } = useBoundedDrag({
55
+ dragMode,
56
+ boundarySelector: '.custom-draggable-list',
57
+ itemSelector:'.custom-draggable-list__item',
58
+ dragHandleSelector: '.custom-draggable-list__handle',
59
+ onDragStart: (index: number) => {
60
+ // Additional drag start logic if needed
61
+ },
62
+ onDragOver: (dragIndex: number | null, dropIndex: number | null) => {
63
+ // Additional drag over logic if needed
64
+ },
65
+ onDragEnd: (dragIndex: number | null, dropIndex: number | null) => {
66
+ if (dragIndex !== null && dropIndex !== null && dragIndex !== dropIndex) {
67
+ // Handle item movement
68
+ const newItems = deepCloneWithReactNode(items);
69
+ const itemsToMove = getItemWithChildrenIndices(newItems, dragIndex);
70
+ const itemsBeingMoved = itemsToMove.map(index => newItems[index]);
71
+
72
+ // ... rest of your existing drag end logic ...
73
+
74
+ setItems(updatedItems);
75
+
76
+ }
77
+ }
78
+ });
79
+
80
+ // Update your JSX to use the new handlers
81
+ return (
82
+ <ul className="custom-draggable-list">
83
+ {items.map((item: any, index: number) => (
84
+ <li
85
+ // ... other props
86
+ draggable={!draggable ? undefined : editingItem !== item.id && "true"}
87
+ onDragStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
88
+ onDragOver={!draggable ? undefined : dragHandlers.handleDragOver}
89
+ onDragEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
90
+ onTouchStart={!draggable ? undefined : (e) => dragHandlers.handleDragStart(e, index)}
91
+ onTouchMove={!draggable ? undefined : dragHandlers.handleDragOver}
92
+ onTouchEnd={!draggable ? undefined : dragHandlers.handleDragEnd}
93
+ >
94
+ <li className="custom-draggable-list__item">
95
+ <span className="custom-draggable-list__handle">☰</span>
96
+ <i>content {indec}<i>
97
+ </li>
98
+ </li>
99
+ ))}
100
+ </ul>
101
+ );
102
+ };
103
+
104
+
105
+ */
106
+
107
+ import { useRef, useState } from 'react';
108
+
109
+ export interface TouchOffset {
110
+ x: number;
111
+ y: number;
112
+ }
113
+
114
+ export interface BoundedDragOptions {
115
+ dragMode?: 'handle' | 'block';
116
+ boundarySelector?: string;
117
+ itemSelector?: string;
118
+ dragHandleSelector?: string;
119
+ onDragStart?: (index: number) => void;
120
+ onDragOver?: (dragIndex: number | null, dropIndex: number | null) => void;
121
+ onDragEnd?: (dragIndex: number | null, dropIndex: number | null) => void;
122
+ }
123
+
124
+ export const useBoundedDrag = (options: BoundedDragOptions = {}) => {
125
+ const {
126
+ dragMode = 'handle',
127
+ boundarySelector = '.custom-draggable-list',
128
+ itemSelector = '.custom-draggable-list__item',
129
+ dragHandleSelector = '.custom-draggable-list__handle',
130
+ onDragStart,
131
+ onDragOver,
132
+ onDragEnd
133
+ } = options;
134
+
135
+ const [isDragging, setIsDragging] = useState(false);
136
+ const dragItem = useRef<number | null>(null);
137
+ const dragOverItem = useRef<number | null>(null);
138
+ const dragNode = useRef<HTMLElement | null>(null);
139
+ const touchOffset = useRef<TouchOffset>({ x: 0, y: 0 });
140
+ const currentHoverItem = useRef<HTMLElement | null>(null);
141
+
142
+ const handleDragStart = (e: React.DragEvent | React.TouchEvent, position: number) => {
143
+ const isTouch = 'touches' in e;
144
+ const target = e.target as HTMLElement;
145
+
146
+ // For block mode or handle mode check
147
+ if (dragMode === 'handle') {
148
+ const handle = target.closest(dragHandleSelector);
149
+ if (!handle) {
150
+ if (!isTouch) e.preventDefault();
151
+ return false;
152
+ }
153
+ }
154
+
155
+ // Find the draggable item
156
+ const listItem = target.closest(itemSelector) as HTMLElement;
157
+ if (!listItem) return;
158
+
159
+ // Check boundary
160
+ const boundary = listItem.closest(boundarySelector);
161
+ if (!boundary) return;
162
+
163
+ dragItem.current = position;
164
+ onDragStart?.(position);
165
+
166
+ if (isTouch) {
167
+ e.preventDefault(); // Prevent scrolling
168
+ const touch = (e as React.TouchEvent).touches[0];
169
+ const rect = listItem.getBoundingClientRect();
170
+ const boundaryRect = boundary.getBoundingClientRect();
171
+
172
+ // Calculate offset relative to the boundary
173
+ touchOffset.current = {
174
+ x: touch.clientX - rect.left,
175
+ y: touch.clientY - rect.top
176
+ };
177
+
178
+ // Clone the item for dragging
179
+ dragNode.current = listItem.cloneNode(true) as HTMLElement;
180
+ dragNode.current.classList.add('dragging');
181
+
182
+ // Style the clone
183
+ Object.assign(dragNode.current.style, {
184
+ position: 'fixed',
185
+ width: `${rect.width}px`,
186
+ height: `${rect.height}px`,
187
+ left: `${rect.left}px`,
188
+ top: `${rect.top}px`,
189
+ zIndex: '1000',
190
+ pointerEvents: 'none',
191
+ transform: 'scale(1.05)',
192
+ transition: 'transform 0.1s',
193
+ opacity: '0.9'
194
+ });
195
+
196
+ document.body.appendChild(dragNode.current);
197
+ setIsDragging(true);
198
+ listItem.classList.add('dragging-placeholder');
199
+ } else {
200
+ // ... desktop drag logic remains the same ...
201
+ }
202
+ };
203
+
204
+ const handleDragOver = (e: React.DragEvent | React.TouchEvent) => {
205
+ e.preventDefault();
206
+ const isTouch = 'touches' in e;
207
+
208
+ if (!isTouch) {
209
+ (e as React.DragEvent).dataTransfer.dropEffect = 'move';
210
+ }
211
+
212
+ // Get the current pointer/touch position
213
+ const point = isTouch ?
214
+ (e as React.TouchEvent).touches[0] :
215
+ { clientX: (e as React.DragEvent).clientX, clientY: (e as React.DragEvent).clientY };
216
+
217
+ // Update dragged element position for touch events
218
+ if (isTouch && isDragging && dragNode.current) {
219
+ dragNode.current.style.left = `${point.clientX - touchOffset.current.x}px`;
220
+ dragNode.current.style.top = `${point.clientY - touchOffset.current.y}px`;
221
+ }
222
+
223
+ // Find the element below the pointer/touch
224
+ const elemBelow = document.elementFromPoint(
225
+ point.clientX,
226
+ point.clientY
227
+ );
228
+
229
+ if (!elemBelow) return;
230
+
231
+ // Find the closest list item
232
+ const listItem = elemBelow.closest(itemSelector) as HTMLElement;
233
+ if (!listItem || listItem === currentHoverItem.current) return;
234
+
235
+ // Check boundary
236
+ const boundary = listItem.closest(boundarySelector);
237
+ if (!boundary) return;
238
+
239
+ // Update hover states
240
+ if (currentHoverItem.current) {
241
+ currentHoverItem.current.classList.remove('drag-over', 'drag-over-top', 'drag-over-bottom');
242
+ }
243
+
244
+ currentHoverItem.current = listItem;
245
+ listItem.classList.add('drag-over');
246
+
247
+ // Calculate position in list
248
+ const position = Array.from(listItem.parentNode!.children).indexOf(listItem);
249
+ dragOverItem.current = position;
250
+
251
+ // Determine drop position (top/bottom)
252
+ const rect = listItem.getBoundingClientRect();
253
+ const middleY = rect.top + rect.height / 2;
254
+
255
+ if (point.clientY < middleY) {
256
+ listItem.classList.add('drag-over-top');
257
+ } else {
258
+ listItem.classList.add('drag-over-bottom');
259
+ }
260
+
261
+ onDragOver?.(dragItem.current, dragOverItem.current);
262
+ };
263
+
264
+ const handleDragEnd = (e: React.DragEvent | React.TouchEvent) => {
265
+ const isTouch = 'touches' in e;
266
+ if (isTouch && !isDragging) return;
267
+
268
+ onDragEnd?.(dragItem.current, dragOverItem.current);
269
+
270
+ // Cleanup
271
+ if (dragNode.current) {
272
+ dragNode.current.remove();
273
+ dragNode.current = null;
274
+ }
275
+
276
+ document.querySelectorAll(itemSelector).forEach(item => {
277
+ (item as HTMLElement).style.opacity = '1';
278
+ item.classList.remove(
279
+ 'dragging',
280
+ 'dragging-placeholder',
281
+ 'drag-over',
282
+ 'drag-over-top',
283
+ 'drag-over-bottom'
284
+ );
285
+ });
286
+
287
+ setIsDragging(false);
288
+ currentHoverItem.current = null;
289
+ dragItem.current = null;
290
+ dragOverItem.current = null;
291
+ };
292
+
293
+ return {
294
+ isDragging,
295
+ dragHandlers: {
296
+ handleDragStart,
297
+ handleDragOver,
298
+ handleDragEnd
299
+ }
300
+ };
301
+ };
302
+
303
+ export default useBoundedDrag;