funda-ui 4.4.15 → 4.5.12

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 (77) hide show
  1. package/Date/index.js +1078 -77
  2. package/EventCalendar/index.css +114 -114
  3. package/EventCalendar/index.d.ts +1 -0
  4. package/EventCalendar/index.js +124 -86
  5. package/EventCalendarTimeline/index.css +274 -270
  6. package/EventCalendarTimeline/index.d.ts +3 -0
  7. package/EventCalendarTimeline/index.js +673 -225
  8. package/Input/index.d.ts +7 -0
  9. package/Input/index.js +699 -57
  10. package/MasonryLayout/index.js +11 -1
  11. package/MultipleCheckboxes/index.js +11 -11
  12. package/MultipleSelect/index.d.ts +1 -0
  13. package/MultipleSelect/index.js +18 -16
  14. package/NativeSelect/index.js +11 -11
  15. package/Radio/index.js +11 -11
  16. package/RangeSlider/index.js +1078 -77
  17. package/Select/index.js +45 -13
  18. package/Table/index.css +1 -0
  19. package/Table/index.js +36 -7
  20. package/TagInput/index.d.ts +1 -0
  21. package/TagInput/index.js +20 -2
  22. package/Textarea/index.d.ts +7 -0
  23. package/Textarea/index.js +707 -10
  24. package/Tree/index.js +13 -11
  25. package/Utils/inputsCalculation.d.ts +18 -1
  26. package/Utils/inputsCalculation.js +26 -0
  27. package/Utils/object.js +11 -11
  28. package/Utils/os.d.ts +2 -0
  29. package/Utils/os.js +104 -0
  30. package/lib/cjs/Date/index.js +1078 -77
  31. package/lib/cjs/EventCalendar/index.d.ts +1 -0
  32. package/lib/cjs/EventCalendar/index.js +124 -86
  33. package/lib/cjs/EventCalendarTimeline/index.d.ts +3 -0
  34. package/lib/cjs/EventCalendarTimeline/index.js +673 -225
  35. package/lib/cjs/Input/index.d.ts +7 -0
  36. package/lib/cjs/Input/index.js +699 -57
  37. package/lib/cjs/MasonryLayout/index.js +11 -1
  38. package/lib/cjs/MultipleCheckboxes/index.js +11 -11
  39. package/lib/cjs/MultipleSelect/index.d.ts +1 -0
  40. package/lib/cjs/MultipleSelect/index.js +18 -16
  41. package/lib/cjs/NativeSelect/index.js +11 -11
  42. package/lib/cjs/Radio/index.js +11 -11
  43. package/lib/cjs/RangeSlider/index.js +1078 -77
  44. package/lib/cjs/Select/index.js +45 -13
  45. package/lib/cjs/Table/index.js +36 -7
  46. package/lib/cjs/TagInput/index.d.ts +1 -0
  47. package/lib/cjs/TagInput/index.js +20 -2
  48. package/lib/cjs/Textarea/index.d.ts +7 -0
  49. package/lib/cjs/Textarea/index.js +707 -10
  50. package/lib/cjs/Tree/index.js +13 -11
  51. package/lib/cjs/Utils/inputsCalculation.d.ts +18 -1
  52. package/lib/cjs/Utils/inputsCalculation.js +26 -0
  53. package/lib/cjs/Utils/object.js +11 -11
  54. package/lib/cjs/Utils/os.d.ts +2 -0
  55. package/lib/cjs/Utils/os.js +104 -0
  56. package/lib/css/EventCalendar/index.css +114 -114
  57. package/lib/css/EventCalendarTimeline/index.css +274 -270
  58. package/lib/css/Table/index.css +1 -0
  59. package/lib/esm/EventCalendar/index.scss +81 -81
  60. package/lib/esm/EventCalendar/index.tsx +144 -104
  61. package/lib/esm/EventCalendarTimeline/index.scss +226 -221
  62. package/lib/esm/EventCalendarTimeline/index.tsx +791 -517
  63. package/lib/esm/Input/index.tsx +299 -77
  64. package/lib/esm/MasonryLayout/index.tsx +9 -2
  65. package/lib/esm/ModalDialog/index.tsx +0 -1
  66. package/lib/esm/MultipleSelect/index.tsx +6 -4
  67. package/lib/esm/Table/Table.tsx +0 -1
  68. package/lib/esm/Table/index.scss +2 -0
  69. package/lib/esm/Table/utils/hooks/useTableDraggable.tsx +47 -6
  70. package/lib/esm/TagInput/index.tsx +23 -1
  71. package/lib/esm/Textarea/index.tsx +332 -39
  72. package/lib/esm/Tree/TreeList.tsx +4 -1
  73. package/lib/esm/Tree/index.tsx +1 -0
  74. package/lib/esm/Utils/libs/inputsCalculation.ts +60 -31
  75. package/lib/esm/Utils/libs/object.ts +67 -67
  76. package/lib/esm/Utils/libs/os.ts +63 -0
  77. package/package.json +1 -1
