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
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
getAbsolutePositionOfStage
|
|
17
17
|
} from 'funda-utils/dist/cjs/getElementProperty';
|
|
18
|
+
import {
|
|
19
|
+
htmlToPlain
|
|
20
|
+
} from 'funda-utils/dist/cjs/format-string';
|
|
18
21
|
import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
19
22
|
|
|
20
23
|
|
|
@@ -29,6 +32,8 @@ export type CascadingSelectProps = {
|
|
|
29
32
|
wrapperClassName?: string;
|
|
30
33
|
controlClassName?: string;
|
|
31
34
|
controlExClassName?: string;
|
|
35
|
+
searchable?: boolean;
|
|
36
|
+
searchPlaceholder?: string;
|
|
32
37
|
perColumnHeadersShow?: boolean;
|
|
33
38
|
exceededSidePosOffset?: number;
|
|
34
39
|
value?: string;
|
|
@@ -89,6 +94,8 @@ const CascadingSelect = (props: CascadingSelectProps) => {
|
|
|
89
94
|
wrapperClassName,
|
|
90
95
|
controlClassName,
|
|
91
96
|
controlExClassName,
|
|
97
|
+
searchable = false,
|
|
98
|
+
searchPlaceholder = '',
|
|
92
99
|
perColumnHeadersShow = true,
|
|
93
100
|
exceededSidePosOffset,
|
|
94
101
|
disabled,
|
|
@@ -135,6 +142,8 @@ const CascadingSelect = (props: CascadingSelectProps) => {
|
|
|
135
142
|
const valRef = useRef<any>(null);
|
|
136
143
|
const listRef = useRef<any>(null);
|
|
137
144
|
|
|
145
|
+
// searchable
|
|
146
|
+
const [columnSearchKeywords, setColumnSearchKeywords] = useState<string[]>([]);
|
|
138
147
|
|
|
139
148
|
// exposes the following methods
|
|
140
149
|
useImperativeHandle(
|
|
@@ -1079,6 +1088,16 @@ const CascadingSelect = (props: CascadingSelectProps) => {
|
|
|
1079
1088
|
|
|
1080
1089
|
}, [value]);
|
|
1081
1090
|
|
|
1091
|
+
// Automatically complete and truncate column Search Keywords each time the number of columns changes
|
|
1092
|
+
useEffect(() => {
|
|
1093
|
+
if (listData.current.length !== columnSearchKeywords.length) {
|
|
1094
|
+
setColumnSearchKeywords(
|
|
1095
|
+
Array(listData.current.length).fill('').map((v, i) => columnSearchKeywords[i] || '')
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
}, [listData.current.length]);
|
|
1099
|
+
|
|
1100
|
+
|
|
1082
1101
|
return (
|
|
1083
1102
|
<>
|
|
1084
1103
|
|
|
@@ -1115,13 +1134,42 @@ const CascadingSelect = (props: CascadingSelectProps) => {
|
|
|
1115
1134
|
{listData.current.map((item: any, level: number) => {
|
|
1116
1135
|
|
|
1117
1136
|
if (item.length > 0) {
|
|
1137
|
+
|
|
1138
|
+
// filter data
|
|
1139
|
+
let filteredItem = item;
|
|
1140
|
+
if (searchable && columnSearchKeywords[level]) {
|
|
1141
|
+
const keyword = columnSearchKeywords[level].toLowerCase();
|
|
1142
|
+
filteredItem = item.filter((opt: any) =>
|
|
1143
|
+
(htmlToPlain(opt.name) || '').toLowerCase().includes(keyword)
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
|
|
1118
1148
|
return (
|
|
1119
1149
|
<li key={level} data-col={level} className="cas-select__items-col">
|
|
1150
|
+
|
|
1151
|
+
{/* SEARCH BOX */}
|
|
1152
|
+
{searchable && (
|
|
1153
|
+
<div className="cas-select__items-col-searchbox">
|
|
1154
|
+
<input
|
|
1155
|
+
type="text"
|
|
1156
|
+
placeholder={searchPlaceholder}
|
|
1157
|
+
value={columnSearchKeywords[level] || ''}
|
|
1158
|
+
onChange={e => {
|
|
1159
|
+
const newKeywords = [...columnSearchKeywords];
|
|
1160
|
+
newKeywords[level] = e.target.value;
|
|
1161
|
+
setColumnSearchKeywords(newKeywords);
|
|
1162
|
+
}}
|
|
1163
|
+
/>
|
|
1164
|
+
</div>
|
|
1165
|
+
)}
|
|
1166
|
+
{/* /SEARCH BOX */}
|
|
1167
|
+
|
|
1120
1168
|
<Group
|
|
1121
1169
|
perColumnHeadersShow={perColumnHeadersShow}
|
|
1122
1170
|
level={level}
|
|
1123
1171
|
columnTitle={columnTitleData}
|
|
1124
|
-
data={
|
|
1172
|
+
data={filteredItem} // filter result
|
|
1125
1173
|
cleanNodeBtnClassName={cleanNodeBtnClassName}
|
|
1126
1174
|
cleanNodeBtnContent={cleanNodeBtnContent}
|
|
1127
1175
|
selectEv={(e, value, index, ) => handleClickItem(e, value, index, level, listData.current)}
|
|
@@ -127,11 +127,12 @@
|
|
|
127
127
|
--cas-select-e2e-loader-color: #000000;
|
|
128
128
|
--cas-select-e2e-clean-btn-color: #b5b5b5;
|
|
129
129
|
|
|
130
|
+
--cas-select-e2e-searchbox-border-color: #ddd;
|
|
130
131
|
|
|
131
132
|
box-shadow: var(--cas-select-e2e-items-box-shadow);
|
|
132
133
|
position: absolute;
|
|
133
134
|
left: auto;
|
|
134
|
-
max-height:
|
|
135
|
+
max-height: 300px;
|
|
135
136
|
border: 1px solid var(--cas-select-e2e-items-border-color);
|
|
136
137
|
background: var(--cas-select-e2e-items-bg);
|
|
137
138
|
padding: 10px;
|
|
@@ -160,6 +161,7 @@
|
|
|
160
161
|
padding: 0;
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
|
|
163
165
|
.cas-select-e2e__items-loader {
|
|
164
166
|
position: absolute;
|
|
165
167
|
pointer-events: none;
|
|
@@ -167,21 +169,25 @@
|
|
|
167
169
|
top: 0;
|
|
168
170
|
margin-left: 5px;
|
|
169
171
|
z-index: 1;
|
|
170
|
-
z-index: 1;
|
|
171
172
|
width: 12px;
|
|
172
173
|
height: 12px;
|
|
173
174
|
text-align: center;
|
|
174
|
-
transform-origin:
|
|
175
|
+
transform-origin: center;
|
|
176
|
+
transform: translate(2px, 5px) rotate(0);
|
|
175
177
|
animation: 1s linear infinite cas-select-e2e__spinner;
|
|
176
178
|
|
|
177
|
-
svg
|
|
178
|
-
|
|
179
|
+
svg {
|
|
180
|
+
vertical-align: top;
|
|
181
|
+
|
|
182
|
+
path {
|
|
183
|
+
fill: var(--cas-select-e2e-loader-color);
|
|
184
|
+
}
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
|
|
182
188
|
@keyframes cas-select-e2e__spinner {
|
|
183
189
|
to {
|
|
184
|
-
transform: rotate(-360deg);
|
|
190
|
+
transform: translate(2px, 5px) rotate(-360deg);
|
|
185
191
|
}
|
|
186
192
|
}
|
|
187
193
|
|
|
@@ -227,6 +233,17 @@
|
|
|
227
233
|
|
|
228
234
|
|
|
229
235
|
|
|
236
|
+
/* Searchbox */
|
|
237
|
+
.cas-select-e2e__items-col-searchbox {
|
|
238
|
+
input {
|
|
239
|
+
border: 1px solid var(--cas-select-e2e-searchbox-border-color);
|
|
240
|
+
border-radius: 0.35rem;
|
|
241
|
+
background: transparent;
|
|
242
|
+
font-size: 0.75rem;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
230
247
|
/* Options */
|
|
231
248
|
.cas-select-e2e__opt {
|
|
232
249
|
padding: var(--cas-select-e2e-opt-padding-y) var(--cas-select-e2e-opt-padding-x);
|
|
@@ -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
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, { useState, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import useIsMobile from 'funda-utils/dist/cjs/useIsMobile';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
interface RefresherProps {
|
|
7
|
+
/** Pulling text */
|
|
8
|
+
pullingText?: React.ReactNode;
|
|
9
|
+
/** Text when reaching the threshold */
|
|
10
|
+
readyToRefreshText?: React.ReactNode;
|
|
11
|
+
/** Refreshing text */
|
|
12
|
+
refreshingText?: React.ReactNode;
|
|
13
|
+
/** The pull distance (px) that triggers the refresh */
|
|
14
|
+
threshold?: number;
|
|
15
|
+
/** The height of the trigger area */
|
|
16
|
+
triggerHeight?: number;
|
|
17
|
+
/** The styles of the trigger area */
|
|
18
|
+
triggerAreaStyle?: React.CSSProperties;
|
|
19
|
+
/** Pull-down is only allowed when the scroll bar of this element is at the top. You can only fire certain actions when the scrollbar is at the top by listening to the scroll event of a given DOM element and determining if its scrollTop is 0. */
|
|
20
|
+
scrollableElementClassName?: string;
|
|
21
|
+
/** Refresh action is async */
|
|
22
|
+
onRefresh: () => Promise<void>;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Refresher = (prpps: RefresherProps) => {
|
|
27
|
+
const {
|
|
28
|
+
onRefresh,
|
|
29
|
+
children,
|
|
30
|
+
pullingText = 'Pull down to refresh',
|
|
31
|
+
readyToRefreshText = 'Release to refresh',
|
|
32
|
+
refreshingText = 'Refreshing...',
|
|
33
|
+
threshold = 100,
|
|
34
|
+
triggerHeight = 50,
|
|
35
|
+
triggerAreaStyle,
|
|
36
|
+
scrollableElementClassName,
|
|
37
|
+
} = prpps;
|
|
38
|
+
|
|
39
|
+
const isMobile = useIsMobile();
|
|
40
|
+
const [pullDistance, setPullDistance] = useState<number>(0);
|
|
41
|
+
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
|
|
42
|
+
const startY = useRef<number | null>(null);
|
|
43
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
44
|
+
|
|
45
|
+
const handleTouchStart = (e: React.TouchEvent) => {
|
|
46
|
+
if (isRefreshing) return;
|
|
47
|
+
// Pull-down is only allowed when the scroll bar is at the top
|
|
48
|
+
const targetScrollableDiv = scrollableElementClassName === null || typeof scrollableElementClassName === 'undefined' ? containerRef.current : document.querySelector(scrollableElementClassName as string);
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if (targetScrollableDiv && targetScrollableDiv.scrollTop > 0) return;
|
|
52
|
+
startY.current = e.touches[0].clientY;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleTouchMove = (e: React.TouchEvent) => {
|
|
56
|
+
if (startY.current === null || isRefreshing) return;
|
|
57
|
+
const distance = e.touches[0].clientY - startY.current;
|
|
58
|
+
if (distance > 0) {
|
|
59
|
+
setPullDistance(distance > threshold ? threshold : distance); // The maximum pull should not exceed "threshold" value
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleTouchEnd = async () => {
|
|
64
|
+
if (isRefreshing) return;
|
|
65
|
+
if (pullDistance >= threshold) {
|
|
66
|
+
setIsRefreshing(true);
|
|
67
|
+
await onRefresh();
|
|
68
|
+
setIsRefreshing(false);
|
|
69
|
+
}
|
|
70
|
+
setPullDistance(0);
|
|
71
|
+
startY.current = null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
{!isMobile ? <React.Fragment>
|
|
77
|
+
{/** CONTENT */}
|
|
78
|
+
{children}
|
|
79
|
+
</React.Fragment> : <div
|
|
80
|
+
ref={containerRef}
|
|
81
|
+
style={{
|
|
82
|
+
overflow: 'hidden',
|
|
83
|
+
touchAction: 'pan-y',
|
|
84
|
+
marginTop: `-${triggerHeight}px`,
|
|
85
|
+
}}
|
|
86
|
+
onTouchStart={handleTouchStart}
|
|
87
|
+
onTouchMove={handleTouchMove}
|
|
88
|
+
onTouchEnd={handleTouchEnd}
|
|
89
|
+
>
|
|
90
|
+
<div
|
|
91
|
+
style={{
|
|
92
|
+
transform: `translateY(${isRefreshing ? triggerHeight : pullDistance}px)`,
|
|
93
|
+
transition: isRefreshing ? 'transform 0.2s' : 'transform 0.3s',
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
|
|
97
|
+
{/** TRIGGER */}
|
|
98
|
+
<div style={{
|
|
99
|
+
...triggerAreaStyle,
|
|
100
|
+
height: `${triggerHeight}px`,
|
|
101
|
+
textAlign: 'center',
|
|
102
|
+
lineHeight: '50px'
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{isRefreshing
|
|
106
|
+
? refreshingText
|
|
107
|
+
: pullDistance >= threshold
|
|
108
|
+
? readyToRefreshText
|
|
109
|
+
: pullingText}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{/** CONTENT */}
|
|
113
|
+
{children}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
}
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default Refresher;
|
|
@@ -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;
|