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.
Files changed (60) 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/Checkbox/index.js +4 -2
  8. package/LiveSearch/index.js +2 -1
  9. package/README.md +1 -2
  10. package/Refresher/index.d.ts +22 -0
  11. package/Refresher/index.js +564 -0
  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 +67 -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 +67 -3
  21. package/all.d.ts +1 -0
  22. package/all.js +1 -0
  23. package/lib/cjs/CascadingSelect/index.d.ts +2 -0
  24. package/lib/cjs/CascadingSelect/index.js +294 -22
  25. package/lib/cjs/CascadingSelectE2E/index.d.ts +2 -0
  26. package/lib/cjs/CascadingSelectE2E/index.js +300 -28
  27. package/lib/cjs/Checkbox/index.js +4 -2
  28. package/lib/cjs/LiveSearch/index.js +2 -1
  29. package/lib/cjs/Refresher/index.d.ts +22 -0
  30. package/lib/cjs/Refresher/index.js +564 -0
  31. package/lib/cjs/Select/index.d.ts +1 -0
  32. package/lib/cjs/Select/index.js +350 -39
  33. package/lib/cjs/SplitterPanel/index.js +67 -3
  34. package/lib/cjs/Switch/index.js +4 -2
  35. package/lib/cjs/Utils/format-string.d.ts +2 -1
  36. package/lib/cjs/Utils/format-string.js +22 -12
  37. package/lib/cjs/Utils/time.d.ts +3 -3
  38. package/lib/cjs/Utils/useIsMobile.js +67 -3
  39. package/lib/cjs/index.d.ts +1 -0
  40. package/lib/cjs/index.js +1 -0
  41. package/lib/css/CascadingSelect/index.css +15 -4
  42. package/lib/css/CascadingSelectE2E/index.css +15 -4
  43. package/lib/css/Select/index.css +33 -0
  44. package/lib/esm/CascadingSelect/index.scss +22 -7
  45. package/lib/esm/CascadingSelect/index.tsx +49 -1
  46. package/lib/esm/CascadingSelectE2E/Group.tsx +1 -0
  47. package/lib/esm/CascadingSelectE2E/index.scss +23 -6
  48. package/lib/esm/CascadingSelectE2E/index.tsx +53 -1
  49. package/lib/esm/Checkbox/index.tsx +5 -3
  50. package/lib/esm/LiveSearch/index.tsx +2 -1
  51. package/lib/esm/Refresher/index.tsx +121 -0
  52. package/lib/esm/Select/index.scss +43 -2
  53. package/lib/esm/Select/index.tsx +81 -24
  54. package/lib/esm/Select/utils/func.ts +0 -10
  55. package/lib/esm/Switch/index.tsx +4 -2
  56. package/lib/esm/Utils/hooks/useIsMobile.tsx +88 -5
  57. package/lib/esm/Utils/libs/format-string.ts +22 -12
  58. package/lib/esm/Utils/libs/time.ts +6 -6
  59. package/lib/esm/index.js +1 -0
  60. 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={item}
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)}
@@ -2,6 +2,7 @@ import React from 'react';
2
2
 
3
3
  import { combinedCls } from 'funda-utils/dist/cjs/cls';
4
4
 
5
+
5
6
  export type GroupFnType = (arg1: any, arg2: any, arg3: number) => void;
6
7
 
7
8
  export type GroupProps = {
@@ -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: 350px;
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: bottom;
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 path {
178
- fill: var(--cas-select-e2e-loader-color);
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={item}
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<any>(null || false); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
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: string, cb?: any) => {
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
- setVal(checked);
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 ? '------' : (fetchTrigger ? '------' : '')));
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;