@@ -64,7 +64,7 @@ function useTableDraggable({
64
64
 
65
65
  const placeholderGenerator = (trHeight: number) => {
66
66
  const tbodyRef: any = getTbody(spyElement);
67
- if (tbodyRef === null) return;
67
+ if (tbodyRef === null || tbodyRef.querySelector('tr') === null) return;
68
68
 
69
69
  // Insert a row at the "index" of the table
70
70
  const newRow = document.createElement('tr');
@@ -85,7 +85,11 @@ function useTableDraggable({
85
85
 
86
86
  const lastRowGenerator = (trHeight: number) => {
87
87
  const tbodyRef: any = getTbody(spyElement);
88
- if (tbodyRef === null) return;
88
+ if (tbodyRef === null || tbodyRef.querySelector('tr') === null) return;
89
+
90
+ const cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
91
+ if (cloneEl !== null) return;
92
+
89
93
 
90
94
  // Insert a row at the "index" of the table
91
95
  const newRow = document.createElement('tr');
@@ -93,6 +97,7 @@ function useTableDraggable({
93
97
  newRow.dataset.order = allRows(spyElement).length.toString();
94
98
  newRow.style.height = trHeight + 'px';
95
99
  newRow.style.display = 'none';
100
+
96
101
 
97
102
  // Insert a cell in the row at index
98
103
  const newCell = newRow.insertCell(0);
@@ -134,7 +139,14 @@ function useTableDraggable({
134
139
  setSortData(listIndexes);
135
140
 
136
141
  //last placeholder
137
- insertAfter(lastRowGenerator((_allRows.at(-1) as any).clientHeight), _allRows.at(-1));
142
+ if (_allRows.length > 0) {
143
+ const lastEl: any = lastRowGenerator((_allRows.at(-1) as any).clientHeight);
144
+ if (typeof _allRows.at(-1) !== 'undefined') {
145
+ insertAfter(lastEl, _allRows.at(-1));
146
+ }
147
+ }
148
+
149
+
138
150
  };
139
151
 
140
152
  const handleTbodyEnter = (e: any) => {
@@ -182,6 +194,9 @@ function useTableDraggable({
182
194
 
183
195
 
184
196
  const handleDragStart = useCallback((e: any) => {
197
+ const tbodyRef: any = getTbody(spyElement);
198
+ if (tbodyRef === null) return;
199
+
185
200
  draggedObj = e.currentTarget;
186
201
  e.dataTransfer.effectAllowed = 'move';
187
202
  e.dataTransfer.setData('text/html', draggedObj);
@@ -196,6 +211,15 @@ function useTableDraggable({
196
211
  };
197
212
  onRowDrag?.(dragStart, null);
198
213
 
214
+
215
+ // init clone <tr>
216
+ // !!! It needs to be put at the end of the code to fix the location of the clone element
217
+ const cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
218
+ if (cloneEl !== null) {
219
+ cloneEl.style.display = 'none';
220
+ }
221
+
222
+
199
223
  }, [handledragOver]);
200
224
 
201
225
  const handleDragEnd = useCallback((e: any) => {
@@ -250,9 +274,12 @@ function useTableDraggable({
250
274
  // sort elements
251
275
  const categoryItemsArray = allRows(spyElement);
252
276
  const sorter = (a: any, b: any) => {
253
- return a.dataset.order.localeCompare(b.dataset.order); // sorts based on alphabetical order
277
+ let txt1 = Number(a.dataset.order),
278
+ txt2 = Number(b.dataset.order);
279
+
280
+ return txt2 < txt1 ? -1 : txt2 > txt1 ? 1 : 0;
254
281
  }
255
- const sorted = categoryItemsArray.sort(sorter);
282
+ const sorted = categoryItemsArray.sort(sorter).reverse();
256
283
  sorted.forEach(e => spyElement.querySelector('table').querySelector('tbody').appendChild(e));
257
284
 
258
285
 
@@ -264,13 +291,27 @@ function useTableDraggable({
264
291
 
265
292
 
266
293
 
294
+ // init clone <tr>
295
+ // !!! It needs to be put at the end of the code to fix the location of the clone element
296
+ const _allRows = allRows(spyElement);
297
+ const cloneEl = tbodyRef.querySelector('.row-obj-clonelast');
298
+ if (cloneEl !== null) {
299
+ if (typeof _allRows.at(-1) !== 'undefined') {
300
+ insertAfter(cloneEl, _allRows.at(-1));
301
+ cloneEl.style.display = 'none';
302
+ }
303
+ }
304
+
305
+
306
+
267
307
  }, [sortData]);
268
308
 
269
309
 
270
310
 
271
311
  useEffect(() => {
272
312
  if (enabled) {
273
- if (Array.isArray(data)) {
313
+ if (Array.isArray(data) && data.length > 0) {
314
+ // !!! REQUIRED "data.length > 0" to avoid data-order cannot be assigned when asynchronous data is empty
274
315
  data.forEach((item: any, i: number) => {
275
316
  item.order = i;
276
317
  });
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef, forwardRef, ChangeEvent, MouseEvent, KeyboardEvent, FocusEvent, CompositionEvent } from 'react';
1
+ import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle, ChangeEvent, MouseEvent, KeyboardEvent, FocusEvent, CompositionEvent } from 'react';
2
2
 
3
3
 
4
4
 
@@ -14,6 +14,7 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
14
14
 
15
15
 
16
16
  export type TagInputProps = {
17
+ contentRef?: React.ForwardedRef<any>;
17
18
  wrapperClassName?: string;
18
19
  value?: string;
19
20
  maxTags?: number;
@@ -45,6 +46,7 @@ export type TagInputProps = {
45
46
 
46
47
  const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
47
48
  const {
49
+ contentRef,
48
50
  wrapperClassName,
49
51
  maxTags,
50
52
  disabled,
@@ -85,6 +87,26 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
85
87
  const [onComposition, setOnComposition] = useState<boolean>(false);
86
88
 
87
89
 
90
+ // exposes the following methods
91
+ useImperativeHandle(
92
+ contentRef,
93
+ () => ({
94
+ control: () => {
95
+ return valRef.current;
96
+ },
97
+ clear: (cb?: any) => {
98
+ initDefaultValue('');
99
+ cb?.();
100
+ },
101
+ set: (value: string, cb?: any) => {
102
+ initDefaultValue(`${value}`);
103
+ cb?.();
104
+ }
105
+ }),
106
+ [contentRef],
107
+ );
108
+
109
+
88
110
  function initDefaultValue(defaultValue: any) {
89
111
 
90
112
  // change the value to trigger component rendering
@@ -1,11 +1,11 @@
1
1
  import React, { useState, useEffect, useRef, forwardRef, KeyboardEvent, useImperativeHandle } from 'react';
2
2
 
3
3
 
4
-
5
4
  import useComId from 'funda-utils/dist/cjs/useComId';
6
5
  import useAutosizeTextArea from 'funda-utils/dist/cjs/useAutosizeTextArea';
7
6
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
8
-
7
+ import { actualPropertyValue, getTextTop } from 'funda-utils/dist/cjs/inputsCalculation';
8
+ import useDebounce from 'funda-utils/dist/cjs/useDebounce';
9
9
 
10
10
 
11
11
  export type TextareaProps = {
@@ -31,6 +31,13 @@ export type TextareaProps = {
31
31
  autoSize?: boolean;
32
32
  iconLeft?: React.ReactNode | string;
33
33
  iconRight?: React.ReactNode | string;
34
+ aiPredict?: boolean;
35
+ aiPredictRemainingTextRGB?: number[];
36
+ aiPredictConfirmKey?: Array<string[]>;
37
+ aiPredictFetchFuncAsync?: any;
38
+ aiPredictFetchFuncMethod?: string;
39
+ aiPredictFetchFuncMethodParams?: any[];
40
+ aiPredictFetchCallback?: (data: any) => void;
34
41
  /** -- */
35
42
  id?: string;
36
43
  style?: React.CSSProperties;
@@ -64,6 +71,13 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
64
71
  autoSize,
65
72
  iconLeft,
66
73
  iconRight,
74
+ aiPredict = false,
75
+ aiPredictRemainingTextRGB = [153, 153, 153],
76
+ aiPredictConfirmKey = [['Enter'],['Tab'],['Shift', ' ']],
77
+ aiPredictFetchFuncAsync,
78
+ aiPredictFetchFuncMethod,
79
+ aiPredictFetchFuncMethodParams,
80
+ aiPredictFetchCallback,
67
81
  readOnly,
68
82
  defaultValue,
69
83
  value,
@@ -93,6 +107,173 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
93
107
  const valRef = useRef<any>(null);
94
108
  const [changedVal, setChangedVal] = useState<string>(value || '');
95
109
 
110
+
111
+ //================================================================
112
+ // AI Predict
113
+ //================================================================
114
+ const [currentSuggestion, setCurrentSuggestion] = useState<string>('');
115
+ const [tempMatchedSuggestion, setTempMatchedSuggestion] = useState<string[]>([]);
116
+ const [textWidth, setTextWidth] = useState<number>(0);
117
+ const aiInputRef = useRef<any>(null);
118
+ const originInputComputedStyle = useRef<Record<string, any>>({
119
+ fontSize: 16,
120
+ fontFamily: 'inherit',
121
+ letterSpacing: 'normal',
122
+ textTop: 10
123
+ });
124
+ const [hasErr, setHasErr] = useState<boolean>(false);
125
+ const currentSuggestionIndex = useRef<number>(0);
126
+
127
+
128
+
129
+ // A list of suggestions
130
+ //----------------
131
+ const [suggestions, setSuggestions] = useState<string[]>([]);
132
+
133
+ //performance
134
+ const handleChangeSuggestionsFetchSafe = useDebounce((e: any, curVal: string) => {
135
+ const _oparams: any[] = aiPredictFetchFuncMethodParams || [];
136
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : curVal);
137
+ fetchSuggestionsData((_params).join(',')).then((resSuggestions: string[]) => {
138
+ handleInputAiPredictChange(curVal, resSuggestions);
139
+ });
140
+ }, 350, []);
141
+
142
+ async function fetchSuggestionsData(params: any) {
143
+
144
+ if (typeof aiPredictFetchFuncAsync === 'object') {
145
+
146
+ const response: any = await aiPredictFetchFuncAsync[`${aiPredictFetchFuncMethod}`](...params.split(','));
147
+ let _ORGIN_DATA = response.data;
148
+
149
+ // reset data structure
150
+ if (typeof (aiPredictFetchCallback) === 'function') {
151
+ _ORGIN_DATA = aiPredictFetchCallback(_ORGIN_DATA);
152
+ }
153
+
154
+ // Determine whether the data structure matches
155
+ if (!Array.isArray(_ORGIN_DATA)) {
156
+ console.warn('The data structure does not match, please refer to the example in the component documentation.');
157
+ setHasErr(true);
158
+ _ORGIN_DATA = [];
159
+ }
160
+
161
+ //
162
+ setSuggestions(_ORGIN_DATA);
163
+
164
+ return _ORGIN_DATA;
165
+ } else {
166
+ return [];
167
+ }
168
+
169
+
170
+ }
171
+
172
+
173
+
174
+ // Calculates the width of the input text
175
+ //----------------
176
+ const calculateTextWidth = (text: string) => {
177
+ if (valRef.current) {
178
+ const canvas = document.createElement('canvas');
179
+ const context: any = canvas.getContext('2d');
180
+ context.font = `${originInputComputedStyle.current.fontSize}px ${originInputComputedStyle.current.fontFamily}`;
181
+ return context.measureText(text).width;
182
+ }
183
+ return 0;
184
+ };
185
+
186
+
187
+
188
+ // Get the rest of the suggested text
189
+ //----------------
190
+ const getRemainingText = (fullSuggestion: string) => {
191
+ if (!changedVal || !fullSuggestion) return '';
192
+
193
+
194
+ // Only the parts of the suggested text that were not entered are returned
195
+ const lastInputChar = changedVal[changedVal.length - 1];
196
+ const lastCharIndex = fullSuggestion.toLowerCase().lastIndexOf(lastInputChar.toLowerCase());
197
+ return fullSuggestion.slice(lastCharIndex + 1);
198
+
199
+ };
200
+
201
+ // Match exactly from the start
202
+ //----------------
203
+ const preciseMatch = (input: any, suggestions: string[]) => {
204
+ if (!input) return '';
205
+
206
+ const filtered = suggestions.filter(s =>
207
+ s.toLowerCase().startsWith(input.toLowerCase())
208
+ );
209
+
210
+ setTempMatchedSuggestion(filtered);
211
+ return filtered;
212
+ };
213
+
214
+
215
+ // Fuzzy matching
216
+ //----------------
217
+ const fuzzyMatch = (input: any, suggestions: string[]) => {
218
+ if (!input) return '';
219
+
220
+ // Convert input to a regular expression pattern with support for arbitrary position matching
221
+ const pattern = input.split('').map((char: string) =>
222
+ char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
223
+ ).join('.*');
224
+ const regex = new RegExp(pattern, 'i');
225
+
226
+ // Find the first matching suggestion (multiple matches)
227
+ const filtered = suggestions.filter((suggestion: string) => regex.test(suggestion));
228
+ setTempMatchedSuggestion(filtered);
229
+ return filtered;
230
+ };
231
+
232
+ // Handle input variations
233
+ //----------------
234
+ const handleInputAiPredictChange = (newValue: string, curSuggestions: string[]) => {
235
+ setTextWidth(calculateTextWidth(newValue));
236
+
237
+ // Match results
238
+ const matchedSuggestion = fuzzyMatch(newValue, curSuggestions);
239
+ setCurrentSuggestion(matchedSuggestion[0] || '');
240
+
241
+ };
242
+
243
+
244
+
245
+
246
+ // Calculate the color shade of the prompt text
247
+ //----------------
248
+ const calculateOpacity = () => {
249
+ // Transparency is calculated based on the input length
250
+ const baseOpacity = 0.5;
251
+ const inputLength = changedVal.length;
252
+ return Math.max(0.2, baseOpacity - (inputLength * 0.05));
253
+ };
254
+
255
+
256
+ // Confirm
257
+ //----------------
258
+ const handleAiPredictKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
259
+ // Prevents the default behavior of the enter key
260
+ e.preventDefault();
261
+
262
+ const remainingText = getRemainingText(currentSuggestion);
263
+ if (remainingText) {
264
+ // Only the second half of the text is added
265
+ setChangedVal(changedVal + remainingText);
266
+ setCurrentSuggestion('');
267
+ }
268
+ };
269
+
270
+ //
271
+ const remainingText = getRemainingText(currentSuggestion);
272
+
273
+
274
+ //================================================================
275
+ // General
276
+ //================================================================
96
277
  // exposes the following methods
97
278
  useImperativeHandle(
98
279
  contentRef,
@@ -121,6 +302,12 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
121
302
  onResize?.(valRef.current, res);
122
303
  }
123
304
  });
305
+
306
+
307
+ const propExist = (p: any) => {
308
+ return typeof p !== 'undefined' && p !== null && p !== '';
309
+ };
310
+
124
311
 
125
312
  function handleFocus(event: any) {
126
313
  const el = event.target;
@@ -130,6 +317,7 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
130
317
  onFocus?.(event, valRef.current);
131
318
  }
132
319
 
320
+
133
321
  function handleChange(event: any) {
134
322
  const val = event.target.value;
135
323
 
@@ -186,6 +374,49 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
186
374
  onPressEnter?.(event, valRef.current);
187
375
  }
188
376
 
377
+ // AI Predict
378
+ //----
379
+ if (aiPredict && currentSuggestion !== '') {
380
+ const keyBindings: Array<string[]> = aiPredictConfirmKey;
381
+ // The parameter 'registerKeyEvents' is an array, and each element is an object
382
+ // eg. { keys: ["Control", "S"], action: () => { console.log("Ctrl+S"); } }
383
+ const registerKeyEvents: Record<string, any>[] = keyBindings.map((s: string[]) => {
384
+ return {
385
+ keys: s,
386
+ action: () => {
387
+ handleAiPredictKeyDown(event);
388
+ },
389
+ };
390
+ });
391
+
392
+ registerKeyEvents.forEach((binding: Record<string, any>) => {
393
+ const keysPressed = binding.keys.every((key: string) =>
394
+ key === "Shift" ? event.shiftKey :
395
+ key === "Control" ? event.ctrlKey :
396
+ key === "Alt" ? event.altKey :
397
+ key === "Meta" ? event.metaKey :
398
+ event.key === key
399
+ );
400
+
401
+ if (keysPressed) {
402
+ binding.action();
403
+ }
404
+ });
405
+
406
+
407
+ // switch result of suggestions
408
+ if (event.code === 'ArrowUp') {
409
+ currentSuggestionIndex.current = (currentSuggestionIndex.current - 1 + tempMatchedSuggestion.length) % tempMatchedSuggestion.length;
410
+ }
411
+
412
+ if (event.code === 'ArrowDown') {
413
+ currentSuggestionIndex.current = (currentSuggestionIndex.current + 1) % tempMatchedSuggestion.length;
414
+ }
415
+ setCurrentSuggestion(tempMatchedSuggestion[currentSuggestionIndex.current] || '');
416
+
417
+ }
418
+
419
+
189
420
  }
190
421
 
191
422
 
@@ -234,6 +465,19 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
234
465
  }
235
466
  }
236
467
 
468
+ // AI Predict initalization
469
+ //--------------
470
+ if (aiPredict && valRef.current !== null) {
471
+ originInputComputedStyle.current = {
472
+ fontSize: actualPropertyValue(valRef.current as HTMLInputElement, 'fontSize'),
473
+ fontFamily: actualPropertyValue(valRef.current as HTMLInputElement, 'fontFamily'),
474
+ letterSpacing: actualPropertyValue(valRef.current as HTMLInputElement, 'letterSpacing'),
475
+ textTop: getTextTop(valRef.current)
476
+ };
477
+ }
478
+
479
+
480
+
237
481
  }, []);
238
482
 
239
483
  return (
@@ -242,45 +486,94 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
242
486
  <div className={clsWrite(wrapperClassName, 'mb-3 position-relative')} ref={rootRef}>
243
487
  {label ? <>{typeof label === 'string' ? <label htmlFor={idRes} className="form-label" dangerouslySetInnerHTML={{__html: `${label}`}}></label> : <label htmlFor={idRes} className="form-label">{label}</label>}</> : null}
244
488
 
245
- <div className={clsWrite(controlGroupWrapperClassName, 'input-group')}>
246
- {typeof iconLeft !== 'undefined' && iconLeft !== null && iconLeft !== '' ? <><span className={clsWrite(controlGroupTextClassName, 'input-group-text')}>{iconLeft}</span></>: null}
247
-
489
+
490
+ <div className={combinedCls(
491
+ 'position-relative z-1',
492
+ clsWrite(controlGroupWrapperClassName, 'input-group'),
493
+ {
494
+ 'has-left-content': propExist(iconLeft),
495
+ 'has-right-content': propExist(iconRight)
496
+ }
497
+ )}>
498
+ {propExist(iconLeft) ? <><span className={clsWrite(controlGroupTextClassName, 'input-group-text')}>{iconLeft}</span></> : null}
499
+
500
+ <div className="input-group-control-container flex-fill position-relative">
248
501
  <textarea
249
- ref={(node) => {
250
- valRef.current = node;
251
- if (typeof externalRef === 'function') {
252
- externalRef(node);
253
- } else if (externalRef) {
254
- externalRef.current = node;
255
- }
256
- }}
257
- tabIndex={tabIndex || 0}
258
- className={combinedCls(
259
- clsWrite(controlClassName, 'form-control'),
260
- controlExClassName
261
- )}
262
- id={idRes}
263
- name={name}
264
- placeholder={placeholder || ''}
265
- defaultValue={defaultValue}
266
- value={changedVal}
267
- minLength={minLength || null}
268
- maxLength={maxLength || null}
269
- onFocus={handleFocus}
270
- onBlur={handleBlur}
271
- onChange={handleChange}
272
- onKeyDown={handleKeyPressed}
273
- disabled={disabled || null}
274
- required={required || null}
275
- readOnly={readOnly || null}
276
- cols={cols || 20}
277
- rows={rows || 2}
278
- style={style}
279
- {...attributes}
280
- />
281
- {typeof iconRight !== 'undefined' && iconRight !== null && iconRight !== '' ? <><span className={clsWrite(controlGroupTextClassName, 'input-group-text')}>{iconRight}</span></>: null}
502
+ ref={(node) => {
503
+ valRef.current = node;
504
+ if (typeof externalRef === 'function') {
505
+ externalRef(node);
506
+ } else if (externalRef) {
507
+ externalRef.current = node;
508
+ }
509
+ }}
510
+ tabIndex={tabIndex || 0}
511
+ className={combinedCls(
512
+ clsWrite(controlClassName, 'form-control'),
513
+ controlExClassName,
514
+ {
515
+ 'rounded': !propExist(iconLeft) && !propExist(iconRight),
516
+ 'rounded-start-0': propExist(iconLeft),
517
+ 'rounded-end-0': propExist(iconRight)
518
+ }
519
+ )}
520
+ id={idRes}
521
+ name={name}
522
+ placeholder={placeholder || ''}
523
+ defaultValue={defaultValue}
524
+ value={changedVal}
525
+ minLength={minLength || null}
526
+ maxLength={maxLength || null}
527
+ onFocus={handleFocus}
528
+ onBlur={handleBlur}
529
+ onChange={(e: any) => {
530
+ handleChange(e);
531
+
532
+ // AI Predict
533
+ if (aiPredict) {
534
+ handleChangeSuggestionsFetchSafe(e, e.target.value);
535
+ }
536
+ }}
537
+ onKeyDown={handleKeyPressed}
538
+ disabled={disabled || null}
539
+ required={required || null}
540
+ readOnly={readOnly || null}
541
+ cols={cols || 20}
542
+ rows={rows || 2}
543
+ style={style}
544
+ {...attributes}
545
+ />
546
+
547
+ {/* AI Predict */}
548
+ {aiPredict && remainingText && (
549
+ <div
550
+ ref={aiInputRef}
551
+ className="position-absolute z-1"
552
+ style={{
553
+ left: `${originInputComputedStyle.current.fontSize + textWidth}px`,
554
+ top: originInputComputedStyle.current.textTop + 'px',
555
+ color: `rgba(${aiPredictRemainingTextRGB[0]}, ${aiPredictRemainingTextRGB[1]}, ${aiPredictRemainingTextRGB[2]}, ${calculateOpacity()})`,
556
+ pointerEvents: 'none',
557
+ fontSize: originInputComputedStyle.current.fontSize + 'px',
558
+ fontFamily: originInputComputedStyle.current.fontFamily,
559
+ letterSpacing: originInputComputedStyle.current.letterSpacing
560
+ }}
561
+ >
562
+ {remainingText}
563
+ </div>
564
+ )}
565
+
566
+ {/* Required marking */}
567
+ {required ? <>{requiredLabel || requiredLabel === '' ? requiredLabel : <span className="position-absolute end-0 top-0 my-2 mx-2"><span className="text-danger">*</span></span>}</> : ''}
568
+
569
+ </div>
570
+
571
+
572
+ {propExist(iconRight) ? <><span className={clsWrite(controlGroupTextClassName, 'input-group-text')}>{iconRight}</span></> : null}
573
+
574
+
282
575
  </div>
283
- {required ? <>{requiredLabel || requiredLabel === '' ? requiredLabel : <span className="position-absolute end-0 top-0 my-2 mx-2"><span className="text-danger">*</span></span>}</> : ''}
576
+
284
577
 
285
578
  </div>
286
579
 
@@ -194,8 +194,9 @@ export default function TreeList(props: TreeListProps) {
194
194
  function handleCollapse(e: any) {
195
195
  if ( disableCollapse ) return;
196
196
 
197
-
198
197
  e.preventDefault();
198
+ e.stopPropagation();
199
+
199
200
  const hyperlink = e.currentTarget;
200
201
  const url = hyperlink.getAttribute('href');
201
202
  const subElement = getNextSiblings(hyperlink, 'ul');
@@ -258,6 +259,8 @@ export default function TreeList(props: TreeListProps) {
258
259
 
259
260
  function handleSelect(e: any) {
260
261
  e.preventDefault();
262
+ e.stopPropagation();
263
+
261
264
  const hyperlink = e.currentTarget;
262
265
 
263
266
  if ( hyperlink.classList.contains('selected') ) {
@@ -7,6 +7,7 @@ import {
7
7
  flatData
8
8
  } from 'funda-utils/dist/cjs/object';
9
9
 
10
+
10
11
  import TreeList from './TreeList';
11
12
  import { initUlHeight, initAsyncItems } from './init-height';
12
13