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.
Files changed (65) hide show
  1. package/ColorPicker/index.js +3 -1
  2. package/Date/index.js +14 -1
  3. package/DragDropList/index.css +188 -0
  4. package/DragDropList/index.d.ts +44 -0
  5. package/DragDropList/index.js +1587 -0
  6. package/Input/index.d.ts +2 -0
  7. package/Input/index.js +14 -1
  8. package/LICENSE +21 -0
  9. package/MultipleSelect/index.css +237 -144
  10. package/MultipleSelect/index.d.ts +24 -10
  11. package/MultipleSelect/index.js +2240 -1225
  12. package/README.md +3 -1
  13. package/RangeSlider/index.js +14 -1
  14. package/Textarea/index.d.ts +2 -0
  15. package/Textarea/index.js +14 -1
  16. package/Tree/index.d.ts +1 -0
  17. package/Tree/index.js +29 -0
  18. package/Utils/useBoundedDrag.d.ts +125 -0
  19. package/Utils/useBoundedDrag.js +380 -0
  20. package/Utils/useDragDropPosition.d.ts +169 -0
  21. package/Utils/useDragDropPosition.js +456 -0
  22. package/Utils/useIsMobile.d.ts +2 -0
  23. package/Utils/useIsMobile.js +168 -0
  24. package/all.d.ts +1 -0
  25. package/all.js +1 -1
  26. package/lib/cjs/ColorPicker/index.js +3 -1
  27. package/lib/cjs/Date/index.js +14 -1
  28. package/lib/cjs/DragDropList/index.d.ts +44 -0
  29. package/lib/cjs/DragDropList/index.js +1587 -0
  30. package/lib/cjs/Input/index.d.ts +2 -0
  31. package/lib/cjs/Input/index.js +14 -1
  32. package/lib/cjs/MultipleSelect/index.d.ts +24 -10
  33. package/lib/cjs/MultipleSelect/index.js +2240 -1225
  34. package/lib/cjs/RangeSlider/index.js +14 -1
  35. package/lib/cjs/Textarea/index.d.ts +2 -0
  36. package/lib/cjs/Textarea/index.js +14 -1
  37. package/lib/cjs/Tree/index.d.ts +1 -0
  38. package/lib/cjs/Tree/index.js +29 -0
  39. package/lib/cjs/Utils/useBoundedDrag.d.ts +125 -0
  40. package/lib/cjs/Utils/useBoundedDrag.js +380 -0
  41. package/lib/cjs/Utils/useDragDropPosition.d.ts +169 -0
  42. package/lib/cjs/Utils/useDragDropPosition.js +456 -0
  43. package/lib/cjs/Utils/useIsMobile.d.ts +2 -0
  44. package/lib/cjs/Utils/useIsMobile.js +168 -0
  45. package/lib/cjs/index.d.ts +1 -0
  46. package/lib/cjs/index.js +1 -1
  47. package/lib/css/DragDropList/index.css +188 -0
  48. package/lib/css/MultipleSelect/index.css +237 -144
  49. package/lib/esm/ColorPicker/index.tsx +53 -49
  50. package/lib/esm/DragDropList/index.scss +245 -0
  51. package/lib/esm/DragDropList/index.tsx +494 -0
  52. package/lib/esm/Input/index.tsx +17 -3
  53. package/lib/esm/MultipleSelect/index.scss +288 -183
  54. package/lib/esm/MultipleSelect/index.tsx +305 -166
  55. package/lib/esm/MultipleSelect/utils/func.ts +21 -1
  56. package/lib/esm/Tabs/Tabs.tsx +1 -1
  57. package/lib/esm/Textarea/index.tsx +18 -1
  58. package/lib/esm/Tree/TreeList.tsx +32 -0
  59. package/lib/esm/Tree/index.tsx +3 -0
  60. package/lib/esm/Utils/hooks/useBoundedDrag.tsx +301 -0
  61. package/lib/esm/Utils/hooks/useDragDropPosition.tsx +420 -0
  62. package/lib/esm/Utils/hooks/useIsMobile.tsx +56 -0
  63. package/lib/esm/index.js +1 -0
  64. package/package.json +1 -1
  65. 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
