funda-ui 1.0.272

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 (246) hide show
  1. package/BackToTop/index.css +34 -0
  2. package/BackToTop/index.d.ts +11 -0
  3. package/BackToTop/index.js +458 -0
  4. package/CascadingSelect/index.css +159 -0
  5. package/CascadingSelect/index.d.ts +56 -0
  6. package/CascadingSelect/index.js +958 -0
  7. package/CascadingSelectE2E/index.css +159 -0
  8. package/CascadingSelectE2E/index.d.ts +60 -0
  9. package/CascadingSelectE2E/index.js +1126 -0
  10. package/Checkbox/index.d.ts +30 -0
  11. package/Checkbox/index.js +226 -0
  12. package/ColorPicker/index.css +38 -0
  13. package/ColorPicker/index.d.ts +27 -0
  14. package/ColorPicker/index.js +246 -0
  15. package/DigitalClock/index.d.ts +7 -0
  16. package/DigitalClock/index.js +208 -0
  17. package/DropdownMenu/index.css +127 -0
  18. package/DropdownMenu/index.d.ts +37 -0
  19. package/DropdownMenu/index.js +237 -0
  20. package/DynamicFields/index.d.ts +26 -0
  21. package/DynamicFields/index.js +412 -0
  22. package/File/index.d.ts +36 -0
  23. package/File/index.js +473 -0
  24. package/Input/index.d.ts +42 -0
  25. package/Input/index.js +286 -0
  26. package/LiveSearch/index.d.ts +37 -0
  27. package/LiveSearch/index.js +1195 -0
  28. package/ModalDialog/index.d.ts +60 -0
  29. package/ModalDialog/index.js +725 -0
  30. package/ModeSwitch/index.d.ts +17 -0
  31. package/ModeSwitch/index.js +202 -0
  32. package/MultiFuncSelect/index.css +178 -0
  33. package/MultiFuncSelect/index.d.ts +67 -0
  34. package/MultiFuncSelect/index.js +1826 -0
  35. package/MultilevelDropdownMenu/index.css +35 -0
  36. package/MultilevelDropdownMenu/index.d.ts +25 -0
  37. package/MultilevelDropdownMenu/index.js +464 -0
  38. package/Pagination/index.d.ts +49 -0
  39. package/Pagination/index.js +341 -0
  40. package/README.md +108 -0
  41. package/Radio/index.d.ts +31 -0
  42. package/Radio/index.js +246 -0
  43. package/RangeSlider/index.css +149 -0
  44. package/RangeSlider/index.d.ts +21 -0
  45. package/RangeSlider/index.js +730 -0
  46. package/ScrollReveal/index.css +23 -0
  47. package/ScrollReveal/index.d.ts +21 -0
  48. package/ScrollReveal/index.js +216 -0
  49. package/Scrollbar/index.css +168 -0
  50. package/Scrollbar/index.d.ts +15 -0
  51. package/Scrollbar/index.js +605 -0
  52. package/SearchBar/index.d.ts +32 -0
  53. package/SearchBar/index.js +246 -0
  54. package/Select/index.d.ts +34 -0
  55. package/Select/index.js +331 -0
  56. package/ShowMoreLess/index.css +23 -0
  57. package/ShowMoreLess/index.d.ts +30 -0
  58. package/ShowMoreLess/index.js +202 -0
  59. package/Switch/index.d.ts +29 -0
  60. package/Switch/index.js +211 -0
  61. package/Table/index.css +533 -0
  62. package/Table/index.d.ts +25 -0
  63. package/Table/index.js +2113 -0
  64. package/Tabs/index.d.ts +3 -0
  65. package/Tabs/index.js +323 -0
  66. package/TagInput/index.css +90 -0
  67. package/TagInput/index.d.ts +28 -0
  68. package/TagInput/index.js +370 -0
  69. package/Textarea/index.d.ts +30 -0
  70. package/Textarea/index.js +242 -0
  71. package/Toast/index.css +95 -0
  72. package/Toast/index.d.ts +35 -0
  73. package/Toast/index.js +340 -0
  74. package/Tooltip/index.css +240 -0
  75. package/Tooltip/index.d.ts +19 -0
  76. package/Tooltip/index.js +200 -0
  77. package/Tree/index.css +225 -0
  78. package/Tree/index.d.ts +37 -0
  79. package/Tree/index.js +1406 -0
  80. package/all.d.ts +33 -0
  81. package/all.js +35 -0
  82. package/lib/cjs/BackToTop/index.d.ts +11 -0
  83. package/lib/cjs/BackToTop/index.js +458 -0
  84. package/lib/cjs/CascadingSelect/index.d.ts +56 -0
  85. package/lib/cjs/CascadingSelect/index.js +958 -0
  86. package/lib/cjs/CascadingSelectE2E/index.d.ts +60 -0
  87. package/lib/cjs/CascadingSelectE2E/index.js +1126 -0
  88. package/lib/cjs/Checkbox/index.d.ts +30 -0
  89. package/lib/cjs/Checkbox/index.js +226 -0
  90. package/lib/cjs/ColorPicker/index.d.ts +27 -0
  91. package/lib/cjs/ColorPicker/index.js +246 -0
  92. package/lib/cjs/DigitalClock/index.d.ts +7 -0
  93. package/lib/cjs/DigitalClock/index.js +208 -0
  94. package/lib/cjs/DropdownMenu/index.d.ts +37 -0
  95. package/lib/cjs/DropdownMenu/index.js +237 -0
  96. package/lib/cjs/DynamicFields/index.d.ts +26 -0
  97. package/lib/cjs/DynamicFields/index.js +412 -0
  98. package/lib/cjs/File/index.d.ts +36 -0
  99. package/lib/cjs/File/index.js +473 -0
  100. package/lib/cjs/Input/index.d.ts +42 -0
  101. package/lib/cjs/Input/index.js +286 -0
  102. package/lib/cjs/LiveSearch/index.d.ts +37 -0
  103. package/lib/cjs/LiveSearch/index.js +1195 -0
  104. package/lib/cjs/ModalDialog/index.d.ts +60 -0
  105. package/lib/cjs/ModalDialog/index.js +725 -0
  106. package/lib/cjs/ModeSwitch/index.d.ts +17 -0
  107. package/lib/cjs/ModeSwitch/index.js +202 -0
  108. package/lib/cjs/MultiFuncSelect/index.d.ts +67 -0
  109. package/lib/cjs/MultiFuncSelect/index.js +1826 -0
  110. package/lib/cjs/MultilevelDropdownMenu/index.d.ts +25 -0
  111. package/lib/cjs/MultilevelDropdownMenu/index.js +464 -0
  112. package/lib/cjs/Pagination/index.d.ts +49 -0
  113. package/lib/cjs/Pagination/index.js +341 -0
  114. package/lib/cjs/Radio/index.d.ts +31 -0
  115. package/lib/cjs/Radio/index.js +246 -0
  116. package/lib/cjs/RangeSlider/index.d.ts +21 -0
  117. package/lib/cjs/RangeSlider/index.js +730 -0
  118. package/lib/cjs/ScrollReveal/index.d.ts +21 -0
  119. package/lib/cjs/ScrollReveal/index.js +216 -0
  120. package/lib/cjs/Scrollbar/index.d.ts +15 -0
  121. package/lib/cjs/Scrollbar/index.js +605 -0
  122. package/lib/cjs/SearchBar/index.d.ts +32 -0
  123. package/lib/cjs/SearchBar/index.js +246 -0
  124. package/lib/cjs/Select/index.d.ts +34 -0
  125. package/lib/cjs/Select/index.js +331 -0
  126. package/lib/cjs/ShowMoreLess/index.d.ts +30 -0
  127. package/lib/cjs/ShowMoreLess/index.js +202 -0
  128. package/lib/cjs/Switch/index.d.ts +29 -0
  129. package/lib/cjs/Switch/index.js +211 -0
  130. package/lib/cjs/Table/index.d.ts +25 -0
  131. package/lib/cjs/Table/index.js +2113 -0
  132. package/lib/cjs/Tabs/index.d.ts +3 -0
  133. package/lib/cjs/Tabs/index.js +323 -0
  134. package/lib/cjs/TagInput/index.d.ts +28 -0
  135. package/lib/cjs/TagInput/index.js +370 -0
  136. package/lib/cjs/Textarea/index.d.ts +30 -0
  137. package/lib/cjs/Textarea/index.js +242 -0
  138. package/lib/cjs/Toast/index.d.ts +35 -0
  139. package/lib/cjs/Toast/index.js +340 -0
  140. package/lib/cjs/Tooltip/index.d.ts +19 -0
  141. package/lib/cjs/Tooltip/index.js +200 -0
  142. package/lib/cjs/Tree/index.d.ts +37 -0
  143. package/lib/cjs/Tree/index.js +1406 -0
  144. package/lib/cjs/index.d.ts +33 -0
  145. package/lib/cjs/index.js +35 -0
  146. package/lib/css/BackToTop/index.css +34 -0
  147. package/lib/css/CascadingSelect/index.css +159 -0
  148. package/lib/css/CascadingSelectE2E/index.css +159 -0
  149. package/lib/css/ColorPicker/index.css +38 -0
  150. package/lib/css/DropdownMenu/index.css +127 -0
  151. package/lib/css/MultiFuncSelect/index.css +178 -0
  152. package/lib/css/MultilevelDropdownMenu/index.css +35 -0
  153. package/lib/css/RangeSlider/index.css +149 -0
  154. package/lib/css/ScrollReveal/index.css +23 -0
  155. package/lib/css/Scrollbar/index.css +168 -0
  156. package/lib/css/ShowMoreLess/index.css +23 -0
  157. package/lib/css/Table/index.css +533 -0
  158. package/lib/css/TagInput/index.css +90 -0
  159. package/lib/css/Toast/index.css +95 -0
  160. package/lib/css/Tooltip/index.css +240 -0
  161. package/lib/css/Tree/index.css +225 -0
  162. package/lib/esm/BackToTop/index.scss +47 -0
  163. package/lib/esm/BackToTop/index.tsx +182 -0
  164. package/lib/esm/BackToTop/utils/easing.js +200 -0
  165. package/lib/esm/BackToTop/utils/performance.js +52 -0
  166. package/lib/esm/CascadingSelect/Group.tsx +39 -0
  167. package/lib/esm/CascadingSelect/index.scss +214 -0
  168. package/lib/esm/CascadingSelect/index.tsx +922 -0
  169. package/lib/esm/CascadingSelect/utils/performance.js +52 -0
  170. package/lib/esm/CascadingSelectE2E/Group.tsx +39 -0
  171. package/lib/esm/CascadingSelectE2E/index.scss +214 -0
  172. package/lib/esm/CascadingSelectE2E/index.tsx +1091 -0
  173. package/lib/esm/CascadingSelectE2E/utils/performance.js +52 -0
  174. package/lib/esm/Checkbox/index.tsx +160 -0
  175. package/lib/esm/ColorPicker/index.scss +48 -0
  176. package/lib/esm/ColorPicker/index.tsx +187 -0
  177. package/lib/esm/DigitalClock/index.tsx +72 -0
  178. package/lib/esm/DigitalClock/utils/useInterval.js +43 -0
  179. package/lib/esm/DropdownMenu/Option.tsx +27 -0
  180. package/lib/esm/DropdownMenu/index.scss +180 -0
  181. package/lib/esm/DropdownMenu/index.tsx +148 -0
  182. package/lib/esm/DynamicFields/index.tsx +386 -0
  183. package/lib/esm/File/index.tsx +302 -0
  184. package/lib/esm/Input/index.tsx +233 -0
  185. package/lib/esm/LiveSearch/index.tsx +582 -0
  186. package/lib/esm/LiveSearch/utils/performance.js +52 -0
  187. package/lib/esm/LiveSearch/utils/useThrottle.js +36 -0
  188. package/lib/esm/ModalDialog/index.tsx +479 -0
  189. package/lib/esm/ModalDialog/plugins/BSL/bodyScrollLock.es6.js +262 -0
  190. package/lib/esm/ModalDialog/plugins/BSL/index.ts +2 -0
  191. package/lib/esm/ModeSwitch/index.tsx +82 -0
  192. package/lib/esm/MultiFuncSelect/index.scss +269 -0
  193. package/lib/esm/MultiFuncSelect/index.tsx +1597 -0
  194. package/lib/esm/MultiFuncSelect/utils/performance.js +52 -0
  195. package/lib/esm/MultiFuncSelect/utils/tree.js +103 -0
  196. package/lib/esm/MultiFuncSelect/utils/useThrottle.js +36 -0
  197. package/lib/esm/MultilevelDropdownMenu/MenuList.tsx +230 -0
  198. package/lib/esm/MultilevelDropdownMenu/index.scss +75 -0
  199. package/lib/esm/MultilevelDropdownMenu/index.tsx +71 -0
  200. package/lib/esm/MultilevelDropdownMenu/utils/dom.js +81 -0
  201. package/lib/esm/Pagination/index.tsx +230 -0
  202. package/lib/esm/Pagination/pagination-navigators.tsx +60 -0
  203. package/lib/esm/Radio/index.tsx +201 -0
  204. package/lib/esm/RangeSlider/index.scss +184 -0
  205. package/lib/esm/RangeSlider/index.tsx +223 -0
  206. package/lib/esm/ScrollReveal/index.scss +27 -0
  207. package/lib/esm/ScrollReveal/index.tsx +146 -0
  208. package/lib/esm/Scrollbar/index.scss +217 -0
  209. package/lib/esm/Scrollbar/index.tsx +497 -0
  210. package/lib/esm/Scrollbar/utils/performance.js +52 -0
  211. package/lib/esm/SearchBar/index.tsx +181 -0
  212. package/lib/esm/Select/index.tsx +276 -0
  213. package/lib/esm/ShowMoreLess/index.scss +27 -0
  214. package/lib/esm/ShowMoreLess/index.tsx +144 -0
  215. package/lib/esm/Switch/index.tsx +143 -0
  216. package/lib/esm/Table/TableColgroup.tsx +29 -0
  217. package/lib/esm/Table/TableField.tsx +40 -0
  218. package/lib/esm/Table/TableFieldRow.tsx +212 -0
  219. package/lib/esm/Table/TableHeaders.tsx +146 -0
  220. package/lib/esm/Table/TableRow.tsx +127 -0
  221. package/lib/esm/Table/TableSummaries.tsx +36 -0
  222. package/lib/esm/Table/index.scss +364 -0
  223. package/lib/esm/Table/index.tsx +576 -0
  224. package/lib/esm/Table/table-utils.ts +65 -0
  225. package/lib/esm/Table/utils/dom.js +81 -0
  226. package/lib/esm/Table/utils/performance.js +52 -0
  227. package/lib/esm/Tabs/TabList.tsx +42 -0
  228. package/lib/esm/Tabs/TabPanel.tsx +34 -0
  229. package/lib/esm/Tabs/Tabs.tsx +232 -0
  230. package/lib/esm/Tabs/index.tsx +3 -0
  231. package/lib/esm/TagInput/index.scss +125 -0
  232. package/lib/esm/TagInput/index.tsx +314 -0
  233. package/lib/esm/Textarea/index.tsx +178 -0
  234. package/lib/esm/Toast/Item.tsx +75 -0
  235. package/lib/esm/Toast/index.scss +120 -0
  236. package/lib/esm/Toast/index.tsx +249 -0
  237. package/lib/esm/Tooltip/index.scss +327 -0
  238. package/lib/esm/Tooltip/index.tsx +142 -0
  239. package/lib/esm/Tree/TreeList.tsx +503 -0
  240. package/lib/esm/Tree/index.scss +375 -0
  241. package/lib/esm/Tree/index.tsx +301 -0
  242. package/lib/esm/Tree/init-height.tsx +27 -0
  243. package/lib/esm/Tree/utils/convert-tree.js +29 -0
  244. package/lib/esm/Tree/utils/dom.js +81 -0
  245. package/lib/esm/index.js +31 -0
  246. package/package.json +40 -0
