funda-ui 4.7.133 → 4.7.152

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 (57) hide show
  1. package/CascadingSelect/index.css +15 -4
  2. package/CascadingSelect/index.d.ts +2 -0
  3. package/CascadingSelect/index.js +294 -22
  4. package/CascadingSelectE2E/index.css +15 -4
  5. package/CascadingSelectE2E/index.d.ts +2 -0
  6. package/CascadingSelectE2E/index.js +300 -28
  7. package/Chatbox/index.d.ts +7 -0
  8. package/Chatbox/index.js +247 -163
  9. package/Checkbox/index.js +4 -2
  10. package/LiveSearch/index.js +2 -1
  11. package/Refresher/index.js +3 -3
  12. package/Select/index.css +33 -0
  13. package/Select/index.d.ts +1 -0
  14. package/Select/index.js +350 -39
  15. package/SplitterPanel/index.js +3 -3
  16. package/Switch/index.js +4 -2
  17. package/Utils/format-string.d.ts +2 -1
  18. package/Utils/format-string.js +22 -12
  19. package/Utils/time.d.ts +3 -3
  20. package/Utils/useIsMobile.js +3 -3
  21. package/lib/cjs/CascadingSelect/index.d.ts +2 -0
  22. package/lib/cjs/CascadingSelect/index.js +294 -22
  23. package/lib/cjs/CascadingSelectE2E/index.d.ts +2 -0
  24. package/lib/cjs/CascadingSelectE2E/index.js +300 -28
  25. package/lib/cjs/Chatbox/index.d.ts +7 -0
  26. package/lib/cjs/Chatbox/index.js +247 -163
  27. package/lib/cjs/Checkbox/index.js +4 -2
  28. package/lib/cjs/LiveSearch/index.js +2 -1
  29. package/lib/cjs/Refresher/index.js +3 -3
  30. package/lib/cjs/Select/index.d.ts +1 -0
  31. package/lib/cjs/Select/index.js +350 -39
  32. package/lib/cjs/SplitterPanel/index.js +3 -3
  33. package/lib/cjs/Switch/index.js +4 -2
  34. package/lib/cjs/Utils/format-string.d.ts +2 -1
  35. package/lib/cjs/Utils/format-string.js +22 -12
  36. package/lib/cjs/Utils/time.d.ts +3 -3
  37. package/lib/cjs/Utils/useIsMobile.js +3 -3
  38. package/lib/css/CascadingSelect/index.css +15 -4
  39. package/lib/css/CascadingSelectE2E/index.css +15 -4
  40. package/lib/css/Select/index.css +33 -0
  41. package/lib/esm/CascadingSelect/index.scss +22 -7
  42. package/lib/esm/CascadingSelect/index.tsx +49 -1
  43. package/lib/esm/CascadingSelectE2E/Group.tsx +1 -0
  44. package/lib/esm/CascadingSelectE2E/index.scss +23 -6
  45. package/lib/esm/CascadingSelectE2E/index.tsx +53 -1
  46. package/lib/esm/Chatbox/index.tsx +96 -11
  47. package/lib/esm/Checkbox/index.tsx +5 -3
  48. package/lib/esm/LiveSearch/index.tsx +2 -1
  49. package/lib/esm/Select/index.scss +43 -2
  50. package/lib/esm/Select/index.tsx +81 -24
  51. package/lib/esm/Select/utils/func.ts +0 -10
  52. package/lib/esm/Switch/index.tsx +4 -2
  53. package/lib/esm/Textarea/index.tsx +0 -1
  54. package/lib/esm/Utils/hooks/useIsMobile.tsx +9 -12
  55. package/lib/esm/Utils/libs/format-string.ts +22 -12
  56. package/lib/esm/Utils/libs/time.ts +6 -6
  57. package/package.json +1 -1