+ }
@@ -18,7 +18,7 @@ export type TabsProps = {
18
18
  animTransitionDuration?: number;
19
19
  /** -- */
20
20
  style?: React.CSSProperties;
21
- onChange?: (nav: any, targetId: any, index: number, persistentIndex: number) => void;
21
+ onChange?: (nav: HTMLElement, targetId: string, index: number, persistentIndex: number) => void;
22
22
  onLoad?: (func: Function) => void;
23
23
  children: React.ReactNode | React.ReactNode[];
24
24
 
@@ -50,6 +50,8 @@ export type TextareaProps = {
50
50
  onBlur?: (e: any, el: any) => void;
51
51
  onFocus?: (e: any, el: any) => void;
52
52
  onPressEnter?: (e: any, el: any) => void;
53
+ onKeyDown?: (e: any, el: any) => void;
54
+ onKeyUp?: (e: any, el: any) => void;
53
55
  onResize?: (el: any, params: number[]) => void;
54
56
 
55
57
  };
@@ -96,6 +98,8 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
96
98
  onBlur,
97
99
  onFocus,
98
100
  onPressEnter,
101
+ onKeyDown,
102
+ onKeyUp,
99
103
  onResize,
100
104
  ...attributes
101
105
  } = props;
@@ -288,7 +292,12 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
288
292
  set: (value: string, cb?: any) => {
289
293
  setChangedVal(`${value}`);
290
294
  cb?.();
291
- }
295
+ },
296
+ aiPredictReset: () => {
297
+ setTimeout(() => { // Avoid conflicts with other asynchronous states, resulting in invalid clearing
298
+ setCurrentSuggestion('');
299
+ }, 0);
300
+ },
292
301
  }),
293
302
  [contentRef]
294
303
  );
@@ -363,6 +372,10 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
363
372
  }
364
373
 
365
374
  function handleKeyPressed(event: KeyboardEvent<HTMLTextAreaElement>) {
375
+
376
+ onKeyDown?.(event, valRef.current);
377
+
378
+
366
379
  if (typeof (onKeyPressedCallback) === 'function') {
367
380
  const newData: any = onKeyPressedCallback(event, valRef.current);
368
381
  if (newData) setChangedVal(newData); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
@@ -418,6 +431,9 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
418
431
 
419
432
  }
420
433
 
434
+ function handleKeyUp(event: KeyboardEvent<HTMLTextAreaElement>) {
435
+ onKeyUp?.(event, valRef.current);
436
+ }
421
437
 
422
438
  useEffect(() => {
423
439
 
@@ -534,6 +550,7 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
534
550
  }
535
551
  }}
536
552
  onKeyDown={handleKeyPressed}
553
+ onKeyUp={handleKeyUp}
537
554
  disabled={disabled || null}
538
555
  required={required || null}
539
556
  readOnly={readOnly || null}