@@ -0,0 +1,1597 @@
1
+ import React, { useId, useEffect, useState, useRef, forwardRef } from 'react';
2
+
3
+ import { debounce } from './utils/performance';
4
+ import useThrottle from './utils/useThrottle';
5
+
6
+
7
+ import {
8
+ flatTree,
9
+ addTreeDepth,
10
+ addTreeIndent
11
+ } from './utils/tree';
12
+
13
+
14
+ declare module 'react' {
15
+ interface ReactI18NextChildren<T> {
16
+ children?: any;
17
+ }
18
+ }
19
+
20
+ type MultiFuncSelectOptionChangeFnType = (arg1: any, arg2: any, arg3: any) => void;
21
+
22
+ interface MultiSelectDataConfig {
23
+ values: string[] | number[];
24
+ labels: string[] | number[];
25
+ queryStrings: string[] | number[];
26
+ }
27
+
28
+ interface OptionConfig {
29
+ label: any;
30
+ value: any;
31
+ queryString: string | number;
32
+ }
33
+
34
+
35
+ interface MultiSelectConfig {
36
+ valid: boolean;
37
+ selectAll: boolean;
38
+ selectAllLabel?: string;
39
+ data: MultiSelectDataConfig | null;
40
+ }
41
+
42
+
43
+ type MultiFuncSelectProps = {
44
+ wrapperClassName?: string;
45
+ controlClassName?: string;
46
+ multiSelect?: MultiSelectConfig;
47
+ value?: string;
48
+ label?: React.ReactNode | string;
49
+ name?: string;
50
+ disabled?: any;
51
+ required?: any;
52
+ readOnly?: any;
53
+ placeholder?: string;
54
+ options?: OptionConfig[] | string;
55
+ hierarchical?: boolean;
56
+ indentation?: string;
57
+ winWidth?: string | Function;
58
+ doubleIndent?: boolean;
59
+ controlArrow?: React.ReactNode;
60
+ fetchTrigger?: boolean;
61
+ fetchTriggerForDefaultData?: MultiSelectDataConfig | null;
62
+ /** Set the depth value of the control to control the display of the pop-up layer appear above.
63
+ * Please set it when multiple controls are used at the same time. */
64
+ depth?: number;
65
+ /** Incoming data, you can set the third parameter of `onFetch` */
66
+ data?: any;
67
+ /** -- */
68
+ id?: string;
69
+ style?: React.CSSProperties;
70
+ tabIndex?: number;
71
+ [key: `data-${string}`]: string | undefined;
72
+ fetchNoneInfo?: string;
73
+ fetchUpdate?: boolean;
74
+ fetchFuncAsync?: any;
75
+ fetchFuncMethod?: string;
76
+ fetchFuncMethodParams?: any[];
77
+ fetchCallback?: (data: any) => void;
78
+ onFetch?: (e: any, e2: any, value: string, data: any, incomingData: string | null | undefined) => void;
79
+ onLoad?: (e: any, e2: any, value: string | null | undefined ) => void;
80
+ onSelect?: (data: any) => void;
81
+ onChange?: MultiFuncSelectOptionChangeFnType | null;
82
+ onBlur?: (e: any) => void;
83
+ onFocus?: (e: any) => void;
84
+ };
85
+
86
+ const MultiFuncSelect = forwardRef((props: MultiFuncSelectProps, ref: any) => {
87
+ const {
88
+ wrapperClassName,
89
+ controlClassName,
90
+ multiSelect,
91
+ disabled,
92
+ required,
93
+ value,
94
+ label,
95
+ name,
96
+ readOnly,
97
+ placeholder,
98
+ id,
99
+ options,
100
+ hierarchical,
101
+ indentation,
102
+ doubleIndent,
103
+ style,
104
+ depth,
105
+ controlArrow,
106
+ winWidth,
107
+ tabIndex,
108
+ fetchTrigger,
109
+ fetchTriggerForDefaultData,
110
+ fetchNoneInfo,
111
+ fetchUpdate,
112
+ fetchFuncAsync,
113
+ fetchFuncMethod,
114
+ fetchFuncMethodParams,
115
+ data,
116
+ fetchCallback,
117
+ onFetch,
118
+ onLoad,
119
+ onSelect,
120
+ onChange,
121
+ onBlur,
122
+ onFocus,
123
+ ...attributes
124
+ } = props;
125
+
126
+
127
+ const WIN_WIDTH = typeof winWidth === 'function' ? winWidth() : winWidth ? winWidth : 'auto';
128
+ const INDENT_PLACEHOLDER = doubleIndent ? `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` : `&nbsp;&nbsp;&nbsp;&nbsp;`;
129
+ const INDENT_LAST_PLACEHOLDER = `${typeof indentation !== 'undefined' && indentation !== '' ? `${indentation}&nbsp;&nbsp;` : ''}`;
130
+ const uniqueID = useId().replace(/\:/g, "-");
131
+ const idRes = id || uniqueID;
132
+ const rootRef = useRef<any>(null);
133
+ const rootSingleRef = useRef<any>(null);
134
+ const rootMultiRef = useRef<any>(null);
135
+ const selectInputRef = useRef<any>(null);
136
+ const valueInputRef = useRef<any>(null);
137
+ const listRef = useRef<any>(null);
138
+ const listContentRef = useRef<any>(null);
139
+ const optionsRes = options ? isJSON( options ) ? JSON.parse( options as string ) : options : [];
140
+ const windowScrollUpdate = debounce(handleScrollEvent, 500);
141
+
142
+
143
+ // return a array of options
144
+ let optionsDataInit: OptionConfig[] = optionsRes;
145
+
146
+ //
147
+ const [orginalData, setOrginalData] = useState<OptionConfig[]>(optionsDataInit);
148
+ const [optionsData, setOptionsData] = useState<OptionConfig[]>(optionsDataInit);
149
+ const [hasErr, setHasErr] = useState<boolean>(false);
150
+ const [controlLabel, setControlLabel] = useState<string | undefined>('');
151
+ const [controlValue, setControlValue] = useState<string | undefined>('');
152
+ const [controlTempValue, setControlTempValue] = useState<string | null>(null);
153
+ const [isOpen, setIsOpen] = useState<boolean>(false);
154
+ const [listContentHeight, setListContentHeight] = useState<number>(0);
155
+ const [incomingData, setIncomingData] = useState<string | null | undefined>(null);
156
+
157
+
158
+ // Multiple selection
159
+ const MULTI_SEL_VALID = multiSelect ? multiSelect.valid : false;
160
+ const [controlArr, setControlArr] = useState<any>({
161
+ labels: [],
162
+ values: []
163
+ });
164
+ const [itemSelectedAll, setItemSelectedAll] = useState<boolean>(false);
165
+ const multiSelControlOptionExist = (arr: any[], val: any) => arr.map((v: any) => v.toString()).includes(val.toString());
166
+
167
+
168
+ //performance
169
+ const handleChangeFetchSafe = useThrottle((val: any) => {
170
+
171
+ let _orginalData: OptionConfig[] = [];
172
+ const update = (inputData: any) => {
173
+ const filterRes = (data: any[]) => {
174
+ return inputData.filter((item: any) => {
175
+ if (
176
+ (
177
+ item.queryString.split(',').some((l: any) => l.charAt(0) === val.toLowerCase()) ||
178
+ item.queryString.split(',').some((l: any) => l.replace(/ /g, '').indexOf(val.toLowerCase()) >= 0) ||
179
+ item.label.toLowerCase().indexOf(val.toLowerCase()) >= 0
180
+ ) &&
181
+ val != ''
182
+ ) {
183
+ return true;
184
+ } else {
185
+ return false;
186
+ }
187
+ });
188
+ }
189
+
190
+ setOptionsData(filterRes);
191
+ };
192
+
193
+ if ( fetchUpdate ) {
194
+
195
+ handleFetch(val).then((response: any) => {
196
+ _orginalData = response;
197
+ update(_orginalData);
198
+ });
199
+ } else {
200
+ _orginalData = orginalData;
201
+ update(_orginalData);
202
+ }
203
+
204
+
205
+ }, 150, [optionsData]);
206
+
207
+
208
+
209
+ /**
210
+ * Format indent value
211
+ * @param {String|Array} str
212
+ * @returns {String|Array}
213
+ */
214
+ function formatIndentVal(str: any) {
215
+ const reVar = new RegExp(INDENT_LAST_PLACEHOLDER, 'g');
216
+ if (Array.isArray(str)) {
217
+ return str.map((s: string) => s.replace(reVar,'').replace(/\&nbsp;/ig,''));
218
+ } else {
219
+ return str.replace(reVar,'').replace(/\&nbsp;/ig,'');
220
+ }
221
+
222
+ }
223
+
224
+
225
+ /**
226
+ * Array unique
227
+ * @param {Array} str
228
+ * @returns {Array}
229
+ */
230
+ function unique(arr: any[]) {
231
+ return Array.from(new Set(arr));
232
+ }
233
+
234
+
235
+ /**
236
+ * Remove html tag content
237
+ * @param {string} str
238
+ * @returns {string}
239
+ */
240
+ function stripHTML(str: string) {
241
+ return str.replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
242
+ }
243
+
244
+
245
+ /**
246
+ * Remove a specific item from an array
247
+ * @param {array} arr
248
+ * @param {string} value
249
+ * @returns {array}
250
+ */
251
+ function removeItemOnce(arr: any[], value: string | number) {
252
+ const arrFormat = arr.map((v: any) => v.toString());
253
+ const index = arrFormat.indexOf(value.toString());
254
+ if (index > -1) {
255
+ arrFormat.splice(index, 1);
256
+ }
257
+ return arrFormat;
258
+ }
259
+
260
+ /**
261
+ * Remove multiple items from an array
262
+ * @param {array} arr
263
+ * @param {array} value
264
+ * @returns {array}
265
+ */
266
+ function removeItems(arr: any[], value: any[]) {
267
+ const arrFormat = arr.map((v: any) => v.toString());
268
+ const valueFormat = value.map((v: any) => v.toString());
269
+ return arrFormat.filter((v: any) => {
270
+ return !valueFormat.includes(v);
271
+ });
272
+ }
273
+
274
+
275
+ /**
276
+ * Check if an element is in the viewport
277
+ * @param {HTMLElement} elem
278
+ * @returns {boolean}
279
+ */
280
+ function isInViewport(elem: HTMLElement) {
281
+ const bounding = elem.getBoundingClientRect();
282
+ return (
283
+ bounding.top >= 0 &&
284
+ bounding.left >= 0 &&
285
+ bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
286
+ bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
287
+ );
288
+ }
289
+
290
+
291
+ function handleScrollEvent() {
292
+ getPlacement(listRef.current, true);
293
+ }
294
+
295
+
296
+ //
297
+ function getPlacement(el: HTMLElement, restorePos: boolean = false) {
298
+
299
+ if ( el === null ) return;
300
+
301
+
302
+ const PLACEMENT_TOP = 'top-0';
303
+ const PLACEMENT_BOTTOMEND = 'bottom-0';
304
+ const PLACEMENT_RIGHT = 'end-0';
305
+ const PLACEMENT_LEFT = 'start-0';
306
+
307
+ const elTop = el.getBoundingClientRect().top;
308
+ const elSpacing = 50 + selectInputRef.current.clientHeight*3;
309
+ const elMinWindowSpacing = selectInputRef.current.clientHeight*2;
310
+
311
+
312
+ //restore position
313
+ if ( restorePos ) {
314
+ if ( isInViewport(el) ) {
315
+ el.classList.remove(PLACEMENT_BOTTOMEND);
316
+ el.style.removeProperty('bottom');
317
+ }
318
+ return;
319
+ }
320
+
321
+ if ( listContentRef.current === null ) return;
322
+
323
+
324
+ // STEP 0:
325
+ // save content height (Suitable for initial data with unchanged open options)
326
+ let _contentHeight = el.offsetHeight;
327
+ if ( listContentHeight === 0 ) {
328
+ setListContentHeight(el.offsetHeight);
329
+ } else {
330
+ _contentHeight = listContentHeight;
331
+ }
332
+
333
+
334
+ // STEP 1:
335
+ // If the content exceeds the height of the window, first limit height and add scrollbar
336
+ let maxHeight = window.innerHeight - elSpacing;
337
+ if ( maxHeight < selectInputRef.current.clientHeight ) maxHeight = elMinWindowSpacing;
338
+
339
+ if ( _contentHeight > 0 && (_contentHeight > maxHeight) ) {
340
+
341
+ const newH = maxHeight - (elTop > window.innerHeight/2 ? 0 : elTop) + elMinWindowSpacing;
342
+
343
+ // default position
344
+ listContentRef.current.style.height = newH + 'px';
345
+
346
+
347
+ // if it's on top
348
+ if ( newH > maxHeight ) {
349
+ listContentRef.current.style.height = elTop - elMinWindowSpacing + 'px';
350
+ }
351
+
352
+
353
+ // Adjust the overall height to fit the wrapper
354
+ const _displayedItems = listContentRef.current.querySelectorAll('.list-group-item');
355
+ const _displayedHeight = _displayedItems[0].clientHeight * _displayedItems.length;
356
+ if ( _displayedHeight < listRef.current.clientHeight ) {
357
+ listContentRef.current.style.height = _displayedHeight + 'px';
358
+ }
359
+
360
+ //
361
+ listContentRef.current.style.overflowY = 'auto';
362
+
363
+
364
+
365
+ } else {
366
+ listContentRef.current.style.height = 'auto';
367
+ listContentRef.current.style.overflowY = 'inherit';
368
+ }
369
+
370
+
371
+ // STEP 2:
372
+ // Adjust position
373
+ if ( !isInViewport(el) ) {
374
+ el.classList.add(PLACEMENT_BOTTOMEND);
375
+ el.style.setProperty('bottom', selectInputRef.current.clientHeight + 5 + 'px', "important");
376
+ }
377
+
378
+
379
+
380
+ // STEP 3:
381
+ // It is on top when no scrollbars have been added
382
+ if ( !isInViewport(el) ) {
383
+ if ( el.getBoundingClientRect().top < 0 ) {
384
+ listContentRef.current.style.height = _contentHeight + el.getBoundingClientRect().top - elMinWindowSpacing + 'px';
385
+ listContentRef.current.style.overflowY = 'auto';
386
+ }
387
+ }
388
+
389
+ }
390
+
391
+
392
+
393
+
394
+ // Determine whether it is in JSON format
395
+ function isJSON( str: any ){
396
+
397
+ if ( typeof(str) === 'string' && str.length > 0 ) {
398
+
399
+ if ( str.replace( /\"\"/g, '' ).replace( /\,/g, '' ) == '[{}]' ) {
400
+ return false;
401
+ } else {
402
+
403
+ if (/^[\],:{}\s]*$/.test( str.replace(/\\["\\\/bfnrtu]/g, '@' ).
404
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
405
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
406
+
407
+ return true;
408
+
409
+ }else{
410
+ return false;
411
+ }
412
+
413
+ }
414
+
415
+ } else {
416
+
417
+ if (
418
+ typeof(str) === 'object' &&
419
+ Object.prototype.toString.call(str) === '[object Object]' &&
420
+ ! str.length
421
+ ) {
422
+ return true;
423
+ } else {
424
+ return false;
425
+ }
426
+
427
+ }
428
+
429
+ }
430
+
431
+
432
+ function adjustMultiControlContainerHeight() {
433
+ setTimeout(() => {
434
+ rootSingleRef.current.style.height = rootMultiRef.current.clientHeight + 'px';
435
+ selectInputRef.current.style.height = rootMultiRef.current.clientHeight + 'px';
436
+ },0);
437
+ }
438
+
439
+
440
+ async function fetchData(params: any, inputDefaultValue: any, init: boolean = true) {
441
+
442
+ // get incoming options from `data-options` of component
443
+ // It is usually used for complex cascading `<MultiFuncSelect />` components
444
+ const incomingOptionsData = valueInputRef.current.dataset.options;
445
+
446
+
447
+ // Determine whether the default value is user query input or default input
448
+ const defaultValue = init ? inputDefaultValue : '';
449
+
450
+ if ( typeof fetchFuncAsync === 'object' ) {
451
+
452
+
453
+ const response: any = await fetchFuncAsync[`${fetchFuncMethod}`](...params.split(','));
454
+ let _ORGIN_DATA = response.data;
455
+
456
+ // reset data structure
457
+ if (typeof (fetchCallback) === 'function') {
458
+ _ORGIN_DATA = fetchCallback(_ORGIN_DATA);
459
+ }
460
+
461
+ // Determine whether the data structure matches
462
+ if ( _ORGIN_DATA.length > 0 && typeof _ORGIN_DATA[0].value === 'undefined' ) {
463
+ console.warn( 'The data structure does not match, please refer to the example in the component documentation.' );
464
+ setHasErr(true);
465
+ _ORGIN_DATA = [];
466
+ }
467
+
468
+
469
+ // STEP 1: ===========
470
+ // get incoming options from `data-options` of component
471
+ if ( typeof incomingOptionsData !== 'undefined' ) {
472
+ _ORGIN_DATA = JSON.parse( incomingOptionsData );
473
+
474
+ // set value if the attribute `data-options` of component exists, only valid for single selection (it may be an empty array)
475
+ if (typeof defaultValue !== 'undefined' && defaultValue !== '') valueInputRef.current.dataset.value = defaultValue;
476
+ }
477
+
478
+
479
+ // STEP 2: ===========
480
+ // Set hierarchical categories ( with sub-categories )
481
+ if ( hierarchical ) {
482
+ addTreeDepth(_ORGIN_DATA);
483
+ _ORGIN_DATA = flatTree(_ORGIN_DATA);
484
+ addTreeIndent(_ORGIN_DATA, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
485
+ }
486
+
487
+
488
+
489
+
490
+ // STEP 3: ===========
491
+ // value & label must be initialized
492
+ let filterRes: any = [];
493
+
494
+
495
+ if ( fetchTrigger ) {
496
+
497
+ // If a manual action is used to trigger the request
498
+ if ( typeof fetchTriggerForDefaultData !== 'undefined' && fetchTriggerForDefaultData !== null && typeof fetchTriggerForDefaultData?.values[0] !== 'undefined' ) {
499
+ filterRes = [{
500
+ value: fetchTriggerForDefaultData?.values[0],
501
+ label: fetchTriggerForDefaultData?.labels[0],
502
+ queryString: fetchTriggerForDefaultData?.queryStrings[0]
503
+ }];
504
+ }
505
+
506
+ } else {
507
+ const filterResQueryValue = _ORGIN_DATA.filter((item: any) => item.value == defaultValue );
508
+ const filterResQueryLabel = _ORGIN_DATA.filter((item: any) => item.label == defaultValue );
509
+
510
+ filterRes = filterResQueryValue;
511
+ if ( filterResQueryValue.length === 0 ) filterRes = filterResQueryLabel;
512
+ }
513
+
514
+
515
+
516
+ // STEP 4: ===========
517
+ // ++++++++++++++++++++
518
+ // Single selection
519
+ // ++++++++++++++++++++
520
+ if ( typeof defaultValue === 'undefined' || defaultValue === '' ) { // Do not use `init`, otherwise the query will revert to the default value if there is no value
521
+ setControlValue('');
522
+ setControlLabel('');
523
+ } else {
524
+ if ( filterRes.length > 0 ) {
525
+ setControlValue(filterRes[0].value);
526
+ setControlLabel(formatIndentVal(filterRes[0].label) as string);
527
+ }
528
+
529
+ }
530
+
531
+
532
+ // ++++++++++++++++++++
533
+ // Multiple selection
534
+ // ++++++++++++++++++++
535
+ if ( MULTI_SEL_VALID ) {
536
+
537
+
538
+ if ( (typeof defaultValue === 'undefined' || defaultValue === '') && init ) {
539
+ setControlArr({
540
+ labels: [],
541
+ values: []
542
+ });
543
+ setItemSelectedAll(false);
544
+ }
545
+
546
+ if ( typeof defaultValue !== 'undefined' && defaultValue !== '' && multiSelect?.data !== null ) {
547
+
548
+
549
+ // initialize default values of Multiple selection
550
+ const _currentData: any = multiSelect?.data;
551
+
552
+ setControlArr({
553
+ labels: _currentData.labels,
554
+ values: _currentData.values,
555
+ });
556
+
557
+
558
+ //
559
+ const _values: string[] = defaultValue.split(',');
560
+ _values.forEach((_value: string, _index: number) => {
561
+
562
+ if ( !multiSelControlOptionExist(_currentData.values, _value) && typeof _currentData.values[_index] !== 'undefined' ) {
563
+
564
+ let filterRes: any = [];
565
+ filterRes = [{
566
+ value: _currentData.values[_index],
567
+ label: _currentData.labels[_index],
568
+ queryString: _currentData.queryStrings[_index]
569
+ }];
570
+
571
+ setControlArr((prevState: any) => {
572
+ return {
573
+ labels: unique([...prevState.labels, typeof filterRes[0] !== 'undefined' ? filterRes[0].label : ''].filter((v: any) => v !== '')),
574
+ values: unique([...prevState.values, typeof filterRes[0] !== 'undefined' ? filterRes[0].value : ''].filter((v: any) => v !== ''))
575
+ }
576
+ });
577
+
578
+ }
579
+
580
+ });
581
+
582
+
583
+
584
+ // Appropriate multi-item container height
585
+ adjustMultiControlContainerHeight();
586
+
587
+
588
+ }
589
+
590
+
591
+ }
592
+
593
+
594
+ // STEP 5: ===========
595
+ //
596
+ setOptionsData(_ORGIN_DATA); // data must be initialized
597
+
598
+ //
599
+ setOrginalData(_ORGIN_DATA);
600
+
601
+
602
+ // STEP 6: ===========
603
+ //
604
+ onFetch?.(selectInputRef.current, valueInputRef.current, defaultValue, _ORGIN_DATA, incomingData);
605
+
606
+
607
+ //
608
+ return _ORGIN_DATA;
609
+
610
+
611
+ } else {
612
+
613
+
614
+ // STEP 1: ===========
615
+ // get incoming options from `data-options` of component
616
+ if ( typeof incomingOptionsData !== 'undefined' ) {
617
+ optionsDataInit = JSON.parse( incomingOptionsData );
618
+
619
+ // set value if the attribute `data-options` of component exists, only valid for single selection (it may be an empty array)
620
+ if (typeof defaultValue !== 'undefined' && defaultValue !== '') valueInputRef.current.dataset.value = defaultValue;
621
+
622
+ }
623
+
624
+
625
+ // STEP 2: ===========
626
+ // Set hierarchical categories ( with sub-categories )
627
+ if ( hierarchical ) {
628
+ addTreeDepth(optionsDataInit);
629
+ optionsDataInit = flatTree(optionsDataInit);
630
+ addTreeIndent(optionsDataInit, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
631
+ }
632
+
633
+
634
+
635
+ // STEP 3: ===========
636
+ // value & label must be initialized
637
+ let filterRes: any = [];
638
+ const filterResQueryValue = optionsDataInit.filter((item: any) => item.value == defaultValue );
639
+ const filterResQueryLabel = optionsDataInit.filter((item: any) => item.label == defaultValue );
640
+
641
+ filterRes = filterResQueryValue;
642
+ if ( filterResQueryValue.length === 0 ) filterRes = filterResQueryLabel;
643
+
644
+ // STEP 4: ===========
645
+ // ++++++++++++++++++++
646
+ // Single selection
647
+ // ++++++++++++++++++++
648
+ if ( typeof defaultValue === 'undefined' || defaultValue === '' ) { // Do not use `init`, otherwise the query will revert to the default value if there is no value
649
+ setControlValue('');
650
+ setControlLabel('');
651
+ } else {
652
+ if ( filterRes.length > 0 ) {
653
+ setControlValue(filterRes[0].value);
654
+ setControlLabel(formatIndentVal(filterRes[0].label));
655
+ }
656
+
657
+ }
658
+
659
+
660
+
661
+ // ++++++++++++++++++++
662
+ // Multiple selection
663
+ // ++++++++++++++++++++
664
+ if ( MULTI_SEL_VALID ) {
665
+
666
+
667
+ if ( (typeof defaultValue === 'undefined' || defaultValue === '') && init ) {
668
+ setControlArr({
669
+ labels: [],
670
+ values: []
671
+ });
672
+ setItemSelectedAll(false);
673
+ }
674
+
675
+ if ( typeof defaultValue !== 'undefined' && defaultValue !== '' && multiSelect?.data !== null ) {
676
+
677
+ // initialize default values of Multiple selection
678
+ const _currentData: any = multiSelect?.data;
679
+
680
+ setControlArr({
681
+ labels: _currentData.labels,
682
+ values: _currentData.values,
683
+ });
684
+
685
+
686
+ //
687
+ const _values: string[] = typeof defaultValue !== 'undefined' ? defaultValue.split(',') : [];
688
+ _values.forEach((_value: string, _index: number) => {
689
+
690
+ if ( !multiSelControlOptionExist(_currentData.values, _value) && typeof _currentData.values[_index] !== 'undefined' ) {
691
+
692
+
693
+ let filterRes: any = [];
694
+ filterRes = [{
695
+ value: _currentData.values[_index],
696
+ label: _currentData.labels[_index],
697
+ queryString: _currentData.queryStrings[_index]
698
+ }];
699
+
700
+ setControlArr((prevState: any) => {
701
+ return {
702
+ labels: unique([...prevState.labels, typeof filterRes[0] !== 'undefined' ? filterRes[0].label : ''].filter((v: any) => v !== '')),
703
+ values: unique([...prevState.values, typeof filterRes[0] !== 'undefined' ? filterRes[0].value : ''].filter((v: any) => v !== ''))
704
+ }
705
+ });
706
+
707
+
708
+ }
709
+ });
710
+
711
+ // Appropriate multi-item container height
712
+ adjustMultiControlContainerHeight();
713
+ }
714
+
715
+
716
+ }
717
+
718
+
719
+ // STEP 5: ===========
720
+ //
721
+ setOptionsData(optionsDataInit); // data must be initialized
722
+
723
+ //
724
+ setOrginalData(optionsDataInit);
725
+
726
+ // STEP 6: ===========
727
+ //
728
+ onFetch?.(selectInputRef.current, valueInputRef.current, defaultValue, optionsDataInit, incomingData);
729
+
730
+ //
731
+ return optionsDataInit;
732
+ }
733
+
734
+
735
+ }
736
+
737
+ function cancel() {
738
+ // hide list
739
+ setIsOpen(false);
740
+
741
+ // restore data
742
+ setOptionsData(orginalData);
743
+
744
+ // update temporary value
745
+ setControlTempValue(null);
746
+ }
747
+
748
+ function activate() {
749
+
750
+ // show list
751
+ setIsOpen(true);
752
+
753
+ // restore data
754
+ setOptionsData(orginalData);
755
+
756
+ // update temporary value
757
+ setControlTempValue('');
758
+
759
+ }
760
+
761
+ function rootWrapperSwitch() {
762
+ // remove active styles from the root container and activate current wrapper
763
+ [].slice.call(document.querySelectorAll('.multifunc-select__wrapper')).forEach((node: any) => {
764
+ node.classList.remove('active', 'focus');
765
+ });
766
+ rootRef.current.classList.add('active', 'focus');
767
+ }
768
+
769
+
770
+ async function handleSelect(el: any, dataInput: any = false, valueArr: any[] = [], labelArr: any[] = []) {
771
+
772
+ if ( typeof el === 'undefined' ) return;
773
+
774
+ let index: number | undefined | string;
775
+
776
+ // get incoming options from `data-options` of component
777
+ // It is usually used for complex cascading `<MultiFuncSelect />` components
778
+ const incomingOptionsData = valueInputRef.current.dataset.options;
779
+
780
+
781
+ // cancel
782
+ if ( !(MULTI_SEL_VALID && isOpen) ) {
783
+ cancel();
784
+ }
785
+
786
+ //remove focus style
787
+ if ( !(MULTI_SEL_VALID && isOpen) ) {
788
+ rootRef.current.classList.remove('focus');
789
+ }
790
+
791
+
792
+ // update value * label
793
+ if ( dataInput ) {
794
+
795
+ // using keyboard
796
+ const _data = JSON.parse(dataInput);
797
+ const _value = _data.value;
798
+ const _label = _data.label;
799
+
800
+ // ++++++++++++++++++++
801
+ // Single selection
802
+ // ++++++++++++++++++++
803
+ setControlValue(_value);
804
+ setControlLabel(formatIndentVal(_label));
805
+
806
+
807
+ // set value if the attribute `data-options` of component exists, only valid for single selection (it may be an empty array)
808
+ if ( typeof incomingOptionsData !== 'undefined' ) {
809
+ valueInputRef.current.dataset.value = _value;
810
+ }
811
+
812
+
813
+ // ++++++++++++++++++++
814
+ // Multiple selection
815
+ // ++++++++++++++++++++
816
+ let currentControlValueArr: any[] = JSON.parse(JSON.stringify(valueArr));
817
+ let currentControlLabelArr: any[] = JSON.parse(JSON.stringify(labelArr));
818
+
819
+ if ( MULTI_SEL_VALID ) {
820
+
821
+ if ( multiSelControlOptionExist(valueArr, _value) ) {
822
+
823
+ setControlArr((prevState: any) => {
824
+
825
+ // update temporary value
826
+ setControlTempValue(prevState.labels.length >= 0 ? null : prevState.labels.join(','));
827
+
828
+ return {
829
+ labels: removeItemOnce(prevState.labels, formatIndentVal(_label)),
830
+ values: removeItemOnce(prevState.values, _value)
831
+ }
832
+ });
833
+
834
+
835
+ currentControlValueArr = removeItemOnce(currentControlValueArr, _value);
836
+ currentControlLabelArr = removeItemOnce(currentControlLabelArr, formatIndentVal(_label));
837
+
838
+
839
+
840
+ } else {
841
+
842
+
843
+ setControlArr((prevState: any) => {
844
+
845
+ // update temporary value
846
+ setControlTempValue(prevState.labels.length >= 0 ? null : prevState.labels.join(','));
847
+
848
+ return {
849
+ labels: [...prevState.labels, formatIndentVal(_label)],
850
+ values: [...prevState.values, _value]
851
+ }
852
+ });
853
+
854
+ currentControlValueArr.push(_value);
855
+ currentControlLabelArr.push(_label);
856
+
857
+ }
858
+
859
+ // Appropriate multi-item container height
860
+ adjustMultiControlContainerHeight();
861
+
862
+ }
863
+
864
+ //
865
+ if ( typeof(onChange) === 'function' ) {
866
+ onChange?.(selectInputRef.current, valueInputRef.current, !MULTI_SEL_VALID ? optionsData[index as never] : {labels: currentControlLabelArr.map((v: any) => v.toString()), values: currentControlValueArr.map((v: any) => v.toString())});
867
+
868
+ //
869
+ selectInputRef.current.blur();
870
+ }
871
+
872
+ } else {
873
+
874
+ index = typeof el.currentTarget !== 'undefined' ? el.currentTarget.dataset.index : el.dataset.index;
875
+
876
+ const _value = optionsData[index as never].value;
877
+ const _label = optionsData[index as never].label;
878
+
879
+ // ++++++++++++++++++++
880
+ // Single selection
881
+ // ++++++++++++++++++++
882
+ setControlValue(_value);
883
+ setControlLabel(formatIndentVal(_label));
884
+
885
+ // set value if the attribute `data-options` of component exists, only valid for single selection (it may be an empty array)
886
+ if ( typeof incomingOptionsData !== 'undefined' ) {
887
+ valueInputRef.current.dataset.value = _value;
888
+ }
889
+
890
+
891
+ // ++++++++++++++++++++
892
+ // Multiple selection
893
+ // ++++++++++++++++++++
894
+ let currentControlValueArr: any[] = JSON.parse(JSON.stringify(controlArr.values));
895
+ let currentControlLabelArr: any[] = JSON.parse(JSON.stringify(controlArr.labels));
896
+
897
+ if ( MULTI_SEL_VALID ) {
898
+
899
+ if ( multiSelControlOptionExist(controlArr.values, _value) ) {
900
+
901
+ setControlArr((prevState: any) => {
902
+
903
+ // update temporary value
904
+ setControlTempValue(prevState.labels.length >= 0 ? null : prevState.labels.join(','));
905
+
906
+ return {
907
+ labels: removeItemOnce(prevState.labels, formatIndentVal(_label)),
908
+ values: removeItemOnce(prevState.values, _value)
909
+ }
910
+ });
911
+
912
+ currentControlValueArr = removeItemOnce(currentControlValueArr, _value);
913
+ currentControlLabelArr = removeItemOnce(currentControlLabelArr, formatIndentVal(_label));
914
+
915
+ } else {
916
+
917
+
918
+ setControlArr((prevState: any) => {
919
+
920
+ // update temporary value
921
+ setControlTempValue(prevState.labels.length >= 0 ? null : prevState.labels.join(','));
922
+
923
+ return {
924
+ labels: [...prevState.labels, formatIndentVal(_label)],
925
+ values: [...prevState.values, _value]
926
+ }
927
+ });
928
+
929
+
930
+ currentControlValueArr.push(_value);
931
+ currentControlLabelArr.push(_label);
932
+
933
+ }
934
+
935
+
936
+ // Appropriate multi-item container height
937
+ adjustMultiControlContainerHeight();
938
+
939
+ }
940
+
941
+ //
942
+ if ( typeof(onChange) === 'function' ) {
943
+ onChange?.(selectInputRef.current, valueInputRef.current, !MULTI_SEL_VALID ? optionsData[index as never] : {labels: currentControlLabelArr.map((v: any) => v.toString()), values: currentControlValueArr.map((v: any) => v.toString())});
944
+
945
+ //
946
+ selectInputRef.current.blur();
947
+ }
948
+ }
949
+
950
+
951
+
952
+ }
953
+
954
+
955
+ function handleSelectAll(event: any) {
956
+ event.preventDefault();
957
+
958
+ const onChangeSelectAll = (labelsArr: string[], valuesArr: string[]) => {
959
+ if ( typeof(onChange) === 'function' ) {
960
+ onChange?.(selectInputRef.current, valueInputRef.current, {labels: labelsArr.map((v: any) => v.toString()), values: valuesArr.map((v: any) => v.toString())});
961
+
962
+
963
+ //
964
+ selectInputRef.current.blur();
965
+ }
966
+ };
967
+
968
+ setItemSelectedAll((prevState) => {
969
+
970
+ if ( !prevState ) {
971
+
972
+
973
+ setControlArr((prevData: any) => {
974
+
975
+ const currentControlValueArr = [...prevData.values, ...optionsData.map((v: any) => v.value)].filter((item: any, index: number, arr: any[]) => arr.indexOf(item, 0) === index );
976
+ const currentControlLabelArr = [...formatIndentVal(prevData.labels), ...formatIndentVal(optionsData.map((v: any) => v.label))].filter((item: any, index: number, arr: any[]) => arr.indexOf(item, 0) === index );
977
+
978
+ //
979
+ onChangeSelectAll(currentControlLabelArr, currentControlValueArr);
980
+
981
+ return {
982
+ labels: currentControlLabelArr,
983
+ values: currentControlValueArr
984
+ }
985
+ });
986
+
987
+
988
+
989
+ } else {
990
+
991
+
992
+ setControlArr((prevData: any) => {
993
+
994
+ const currentControlValueArr = removeItems(prevData.values, optionsData.map((v: any) => v.value));
995
+ const currentControlLabelArr = removeItems(formatIndentVal(prevData.labels), formatIndentVal(optionsData.map((v: any) => v.label)));
996
+
997
+
998
+ //
999
+ onChangeSelectAll(currentControlLabelArr, currentControlValueArr);
1000
+
1001
+ return {
1002
+ labels: currentControlLabelArr,
1003
+ values: currentControlValueArr
1004
+ }
1005
+ });
1006
+
1007
+ }
1008
+
1009
+ return !prevState;
1010
+ } );
1011
+
1012
+ }
1013
+
1014
+
1015
+ function handleMultiControlItemRemove(event: any) {
1016
+ event.preventDefault();
1017
+
1018
+ const valueToRemove = String(event.currentTarget.dataset.item);
1019
+ const getCurrentIndex = controlArr.values.findIndex((item: any) => item.toString() === valueToRemove );
1020
+
1021
+ let currentControlValueArr: any[] = JSON.parse(JSON.stringify(controlArr.values));
1022
+ let currentControlLabelArr: any[] = JSON.parse(JSON.stringify(controlArr.labels));
1023
+
1024
+ const _value = valueToRemove;
1025
+ const _label = controlArr.labels[getCurrentIndex];
1026
+
1027
+
1028
+ setControlArr((prevState: any) => {
1029
+
1030
+ // update temporary value
1031
+ setControlTempValue(prevState.labels.length >= 0 ? null : prevState.labels.join(','));
1032
+
1033
+ return {
1034
+ labels: removeItemOnce(prevState.labels, formatIndentVal(_label)),
1035
+ values: removeItemOnce(prevState.values, _value)
1036
+ }
1037
+ });
1038
+
1039
+ currentControlValueArr = removeItemOnce(currentControlValueArr, _value);
1040
+ currentControlLabelArr = removeItemOnce(currentControlLabelArr, formatIndentVal(_label));
1041
+
1042
+
1043
+ // Appropriate multi-item container height
1044
+ adjustMultiControlContainerHeight();
1045
+
1046
+
1047
+ //
1048
+ if ( typeof(onChange) === 'function' ) {
1049
+ onChange?.(selectInputRef.current, valueInputRef.current, {labels: currentControlLabelArr.map((v: any) => v.toString()), values: currentControlValueArr.map((v: any) => v.toString())});
1050
+
1051
+ //
1052
+ selectInputRef.current.blur();
1053
+ }
1054
+
1055
+ }
1056
+
1057
+
1058
+
1059
+ function handleShowList() {
1060
+
1061
+ //
1062
+ rootWrapperSwitch();
1063
+
1064
+ //
1065
+ if ( !isOpen ) {
1066
+ activate();
1067
+
1068
+
1069
+ // window position
1070
+ setTimeout( ()=> {
1071
+ getPlacement(listRef.current);
1072
+ }, 0 );
1073
+ }
1074
+
1075
+ }
1076
+
1077
+
1078
+ async function handleFetch(inputVal: any = null) {
1079
+
1080
+ // data init
1081
+ const searchStr: string = typeof inputVal === 'string' ? inputVal : (controlTempValue || controlTempValue === '' ? controlTempValue : '');
1082
+ const _oparams: any[] = fetchFuncMethodParams || [];
1083
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : searchStr);
1084
+
1085
+ // if empty
1086
+ if ( searchStr.replace(/\s/g, "") === '' ) return [];
1087
+
1088
+
1089
+
1090
+ const res = await fetchData((_params).join(','), value, false);
1091
+ return res;
1092
+ }
1093
+
1094
+
1095
+
1096
+ function handleComposition(event: any) {
1097
+ if (event.type === 'compositionstart' || event.type === 'compositionend') {
1098
+ //fire change method to update for Chrome v53
1099
+ handleChange(event);
1100
+ }
1101
+ }
1102
+
1103
+ function handleChange(event: any) {
1104
+ const val = event.target.value;
1105
+
1106
+ // update temporary value
1107
+ setControlTempValue(val);
1108
+
1109
+ //
1110
+ if ( val.replace(/\s/g, "") === '' ) {
1111
+ // No elements found. Consider changing the search query.
1112
+ // restore data
1113
+ setOptionsData(orginalData);
1114
+ } else {
1115
+ handleChangeFetchSafe(val);
1116
+ }
1117
+
1118
+ // window position
1119
+ setTimeout( ()=> {
1120
+ getPlacement(listRef.current);
1121
+ }, 0 );
1122
+
1123
+ }
1124
+
1125
+ //
1126
+ function handleFocus(event: any) {
1127
+
1128
+ rootRef.current.classList.add('focus');
1129
+
1130
+ // update temporary value
1131
+ setControlTempValue('');
1132
+
1133
+ //
1134
+ onFocus?.(event);
1135
+ }
1136
+
1137
+ function handleBlur(event: any) {
1138
+
1139
+ //remove focus style
1140
+ if ( !(MULTI_SEL_VALID && isOpen) ) {
1141
+ rootRef.current.classList.remove('focus');
1142
+ }
1143
+
1144
+ setTimeout(() => {
1145
+
1146
+ // cancel
1147
+ if ( !(MULTI_SEL_VALID && isOpen) ) {
1148
+ cancel();
1149
+ }
1150
+
1151
+ onBlur?.(event);
1152
+ }, 300);
1153
+
1154
+ }
1155
+
1156
+
1157
+
1158
+ function handleClose(event: any) {
1159
+ if (event.target.closest(`.${wrapperClassName || wrapperClassName === '' ? wrapperClassName : 'multifunc-select__wrapper'}`) === null) {
1160
+ // cancel
1161
+ cancel();
1162
+ }
1163
+ }
1164
+
1165
+
1166
+ function generateInputFocusStr() {
1167
+ return controlTempValue || controlTempValue === '' ? (controlTempValue.length === 0 ? '|' : controlTempValue) : (placeholder || '');
1168
+ }
1169
+
1170
+
1171
+ function optionFocus(type: string) {
1172
+
1173
+ return new Promise(function (resolve) {
1174
+
1175
+ // Determine the "active" class name to avoid listening to other unused components of the same type
1176
+ if ( listRef.current === null || !rootRef.current.classList.contains('active') ) return;
1177
+
1178
+ if ( fetchTrigger ) return;
1179
+
1180
+
1181
+ const options = [].slice.call(listRef.current.querySelectorAll('.list-group-item'));
1182
+ const currentIndex = options.findIndex((e) => e === listRef.current.querySelector('.list-group-item.active'));
1183
+
1184
+
1185
+ // get the next element in the list, "%" will loop around to 0
1186
+ let nextIndex;
1187
+ if ( type === 'increase' ) {
1188
+ nextIndex = currentIndex + 1 % options.length;
1189
+ } else {
1190
+ nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
1191
+ }
1192
+
1193
+
1194
+ //only one
1195
+ if ( options.length === 1 ) nextIndex = 0;
1196
+
1197
+
1198
+ if ( !isNaN(nextIndex) ) {
1199
+ options.forEach( (node: any, index: number) => {
1200
+ node?.classList.remove('active');
1201
+ });
1202
+
1203
+ const targetOption = options[nextIndex] as HTMLElement;
1204
+ if ( typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match') ) {
1205
+ targetOption.classList.add('active');
1206
+ resolve(targetOption);
1207
+ }
1208
+
1209
+ }
1210
+ });
1211
+
1212
+
1213
+ }
1214
+
1215
+
1216
+
1217
+
1218
+ useEffect(() => {
1219
+
1220
+
1221
+ // Call a function when the component has been rendered completely
1222
+ //--------------
1223
+ onLoad?.(selectInputRef.current, valueInputRef.current, value);
1224
+
1225
+
1226
+ // update incoming data
1227
+ //--------------
1228
+ setIncomingData(data);
1229
+
1230
+ // data init
1231
+ //--------------
1232
+ const _oparams: any[] = fetchFuncMethodParams || [];
1233
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (fetchTrigger ? '-' : ''));
1234
+ fetchData((_params).join(','), value);
1235
+
1236
+
1237
+ // If you use the dynamic form assignment (such as document.getElementById(xxx).value),
1238
+ // you need to judge the value of the input obtained by using the macrotask "setInterval()"
1239
+ let timer: any = null;
1240
+ let initTimes: number = 0;
1241
+ let hasValue: boolean = false;
1242
+ timer = setInterval( () => {
1243
+ if ( initTimes > 5 || hasValue ) {
1244
+ clearInterval(timer);
1245
+ } else {
1246
+
1247
+ // get value if the attribute `data-value` of component exists
1248
+ // Using `<DynamicFields />` will assign values ​​according to `data-value`
1249
+ const incomingOptionsData = valueInputRef.current.dataset.options;
1250
+ if ( valueInputRef.current !== null && typeof incomingOptionsData !== 'undefined' && typeof valueInputRef.current.dataset.value !== 'undefined' && valueInputRef.current.dataset.value !== '') {
1251
+ fetchData((_params).join(','), valueInputRef.current.dataset.value);
1252
+ hasValue = true;
1253
+ }
1254
+
1255
+ //
1256
+ if ( valueInputRef.current !== null && valueInputRef.current.value !== '' && ( typeof value === 'undefined' || value === '' ) ) {
1257
+ fetchData((_params).join(','), valueInputRef.current.value);
1258
+ hasValue = true;
1259
+ }
1260
+ initTimes++;
1261
+
1262
+ }
1263
+ }, 500);
1264
+
1265
+
1266
+
1267
+ // keyboard listener
1268
+ //--------------
1269
+ const listener = async (event: any) => {
1270
+
1271
+ let res: any = null;
1272
+
1273
+ if (event.code === "Enter" || event.code === "NumpadEnter") {
1274
+
1275
+ // Determine the "active" class name to avoid listening to other unused components of the same type
1276
+ if ( listRef.current === null || !rootRef.current.classList.contains('active') ) return;
1277
+
1278
+ if ( fetchTrigger ) {
1279
+ handleFetch();
1280
+ return;
1281
+ }
1282
+
1283
+ if ( listRef.current !== null ) {
1284
+ const currentData = await listRef.current.dataset.data;
1285
+ if ( typeof currentData !== 'undefined' ) {
1286
+
1287
+ const currentControlValueArr: any[] = [];
1288
+ const currentControlLabelArr: any[] = [];
1289
+
1290
+ const options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.no-match)'));
1291
+ options.forEach((node: any) => {
1292
+ node.classList.remove('active');
1293
+
1294
+ if ( node.classList.contains('item-selected') ) {
1295
+ currentControlValueArr.push(node.dataset.value);
1296
+ currentControlLabelArr.push(node.dataset.label)
1297
+ }
1298
+
1299
+ });
1300
+
1301
+ handleSelect(null, currentData, currentControlValueArr, currentControlLabelArr);
1302
+
1303
+
1304
+ }
1305
+ }
1306
+
1307
+ return;
1308
+ }
1309
+
1310
+
1311
+ switch (event.code) {
1312
+ case "ArrowLeft":
1313
+ // Left pressed
1314
+ break;
1315
+ case "ArrowRight":
1316
+ // Right pressed
1317
+ break;
1318
+ case "ArrowUp":
1319
+ // Up pressed
1320
+ res = await optionFocus('decrease');
1321
+ break;
1322
+ case "ArrowDown":
1323
+ // Down pressed
1324
+ res = await optionFocus('increase');
1325
+ break;
1326
+ }
1327
+
1328
+ // temporary data
1329
+ if ( res !== null ) listRef.current.dataset.data = JSON.stringify({
1330
+ value: res.dataset.value,
1331
+ label: res.dataset.label,
1332
+ queryString: res.dataset.querystring
1333
+ });
1334
+
1335
+
1336
+
1337
+ };
1338
+
1339
+ document.removeEventListener("keydown", listener);
1340
+ document.addEventListener("keydown", listener);
1341
+
1342
+
1343
+
1344
+ //--------------
1345
+ document.removeEventListener('pointerdown', handleClose);
1346
+ document.addEventListener('pointerdown', handleClose);
1347
+
1348
+
1349
+
1350
+
1351
+ // Add function to the element that should be used as the scrollable area.
1352
+ //--------------
1353
+ window.removeEventListener('scroll', windowScrollUpdate);
1354
+ window.removeEventListener('touchmove', windowScrollUpdate);
1355
+ window.addEventListener('scroll', windowScrollUpdate);
1356
+ window.addEventListener('touchmove', windowScrollUpdate);
1357
+ // windowScrollUpdate();
1358
+
1359
+
1360
+ return () => {
1361
+ document.removeEventListener("keydown", listener);
1362
+ document.removeEventListener('pointerdown', handleClose);
1363
+ window.removeEventListener('scroll', windowScrollUpdate);
1364
+ window.removeEventListener('touchmove', windowScrollUpdate);
1365
+
1366
+ //
1367
+ clearInterval(timer);
1368
+ }
1369
+
1370
+ }, [value, options, data]);
1371
+
1372
+
1373
+ return (
1374
+ <>
1375
+
1376
+ {label ? <><div className="multifunc-select__label"><label htmlFor={`label-${idRes}`} className="form-label">{label}</label></div></> : null}
1377
+
1378
+ <div id={`multifunc-select__wrapper-${idRes}`} className={`multifunc-select__wrapper ${wrapperClassName || wrapperClassName === '' ? wrapperClassName : 'mb-3 position-relative'} ${MULTI_SEL_VALID ? 'multiple-selection' : ''} ${isOpen ? 'active focus' : ''}`} ref={rootRef}>
1379
+
1380
+ {/*
1381
+ // ++++++++++++++++++++
1382
+ // Single selection Control (includes result container)
1383
+ // ++++++++++++++++++++
1384
+ */}
1385
+ <div ref={rootSingleRef} className="position-relative">
1386
+ <input
1387
+ ref={(node) => {
1388
+ selectInputRef.current = node;
1389
+ if (typeof ref === 'function') {
1390
+ ref(node);
1391
+ } else if (ref) {
1392
+ ref.current = node;
1393
+ }
1394
+ }}
1395
+ tabIndex={tabIndex || 0}
1396
+ type="text"
1397
+ id={`label-${idRes}`}
1398
+
1399
+ // Don't use "name", it's just a container to display the label
1400
+ data-name={name?.match(/(\[.*?\])/gi) ? `${name.split('[')[0]}-label[]` : `${name}-label`}
1401
+ data-select
1402
+ placeholder={placeholder || ''}
1403
+ className={controlClassName || controlClassName === '' ? controlClassName : "form-control"}
1404
+ onFocus={handleFocus}
1405
+ onBlur={handleBlur}
1406
+ onClick={handleShowList}
1407
+ onChange={handleChange}
1408
+ onCompositionStart={handleComposition}
1409
+ onCompositionUpdate={handleComposition}
1410
+ onCompositionEnd={handleComposition}
1411
+ disabled={disabled || null}
1412
+ required={required || null}
1413
+ readOnly={readOnly || null}
1414
+ value={controlTempValue || controlTempValue === '' ? controlTempValue : (MULTI_SEL_VALID ? formatIndentVal(controlArr.labels).map((v: any) => stripHTML(v)).join(',') : stripHTML(controlLabel as never))} // do not use `defaultValue`
1415
+
1416
+ style={{cursor: 'pointer', borderBottomWidth: MULTI_SEL_VALID? '0' : '1px', ...style}}
1417
+ autoComplete='off'
1418
+ {...attributes}
1419
+ />
1420
+
1421
+ <input
1422
+ ref={valueInputRef}
1423
+ type="hidden"
1424
+ id={idRes}
1425
+ name={name}
1426
+ value={MULTI_SEL_VALID ? controlArr.values.join(',') : controlValue} // do not use `defaultValue`
1427
+ {...attributes}
1428
+ />
1429
+
1430
+
1431
+ <span className="arrow position-absolute top-0 end-0 me-2 mt-1" style={{translate: 'all .2s', transform: isOpen ? 'rotate(180deg) translateY(-4px)' : 'rotate(0) translateY(0)', pointerEvents: 'none', display: fetchTrigger ? 'none' : 'inline-block'}}>
1432
+ {controlArrow ? controlArrow : <svg width="10px" height="10px" viewBox="0 -4.5 20 20">
1433
+ <g stroke="none" strokeWidth="1" fill="none">
1434
+ <g transform="translate(-180.000000, -6684.000000)" className="arrow-fill-g" fill="#a5a5a5">
1435
+ <g transform="translate(56.000000, 160.000000)">
1436
+ <path d="M144,6525.39 L142.594,6524 L133.987,6532.261 L133.069,6531.38 L133.074,6531.385 L125.427,6524.045 L124,6525.414 C126.113,6527.443 132.014,6533.107 133.987,6535 C135.453,6533.594 134.024,6534.965 144,6525.39">
1437
+ </path>
1438
+ </g>
1439
+ </g>
1440
+ </g>
1441
+ </svg>}
1442
+ </span>
1443
+
1444
+
1445
+ {fetchTrigger ? <>
1446
+ <span className="multifunc-select-multi__control-searchbtn position-absolute top-0 end-0">
1447
+ <button type="button" className={'btn border-end-0 rounded-pill'} onClick={handleFetch}>
1448
+ <svg width="1em" height="1em" fill="#a5a5a5" viewBox="0 0 16 16">
1449
+ <path d="M12.027 9.92L16 13.95 14 16l-4.075-3.976A6.465 6.465 0 0 1 6.5 13C2.91 13 0 10.083 0 6.5 0 2.91 2.917 0 6.5 0 10.09 0 13 2.917 13 6.5a6.463 6.463 0 0 1-.973 3.42zM1.997 6.452c0 2.48 2.014 4.5 4.5 4.5 2.48 0 4.5-2.015 4.5-4.5 0-2.48-2.015-4.5-4.5-4.5-2.48 0-4.5 2.014-4.5 4.5z" fillRule="evenodd" />
1450
+ </svg>
1451
+ </button>
1452
+
1453
+ </span>
1454
+ </> : null}
1455
+
1456
+
1457
+
1458
+ </div>
1459
+
1460
+
1461
+ {/*
1462
+ // ++++++++++++++++++++
1463
+ // Multiple selection Control
1464
+ // ++++++++++++++++++++
1465
+ */}
1466
+ {MULTI_SEL_VALID ? <div ref={rootMultiRef} className="multifunc-select-multi__wrapper">
1467
+
1468
+ <div className="multifunc-select-multi__control-wrapper">
1469
+ <div>
1470
+ <ul className="multifunc-select-multi__list">
1471
+
1472
+ {controlArr.labels.map((item: any, index: number) => (
1473
+ <li key={index}>
1474
+ {stripHTML(item)}
1475
+
1476
+ <a href="#" tabIndex={-1} onClick={handleMultiControlItemRemove} data-item={controlArr.values[index]}><svg width="10px" height="10px" viewBox="0 0 1024 1024"><path fill="#000" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z" /></svg></a>
1477
+ </li>
1478
+ ))}
1479
+
1480
+ <li className={`multifunc-select-multi__list-item-placeholder ${typeof placeholder === 'undefined' || placeholder === '' ? 'hide' : ''}`}>
1481
+ <span className={`multifunc-select-multi__control-blinking-cursor ${generateInputFocusStr() === placeholder && placeholder !== '' && typeof placeholder !== 'undefined' ? 'control-placeholder' : ''} ${generateInputFocusStr() === '|' ? 'animated' : ''}`}>
1482
+ {generateInputFocusStr()}
1483
+ </span>
1484
+ </li>
1485
+ </ul>
1486
+
1487
+ </div>
1488
+
1489
+ </div>
1490
+
1491
+ <span className="arrow position-absolute top-0 end-0 me-2 mt-1" style={{ translate: 'all .2s', transform: isOpen ? 'rotate(180deg) translateY(-4px)' : 'rotate(0) translateY(0)', pointerEvents: 'none', display: fetchTrigger ? 'none' : 'inline-block' }}>
1492
+ {controlArrow ? controlArrow : <svg width="10px" height="10px" viewBox="0 -4.5 20 20">
1493
+ <g stroke="none" strokeWidth="1" fill="none">
1494
+ <g transform="translate(-180.000000, -6684.000000)" className="arrow-fill-g" fill="#a5a5a5">
1495
+ <g transform="translate(56.000000, 160.000000)">
1496
+ <path d="M144,6525.39 L142.594,6524 L133.987,6532.261 L133.069,6531.38 L133.074,6531.385 L125.427,6524.045 L124,6525.414 C126.113,6527.443 132.014,6533.107 133.987,6535 C135.453,6533.594 134.024,6534.965 144,6525.39">
1497
+ </path>
1498
+ </g>
1499
+ </g>
1500
+ </g>
1501
+ </svg>}
1502
+ </span>
1503
+
1504
+
1505
+ {fetchTrigger ? <>
1506
+ <span className="multifunc-select-multi__control-searchbtn position-absolute top-0 end-0">
1507
+ <button type="button" className={'btn border-end-0 rounded-pill'} onClick={handleFetch}>
1508
+ <svg width="1em" height="1em" fill="#a5a5a5" viewBox="0 0 16 16">
1509
+ <path d="M12.027 9.92L16 13.95 14 16l-4.075-3.976A6.465 6.465 0 0 1 6.5 13C2.91 13 0 10.083 0 6.5 0 2.91 2.917 0 6.5 0 10.09 0 13 2.917 13 6.5a6.463 6.463 0 0 1-.973 3.42zM1.997 6.452c0 2.48 2.014 4.5 4.5 4.5 2.48 0 4.5-2.015 4.5-4.5 0-2.48-2.015-4.5-4.5-4.5-2.48 0-4.5 2.014-4.5 4.5z" fillRule="evenodd" />
1510
+ </svg>
1511
+ </button>
1512
+
1513
+ </span>
1514
+ </> : null}
1515
+
1516
+
1517
+ </div> : null}
1518
+
1519
+
1520
+
1521
+
1522
+ {optionsData && !hasErr ? <>
1523
+ <div ref={listRef} className={`list-group position-absolute border shadow small ${winWidth ? '' : 'w-100'}`} style={{ marginTop: '0.2rem', zIndex: (depth ? depth : 100), width: WIN_WIDTH}} role="tablist">
1524
+
1525
+ {controlTempValue !== null && optionsData.length === 0 ? <>
1526
+ <button tabIndex={-1} type="button" className="list-group-item list-group-item-action no-match" disabled>{fetchNoneInfo || 'No match yet'}</button>
1527
+ </> : <>
1528
+ <div className="rounded" style={{backgroundColor: 'var(--bs-list-group-bg)'}} ref={listContentRef}>
1529
+
1530
+ {MULTI_SEL_VALID ? <>
1531
+ <button tabIndex={-1} type="button" className="list-group-item list-group-item-action border-start-0 border-end-0 text-secondary bg-light multifunc-select-multi__control-select-all" role="tab" style={{display: multiSelect?.selectAll ? 'block' : 'none'}}>
1532
+ <a tabIndex={-1} href="#" onClick={handleSelectAll} className="btn btn-secondary" dangerouslySetInnerHTML={{
1533
+ __html: `${multiSelect?.selectAllLabel || 'Select all options'}`
1534
+ }}></a>
1535
+ </button>
1536
+ </> : null}
1537
+
1538
+
1539
+ {optionsData ? optionsData.map((item, index) => {
1540
+ const startItemBorder = index === 0 ? 'border-top-0' : '';
1541
+ const endItemBorder = index === optionsData.length - 1 ? 'border-bottom-0' : '';
1542
+
1543
+
1544
+
1545
+ if (!MULTI_SEL_VALID) {
1546
+
1547
+ // ++++++++++++++++++++
1548
+ // Single selection
1549
+ // ++++++++++++++++++++
1550
+ return <button tabIndex={-1} onClick={handleSelect} type="button" data-index={index} key={index} className={`list-group-item list-group-item-action border-start-0 border-end-0 ${startItemBorder} ${endItemBorder} border-bottom-0`} data-value={`${item.value}`} data-label={`${item.label}`} data-querystring={`${item.queryString}`} role="tab" dangerouslySetInnerHTML={{
1551
+ __html: item.label
1552
+ }}></button>
1553
+
1554
+ } else {
1555
+
1556
+ // ++++++++++++++++++++
1557
+ // Multiple selection
1558
+ // ++++++++++++++++++++
1559
+ const itemSelected = multiSelControlOptionExist(controlArr.values, item.value) ? true : false;
1560
+
1561
+ return <button tabIndex={-1} onClick={handleSelect} type="button" data-index={index} key={index} className={`list-group-item list-group-item-action border-start-0 border-end-0 ${startItemBorder} ${endItemBorder} border-bottom-0 ${itemSelected ? 'list-group-item-secondary item-selected' : ''}`} data-value={`${item.value}`} data-label={`${item.label}`} data-querystring={`${item.queryString}`} role="tab">
1562
+ <var className="d-inline-block me-1 ">
1563
+ {!itemSelected ? <svg width="1.2em" height="1.2em" viewBox="0 0 24 24" fill="none">
1564
+ <path id="Vector" d="M4 7.2002V16.8002C4 17.9203 4 18.4801 4.21799 18.9079C4.40973 19.2842 4.71547 19.5905 5.0918 19.7822C5.5192 20 6.07899 20 7.19691 20H16.8031C17.921 20 18.48 20 18.9074 19.7822C19.2837 19.5905 19.5905 19.2842 19.7822 18.9079C20 18.4805 20 17.9215 20 16.8036V7.19691C20 6.07899 20 5.5192 19.7822 5.0918C19.5905 4.71547 19.2837 4.40973 18.9074 4.21799C18.4796 4 17.9203 4 16.8002 4H7.2002C6.08009 4 5.51962 4 5.0918 4.21799C4.71547 4.40973 4.40973 4.71547 4.21799 5.0918C4 5.51962 4 6.08009 4 7.2002Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
1565
+ </svg> : <svg width="1.2em" height="1.2em" fill="#000000" viewBox="0 0 24 24"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /></svg>}
1566
+
1567
+ </var>
1568
+ <span dangerouslySetInnerHTML={{
1569
+ __html: item.label
1570
+ }}></span>
1571
+ </button>
1572
+
1573
+ }
1574
+
1575
+
1576
+ }) : null}
1577
+
1578
+ </div>
1579
+
1580
+ </>}
1581
+
1582
+
1583
+ </div>
1584
+
1585
+ </> : null}
1586
+
1587
+
1588
+
1589
+
1590
+ </div>
1591
+
1592
+
1593
+ </>
1594
+ )
1595
+ });
1596
+
1597
+ export default MultiFuncSelect;