funda-ui 4.7.125 → 4.7.150
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.
- package/CascadingSelect/index.css +15 -4
- package/CascadingSelect/index.d.ts +2 -0
- package/CascadingSelect/index.js +294 -22
- package/CascadingSelectE2E/index.css +15 -4
- package/CascadingSelectE2E/index.d.ts +2 -0
- package/CascadingSelectE2E/index.js +300 -28
- package/Checkbox/index.js +4 -2
- package/LiveSearch/index.js +2 -1
- package/README.md +1 -2
- package/Refresher/index.d.ts +22 -0
- package/Refresher/index.js +564 -0
- package/Select/index.css +33 -0
- package/Select/index.d.ts +1 -0
- package/Select/index.js +350 -39
- package/SplitterPanel/index.js +67 -3
- package/Switch/index.js +4 -2
- package/Utils/format-string.d.ts +2 -1
- package/Utils/format-string.js +22 -12
- package/Utils/time.d.ts +3 -3
- package/Utils/useIsMobile.js +67 -3
- package/all.d.ts +1 -0
- package/all.js +1 -0
- package/lib/cjs/CascadingSelect/index.d.ts +2 -0
- package/lib/cjs/CascadingSelect/index.js +294 -22
- package/lib/cjs/CascadingSelectE2E/index.d.ts +2 -0
- package/lib/cjs/CascadingSelectE2E/index.js +300 -28
- package/lib/cjs/Checkbox/index.js +4 -2
- package/lib/cjs/LiveSearch/index.js +2 -1
- package/lib/cjs/Refresher/index.d.ts +22 -0
- package/lib/cjs/Refresher/index.js +564 -0
- package/lib/cjs/Select/index.d.ts +1 -0
- package/lib/cjs/Select/index.js +350 -39
- package/lib/cjs/SplitterPanel/index.js +67 -3
- package/lib/cjs/Switch/index.js +4 -2
- package/lib/cjs/Utils/format-string.d.ts +2 -1
- package/lib/cjs/Utils/format-string.js +22 -12
- package/lib/cjs/Utils/time.d.ts +3 -3
- package/lib/cjs/Utils/useIsMobile.js +67 -3
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/css/CascadingSelect/index.css +15 -4
- package/lib/css/CascadingSelectE2E/index.css +15 -4
- package/lib/css/Select/index.css +33 -0
- package/lib/esm/CascadingSelect/index.scss +22 -7
- package/lib/esm/CascadingSelect/index.tsx +49 -1
- package/lib/esm/CascadingSelectE2E/Group.tsx +1 -0
- package/lib/esm/CascadingSelectE2E/index.scss +23 -6
- package/lib/esm/CascadingSelectE2E/index.tsx +53 -1
- package/lib/esm/Checkbox/index.tsx +5 -3
- package/lib/esm/LiveSearch/index.tsx +2 -1
- package/lib/esm/Refresher/index.tsx +121 -0
- package/lib/esm/Select/index.scss +43 -2
- package/lib/esm/Select/index.tsx +81 -24
- package/lib/esm/Select/utils/func.ts +0 -10
- package/lib/esm/Switch/index.tsx +4 -2
- package/lib/esm/Utils/hooks/useIsMobile.tsx +88 -5
- package/lib/esm/Utils/libs/format-string.ts +22 -12
- package/lib/esm/Utils/libs/time.ts +6 -6
- package/lib/esm/index.js +1 -0
- package/package.json +1 -1
package/lib/esm/Select/index.tsx
CHANGED
|
@@ -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 =
|
|
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 >
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1190
|
+
_noDataDiv.classList.remove('hide');
|
|
1167
1191
|
if (_btnSelectAll !== null) _btnSelectAll.classList.add('hide');
|
|
1168
1192
|
} else {
|
|
1169
|
-
|
|
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
|
|
1225
|
-
|
|
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) =>
|
|
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>) : (
|
|
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(
|
|
2509
|
+
data-label-text={formatIndentVal(stripTagsAndContent(item), INDENT_LAST_PLACEHOLDER)}
|
|
2462
2510
|
>
|
|
2463
|
-
{formatIndentVal(
|
|
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>
|
|
2661
|
-
|
|
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
|
package/lib/esm/Switch/index.tsx
CHANGED
|
@@ -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<
|
|
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
|
-
|
|
96
|
+
if (typeof checked === 'boolean') {
|
|
97
|
+
setVal(checked);
|
|
98
|
+
}
|
|
97
99
|
}, [checked]);
|
|
98
100
|
|
|
99
101
|
|
|
@@ -23,8 +23,7 @@ const App = () => {
|
|
|
23
23
|
*/
|
|
24
24
|
import { useEffect, useState } from 'react';
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
const useIsMobile = (breakpoint: number = 768): boolean => {
|
|
26
|
+
const useIsMobile = (breakpoint: number = 600): boolean => {
|
|
28
27
|
const [isMobile, setIsMobile] = useState<boolean>(false);
|
|
29
28
|
const [isMounted, setIsMounted] = useState<boolean>(false);
|
|
30
29
|
|
|
@@ -33,8 +32,92 @@ const useIsMobile = (breakpoint: number = 768): boolean => {
|
|
|
33
32
|
setIsMounted(true);
|
|
34
33
|
|
|
35
34
|
const handleResize = () => {
|
|
36
|
-
if (window) {
|
|
37
|
-
|
|
35
|
+
if (typeof window !== 'undefined') {
|
|
36
|
+
|
|
37
|
+
const detectDeviceType = (): 'mobile' | 'tablet' | 'desktop' => {
|
|
38
|
+
// 1. First check if window and navigator are available (SSR compatibility)
|
|
39
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
40
|
+
return 'desktop'; // Default to desktop
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2. Get user agent string
|
|
44
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
45
|
+
|
|
46
|
+
// 3. Get platform info
|
|
47
|
+
const platform = navigator.platform ? navigator.platform.toLowerCase() : '';
|
|
48
|
+
|
|
49
|
+
// 4. Check screen characteristics using window.matchMedia
|
|
50
|
+
const isTouch = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
51
|
+
|
|
52
|
+
const isPortrait = window.matchMedia('(orientation: portrait)').matches;
|
|
53
|
+
const isLandscape = window.matchMedia('(orientation: landscape)').matches;
|
|
54
|
+
|
|
55
|
+
// 5. Get screen dimensions
|
|
56
|
+
const screenWidth = window.screen.width;
|
|
57
|
+
const screenHeight = window.screen.height;
|
|
58
|
+
const minScreenSize = Math.min(screenWidth, screenHeight);
|
|
59
|
+
const maxScreenSize = Math.max(screenWidth, screenHeight);
|
|
60
|
+
|
|
61
|
+
// Define device characteristics
|
|
62
|
+
const isTablet = (
|
|
63
|
+
// Traditional UA detection
|
|
64
|
+
/ipad/.test(ua) ||
|
|
65
|
+
(/android/.test(ua) && !/mobile/.test(ua)) ||
|
|
66
|
+
/tablet/.test(ua) ||
|
|
67
|
+
/playbook/.test(ua) ||
|
|
68
|
+
/nexus (7|9|10)/.test(ua) ||
|
|
69
|
+
/sm-t/.test(ua) ||
|
|
70
|
+
/huawei(.*)mediapad/.test(ua) ||
|
|
71
|
+
|
|
72
|
+
// Special detection for iPad Pro and newer iPads
|
|
73
|
+
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) ||
|
|
74
|
+
|
|
75
|
+
// Screen size characteristics (tablets typically fall within this range)
|
|
76
|
+
(minScreenSize >= breakpoint && maxScreenSize <= 1366 && isTouch) ||
|
|
77
|
+
|
|
78
|
+
// Specific device detection
|
|
79
|
+
/kindle|silk|kftt|kfot|kfjwa|kfjwi|kfsowi|kfthwa|kfthwi|kfapwa|kfapwi/i.test(ua)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const isMobile = (
|
|
83
|
+
!isTablet && ( // Prevent tablets from being detected as phones
|
|
84
|
+
// Traditional mobile device detection
|
|
85
|
+
/iphone|ipod|android.*mobile|windows phone|mobi/.test(ua) ||
|
|
86
|
+
|
|
87
|
+
// Screen size characteristics (phones typically smaller than 600px)
|
|
88
|
+
(minScreenSize < breakpoint && isTouch) ||
|
|
89
|
+
|
|
90
|
+
// Additional mobile device detection
|
|
91
|
+
/blackberry|\bbb\d+|meego|webos|palm|phone|pocket|mobile|mini|iemobile/i.test(ua)
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// 6. Comprehensive decision logic
|
|
96
|
+
if (isMobile) {
|
|
97
|
+
// Additional check for small tablets
|
|
98
|
+
if (maxScreenSize >= 1024 && isTouch) {
|
|
99
|
+
return 'tablet';
|
|
100
|
+
}
|
|
101
|
+
return 'mobile';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (isTablet) {
|
|
105
|
+
// Additional check for touch-enabled laptops
|
|
106
|
+
if (maxScreenSize > 1366 && /windows/.test(ua)) {
|
|
107
|
+
return 'desktop';
|
|
108
|
+
}
|
|
109
|
+
return 'tablet';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 7. Check for touch-enabled laptops
|
|
113
|
+
if (isTouch && /windows/.test(ua) && maxScreenSize > 1366) {
|
|
114
|
+
return 'desktop';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return 'desktop';
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
setIsMobile(detectDeviceType() === 'mobile');
|
|
38
121
|
}
|
|
39
122
|
};
|
|
40
123
|
|
|
@@ -53,4 +136,4 @@ const useIsMobile = (breakpoint: number = 768): boolean => {
|
|
|
53
136
|
return isMounted ? isMobile : false;
|
|
54
137
|
};
|
|
55
138
|
|
|
56
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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/lib/esm/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { default as NumberInput } from './NumberInput';
|
|
|
27
27
|
export { default as Pagination } from './Pagination';
|
|
28
28
|
export { default as Radio } from './Radio';
|
|
29
29
|
export { default as RangeSlider } from './RangeSlider';
|
|
30
|
+
export { default as Refresher } from './Refresher';
|
|
30
31
|
export { default as RootPortal } from './RootPortal';
|
|
31
32
|
export { default as ScrollReveal } from './ScrollReveal';
|
|
32
33
|
export { default as Scrollbar } from './Scrollbar';
|
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.
|
|
5
|
+
"version": "4.7.150",
|
|
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",
|