@@ -40,6 +40,7 @@ export type TreeListProps = {
40
40
  getCheckedData?: any[];
41
41
  updategetCheckedData?: any;
42
42
  onSelect?: (e: any, val: any, func: Function) => void;
43
+ onDoubleSelect?: (e: any, val: any, func: Function) => void;
43
44
  onCollapse?: (e: any, val: any, func: Function) => void;
44
45
  onCheck?: (val: any) => void;
45
46
  evInitValue?: (key: React.Key | null, fetch: FetchConfig | null, firstRender: boolean) => void;
@@ -64,6 +65,7 @@ export default function TreeList(props: TreeListProps) {
64
65
  getCheckedData,
65
66
  updategetCheckedData,
66
67
  onSelect,
68
+ onDoubleSelect,
67
69
  onCollapse,
68
70
  onCheck,
69
71
  evInitValue
@@ -285,6 +287,34 @@ export default function TreeList(props: TreeListProps) {
285
287
  }
286
288
  }
287
289
 
290
+ function handleDoubleSelect(e: any) {
291
+ e.preventDefault();
292
+ e.stopPropagation();
293
+
294
+ const hyperlink = e.currentTarget;
295
+
296
+ if ( hyperlink.classList.contains('selected') ) {
297
+ activeClass(hyperlink, 'remove', 'selected');
298
+ } else {
299
+ [].slice.call(hyperlink.closest('.tree-diagram__wrapper').querySelectorAll('li > a')).forEach((node: any) => {
300
+ activeClass(node, 'remove', 'selected');
301
+ });
302
+ activeClass(hyperlink, 'add', 'selected');
303
+ }
304
+
305
+
306
+ onDoubleSelect?.(e, {
307
+ key: hyperlink.dataset.key,
308
+ slug: hyperlink.dataset.slug,
309
+ link: hyperlink.dataset.link,
310
+ optiondata: hyperlink.dataset.optiondata
311
+ }, typeof evInitValue !== 'function' ? ()=>void(0) : evInitValue);
312
+
313
+ if ( disableArrow ) {
314
+ handleCollapse(e);
315
+ }
316
+ }
317
+
288
318
 
289
319
  function titleArrowGenerator() {
290
320
  return disableArrow ? loadingIcon : null;
@@ -522,6 +552,7 @@ export default function TreeList(props: TreeListProps) {
522
552
  href={item.link === '#' ? `${item.link}-${i}` : item.link}
523
553
  aria-expanded="false"
524
554
  onClick={handleSelect}
555
+ onDoubleClick={handleDoubleSelect}
525
556
  data-link={item.link}
526
557
  data-slug={item.slug}
527
558
  data-key={item.key}
@@ -553,6 +584,7 @@ export default function TreeList(props: TreeListProps) {
553
584
  first={false}
554
585
  arrow={arrow}
555
586
  onSelect={onSelect}
587
+ onDoubleSelect={onDoubleSelect}
556
588
  onCollapse={onCollapse}
557
589
  onCheck={onCheck}
558
590
  disableArrow={disableArrow}
@@ -75,6 +75,7 @@ export type TreeProps = {
75
75
  /** -- */
76
76
  id?: string;
77
77
  onSelect?: (e: any, val: any, func: Function) => void;
78
+ onDoubleSelect?: (e: any, val: any, func: Function) => void;
78
79
  onCollapse?: (e: any, val: any, func: Function) => void;
79
80
  onCheck?: (val: any) => void;
80
81
  };
@@ -95,6 +96,7 @@ const Tree = (props: TreeProps) => {
95
96
  data,
96
97
  retrieveData,
97
98
  onSelect,
99
+ onDoubleSelect,
98
100
  onCollapse,
99
101
  onCheck
100
102
  } = props;
@@ -374,6 +376,7 @@ const Tree = (props: TreeProps) => {
374
376
  data={Array.isArray(retrieveData) && retrieveData.length > 0 ? filterRetriveData(flatList, retrieveData) : list}
375
377
  childClassName={childClassName || 'tree-diagram-default-nav'}
376
378
  onSelect={onSelect}
379
+ onDoubleSelect={onDoubleSelect}
377
380
  onCollapse={onCollapse}
378
381
  onCheck={onCheck}
379
382
  evInitValue={initDefaultValue}
@@ -0,0 +1,301 @@
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
+ import { useRef, useState } from 'react';
106
+
107
+ export interface TouchOffset {
108
+ x: number;
109
+ y: number;
110
+ }
111
+
112
+ export interface BoundedDragOptions {
113
+ dragMode?: 'handle' | 'block';
114
+ boundarySelector?: string;
115
+ itemSelector?: string;
116
+ dragHandleSelector?: string;
117
+ onDragStart?: (index: number) => void;
118
+ onDragOver?: (dragIndex: number | null, dropIndex: number | null) => void;
119
+ onDragEnd?: (dragIndex: number | null, dropIndex: number | null) => void;
120
+ }
121
+
122
+ export const useBoundedDrag = (options: BoundedDragOptions = {}) => {
123
+ const {
124
+ dragMode = 'handle',
125
+ boundarySelector = '.custom-draggable-list',
126
+ itemSelector = '.custom-draggable-list__item',
127
+ dragHandleSelector = '.custom-draggable-list__handle',
128
+ onDragStart,
129
+ onDragOver,
130
+ onDragEnd
131
+ } = options;
132
+
133
+ const [isDragging, setIsDragging] = useState(false);
134
+ const dragItem = useRef<number | null>(null);
135
+ const dragOverItem = useRef<number | null>(null);
136
+ const dragNode = useRef<HTMLElement | null>(null);
137
+ const touchOffset = useRef<TouchOffset>({ x: 0, y: 0 });
138
+ const currentHoverItem = useRef<HTMLElement | null>(null);
139
+
140
+ const handleDragStart = (e: React.DragEvent | React.TouchEvent, position: number) => {
141
+ const isTouch = 'touches' in e;
142
+ const target = e.target as HTMLElement;
143
+
144
+ // For block mode or handle mode check
145
+ if (dragMode === 'handle') {
146
+ const handle = target.closest(dragHandleSelector);
147
+ if (!handle) {
148
+ if (!isTouch) e.preventDefault();
149
+ return false;
150
+ }
151
+ }
152
+
153
+ // Find the draggable item
154
+ const listItem = target.closest(itemSelector) as HTMLElement;
155
+ if (!listItem) return;
156
+
157
+ // Check boundary
158
+ const boundary = listItem.closest(boundarySelector);
159
+ if (!boundary) return;
160
+
161
+ dragItem.current = position;
162
+ onDragStart?.(position);
163
+
164
+ if (isTouch) {
165
+ e.preventDefault(); // Prevent scrolling
166
+ const touch = (e as React.TouchEvent).touches[0];
167
+ const rect = listItem.getBoundingClientRect();
168
+ const boundaryRect = boundary.getBoundingClientRect();
169
+
170
+ // Calculate offset relative to the boundary
171
+ touchOffset.current = {
172
+ x: touch.clientX - rect.left,
173
+ y: touch.clientY - rect.top
174
+ };
175
+
176
+ // Clone the item for dragging
177
+ dragNode.current = listItem.cloneNode(true) as HTMLElement;
178
+ dragNode.current.classList.add('dragging');
179
+
180
+ // Style the clone
181
+ Object.assign(dragNode.current.style, {
182
+ position: 'fixed',
183
+ width: `${rect.width}px`,
184
+ height: `${rect.height}px`,
185
+ left: `${rect.left}px`,
186
+ top: `${rect.top}px`,
187
+ zIndex: '1000',
188
+ pointerEvents: 'none',
189
+ transform: 'scale(1.05)',
190
+ transition: 'transform 0.1s',
191
+ opacity: '0.9'
192
+ });
193
+
194
+ document.body.appendChild(dragNode.current);
195
+ setIsDragging(true);
196
+ listItem.classList.add('dragging-placeholder');
197
+ } else {
198
+ // ... desktop drag logic remains the same ...
199
+ }
200
+ };
201
+
202
+ const handleDragOver = (e: React.DragEvent | React.TouchEvent) => {
203
+ e.preventDefault();
204
+ const isTouch = 'touches' in e;
205
+
206
+ if (!isTouch) {
207
+ (e as React.DragEvent).dataTransfer.dropEffect = 'move';
208
+ }
209
+
210
+ // Get the current pointer/touch position
211
+ const point = isTouch ?
212
+ (e as React.TouchEvent).touches[0] :
213
+ { clientX: (e as React.DragEvent).clientX, clientY: (e as React.DragEvent).clientY };
214
+
215
+ // Update dragged element position for touch events
216
+ if (isTouch && isDragging && dragNode.current) {
217
+ dragNode.current.style.left = `${point.clientX - touchOffset.current.x}px`;
218
+ dragNode.current.style.top = `${point.clientY - touchOffset.current.y}px`;
219
+ }
220
+
221
+ // Find the element below the pointer/touch
222
+ const elemBelow = document.elementFromPoint(
223
+ point.clientX,
224
+ point.clientY
225
+ );
226
+
227
+ if (!elemBelow) return;
228
+
229
+ // Find the closest list item
230
+ const listItem = elemBelow.closest(itemSelector) as HTMLElement;
231
+ if (!listItem || listItem === currentHoverItem.current) return;
232
+
233
+ // Check boundary
234
+ const boundary = listItem.closest(boundarySelector);
235
+ if (!boundary) return;
236
+
237
+ // Update hover states
238
+ if (currentHoverItem.current) {
239
+ currentHoverItem.current.classList.remove('drag-over', 'drag-over-top', 'drag-over-bottom');
240
+ }
241
+
242
+ currentHoverItem.current = listItem;
243
+ listItem.classList.add('drag-over');
244
+
245
+ // Calculate position in list
246
+ const position = Array.from(listItem.parentNode!.children).indexOf(listItem);
247
+ dragOverItem.current = position;
248
+
249
+ // Determine drop position (top/bottom)
250
+ const rect = listItem.getBoundingClientRect();
251
+ const middleY = rect.top + rect.height / 2;
252
+
253
+ if (point.clientY < middleY) {
254
+ listItem.classList.add('drag-over-top');
255
+ } else {
256
+ listItem.classList.add('drag-over-bottom');
257
+ }
258
+
259
+ onDragOver?.(dragItem.current, dragOverItem.current);
260
+ };
261
+
262
+ const handleDragEnd = (e: React.DragEvent | React.TouchEvent) => {
263
+ const isTouch = 'touches' in e;
264
+ if (isTouch && !isDragging) return;
265
+
266
+ onDragEnd?.(dragItem.current, dragOverItem.current);
267
+
268
+ // Cleanup
269
+ if (dragNode.current) {
270
+ dragNode.current.remove();
271
+ dragNode.current = null;
272
+ }
273
+
274
+ document.querySelectorAll(itemSelector).forEach(item => {
275
+ (item as HTMLElement).style.opacity = '1';
276
+ item.classList.remove(
277
+ 'dragging',
278
+ 'dragging-placeholder',
279
+ 'drag-over',
280
+ 'drag-over-top',
281
+ 'drag-over-bottom'
282
+ );
283
+ });
284
+
285
+ setIsDragging(false);
286
+ currentHoverItem.current = null;
287
+ dragItem.current = null;
288
+ dragOverItem.current = null;
289
+ };
290
+
291
+ return {
292
+ isDragging,
293
+ dragHandlers: {
294
+ handleDragStart,
295
+ handleDragOver,
296
+ handleDragEnd
297
+ }
298
+ };
299
+ };
300
+
301
+ export default useBoundedDrag;