funda-ui 4.7.133 → 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/Refresher/index.js +3 -3
- package/Select/index.css +33 -0
- package/Select/index.d.ts +1 -0
- package/Select/index.js +350 -39
- package/SplitterPanel/index.js +3 -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 +3 -3
- 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.js +3 -3
- package/lib/cjs/Select/index.d.ts +1 -0
- package/lib/cjs/Select/index.js +350 -39
- package/lib/cjs/SplitterPanel/index.js +3 -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 +3 -3
- 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/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 +9 -12
- package/lib/esm/Utils/libs/format-string.ts +22 -12
- package/lib/esm/Utils/libs/time.ts +6 -6
- package/package.json +1 -1
|
@@ -20,9 +20,13 @@ import {
|
|
|
20
20
|
addTreeDepth,
|
|
21
21
|
addTreeIndent
|
|
22
22
|
} from 'funda-utils/dist/cjs/tree';
|
|
23
|
+
import {
|
|
24
|
+
htmlToPlain
|
|
25
|
+
} from 'funda-utils/dist/cjs/format-string';
|
|
23
26
|
import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
24
27
|
|
|
25
28
|
|
|
29
|
+
|
|
26
30
|
import Group from './Group';
|
|
27
31
|
|
|
28
32
|
|
|
@@ -45,6 +49,8 @@ export type CascadingSelectE2EProps = {
|
|
|
45
49
|
wrapperClassName?: string;
|
|
46
50
|
controlClassName?: string;
|
|
47
51
|
controlExClassName?: string;
|
|
52
|
+
searchable?: boolean;
|
|
53
|
+
searchPlaceholder?: string;
|
|
48
54
|
perColumnHeadersShow?: boolean;
|
|
49
55
|
exceededSidePosOffset?: number;
|
|
50
56
|
value?: string;
|
|
@@ -107,6 +113,8 @@ const CascadingSelectE2E = (props: CascadingSelectE2EProps) => {
|
|
|
107
113
|
wrapperClassName,
|
|
108
114
|
controlClassName,
|
|
109
115
|
controlExClassName,
|
|
116
|
+
searchable = false,
|
|
117
|
+
searchPlaceholder = '',
|
|
110
118
|
perColumnHeadersShow = true,
|
|
111
119
|
exceededSidePosOffset,
|
|
112
120
|
disabled,
|
|
@@ -151,6 +159,9 @@ const CascadingSelectE2E = (props: CascadingSelectE2EProps) => {
|
|
|
151
159
|
const valRef = useRef<any>(null);
|
|
152
160
|
const listRef = useRef<any>(null);
|
|
153
161
|
|
|
162
|
+
// searchable
|
|
163
|
+
const [columnSearchKeywords, setColumnSearchKeywords] = useState<string[]>([]);
|
|
164
|
+
|
|
154
165
|
|
|
155
166
|
// exposes the following methods
|
|
156
167
|
useImperativeHandle(
|
|
@@ -1337,6 +1348,17 @@ const CascadingSelectE2E = (props: CascadingSelectE2EProps) => {
|
|
|
1337
1348
|
|
|
1338
1349
|
}, [value]);
|
|
1339
1350
|
|
|
1351
|
+
|
|
1352
|
+
// Automatically complete and truncate column Search Keywords each time the number of columns changes
|
|
1353
|
+
useEffect(() => {
|
|
1354
|
+
if (listData.current.length !== columnSearchKeywords.length) {
|
|
1355
|
+
setColumnSearchKeywords(
|
|
1356
|
+
Array(listData.current.length).fill('').map((v, i) => columnSearchKeywords[i] || '')
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
}, [listData.current.length]);
|
|
1360
|
+
|
|
1361
|
+
|
|
1340
1362
|
return (
|
|
1341
1363
|
<>
|
|
1342
1364
|
|
|
@@ -1372,13 +1394,43 @@ const CascadingSelectE2E = (props: CascadingSelectE2EProps) => {
|
|
|
1372
1394
|
{listData.current.map((item: any, level: number) => {
|
|
1373
1395
|
|
|
1374
1396
|
if (item.length > 0) {
|
|
1397
|
+
|
|
1398
|
+
// filter data
|
|
1399
|
+
let filteredItem = item;
|
|
1400
|
+
if (searchable && columnSearchKeywords[level]) {
|
|
1401
|
+
const keyword = columnSearchKeywords[level].toLowerCase();
|
|
1402
|
+
filteredItem = item.filter((opt: any) =>
|
|
1403
|
+
(htmlToPlain(opt.name) || '').toLowerCase().includes(keyword)
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
|
|
1375
1408
|
return (
|
|
1376
1409
|
<li key={level} data-col={level} className="cas-select-e2e__items-col">
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
{/* SEARCH BOX */}
|
|
1413
|
+
{searchable && (
|
|
1414
|
+
<div className="cas-select-e2e__items-col-searchbox">
|
|
1415
|
+
<input
|
|
1416
|
+
type="text"
|
|
1417
|
+
placeholder={searchPlaceholder}
|
|
1418
|
+
value={columnSearchKeywords[level] || ''}
|
|
1419
|
+
onChange={e => {
|
|
1420
|
+
const newKeywords = [...columnSearchKeywords];
|
|
1421
|
+
newKeywords[level] = e.target.value;
|
|
1422
|
+
setColumnSearchKeywords(newKeywords);
|
|
1423
|
+
}}
|
|
1424
|
+
/>
|
|
1425
|
+
</div>
|
|
1426
|
+
)}
|
|
1427
|
+
{/* /SEARCH BOX */}
|
|
1428
|
+
|
|
1377
1429
|
<Group
|
|
1378
1430
|
perColumnHeadersShow={perColumnHeadersShow}
|
|
1379
1431
|
level={level}
|
|
1380
1432
|
columnTitle={columnTitleData}
|
|
1381
|
-
data={
|
|
1433
|
+
data={filteredItem} // filter result
|
|
1382
1434
|
cleanNodeBtnClassName={cleanNodeBtnClassName}
|
|
1383
1435
|
cleanNodeBtnContent={cleanNodeBtnContent}
|
|
1384
1436
|
selectEv={(e, value, index) => handleClickItem(e, value, index, level, listData.current)}
|
|
@@ -58,7 +58,7 @@ const Checkbox = forwardRef((props: CheckboxProps, externalRef: any) => {
|
|
|
58
58
|
const idRes = id || uniqueID;
|
|
59
59
|
const rootRef = useRef<any>(null);
|
|
60
60
|
const valRef = useRef<any>(null);
|
|
61
|
-
const [val, setVal] = useState<
|
|
61
|
+
const [val, setVal] = useState<boolean>(false); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
|
|
62
62
|
|
|
63
63
|
// exposes the following methods
|
|
64
64
|
useImperativeHandle(
|
|
@@ -78,7 +78,7 @@ const Checkbox = forwardRef((props: CheckboxProps, externalRef: any) => {
|
|
|
78
78
|
onChange(null, false);
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
-
set: (value:
|
|
81
|
+
set: (value: boolean, cb?: any) => {
|
|
82
82
|
setVal(value);
|
|
83
83
|
cb?.();
|
|
84
84
|
|
|
@@ -133,7 +133,9 @@ const Checkbox = forwardRef((props: CheckboxProps, externalRef: any) => {
|
|
|
133
133
|
useEffect(() => {
|
|
134
134
|
|
|
135
135
|
// default value
|
|
136
|
-
|
|
136
|
+
if (typeof checked === 'boolean') {
|
|
137
|
+
setVal(checked);
|
|
138
|
+
}
|
|
137
139
|
|
|
138
140
|
// Set a checkbox to indeterminate state
|
|
139
141
|
if (typeof indeterminate !== 'undefined') {
|
|
@@ -144,6 +144,7 @@ const LiveSearch = forwardRef((props: LiveSearchProps, externalRef: any) => {
|
|
|
144
144
|
} = props;
|
|
145
145
|
|
|
146
146
|
|
|
147
|
+
const QUERY_STRING_PLACEHOLDER = '------'; // Invalid parameters for the first automatic request
|
|
147
148
|
const DEPTH = depth || 1055; // the default value same as bootstrap
|
|
148
149
|
const POS_OFFSET = 0;
|
|
149
150
|
const EXCEEDED_SIDE_POS_OFFSET = Number(exceededSidePosOffset) || 15;
|
|
@@ -773,7 +774,7 @@ const LiveSearch = forwardRef((props: LiveSearchProps, externalRef: any) => {
|
|
|
773
774
|
// data init
|
|
774
775
|
//--------------
|
|
775
776
|
const _oparams: any[] = fetchFuncMethodParams || [];
|
|
776
|
-
const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (fetchTrigger && !fetchUpdate) ? '' : (fetchUpdate ?
|
|
777
|
+
const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (fetchTrigger && !fetchUpdate) ? '' : (fetchUpdate ? QUERY_STRING_PLACEHOLDER : (fetchTrigger ? QUERY_STRING_PLACEHOLDER : '')));
|
|
777
778
|
if (!firstFetch) {
|
|
778
779
|
fetchData((_params).join(','));
|
|
779
780
|
setFirstFetch(true); // avoid triggering two data requests if the input value has not changed
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
--cus-sel-removebtn-fill: #000;
|
|
18
18
|
--cus-sel-removebtn-hover-fill: #f00;
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
position: relative; /* Required */
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
/*------ Placeholder for input ------*/
|
|
25
27
|
input::placeholder {
|
|
@@ -33,7 +35,6 @@
|
|
|
33
35
|
fill: var(--cus-sel-arrow-fill);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
|
|
37
38
|
/*------ Clean ------*/
|
|
38
39
|
.clean {
|
|
39
40
|
svg .clean-fill-g {
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
.custom-select-multi__control-blinking-cursor {
|
|
72
73
|
display: inline-block;
|
|
73
74
|
color: var(--cus-sel-placeholder-color);
|
|
75
|
+
width: 100%;
|
|
74
76
|
|
|
75
77
|
&.animated {
|
|
76
78
|
animation: 1s mf-sel-blink step-end infinite;
|
|
@@ -80,6 +82,15 @@
|
|
|
80
82
|
color: var(--cus-sel-input-placeholder-color);
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/* Text preview */
|
|
86
|
+
> span {
|
|
87
|
+
display: inline-block;
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
overflow: hidden;
|
|
91
|
+
max-width: 100%;
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
|
|
84
95
|
}
|
|
85
96
|
.custom-select-multi__control-blinking-following-cursor {
|
|
@@ -383,7 +394,7 @@
|
|
|
383
394
|
--cus-sel-listgroup-content-scrollbar-w: 10px;
|
|
384
395
|
--cus-sel-listgroup-grouptitle-color: #a2a2a2;
|
|
385
396
|
--cus-sel-listgroup-groupborder-color: #d8d8d8;
|
|
386
|
-
|
|
397
|
+
--cus-sel-loader-color: #000000;
|
|
387
398
|
|
|
388
399
|
|
|
389
400
|
|
|
@@ -391,11 +402,41 @@
|
|
|
391
402
|
min-width: var(--cus-sel-listgroup-popwin-min-width);
|
|
392
403
|
z-index: 1055; /* --bs-modal-zindex */
|
|
393
404
|
|
|
405
|
+
|
|
394
406
|
&.active {
|
|
395
407
|
display: block !important;
|
|
396
408
|
}
|
|
397
409
|
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
/*------ Loader ------*/
|
|
413
|
+
.cus-select-loader {
|
|
414
|
+
pointer-events: none;
|
|
415
|
+
z-index: 1;
|
|
416
|
+
width: 12px;
|
|
417
|
+
height: 12px;
|
|
418
|
+
text-align: center;
|
|
419
|
+
transform-origin: center;
|
|
420
|
+
transform: translate(-5px, 0) rotate(0);
|
|
421
|
+
animation: 1s linear infinite cus-select__spinner;
|
|
422
|
+
|
|
423
|
+
svg {
|
|
424
|
+
vertical-align: top;
|
|
425
|
+
|
|
426
|
+
path {
|
|
427
|
+
fill: var(--cus-sel-loader-color);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@keyframes cus-select__spinner {
|
|
433
|
+
to {
|
|
434
|
+
transform: translate(-5px, 0) rotate(-360deg);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
398
437
|
|
|
438
|
+
|
|
439
|
+
/*------ Options ------*/
|
|
399
440
|
.custom-select__options-contentlist {
|
|
400
441
|
overflow: hidden;
|
|
401
442
|
overflow-y: auto;
|
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,23 +23,20 @@ const App = () => {
|
|
|
23
23
|
*/
|
|
24
24
|
import { useEffect, useState } from 'react';
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const [
|
|
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' ||
|
|
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;
|