funda-ui 4.6.101 → 4.6.151

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.
@@ -9,6 +9,8 @@ import useComId from 'funda-utils/dist/cjs/useComId';
9
9
  import useDebounce from 'funda-utils/dist/cjs/useDebounce';
10
10
  import useThrottle from 'funda-utils/dist/cjs/useThrottle';
11
11
  import useClickOutside from 'funda-utils/dist/cjs/useClickOutside';
12
+ import { htmlEncode } from 'funda-utils/dist/cjs/sanitize';
13
+
12
14
 
13
15
 
14
16
  // loader
@@ -20,8 +22,7 @@ import {
20
22
  formatLatestDisplayContent,
21
23
  formatName,
22
24
  fixHtmlTags,
23
- isStreamResponse,
24
- htmlEncode
25
+ isStreamResponse
25
26
  } from './utils/func';
26
27
 
27
28
  import useStreamController from './useStreamController';
@@ -38,6 +39,13 @@ export type MessageDetail = {
38
39
  tag: string; // such as '[reply]'
39
40
  };
40
41
 
42
+
43
+ export type QuestionData = {
44
+ title: string;
45
+ list: Array<string>;
46
+ };
47
+
48
+
41
49
  export interface FloatingButton {
42
50
  label: string; // HTML string
43
51
  value: string;
@@ -77,6 +85,7 @@ export type CustomRequestFunction = (
77
85
 
78
86
  export type ChatboxProps = {
79
87
  debug?: boolean;
88
+ defaultRows?: number;
80
89
  prefix?: string;
81
90
  contentRef?: React.RefObject<any>;
82
91
  model?: string;
@@ -104,10 +113,12 @@ export type ChatboxProps = {
104
113
  toolkitButtons?: FloatingButton[];
105
114
  newChatButton?: FloatingButton;
106
115
  customMethods?: CustomMethod[]; // [{"name": "method1", "func": "() => { console.log('test'); }"}, ...]
116
+ defaultQuestions?: QuestionData;
107
117
  customRequest?: CustomRequestFunction;
108
118
  renderParser?: (input: string) => Promise<string>;
109
119
  requestBodyFormatter?: (body: any, contextData: Record<string, any>, conversationHistory: MessageDetail[]) => Promise<Record<string, any>>;
110
120
  nameFormatter?: (input: string) => string;
121
+ onQuestionClick?: (text: string, methods: Record<string, Function>) => void;
111
122
  onInputChange?: (controlRef: React.RefObject<any>, val: string) => any;
112
123
  onInputCallback?: (input: string) => Promise<string>;
113
124
  onChunk?: (controlRef: React.RefObject<any>, lastContent: string, conversationHistory: MessageDetail[]) => any;
@@ -253,6 +264,7 @@ const Chatbox = (props: ChatboxProps) => {
253
264
 
254
265
  const {
255
266
  debug,
267
+ defaultRows,
256
268
  prefix,
257
269
  contentRef,
258
270
  model,
@@ -276,6 +288,7 @@ const Chatbox = (props: ChatboxProps) => {
276
288
  newChatButton,
277
289
  maxHistoryLength,
278
290
  customRequest,
291
+ onQuestionClick,
279
292
  renderParser,
280
293
  requestBodyFormatter,
281
294
  nameFormatter,
@@ -328,6 +341,7 @@ const Chatbox = (props: ChatboxProps) => {
328
341
 
329
342
  return {
330
343
  debug,
344
+ defaultRows,
331
345
  prefix,
332
346
  contentRef,
333
347
  model,
@@ -348,6 +362,7 @@ const Chatbox = (props: ChatboxProps) => {
348
362
  toolkitButtons,
349
363
  newChatButton,
350
364
  customRequest,
365
+ onQuestionClick,
351
366
  renderParser,
352
367
  requestBodyFormatter,
353
368
  nameFormatter,
@@ -357,6 +372,7 @@ const Chatbox = (props: ChatboxProps) => {
357
372
  onComplete,
358
373
 
359
374
  //
375
+ defaultQuestionsRes: questions,
360
376
  latestContextData,
361
377
  questionNameRes: _questionName,
362
378
  answerNameRes: _answerName,
@@ -371,6 +387,28 @@ const Chatbox = (props: ChatboxProps) => {
371
387
  }
372
388
 
373
389
 
390
+
391
+ //================================================================
392
+ // Custom Questions
393
+ //================================================================
394
+ const [questions, setQuestions] = useState<QuestionData | undefined>(props.defaultQuestions);
395
+ useEffect(() => {
396
+ if (props.defaultQuestions) {
397
+ setQuestions(props.defaultQuestions);
398
+ }
399
+ }, [props.defaultQuestions]);
400
+ const hasQuestion = () => {
401
+ return args().defaultQuestionsRes && (args().defaultQuestionsRes as QuestionData).list.length > 0;
402
+ };
403
+ const handleQuestionClick = (text: string) => {
404
+ if (inputContentRef.current) {
405
+ inputContentRef.current.set(text);
406
+ }
407
+
408
+ args().onQuestionClick?.(text, exposedMethods());
409
+ };
410
+
411
+
374
412
  //================================================================
375
413
  // Custom buttons
376
414
  //================================================================
@@ -380,7 +418,7 @@ const Chatbox = (props: ChatboxProps) => {
380
418
  setActiveButtons(prev => {
381
419
  const newState = { ...prev };
382
420
  // Turn off only buttons with "isSelect"
383
- args().toolkitButtons?.forEach((btn, index) => {
421
+ args().toolkitButtons?.forEach((btn: FloatingButton, index: number) => {
384
422
  if (btn.isSelect) {
385
423
  const _id = `${args().prefix || 'custom-'}chatbox-btn-tools-${chatId}${index}`;
386
424
  newState[_id] = false;
@@ -1120,7 +1158,7 @@ const Chatbox = (props: ChatboxProps) => {
1120
1158
  {/**------------- NO DATA -------------*/}
1121
1159
  {msgList.length === 0 ? <>
1122
1160
 
1123
- <div className="d-flex flex-column align-items-center justify-content-center h-50">
1161
+ <div className={`d-flex flex-column align-items-center justify-content-center ${hasQuestion() ? '' : 'h-50'}`}>
1124
1162
  <p>
1125
1163
  <svg width="70px" height="70px" viewBox="0 0 24 24" fill="none">
1126
1164
  <path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 13.5997 2.37562 15.1116 3.04346 16.4525C3.22094 16.8088 3.28001 17.2161 3.17712 17.6006L2.58151 19.8267C2.32295 20.793 3.20701 21.677 4.17335 21.4185L6.39939 20.8229C6.78393 20.72 7.19121 20.7791 7.54753 20.9565C8.88837 21.6244 10.4003 22 12 22Z" stroke="#858297" strokeWidth="1.5" />
@@ -1130,7 +1168,26 @@ const Chatbox = (props: ChatboxProps) => {
1130
1168
 
1131
1169
  </p>
1132
1170
  <p className="text-primary" dangerouslySetInnerHTML={{ __html: `${args().noDataPlaceholder}` }}></p>
1171
+
1172
+ {/** DEFAULT QUESTIONS */}
1173
+ {hasQuestion() && (
1174
+ <div className="default-questions">
1175
+ <div className="default-questions-title" dangerouslySetInnerHTML={{ __html: `${(args().defaultQuestionsRes as QuestionData).title}` }}></div>
1176
+ {(args().defaultQuestionsRes as QuestionData).list?.map((question: string, index: number) => (
1177
+ <div
1178
+ key={index}
1179
+ className="default-question-item"
1180
+ onClick={() => handleQuestionClick(question)}
1181
+ dangerouslySetInnerHTML={{ __html: `${question}` }}
1182
+ />
1183
+ ))}
1184
+ </div>
1185
+ )}
1186
+ {/** /DEFAULT QUESTIONS */}
1187
+
1133
1188
  </div>
1189
+
1190
+
1134
1191
  </> : null}
1135
1192
  {/**------------- /NO DATA -------------*/}
1136
1193
 
@@ -1300,7 +1357,6 @@ const Chatbox = (props: ChatboxProps) => {
1300
1357
  {/**------------- CONTROL AREA -------------*/}
1301
1358
  <div className="msgcontrol">
1302
1359
 
1303
-
1304
1360
  <Textarea
1305
1361
  ref={msInput}
1306
1362
  contentRef={inputContentRef}
@@ -1309,15 +1365,21 @@ const Chatbox = (props: ChatboxProps) => {
1309
1365
  placeholder={args().placeholder}
1310
1366
  disabled={loading ? true : false}
1311
1367
  onKeyDown={(event: React.KeyboardEvent) => {
1312
- if (event.key === 'Enter') {
1313
- event.preventDefault();
1368
+ // line breaks
1369
+ if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
1370
+ return;
1371
+ }
1372
+
1373
+ if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
1374
+ event.preventDefault(); // Prevent line breaks
1314
1375
  handleClickSafe();
1315
1376
  }
1377
+
1316
1378
  }}
1317
1379
  onChange={(e) => {
1318
1380
  args().onInputChange?.(inputContentRef.current, e.target.value);
1319
1381
  }}
1320
- rows={3}
1382
+ rows={args().defaultRows || 3}
1321
1383
  autoSize
1322
1384
  autoSizeMaxHeight={200}
1323
1385
  />
@@ -127,57 +127,3 @@ export function isStreamResponse(response: Response): boolean {
127
127
  return response.body instanceof ReadableStream;
128
128
  };
129
129
 
130
-
131
-
132
- /**
133
- * HTML entities encode
134
- *
135
- * @param {String} str Input text
136
- * @return {String} Filtered text
137
- */
138
- export function htmlEncode(str) {
139
-
140
- return str.replace(/[&<>'"]/g, tag => ({
141
- '&': '&amp;',
142
- '<': '&lt;',
143
- '>': '&gt;',
144
- "'": '&#39;',
145
- '"': '&quot;'
146
- }[tag]));
147
-
148
- }
149
-
150
-
151
- /**
152
- * HTML entities decode
153
- *
154
- * @param {String} str Input text
155
- * @return {String} Filtered text
156
- */
157
- export function htmlDecode(str) {
158
-
159
- let res = '';
160
- const entities = [
161
- ['amp', '&'],
162
- ['apos', '\''],
163
- ['#x27', '\''],
164
- ['#x2F', '/'],
165
- ['#39', '\''],
166
- ['#47', '/'],
167
- ['lt', '<'],
168
- ['gt', '>'],
169
- ['nbsp', ' '],
170
- ['quot', '"'],
171
- ['#60', '<'],
172
- ['#62', '>']
173
- ];
174
-
175
- for (let i = 0, max = entities.length; i < max; i++) {
176
- str = str.replace(new RegExp('&' + entities[i][0] + ';', 'g'), entities[i][1]);
177
- }
178
- res = str;
179
-
180
- return res;
181
-
182
- }
183
-
@@ -28,6 +28,8 @@ import {
28
28
  import { isNumeric } from 'funda-utils/dist/cjs/math';
29
29
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
30
30
 
31
+
32
+
31
33
  import Calendar from './Calendar';
32
34
 
33
35
 
@@ -196,6 +198,9 @@ const Date = forwardRef((props: DateProps, externalRef: any) => {
196
198
  return typeof s === 'undefined' || s === null || s === 'null' || s === '';
197
199
  };
198
200
 
201
+ const propExist = (p: any) => {
202
+ return typeof p !== 'undefined' && p !== null && p !== '';
203
+ };
199
204
 
200
205
 
201
206
 
@@ -901,7 +906,52 @@ const Date = forwardRef((props: DateProps, externalRef: any) => {
901
906
  return [_res, v]
902
907
 
903
908
  }
904
-
909
+
910
+ function tools() {
911
+ return <span className={combinedCls(
912
+ 'date2d__control-tools',
913
+ {
914
+ 'date2d__control-tools--hover-show-tools': SHOW_TOOLS_ENABLED
915
+ }
916
+
917
+ )}>
918
+
919
+ <a
920
+ tabIndex={-1}
921
+ href="#"
922
+ className={combinedCls(
923
+ 'date2d__control-tools__close',
924
+ {
925
+ 'd-none': HIDE_CLEAR_BTN_ENABLED || !dateDefaultValueExist
926
+ }
927
+ )} onClick={(e: React.MouseEvent) => {
928
+ e.preventDefault();
929
+ e.stopPropagation(); // Avoid triggering pop-ups
930
+
931
+ clearAll();
932
+ onClear?.(getFullTimeData(''));
933
+ }}
934
+ ><svg width="12px" height="12px" viewBox="0 0 1024 1024"><path fill="#000" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z" /></svg></a>
935
+
936
+ <a
937
+ tabIndex={-1}
938
+ href="#"
939
+ className="date2d__control-tools__trigger"
940
+ onClick={(e: React.MouseEvent) => {
941
+ e.preventDefault();
942
+ e.stopPropagation(); // Avoid triggering pop-ups
943
+
944
+ handleShow();
945
+ }}
946
+ >
947
+ <svg width="14px" height="14px" viewBox="0 0 24 24" fill="none">
948
+ <path d="M3 9H21M9 15L11 17L15 13M7 3V5M17 3V5M6.2 21H17.8C18.9201 21 19.4802 21 19.908 20.782C20.2843 20.5903 20.5903 20.2843 20.782 19.908C21 19.4802 21 18.9201 21 17.8V8.2C21 7.07989 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V17.8C3 18.9201 3 19.4802 3.21799 19.908C3.40973 20.2843 3.71569 20.5903 4.09202 20.782C4.51984 21 5.07989 21 6.2 21Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
949
+ </svg>
950
+ </a>
951
+
952
+ </span>;
953
+ }
954
+
905
955
 
906
956
  useEffect(() => {
907
957
 
@@ -1309,54 +1359,21 @@ const Date = forwardRef((props: DateProps, externalRef: any) => {
1309
1359
  */}
1310
1360
 
1311
1361
  </div>
1362
+
1363
+
1364
+
1365
+ {/* TOOLS */}
1366
+ {propExist(iconRight) ? tools() : null}
1367
+ {/* /TOOLS */}
1368
+
1369
+
1312
1370
  </>}
1313
1371
  style={style}
1314
1372
  {...attributes}
1315
1373
  />
1316
1374
 
1317
1375
  {/* TOOLS */}
1318
- <span className={combinedCls(
1319
- 'date2d__control-tools',
1320
- {
1321
- 'date2d__control-tools--hover-show-tools': SHOW_TOOLS_ENABLED
1322
- }
1323
-
1324
- )}>
1325
-
1326
- <a
1327
- tabIndex={-1}
1328
- href="#"
1329
- className={combinedCls(
1330
- 'date2d__control-tools__close',
1331
- {
1332
- 'd-none': HIDE_CLEAR_BTN_ENABLED || !dateDefaultValueExist
1333
- }
1334
- )} onClick={(e: React.MouseEvent) => {
1335
- e.preventDefault();
1336
- e.stopPropagation(); // Avoid triggering pop-ups
1337
-
1338
- clearAll();
1339
- onClear?.(getFullTimeData(''));
1340
- }}
1341
- ><svg width="12px" height="12px" viewBox="0 0 1024 1024"><path fill="#000" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z" /></svg></a>
1342
-
1343
- <a
1344
- tabIndex={-1}
1345
- href="#"
1346
- className="date2d__control-tools__trigger"
1347
- onClick={(e: React.MouseEvent) => {
1348
- e.preventDefault();
1349
- e.stopPropagation(); // Avoid triggering pop-ups
1350
-
1351
- handleShow();
1352
- }}
1353
- >
1354
- <svg width="14px" height="14px" viewBox="0 0 24 24" fill="none">
1355
- <path d="M3 9H21M9 15L11 17L15 13M7 3V5M17 3V5M6.2 21H17.8C18.9201 21 19.4802 21 19.908 20.782C20.2843 20.5903 20.5903 20.2843 20.782 19.908C21 19.4802 21 18.9201 21 17.8V8.2C21 7.07989 21 6.51984 20.782 6.09202C20.5903 5.71569 20.2843 5.40973 19.908 5.21799C19.4802 5 18.9201 5 17.8 5H6.2C5.0799 5 4.51984 5 4.09202 5.21799C3.71569 5.40973 3.40973 5.71569 3.21799 6.09202C3 6.51984 3 7.07989 3 8.2V17.8C3 18.9201 3 19.4802 3.21799 19.908C3.40973 20.2843 3.71569 20.5903 4.09202 20.782C4.51984 21 5.07989 21 6.2 21Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
1356
- </svg>
1357
- </a>
1358
-
1359
- </span>
1376
+ {!propExist(iconRight) ? tools() : null}
1360
1377
  {/* /TOOLS */}
1361
1378
 
1362
1379
 
@@ -6,6 +6,7 @@ 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
+
9
10
  export type InputProps = {
10
11
  contentRef?: React.ForwardedRef<any>;
11
12
  wrapperClassName?: string;
@@ -50,7 +50,6 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
50
50
 
51
51
 
52
52
 
53
-
54
53
  export type SelectOptionChangeFnType = (arg1: any, arg2: any, arg3: any) => void;
55
54
 
56
55
  export interface MultiSelectDataConfig {
@@ -68,9 +67,9 @@ export interface OptionConfig {
68
67
  disabled?: boolean;
69
68
  optgroup?: any[];
70
69
  group?: boolean;
71
- label: any;
72
- listItemLabel?: any;
73
- value: any;
70
+ label: string;
71
+ listItemLabel?: string;
72
+ value: string | number | boolean;
74
73
  queryString: string | number;
75
74
  callback?: () => void;
76
75
  }
@@ -109,7 +108,7 @@ export type SelectProps = {
109
108
  multiSelect?: MultiSelectConfig;
110
109
  multiSelectEntireAreaTrigger?: boolean;
111
110
  multiSelectSelectedItemOnlyStatus?: multiSelectSelectedItemOnlyStatusConfig;
112
- renderSelectedValue?: (selectedData: MultiSelectControlValConfig, removeFunc: (e: React.MouseEvent) => void) => void;
111
+ renderSelectedValue?: (selectedData: MultiSelectControlValConfig, removeFunc: (e: React.MouseEvent) => void) => React.ReactNode;
113
112
  cleanTrigger?: CleanTriggerConfig;
114
113
  defaultValue?: string | OptionConfig;
115
114
  value?: string | OptionConfig;
@@ -13,6 +13,12 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
13
13
 
14
14
 
15
15
 
16
+ export interface TagValConfig {
17
+ content: string;
18
+ id: number;
19
+ [key: string]: string | boolean | number; // Allows dynamic attributes
20
+ }
21
+
16
22
  export type TagInputProps = {
17
23
  contentRef?: React.ForwardedRef<any>;
18
24
  wrapperClassName?: string;
@@ -27,6 +33,7 @@ export type TagInputProps = {
27
33
  required?: any;
28
34
  readOnly?: any;
29
35
  placeholder?: string;
36
+ renderSelectedValue?: (selectedData: TagValConfig[], removeFunc: (e: React.MouseEvent) => void) => React.ReactNode;
30
37
  /** Whether to use square brackets to save result and initialize default value */
31
38
  extractValueByBrackets?: boolean;
32
39
  /** -- */
@@ -52,6 +59,7 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
52
59
  disabled,
53
60
  required,
54
61
  placeholder,
62
+ renderSelectedValue,
55
63
  readOnly,
56
64
  value,
57
65
  requiredLabel,
@@ -82,7 +90,7 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
82
90
  const max = maxTags ? maxTags : 10;
83
91
  const [lastId, setLastId] = useState<number>(-1);
84
92
  const [userInput, setUserInput] = useState<string>('');
85
- const [items, setItems] = useState<any[]>([]);
93
+ const [items, setItems] = useState<TagValConfig[]>([]);
86
94
  const [alreadyInItems, setAlreadyInItems] = useState<boolean>(false);
87
95
  const [onComposition, setOnComposition] = useState<boolean>(false);
88
96
 
@@ -115,7 +123,7 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
115
123
  } else {
116
124
  const _val = VALUE_BY_BRACKETS ? extractContentsOfBrackets(defaultValue) : defaultValue.trim().replace(/^\,|\,$/g, '').split(',');
117
125
  if (Array.isArray(_val)) {
118
- setItems(_val.map((item: any, index: number) => {
126
+ setItems(_val.map((item: string, index: number) => {
119
127
  return {
120
128
  content: item,
121
129
  id: index
@@ -129,13 +137,15 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
129
137
  }
130
138
 
131
139
 
132
- function handleRemove(e: MouseEvent<HTMLAnchorElement>) {
140
+ function handleRemove(e: any) {
133
141
  e.preventDefault();
142
+ e.stopPropagation(); /* REQUIRED */
134
143
 
135
144
  const idToRemove = Number(e.currentTarget.dataset.item);
136
145
  const newArray = items!.filter((item: any) => item.id !== idToRemove);
137
146
  setItems(newArray);
138
147
 
148
+
139
149
  //
140
150
  onChange?.(inputRef.current, newArray, VALUE_BY_BRACKETS ? convertArrToValByBrackets(newArray.map(v => v.content)) : newArray.map(v => v.content).join(','));
141
151
  }
@@ -270,13 +280,23 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
270
280
  <div className="tag-input__control-wrapper">
271
281
  <div>
272
282
  <ul className="tag-input__list">
273
- {items ? items.map((item: any, index: number) => (
274
- <li key={index}>
275
- {item.content}
276
-
277
- <a href="#" tabIndex={-1} onClick={handleRemove} data-item={item.id}><svg width="10px" height="10px" viewBox="0 0 1024 1024"><path fill="#000" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z"/></svg></a>
278
- </li>
279
- )) : null}
283
+
284
+ {/* ITEMS LIST */}
285
+ {typeof renderSelectedValue === 'function' ? <>
286
+ {renderSelectedValue(items, handleRemove)}
287
+ </> : <>
288
+ {items ? items.map((item: any, index: number) => (
289
+ <li key={index}>
290
+ {item.content}
291
+
292
+ <a href="#" tabIndex={-1} onClick={handleRemove} data-item={item.id}><svg width="10px" height="10px" viewBox="0 0 1024 1024"><path fill="#000" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z"/></svg></a>
293
+ </li>
294
+ )) : null}
295
+ </>}
296
+
297
+
298
+
299
+
280
300
  </ul>
281
301
 
282
302
 
@@ -8,6 +8,7 @@ import { actualPropertyValue, getTextTop } from 'funda-utils/dist/cjs/inputsCalc
8
8
  import useDebounce from 'funda-utils/dist/cjs/useDebounce';
9
9
 
10
10
 
11
+
11
12
  export type TextareaProps = {
12
13
  contentRef?: React.ForwardedRef<any>; // could use "Array" on contentRef.current, such as contentRef.current[0], contentRef.current[1]
13
14
  wrapperClassName?: string;
@@ -51,7 +51,6 @@ const App = () => {
51
51
 
52
52
  */
53
53
  import { useEffect, useState, useCallback } from "react";
54
-
55
54
  export interface AutosizeTextAreaProps {
56
55
  el: any;
57
56
  value: string;
@@ -66,68 +65,67 @@ const useAutosizeTextArea = ({
66
65
  cb
67
66
  }: AutosizeTextAreaProps): { reset: () => void } => {
68
67
 
69
- const [defaultRowHeight, setDefaultRowHeight] = useState(0);
70
68
  const [defaultRowHeightInit, setDefaultRowHeightInit] = useState(false);
71
69
 
72
70
  // Reset function to restore default height
73
71
  const reset = useCallback(() => {
74
- if (el && defaultRowHeight > 0) {
75
- el.style.height = defaultRowHeight + "px";
76
-
77
- // Get current dimensions after reset
78
- const style = (el as any).currentStyle || window.getComputedStyle(el);
79
- const _controlWidth = el.scrollWidth + parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth);
80
- cb?.([_controlWidth, defaultRowHeight]);
81
- }
82
- }, [el, defaultRowHeight, cb]);
72
+ if (!el) return;
73
+
74
+ const scrollHeight = el.scrollHeight;
75
+ el.style.height = scrollHeight + "px";
76
+
77
+ // Get current dimensions after reset
78
+ const style = window.getComputedStyle(el);
79
+ const _controlWidth = el.scrollWidth + parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth);
80
+ cb?.([_controlWidth, scrollHeight]);
81
+ }, [el, cb]);
83
82
 
84
83
  useEffect(() => {
85
- if (el) {
86
- const style = (el as any).currentStyle || window.getComputedStyle(el);
87
- const _controlWidth = el.scrollWidth + parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth);
88
-
89
- // initialize default row height
90
- if (el.scrollHeight > 0 && !defaultRowHeightInit) {
91
-
92
- let _defaultRowHeight = el.scrollHeight + parseInt(style.borderTopWidth) + parseInt(style.borderBottomWidth);
93
- if (maxHeight != 0 && _defaultRowHeight >= maxHeight) {
94
- _defaultRowHeight = maxHeight;
95
- }
96
-
97
- setDefaultRowHeight(_defaultRowHeight);
98
- setDefaultRowHeightInit(true);
99
- }
100
-
101
- // restore default row height
102
- if (defaultRowHeight > 0) {
103
- el.style.height = defaultRowHeight + "px";
84
+ if (!el) return;
85
+
86
+ // Initialize default height
87
+ if (!defaultRowHeightInit) {
88
+ el.style.height = 'auto';
89
+ const initialHeight = el.scrollHeight;
90
+ setDefaultRowHeightInit(true);
91
+
92
+ // If the height is 0, set it to "auto"
93
+ if (initialHeight === 0) {
94
+ el.style.height = "auto";
95
+ } else {
96
+ el.style.height = initialHeight + "px";
104
97
  }
105
98
 
106
- // reset the height momentarily to get the correct scrollHeight for the textarea
107
- const scrollHeight = el.scrollHeight;
99
+ }
108
100
 
101
+ // Get dimensions
102
+ const style = window.getComputedStyle(el);
103
+ const _controlWidth = el.scrollWidth + parseInt(style.borderLeftWidth) + parseInt(style.borderRightWidth);
104
+
105
+ // Calculate height
106
+ el.style.height = 'auto';
107
+ let finalHeight = el.scrollHeight;
108
+
109
+ // Apply max height limit if needed
110
+ if (maxHeight > 0 && finalHeight > maxHeight) {
111
+ finalHeight = maxHeight;
112
+ }
109
113
 
110
- // then set the height directly, outside of the render loop
111
- // Trying to set this with state or a ref will product an incorrect value.
114
+ // Set final height
115
+ // If the height is 0, set it to "auto"
116
+ if (finalHeight === 0) {
117
+ el.style.height = "auto";
118
+ } else {
119
+ el.style.height = finalHeight + "px";
120
+ }
112
121
 
113
- // !!! Compare initial height and changed height
114
- if (scrollHeight > defaultRowHeight && defaultRowHeight > 0) {
115
122
 
116
- let _scrollHeight = scrollHeight;
117
- if (maxHeight != 0 && _scrollHeight >= maxHeight) {
118
- _scrollHeight = maxHeight;
119
- }
120
-
121
- el.style.height = _scrollHeight + "px";
122
-
123
- }
123
+ // Callback
124
+ cb?.([_controlWidth, finalHeight]);
124
125
 
125
- cb?.([_controlWidth, scrollHeight]);
126
- }
127
- }, [el, value]);
126
+ }, [el, value, maxHeight, defaultRowHeightInit]);
128
127
 
129
128
  return { reset };
130
129
  };
131
130
 
132
- export default useAutosizeTextArea;
133
-
131
+ export default useAutosizeTextArea;