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,582 @@
1
+ import React, { useId, useEffect, useState, useRef } from 'react';
2
+
3
+ import { debounce } from './utils/performance';
4
+ import useThrottle from './utils/useThrottle';
5
+
6
+ import SearchBar from 'rpb-searchbar';
7
+
8
+
9
+ type LiveSearchProps = {
10
+ wrapperClassName?: string;
11
+ controlClassName?: string;
12
+ appearance?: string;
13
+ value?: string;
14
+ label?: React.ReactNode | string;
15
+ name?: string;
16
+ maxLength?: any;
17
+ disabled?: any;
18
+ required?: any;
19
+ placeholder?: string;
20
+ winWidth?: string | Function;
21
+ icon?: React.ReactNode | string;
22
+ btnId?: string;
23
+ fetchTrigger?: boolean;
24
+ /** Set the depth value of the control to control the display of the pop-up layer appear above.
25
+ * Please set it when multiple controls are used at the same time. */
26
+ depth?: number;
27
+ /** -- */
28
+ id?: string;
29
+ style?: React.CSSProperties;
30
+ tabIndex?: number;
31
+ [key: `data-${string}`]: string | undefined;
32
+ fetchNoneInfo?: string;
33
+ fetchUpdate?: boolean;
34
+ fetchFuncAsync?: any;
35
+ fetchFuncMethod?: string;
36
+ fetchFuncMethodParams?: any[];
37
+ fetchCallback?: (data: any) => void;
38
+ onFetch?: (data: any) => void;
39
+ onSelect?: (e: any, data: any) => void;
40
+ onChange?: (e: any, data: any) => void;
41
+ onBlur?: (e: any, data: any) => void;
42
+ };
43
+
44
+
45
+ const LiveSearch = (props: LiveSearchProps) => {
46
+ const {
47
+ wrapperClassName,
48
+ controlClassName,
49
+ appearance,
50
+ disabled,
51
+ required,
52
+ placeholder,
53
+ value,
54
+ label,
55
+ name,
56
+ id,
57
+ icon,
58
+ btnId,
59
+ fetchTrigger,
60
+ depth,
61
+ maxLength,
62
+ style,
63
+ winWidth,
64
+ tabIndex,
65
+ fetchNoneInfo,
66
+ fetchUpdate,
67
+ fetchFuncAsync,
68
+ fetchFuncMethod,
69
+ fetchFuncMethodParams,
70
+ fetchCallback,
71
+ onFetch,
72
+ onSelect,
73
+ onChange,
74
+ onBlur,
75
+ } = props;
76
+
77
+
78
+ const WIN_WIDTH = typeof winWidth === 'function' ? winWidth() : winWidth ? winWidth : 'auto';
79
+ const uniqueID = useId();
80
+ const idRes = id || uniqueID;
81
+ const rootRef = useRef<any>(null);
82
+ const inputRef = useRef<any>(null);
83
+ const listRef = useRef<any>(null);
84
+ const listContentRef = useRef<any>(null);
85
+ const windowScrollUpdate = debounce(handleScrollEvent, 500);
86
+
87
+
88
+
89
+ //
90
+ const [firstFetch, setFirstFetch] = useState<boolean>(false);
91
+ const [dataInit, setDataInit] = useState<any[]>([]);
92
+ const [data, setData] = useState<any[]>([]);
93
+ const [changedVal, setChangedVal] = useState<string>(value || '');
94
+ const [isOpen, setIsOpen] = useState<boolean>(false);
95
+ const [hasErr, setHasErr] = useState<boolean>(false);
96
+
97
+
98
+ //performance
99
+ const handleChangeFetchSafe = useThrottle((e: any) => {
100
+ handleChange(e);
101
+ }, 150, [dataInit]);
102
+
103
+
104
+
105
+
106
+ /**
107
+ * Check if an element is in the viewport
108
+ * @param {HTMLElement} elem
109
+ * @returns {boolean}
110
+ */
111
+ function isInViewport(elem: HTMLElement) {
112
+ const bounding = elem.getBoundingClientRect();
113
+ return (
114
+ bounding.top >= 0 &&
115
+ bounding.left >= 0 &&
116
+ bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
117
+ bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
118
+ );
119
+ }
120
+
121
+
122
+ function handleScrollEvent() {
123
+ getPlacement(listRef.current, true);
124
+ }
125
+
126
+ //
127
+ function getPlacement(el: HTMLElement, restorePos: boolean = false) {
128
+
129
+ if ( el === null ) return;
130
+
131
+
132
+ const PLACEMENT_TOP = 'top-0';
133
+ const PLACEMENT_BOTTOMEND = 'bottom-0';
134
+ const PLACEMENT_RIGHT = 'end-0';
135
+ const PLACEMENT_LEFT = 'start-0';
136
+
137
+ const elTop = el.getBoundingClientRect().top;
138
+ const elSpacing = 50 + inputRef.current.clientHeight*3;
139
+ const elMinWindowSpacing = inputRef.current.clientHeight*2;
140
+
141
+
142
+ //restore position
143
+ if ( restorePos ) {
144
+ if ( isInViewport(el) ) {
145
+ el.classList.remove(PLACEMENT_BOTTOMEND);
146
+ el.style.removeProperty('bottom');
147
+ }
148
+ return;
149
+ }
150
+
151
+ // STEP 1:
152
+ // If the content exceeds the height of the window, first limit height and add scrollbar
153
+ let maxHeight = window.innerHeight - elSpacing;
154
+ if ( maxHeight < inputRef.current.clientHeight ) maxHeight = elMinWindowSpacing;
155
+
156
+ if ( el.offsetHeight > 0 && (el.offsetHeight > maxHeight) ) {
157
+
158
+ const newH = maxHeight - (elTop > window.innerHeight/2 ? 0 : elTop) + elMinWindowSpacing;
159
+
160
+ // default position
161
+ listContentRef.current.style.height = newH + 'px';
162
+
163
+
164
+ // if it's on top
165
+ if ( newH > maxHeight ) {
166
+ listContentRef.current.style.height = elTop - elMinWindowSpacing + 'px';
167
+ }
168
+
169
+ //
170
+ listContentRef.current.style.overflowY = 'auto';
171
+
172
+ } else {
173
+ listContentRef.current.style.height = 'auto';
174
+ listContentRef.current.style.overflowY = 'inherit';
175
+ }
176
+
177
+ // STEP 2:
178
+ // Adjust position
179
+ if ( !isInViewport(el) ) {
180
+ el.classList.add(PLACEMENT_BOTTOMEND);
181
+ el.style.setProperty('bottom', inputRef.current.clientHeight + 5 + 'px', "important");
182
+ }
183
+
184
+
185
+
186
+ // STEP 3:
187
+ // It is on top when no scrollbars have been added
188
+ if ( !isInViewport(el) ) {
189
+ if ( el.getBoundingClientRect().top < 0 ) {
190
+ listContentRef.current.style.height = el.offsetHeight + el.getBoundingClientRect().top - elMinWindowSpacing + 'px';
191
+ listContentRef.current.style.overflowY = 'auto';
192
+ }
193
+ }
194
+
195
+
196
+ }
197
+
198
+
199
+ //
200
+ async function matchData(val: string = '', query: boolean = false) {
201
+
202
+ let res: any[] = [];
203
+ let filterRes = (data: any[]) => {
204
+ return data.filter((item: any) => {
205
+ if (
206
+ (
207
+ item.queryString.split(',').some((l: any) => l.charAt(0) === val.toLowerCase()) ||
208
+ item.queryString.split(',').some((l: any) => l.replace(/ /g, '').indexOf(val.toLowerCase()) >= 0) ||
209
+ item.label.toLowerCase().indexOf(val.toLowerCase()) >= 0
210
+ ) &&
211
+ val != ''
212
+ ) {
213
+ return true;
214
+ } else {
215
+ return false;
216
+ }
217
+ });
218
+ }
219
+
220
+
221
+ if (query) {
222
+
223
+ const _oparams: any[] = fetchFuncMethodParams || [];
224
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : val);
225
+ const response: any = await fetchData((_params).join(','));
226
+ res = filterRes(response) as never;
227
+ return res;
228
+ } else {
229
+ res = filterRes(dataInit) as never;
230
+ return res;
231
+ }
232
+
233
+
234
+ }
235
+
236
+
237
+ function handleChange(e: any) {
238
+ const val = e.target.value;
239
+
240
+ setChangedVal(val);
241
+
242
+ // detect string which contains only spaces
243
+ if ( !val.replace(/\s/g, '').length === true ) return;
244
+
245
+ //
246
+ if ( !fetchTrigger ) {
247
+ matchData(val, fetchUpdate).then((response: any) => {
248
+
249
+ setData(response);
250
+
251
+ //
252
+ onChange?.(inputRef.current, response);
253
+
254
+ //
255
+ setIsOpen(true);
256
+ });
257
+ }
258
+
259
+ // window position
260
+ setTimeout( ()=> {
261
+ getPlacement(listRef.current);
262
+ }, 0 );
263
+
264
+ }
265
+
266
+
267
+ async function activate() {
268
+ if ( fetchTrigger ) {
269
+ const res: any = await matchData(changedVal, fetchUpdate);
270
+ setData(res);
271
+
272
+
273
+ //
274
+ setIsOpen(res.length === 0 ? true : false);
275
+ }
276
+ }
277
+
278
+ async function fetchData(params: any) {
279
+
280
+ if ( typeof fetchFuncAsync === 'object' ) {
281
+
282
+ const response: any = await fetchFuncAsync[`${fetchFuncMethod}`](...params.split(','));
283
+ let _ORGIN_DATA = response.data;
284
+
285
+ // reset data structure
286
+ if (typeof (fetchCallback) === 'function') {
287
+ _ORGIN_DATA = fetchCallback(_ORGIN_DATA);
288
+ }
289
+
290
+ // Determine whether the data structure matches
291
+ if ( _ORGIN_DATA.length > 0 && typeof _ORGIN_DATA[0].value === 'undefined' ) {
292
+ console.warn( 'The data structure does not match, please refer to the example in the component documentation.' );
293
+ setHasErr(true);
294
+ _ORGIN_DATA = [];
295
+ }
296
+
297
+
298
+
299
+ //
300
+ onFetch?.(_ORGIN_DATA);
301
+
302
+ //
303
+ setDataInit(_ORGIN_DATA);
304
+
305
+ return _ORGIN_DATA;
306
+ } else {
307
+ return [];
308
+ }
309
+
310
+
311
+ }
312
+
313
+ async function handleSelect(el: any, dataInput: any = false) {
314
+
315
+ if ( typeof el === 'undefined' ) return;
316
+
317
+ let index: number | undefined | string;
318
+
319
+
320
+ // update value
321
+ if ( dataInput ) {
322
+ const _data = JSON.parse(dataInput);
323
+
324
+ onSelect?.(inputRef.current, _data);
325
+ setChangedVal(_data.label);
326
+
327
+ } else {
328
+ index = typeof el.target !== 'undefined' ? el.target.dataset.index : el.dataset.index;
329
+
330
+ const res: any = await matchData(inputRef.current.value, false);
331
+ onSelect?.(inputRef.current, res[index as never]);
332
+ setChangedVal(res[index as never].label);
333
+ }
334
+
335
+ setData([]);
336
+
337
+ }
338
+
339
+
340
+ function handleFetch() {
341
+ activate();
342
+
343
+
344
+ // window position
345
+ setTimeout( ()=> {
346
+ getPlacement(listRef.current);
347
+ }, 0 );
348
+ }
349
+
350
+
351
+ function handleBlur(e: any) {
352
+
353
+ setIsOpen(false);
354
+ if ( !fetchTrigger ) {
355
+ setTimeout(() => {
356
+ //
357
+ onBlur?.(inputRef.current, data);
358
+ setData([]);
359
+
360
+ }, 300);
361
+ }
362
+
363
+ }
364
+
365
+ function handleMouseLeaveTrigger() {
366
+ setIsOpen(false);
367
+ }
368
+
369
+ function optionFocus(type: string) {
370
+
371
+ return new Promise(function (resolve) {
372
+
373
+ // Determine the "active" class name to avoid listening to other unused components of the same type
374
+ if ( listRef.current === null || !rootRef.current.classList.contains('active') ) return;
375
+
376
+
377
+ const options = [].slice.call(listRef.current.querySelectorAll('.list-group-item'));
378
+ const currentIndex = options.findIndex((e) => e === listRef.current.querySelector('.list-group-item.active'));
379
+
380
+
381
+ // get the next element in the list, "%" will loop around to 0
382
+ let nextIndex;
383
+ if ( type === 'increase' ) {
384
+ nextIndex = currentIndex + 1 % options.length;
385
+ } else {
386
+ nextIndex = (currentIndex < 0 ? options.length : currentIndex) - 1 % options.length;
387
+ }
388
+
389
+
390
+ //only one
391
+ if ( options.length === 1 ) nextIndex = 0;
392
+
393
+
394
+ if ( !isNaN(nextIndex) ) {
395
+ options.forEach( (node: any, index: number) => {
396
+ node?.classList.remove('active');
397
+ });
398
+
399
+ const targetOption = options[nextIndex] as HTMLElement;
400
+ if ( typeof targetOption !== 'undefined' && !targetOption.classList.contains('no-match') ) {
401
+ targetOption.classList.add('active');
402
+ resolve(targetOption);
403
+ }
404
+
405
+ }
406
+ });
407
+
408
+
409
+ }
410
+
411
+
412
+
413
+ useEffect(() => {
414
+
415
+ // update default value
416
+ //--------------
417
+ setChangedVal(value || '');
418
+
419
+
420
+ // data init
421
+ //--------------
422
+ const _oparams: any[] = fetchFuncMethodParams || [];
423
+ const _params: any[] = _oparams.map((item: any) => item !== '$QUERY_STRING' ? item : (fetchTrigger ? '-' : ''));
424
+ if ( !firstFetch ) {
425
+ fetchData((_params).join(','));
426
+ setFirstFetch(true); // avoid triggering two data requests if the input value has not changed
427
+ }
428
+
429
+
430
+
431
+ // keyboard listener
432
+ //--------------
433
+ const listener = async (event: any) => {
434
+
435
+
436
+ let res: any = null;
437
+
438
+ if (event.code === "Enter" || event.code === "NumpadEnter") {
439
+
440
+ // Determine the "active" class name to avoid listening to other unused components of the same type
441
+ if ( listRef.current === null || !rootRef.current.classList.contains('active') ) return;
442
+
443
+ if ( fetchTrigger ) {
444
+ handleFetch();
445
+ return;
446
+ }
447
+
448
+ if ( listRef.current !== null ) {
449
+ const currentData = listRef.current.dataset.data;
450
+ if ( typeof currentData !== 'undefined' ) {
451
+ handleSelect(null, currentData);
452
+ const options = [].slice.call(listRef.current.querySelectorAll('.list-group-item:not(.no-match)'));
453
+ options.forEach((node: any) => {
454
+ node.classList.remove('active');
455
+ });
456
+ }
457
+ }
458
+
459
+ return;
460
+ }
461
+
462
+ switch (event.code) {
463
+ case "ArrowLeft":
464
+ // Left pressed
465
+ break;
466
+ case "ArrowRight":
467
+ // Right pressed
468
+ break;
469
+ case "ArrowUp":
470
+ // Up pressed
471
+ res = await optionFocus('decrease');
472
+ break;
473
+ case "ArrowDown":
474
+ // Down pressed
475
+ res = await optionFocus('increase');
476
+ break;
477
+ }
478
+
479
+ // temporary data
480
+ if ( res !== null ) listRef.current.dataset.data = JSON.stringify({
481
+ value: res.dataset.value,
482
+ label: res.dataset.label,
483
+ queryString: res.dataset.querystring
484
+ });
485
+
486
+
487
+ };
488
+
489
+ document.removeEventListener("keydown", listener);
490
+ document.addEventListener("keydown", listener);
491
+
492
+
493
+ // Add function to the element that should be used as the scrollable area.
494
+ //--------------
495
+ window.removeEventListener('scroll', windowScrollUpdate);
496
+ window.removeEventListener('touchmove', windowScrollUpdate);
497
+ window.addEventListener('scroll', windowScrollUpdate);
498
+ window.addEventListener('touchmove', windowScrollUpdate);
499
+ windowScrollUpdate();
500
+
501
+
502
+
503
+
504
+ // Remove the global list of events, especially as scroll and interval.
505
+ //--------------
506
+ return () => {
507
+
508
+ document.removeEventListener("keydown", listener);
509
+ window.removeEventListener('scroll', windowScrollUpdate);
510
+ window.removeEventListener('touchmove', windowScrollUpdate);
511
+
512
+ };
513
+
514
+ }, [value]);
515
+
516
+
517
+ return (
518
+ <>
519
+
520
+ <div className={`livesearch__wrapper ${wrapperClassName || wrapperClassName === '' ? wrapperClassName : 'mb-3 position-relative'} ${isOpen ? 'active' : ''}`} ref={rootRef} onMouseLeave={handleMouseLeaveTrigger}>
521
+ <SearchBar
522
+ wrapperClassName=""
523
+ controlClassName={controlClassName}
524
+ ref={inputRef}
525
+ value={changedVal}
526
+ label={label}
527
+ tabIndex={tabIndex}
528
+ id={idRes}
529
+ name={name}
530
+ placeholder={placeholder}
531
+ maxLength={maxLength}
532
+ disabled={disabled}
533
+ required={required}
534
+ style={style}
535
+ appearance={appearance}
536
+ onChange={(e: any) => {
537
+ handleChangeFetchSafe(e);
538
+ }}
539
+ onBlur={handleBlur}
540
+ onClick={handleFetch}
541
+ icon={!fetchTrigger ? '' : icon}
542
+ btnId={btnId}
543
+ autoComplete='off'
544
+ />
545
+
546
+
547
+ {data && data.length > 0 && !hasErr ? <>
548
+ <div ref={listRef} className={`list-group position-absolute border shadow small ${winWidth ? '' : 'w-100'}`} style={{ marginTop: '0.2rem', zIndex: (depth ? depth : 100), minWidth: '200px', width: WIN_WIDTH}} role="tablist">
549
+ <div className="rounded" ref={listContentRef}>
550
+ {data ? data.map((item, index) => {
551
+ const startItemBorder = index === 0 ? 'border-top-0' : '';
552
+ const endItemBorder = index === data.length-1 ? 'border-bottom-0' : '';
553
+
554
+ 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}`} data-value={`${item.value}`} data-label={`${item.label}`} data-querystring={`${item.queryString}`} role="tab">{item.label}</button>
555
+ }) : null}
556
+ </div>
557
+
558
+
559
+ </div>
560
+
561
+ </> : null}
562
+
563
+ {data && data.length === 0 && !hasErr && isOpen ? <>
564
+ <div ref={listRef} className={`list-group position-absolute border shadow small ${winWidth ? '' : 'w-100'}`} style={{ marginTop: '0.2rem', zIndex: (depth ? depth : 100), minWidth: '200px', width: WIN_WIDTH}} role="tablist">
565
+
566
+ <div className="rounded" ref={listContentRef}>
567
+ <button tabIndex={-1} type="button" className="list-group-item list-group-item-action no-match" disabled>{fetchNoneInfo || 'No match yet'}</button>
568
+ </div>
569
+
570
+
571
+ </div>
572
+ </> : null}
573
+
574
+
575
+ </div>
576
+
577
+ </>
578
+ )
579
+ };
580
+
581
+
582
+ export default LiveSearch;
@@ -0,0 +1,52 @@
1
+
2
+ /*
3
+ * Debounce
4
+ *
5
+ * @param {Function} fn - A function to be executed within the time limit.
6
+ * @param {Number} limit - Waiting time.
7
+ * @return {Function} - Returns a new function.
8
+ */
9
+ function debounce( fn, limit = 300 ) {
10
+ let timer;
11
+ return function() {
12
+
13
+ //Every time this returned function is called, the timer is cleared to ensure that fn is not executed
14
+ clearTimeout(timer);
15
+
16
+ // When the returned function is called for the last time (that is the user stops a continuous operation)
17
+ // Execute fn after another delay milliseconds
18
+ timer = setTimeout(function() {
19
+ fn.apply(this, arguments);
20
+ }, limit);
21
+ }
22
+ }
23
+
24
+
25
+
26
+
27
+
28
+ /*
29
+ * Throttle
30
+ *
31
+ * @param {Function} fn - A function to be executed within the time limit.
32
+ * @param {Number} limit - Waiting time.
33
+ * @return {Function} - Returns a new function.
34
+ */
35
+ function throttle( fn, limit = 300 ) {
36
+ let waiting = false;
37
+ return function () {
38
+ if (!waiting) {
39
+ fn.apply(this, arguments);
40
+ waiting = true;
41
+ setTimeout(function () {
42
+ waiting = false;
43
+ }, limit);
44
+ }
45
+ }
46
+ }
47
+
48
+
49
+ module.exports = {
50
+ debounce,
51
+ throttle
52
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Limiting the rate of execution
3
+ *
4
+ * @usage:
5
+
6
+ const App = () => {
7
+ const [count, setCount] = useState(0);
8
+ const handleClick = useThrottle(() => setCount(count + 1), 500, [count]);
9
+
10
+ return (
11
+ <div className="app">
12
+ <button onClick={handleClick}>click</button>
13
+ <p>click {count} time</p>
14
+ </div>
15
+ );
16
+ };
17
+
18
+ */
19
+ import { useRef, useCallback } from "react";
20
+
21
+ const useThrottle = (fn, delay, dependence) => {
22
+ const ref = useRef({ lastTime: 0 });
23
+
24
+ return useCallback((...args) => {
25
+ const now = Date.now();
26
+
27
+ if (now - ref.current.lastTime >= delay) {
28
+ fn(...args);
29
+ ref.current.lastTime = now;
30
+ }
31
+ }, dependence);
32
+ };
33
+
34
+
35
+ export default useThrottle;
36
+