@@ -3,7 +3,6 @@ import React, { useEffect, useState, useRef, KeyboardEvent, forwardRef, useImper
3
3
  import {
4
4
  formatIndentVal,
5
5
  unique,
6
- stripHTML,
7
6
  removeItemOnce,
8
7
  optionsCustomSelectFlat,
9
8
  isObject
@@ -46,10 +45,12 @@ import {
46
45
  disableBodyScroll,
47
46
  enableBodyScroll,
48
47
  } from 'funda-utils/dist/cjs/bodyScrollLock';
48
+ import {
49
+ stripTagsAndContent
50
+ } from 'funda-utils/dist/cjs/format-string';
49
51
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
50
52
 
51
53
 
52
-
53
54
  export type SelectOptionChangeFnType = (arg1: any, arg2: any, arg3: any) => void;
54
55
 
55
56
  export interface MultiSelectDataConfig {
@@ -120,6 +121,7 @@ export type SelectProps = {
120
121
  placeholder?: string;
121
122
  options?: OptionConfig[] | string;
122
123
  lockBodyScroll?: boolean;
124
+ loader?: React.ReactNode;
123
125
  hierarchical?: boolean;
124
126
  indentation?: string;
125
127
  doubleIndent?: boolean;
@@ -186,6 +188,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
186
188
  spellCheck,
187
189
  options,
188
190
  cleanTrigger,
191
+ loader,
189
192
  lockBodyScroll,
190
193
  hierarchical,
191
194
  indentation,
@@ -217,6 +220,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
217
220
  } = props;
218
221
 
219
222
 
223
+ const QUERY_STRING_PLACEHOLDER = '------'; // Invalid parameters for the first automatic request
220
224
  const DEPTH = depth || 1055; // the default value same as bootstrap
221
225
  const MANUAL_REQ = typeof fetchTrigger !== 'undefined' && fetchTrigger === true ? true : false; // Manual requests
222
226
  const LIVE_SEARCH_DISABLED = !MANUAL_REQ && typeof window !== 'undefined' && typeof (window as any)['funda-ui__Select-disable-livesearch'] !== 'undefined' ? true : false; // Globally disable real-time search functionality (only valid for non-dynamic requests)
@@ -240,7 +244,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
240
244
  const listRef = useRef<any>(null);
241
245
  const listContentRef = useRef<any>(null);
242
246
  const optionsRes = options ? (isJSON(options) ? JSON.parse(options as string) : options) : [];
243
- const LIST_CONTAINER_MAX_HEIGHT = 350;
247
+ const LIST_CONTAINER_MAX_HEIGHT = 300;
244
248
 
245
249
  const keyboardSelectedItem = useRef<any>(null);
246
250
 
@@ -252,12 +256,20 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
252
256
  const [orginalData, setOrginalData] = useState<OptionConfig[]>(staticOptionsData);
253
257
  const [optionsData, setOptionsData] = useState<OptionConfig[]>(staticOptionsData);
254
258
  const [hasErr, setHasErr] = useState<boolean>(false);
259
+
260
+ // Set the final result
255
261
  const [controlLabel, setControlLabel] = useState<string | undefined>('');
256
262
  const [controlValue, setControlValue] = useState<string | undefined>('');
263
+
264
+ //
257
265
  const [controlTempValue, setControlTempValue] = useState<string | null>(null);
258
266
  const [isOpen, setIsOpen] = useState<boolean>(false);
259
267
  const [incomingData, setIncomingData] = useState<string | null | undefined>(null);
260
268
  const [firstRequestExecuted, setFirstRequestExecuted] = useState<boolean>(false);
269
+ const [handleFirstFetchCompleted, setHandleFirstFetchCompleted] = useState<boolean>(false);
270
+
271
+ // filter status
272
+ const [filterItemsHasNoMatchData, setFilterItemsHasNoMatchData] = useState<boolean>(false);
261
273
 
262
274
 
263
275
  // blinking cursor
@@ -458,6 +470,11 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
458
470
 
459
471
  if (fetchUpdate) {
460
472
 
473
+ // update filter status
474
+ setFilterItemsHasNoMatchData(false);
475
+
476
+
477
+ // Make a request
461
478
  handleFetch(val).then((response: any) => {
462
479
  _orginalData = response;
463
480
  const _filterRes = update(_orginalData);
@@ -827,6 +844,8 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
827
844
 
828
845
 
829
846
  function adjustMultiControlContainerHeight(scrollbarInit: boolean = true) {
847
+ if (rootMultiRef.current === null) return;
848
+
830
849
  setTimeout(() => {
831
850
 
832
851
 
@@ -904,7 +923,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
904
923
 
905
924
  // You need to wait for the height of the pop-up container to be set
906
925
  // Detect position、
907
- if (window.innerHeight - _triggerBox.top > _contentOldHeight) {
926
+ if (window.innerHeight - _triggerBox.top > 100) {
908
927
  targetPos = 'bottom';
909
928
  } else {
910
929
  targetPos = 'top';
@@ -973,9 +992,6 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
973
992
  }
974
993
 
975
994
 
976
-
977
-
978
-
979
995
  // STEP 4:
980
996
  //-----------
981
997
  // Adjust position
@@ -1067,9 +1083,9 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1067
1083
 
1068
1084
 
1069
1085
  // nomatch & button of select all
1070
- const _nodataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1086
+ const _noDataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1071
1087
  const _btnSelectAll = listContentRef.current.querySelector('.custom-select-multi__control-option-item--select-all');
1072
- _nodataDiv.classList.add('hide');
1088
+ _noDataDiv.classList.add('hide');
1073
1089
  if (_btnSelectAll !== null) _btnSelectAll.classList.remove('hide');
1074
1090
 
1075
1091
 
@@ -1083,6 +1099,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1083
1099
  if (listContentRef.current === null) return;
1084
1100
 
1085
1101
 
1102
+ let invisibleItems: number = 0;
1086
1103
  [].slice.call(listContentRef.current.querySelectorAll('.custom-select-multi__control-option-item')).forEach((node: any) => {
1087
1104
 
1088
1105
  // Avoid fatal errors causing page crashes
@@ -1104,22 +1121,29 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1104
1121
 
1105
1122
  });
1106
1123
 
1124
+ // Determine if all options are hidden
1125
+ const allHidden = [].slice.call(listContentRef.current.querySelectorAll('.custom-select-multi__control-option-item'))
1126
+ .every((node: any) => node.classList.contains('hide'));
1107
1127
 
1128
+
1108
1129
  // no data label
1109
1130
  popwinNoMatchInit();
1110
1131
 
1111
1132
 
1112
1133
  // display all filtered items
1113
1134
  const _btnSelectAll = listContentRef.current.querySelector('.custom-select-multi__control-option-item--select-all');
1114
- const _nodataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1135
+ const _noDataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1115
1136
  if ((val === null ? '' : val).replace(/\s/g, "") === '') {
1116
1137
  [].slice.call(listContentRef.current.querySelectorAll('.custom-select-multi__control-option-item')).forEach((node: any) => {
1117
1138
  node.classList.remove('hide');
1118
1139
  });
1119
- _nodataDiv.classList.add('hide');
1140
+ _noDataDiv.classList.add('hide');
1120
1141
  if (_btnSelectAll !== null) _btnSelectAll.classList.remove('hide');
1121
1142
  }
1122
1143
 
1144
+ // filter status
1145
+ setFilterItemsHasNoMatchData(allHidden);
1146
+
1123
1147
 
1124
1148
  // Appropriate list container height
1125
1149
  popwinContainerHeightAdjust();
@@ -1154,7 +1178,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1154
1178
  if (listContentRef.current === null) return;
1155
1179
 
1156
1180
  const _btnSelectAll = listContentRef.current.querySelector('.custom-select-multi__control-option-item--select-all');
1157
- const _nodataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1181
+ const _noDataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1158
1182
  const emptyFieldsCheck = [].slice.call(listContentRef.current.querySelectorAll('.custom-select-multi__control-option-item')).every((node: any) => {
1159
1183
  if (!node.classList.contains('hide')) {
1160
1184
  return false;
@@ -1163,10 +1187,10 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1163
1187
  });
1164
1188
 
1165
1189
  if (emptyFieldsCheck) {
1166
- _nodataDiv.classList.remove('hide');
1190
+ _noDataDiv.classList.remove('hide');
1167
1191
  if (_btnSelectAll !== null) _btnSelectAll.classList.add('hide');
1168
1192
  } else {
1169
- _nodataDiv.classList.add('hide');
1193
+ _noDataDiv.classList.add('hide');
1170
1194
  if (_btnSelectAll !== null) _btnSelectAll.classList.remove('hide');
1171
1195
  }
1172
1196
 
@@ -1203,6 +1227,9 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1203
1227
  // update temporary value
1204
1228
  setControlTempValue(null);
1205
1229
 
1230
+ // update filter status
1231
+ setFilterItemsHasNoMatchData(false);
1232
+
1206
1233
 
1207
1234
  // Unlocks the page
1208
1235
  if (LOCK_BODY_SCROLL) enableBodyScroll(document.querySelector('body'));
@@ -1221,8 +1248,14 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1221
1248
  handleFirstFetch(curValue).then((response: any) => {
1222
1249
  if (response.length > 0) {
1223
1250
  // nomatch
1224
- const _nodataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1225
- _nodataDiv.classList.add('hide');
1251
+ const _noDataDiv = listContentRef.current.querySelector('.custom-select-multi__control-option-item--nomatch');
1252
+ _noDataDiv.classList.add('hide');
1253
+
1254
+ // After the data is loaded, reinitialize the pop-up window position and height
1255
+ setTimeout(() => {
1256
+ popwinPosInit();
1257
+ }, 0);
1258
+
1226
1259
  }
1227
1260
  });
1228
1261
 
@@ -1748,6 +1781,9 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1748
1781
  // update temporary value
1749
1782
  setControlTempValue(null);
1750
1783
 
1784
+ // update filter status
1785
+ setFilterItemsHasNoMatchData(false);
1786
+
1751
1787
  }
1752
1788
 
1753
1789
 
@@ -1833,9 +1869,12 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
1833
1869
 
1834
1870
  async function handleFirstFetch(inputVal: any = null) {
1835
1871
  const _oparams: any[] = fetchFuncMethodParams || [];
1836
- const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (MANUAL_REQ ? '------' : ''));
1872
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (MANUAL_REQ ? QUERY_STRING_PLACEHOLDER : ''));
1837
1873
  const res = await fetchData((_params).join(','), finalRes(inputVal), inputVal);
1838
1874
 
1875
+ // Set an identifier indicating that the first request has been completed
1876
+ if (!handleFirstFetchCompleted) setHandleFirstFetchCompleted(true);
1877
+
1839
1878
  return res;
1840
1879
  }
1841
1880
 
@@ -2077,8 +2116,16 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2077
2116
  handleFirstFetch(value);
2078
2117
  }
2079
2118
 
2119
+ // Forced assignment does not depend on "fetch" or "firstRequestAutoExe"
2120
+ // Don't use "value.value && value.label" directly, if it is empty, it will be treated as FALSE
2121
+ if ( value && typeof value === 'object' ) {
2122
+ if (typeof value.value !== 'undefined' && value.value !== null) setControlValue(value.value as string);
2123
+ if (typeof value.label !== 'undefined' && value.label !== null) setControlLabel(formatIndentVal(value.label, INDENT_LAST_PLACEHOLDER));
2124
+ }
2080
2125
 
2081
2126
 
2127
+ //
2128
+ //--------------
2082
2129
  return () => {
2083
2130
  if (LOCK_BODY_SCROLL) clearAllBodyScrollLocks();
2084
2131
  }
@@ -2122,6 +2169,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2122
2169
  }, [orginalData]); // Avoid the issue that `setOptionsData(orginalData)` sets the original value to empty on the first entry
2123
2170
 
2124
2171
 
2172
+
2125
2173
  return (
2126
2174
  <>
2127
2175
 
@@ -2182,7 +2230,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2182
2230
  disabled={disabled || null}
2183
2231
  required={required || null}
2184
2232
  readOnly={INPUT_READONLY}
2185
- value={controlTempValue || controlTempValue === '' ? controlTempValue : (MULTI_SEL_VALID ? (VALUE_BY_BRACKETS ? convertArrToValByBrackets(formatIndentVal(controlArr.labels, INDENT_LAST_PLACEHOLDER).map((v: any) => stripHTML(v))) : formatIndentVal(controlArr.labels, INDENT_LAST_PLACEHOLDER).map((v: any) => stripHTML(v)).join(',')) : stripHTML(controlLabel as never))} // do not use `defaultValue`
2233
+ value={controlTempValue || controlTempValue === '' ? controlTempValue : (MULTI_SEL_VALID ? (VALUE_BY_BRACKETS ? convertArrToValByBrackets(formatIndentVal(controlArr.labels, INDENT_LAST_PLACEHOLDER).map((v: any) => stripTagsAndContent(v))) : formatIndentVal(controlArr.labels, INDENT_LAST_PLACEHOLDER).map((v: any) => stripTagsAndContent(v)).join(',')) : stripTagsAndContent(controlLabel as never))} // do not use `defaultValue`
2186
2234
 
2187
2235
  style={{
2188
2236
  cursor: 'pointer',
@@ -2261,7 +2309,7 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2261
2309
  'animated': generateInputFocusStr() === BLINKING_CURSOR_STR
2262
2310
  }
2263
2311
  )}>
2264
- {controlTempValue || controlTempValue === '' ? (controlTempValue.length === 0 ? <span className={`${!hideBlinkingCursor() ? 'control-placeholder' : ''}`}>{generateInputFocusStr()}</span> : <span>{controlTempValue}</span>) : (stripHTML(controlLabel as never).length === 0 ? <span className={`${!hideBlinkingCursor() ? 'control-placeholder' : ''}`}>{generateInputFocusStr()}</span> : <span>{stripHTML(controlLabel as never)}</span>)}
2312
+ {controlTempValue || controlTempValue === '' ? (controlTempValue.length === 0 ? <span className={`${!hideBlinkingCursor() ? 'control-placeholder' : ''}`}>{generateInputFocusStr()}</span> : <span>{controlTempValue}</span>) : (stripTagsAndContent(controlLabel as never).length === 0 ? <span className={`${!hideBlinkingCursor() ? 'control-placeholder' : ''}`}>{generateInputFocusStr()}</span> : <span>{stripTagsAndContent(controlLabel as never)}</span>)}
2265
2313
  </span>
2266
2314
 
2267
2315
  </div>
@@ -2458,9 +2506,9 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2458
2506
  key={'selected-item-' + index}
2459
2507
  data-value={controlArr.values[index]}
2460
2508
  data-label-full={item}
2461
- data-label-text={formatIndentVal(stripHTML(item), INDENT_LAST_PLACEHOLDER)}
2509
+ data-label-text={formatIndentVal(stripTagsAndContent(item), INDENT_LAST_PLACEHOLDER)}
2462
2510
  >
2463
- {formatIndentVal(stripHTML(item), INDENT_LAST_PLACEHOLDER)}
2511
+ {formatIndentVal(stripTagsAndContent(item), INDENT_LAST_PLACEHOLDER)}
2464
2512
 
2465
2513
  <a
2466
2514
  href="#"
@@ -2656,9 +2704,18 @@ const Select = forwardRef((props: SelectProps, externalRef: any) => {
2656
2704
  {/* /CLEAN BUTTON (Only Single selection) */}
2657
2705
 
2658
2706
 
2659
- {/* NO MATCH */}
2660
- <button tabIndex={-1} type="button" className="list-group-item list-group-item-action no-match border-0 custom-select-multi__control-option-item--nomatch hide" disabled>{fetchNoneInfo || 'No match yet'}</button>
2661
- {/* /NO MATCH */}
2707
+ {/* NO MATCH & LOADER */}
2708
+ <button tabIndex={-1} type="button" className="list-group-item list-group-item-action no-match border-0 custom-select-multi__control-option-item--nomatch hide" disabled>
2709
+ {
2710
+ // (1) Handling async data with the click event
2711
+ (!FIRST_REQUEST_AUTO && !handleFirstFetchCompleted) ||
2712
+
2713
+ // (2) Every time the input changes or the search button is clicked, a data request will be triggered
2714
+ (fetchUpdate && !filterItemsHasNoMatchData && controlTempValue !== '')
2715
+ ? <><div className="cus-select-loader">{loader || <svg height="12px" width="12px" viewBox="0 0 512 512"><g><path fill="inherit" d="M256,0c-23.357,0-42.297,18.932-42.297,42.288c0,23.358,18.94,42.288,42.297,42.288c23.357,0,42.279-18.93,42.279-42.288C298.279,18.932,279.357,0,256,0z" /><path fill="inherit" d="M256,427.424c-23.357,0-42.297,18.931-42.297,42.288C213.703,493.07,232.643,512,256,512c23.357,0,42.279-18.93,42.279-42.288C298.279,446.355,279.357,427.424,256,427.424z" /><path fill="inherit" d="M74.974,74.983c-16.52,16.511-16.52,43.286,0,59.806c16.52,16.52,43.287,16.52,59.806,0c16.52-16.511,16.52-43.286,0-59.806C118.261,58.463,91.494,58.463,74.974,74.983z" /><path fill="inherit" d="M377.203,377.211c-16.503,16.52-16.503,43.296,0,59.815c16.519,16.52,43.304,16.52,59.806,0c16.52-16.51,16.52-43.295,0-59.815C420.489,360.692,393.722,360.7,377.203,377.211z" /><path fill="inherit" d="M84.567,256c0.018-23.348-18.922-42.279-42.279-42.279c-23.357-0.009-42.297,18.932-42.279,42.288c-0.018,23.348,18.904,42.279,42.279,42.279C65.645,298.288,84.567,279.358,84.567,256z" /><path fill="inherit" d="M469.712,213.712c-23.357,0-42.279,18.941-42.297,42.288c0,23.358,18.94,42.288,42.297,42.297c23.357,0,42.297-18.94,42.279-42.297C512.009,232.652,493.069,213.712,469.712,213.712z" /><path fill="inherit" d="M74.991,377.22c-16.519,16.511-16.519,43.296,0,59.806c16.503,16.52,43.27,16.52,59.789,0c16.52-16.519,16.52-43.295,0-59.815C118.278,360.692,91.511,360.692,74.991,377.22z" /><path fill="inherit" d="M437.026,134.798c16.52-16.52,16.52-43.304,0-59.824c-16.519-16.511-43.304-16.52-59.823,0c-16.52,16.52-16.503,43.295,0,59.815C393.722,151.309,420.507,151.309,437.026,134.798z" /></g></svg>}</div></> : <>{fetchNoneInfo || 'No match yet'}</>}
2716
+ </button>
2717
+ {/* /NO MATCH & LOADER */}
2718
+
2662
2719
 
2663
2720
 
2664
2721
  {/* OPTIONS LIST */}
@@ -35,16 +35,6 @@ export function unique(arr: any[]) {
35
35
  }
36
36
 
37
37
 
38
- /**
39
- * Remove html tag content
40
- * @param {string | number} str
41
- * @returns {string}
42
- */
43
- export function stripHTML(str: string | number) {
44
- return String(str).replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
45
- }
46
-
47
-
48
38
  /**
49
39
  * Remove a specific item from an array
50
40
  * @param {array} arr
@@ -52,7 +52,7 @@ const Switch = forwardRef((props: SwitchProps, externalRef: any) => {
52
52
  const uniqueID = useComId();
53
53
  const idRes = id || uniqueID;
54
54
  const rootRef = useRef<any>(null);
55
- const [val, setVal] = useState<any>(null || false); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
55
+ const [val, setVal] = useState<boolean>(false); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
56
56
 
57
57
  function handleFocus(event: any) {
58
58
  rootRef.current?.classList.add('focus');
@@ -93,7 +93,9 @@ const Switch = forwardRef((props: SwitchProps, externalRef: any) => {
93
93
 
94
94
 
95
95
  useEffect(() => {
96
- setVal(checked);
96
+ if (typeof checked === 'boolean') {
97
+ setVal(checked);
98
+ }
97
99
  }, [checked]);
98
100
 
99
101
 
@@ -7,7 +7,6 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
7
7
  import { actualPropertyValue, getTextTop } from 'funda-utils/dist/cjs/inputsCalculation';
8
8
  import useDebounce from 'funda-utils/dist/cjs/useDebounce';
9
9
 
10
-
11
10
  export type TextareaProps = {
12
11
  contentRef?: React.ForwardedRef<any>; // could use "Array" on contentRef.current, such as contentRef.current[0], contentRef.current[1]
13
12
  wrapperClassName?: string;
@@ -23,23 +23,20 @@ const App = () => {
23
23
  */
24
24
  import { useEffect, useState } from 'react';
25
25
 
26
-
27
- const useIsMobile = (breakpoint = 600) => {
28
- const [isMobile, setIsMobile] = useState(false);
29
- const [isMounted, setIsMounted] = useState(false);
30
-
26
+ const useIsMobile = (breakpoint: number = 600): boolean => {
27
+ const [isMobile, setIsMobile] = useState<boolean>(false);
28
+ const [isMounted, setIsMounted] = useState<boolean>(false);
31
29
 
32
30
  useEffect(() => {
33
31
  // Set the mounted state to true after the component has mounted
34
32
  setIsMounted(true);
35
33
 
36
34
  const handleResize = () => {
37
- if (window) {
38
-
35
+ if (typeof window !== 'undefined') {
39
36
 
40
- const detectDeviceType = () => {
37
+ const detectDeviceType = (): 'mobile' | 'tablet' | 'desktop' => {
41
38
  // 1. First check if window and navigator are available (SSR compatibility)
42
- if (typeof window === 'undefined' || !navigator) {
39
+ if (typeof window === 'undefined' || typeof navigator === 'undefined') {
43
40
  return 'desktop'; // Default to desktop
44
41
  }
45
42
 
@@ -47,10 +44,10 @@ const useIsMobile = (breakpoint = 600) => {
47
44
  const ua = navigator.userAgent.toLowerCase();
48
45
 
49
46
  // 3. Get platform info
50
- const platform = navigator.platform.toLowerCase();
47
+ const platform = navigator.platform ? navigator.platform.toLowerCase() : '';
51
48
 
52
49
  // 4. Check screen characteristics using window.matchMedia
53
- const isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
50
+ const isTouch = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
54
51
 
55
52
  const isPortrait = window.matchMedia('(orientation: portrait)').matches;
56
53
  const isLandscape = window.matchMedia('(orientation: landscape)').matches;
@@ -139,4 +136,4 @@ const useIsMobile = (breakpoint = 600) => {
139
136
  return isMounted ? isMobile : false;
140
137
  };
141
138
 
142
- export default useIsMobile;
139
+ export default useIsMobile;
@@ -8,7 +8,7 @@
8
8
  * @returns {string} The processed string
9
9
  */
10
10
  function rmSpec(input: string): string {
11
- return input.replace(/[^a-zA-Z0-9 \u4E00-\u9FFF]/g, "");
11
+ return input?.replace(/[^a-zA-Z0-9 \u4E00-\u9FFF]/g, "");
12
12
  }
13
13
 
14
14
  /**
@@ -17,7 +17,7 @@ function rmSpec(input: string): string {
17
17
  * @returns {string} The processed string
18
18
  */
19
19
  function onlyNumAndLetter(input: string): string {
20
- return input.replace(/[^a-zA-Z0-9 ]/g, "");
20
+ return input?.replace(/[^a-zA-Z0-9 ]/g, "");
21
21
  }
22
22
 
23
23
  /**
@@ -26,7 +26,7 @@ function onlyNumAndLetter(input: string): string {
26
26
  * @returns {string} The processed string
27
27
  */
28
28
  function rmAllSpace(input: string): string {
29
- return input.replace(/\s/g, "");
29
+ return input?.replace(/\s/g, "");
30
30
  }
31
31
 
32
32
  /**
@@ -35,7 +35,7 @@ function rmAllSpace(input: string): string {
35
35
  * @returns {string} The processed string
36
36
  */
37
37
  function trimAll(input: string): string {
38
- return input.replace(/(^\s+)|(\s+$)/g, "");
38
+ return input?.replace(/(^\s+)|(\s+$)/g, "");
39
39
  }
40
40
 
41
41
  /**
@@ -44,25 +44,35 @@ function trimAll(input: string): string {
44
44
  * @returns {string} The processed string
45
45
  */
46
46
  function multiSpacesToSingle(input: string): string {
47
- return input.replace(/\s+(\W)/g, ' ');
47
+ return input?.replace(/\s+(\W)/g, ' ');
48
48
  }
49
49
 
50
50
  /**
51
- * Convert HTML text to plain text
51
+ * Convert HTML text to plain text (Remove html tag content)
52
52
  * @param {string} input - The input string to process
53
53
  * @returns {string} The processed string
54
54
  */
55
+ /*
56
+ Examples:
57
+ console.log(htmlToPlain("<p>Hello <b>World</b></p>")); // Hello World
58
+ */
55
59
  function htmlToPlain(input: string): string {
56
- return input.replace(/(<([^>]+)>)/ig, '');
60
+ return input?.replace(/(<([^>]+)>)/ig, '');
57
61
  }
58
62
 
59
63
  /**
60
- * Strip HTML tags and their content
64
+ * Strip HTML tags and their content
65
+ * !!!Important: It will remove nested tags
61
66
  * @param {string} input - The input string to process
62
67
  * @returns {string} The processed string
63
68
  */
69
+ /*
70
+ Examples:
71
+ console.log(stripTagsAndContent("<p>Hello <b>World</b></p>")); // World
72
+ console.log(stripTagsAndContent("Hello <b>World</b>")); // Hello
73
+ */
64
74
  function stripTagsAndContent(input: string): string {
65
- return input.replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
75
+ return input?.replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
66
76
  }
67
77
 
68
78
  /**
@@ -71,7 +81,7 @@ function stripTagsAndContent(input: string): string {
71
81
  * @returns {string} The processed URL
72
82
  */
73
83
  function removeFirstLastSlash(input: string): string {
74
- return input.replace(/^\/|\/$/g, '');
84
+ return input?.replace(/^\/|\/$/g, '');
75
85
  }
76
86
 
77
87
  /**
@@ -80,7 +90,7 @@ function removeFirstLastSlash(input: string): string {
80
90
  * @returns {string} The processed URL
81
91
  */
82
92
  function removeTrailingSlash(input: string): string {
83
- return input.replace(/\/+$/, '');
93
+ return input?.replace(/\/+$/, '');
84
94
  }
85
95
 
86
96
  /**
@@ -89,7 +99,7 @@ function removeTrailingSlash(input: string): string {
89
99
  * @returns {string} The processed URL
90
100
  */
91
101
  function removeFirstSlash(input: string): string {
92
- return input.replace(/\//, '');
102
+ return input?.replace(/\//, '');
93
103
  }
94
104
 
95
105
  export {
@@ -69,7 +69,7 @@ function getTimeslots(
69
69
  * @param {Date} endDate - ebd date
70
70
  * @returns Number
71
71
  */
72
- function getMinutesBetweenDates(startDate, endDate) {
72
+ function getMinutesBetweenDates(startDate: Date, endDate: Date) {
73
73
  const diff = endDate.getTime() - startDate.getTime();
74
74
  return (diff / 60000);
75
75
  }
@@ -81,12 +81,12 @@ function getMinutesBetweenDates(startDate, endDate) {
81
81
  * @param {String} endTime - ebd time
82
82
  * @returns Number
83
83
  */
84
- function getMinutesBetweenTime(startTime, endTime) {
85
- const pad = (num) => {
84
+ function getMinutesBetweenTime(startTime: string, endTime: string) {
85
+ const pad = (num: string | number) => {
86
86
  return ("0" + num).slice(-2);
87
87
  };
88
- let s = startTime.split(":"), sMin = +s[1] + s[0] * 60,
89
- e = endTime.split(":"), eMin = +e[1] + e[0] * 60,
88
+ let s: any = startTime.split(":"), sMin = +s[1] + s[0] * 60,
89
+ e: any = endTime.split(":"), eMin = +e[1] + e[0] * 60,
90
90
  diff = eMin - sMin;
91
91
 
92
92
  if (diff < 0) { sMin -= 12 * 60; diff = eMin - sMin }
@@ -102,7 +102,7 @@ function getMinutesBetweenTime(startTime, endTime) {
102
102
  * @param {String} timeStr - time string
103
103
  * @returns Number
104
104
  */
105
- function convertTimeToMin(timeStr) {
105
+ function convertTimeToMin(timeStr: string) {
106
106
  const _time = timeStr.split(':').length === 3 ? `${timeStr}` : `${timeStr}:00`;
107
107
 
108
108
  const res = _time.split(':'); // split it at the colons
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "4.7.133",
5
+ "version": "4.7.152",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",