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,497 @@
1
+ import React, { useId, useState, useEffect, useRef, useCallback } from 'react';
2
+
3
+ import { throttle } from './utils/performance';
4
+
5
+ type ScrollbarProps = {
6
+ onlyVerticle?: boolean;
7
+ onlyHorizontal?: boolean;
8
+ arrowIcons?: React.ReactNode[];
9
+ disableArrow?: boolean;
10
+ /** Incoming data, changes in the `data` value will cause the component to re-render. */
11
+ data?: any;
12
+ onMove?: (data: any) => void;
13
+ /** -- */
14
+ id?: string;
15
+ children: React.ReactNode;
16
+ };
17
+
18
+
19
+ const Scrollbar = (props: ScrollbarProps) => {
20
+ const {
21
+ onlyVerticle,
22
+ onlyHorizontal,
23
+ arrowIcons,
24
+ disableArrow,
25
+ data,
26
+ onMove,
27
+ id,
28
+ children
29
+ } = props;
30
+
31
+
32
+ const uniqueID = useId();
33
+ const idRes = id || uniqueID;
34
+ const rootRef = useRef<HTMLDivElement>(null);
35
+ const contentRef = useRef<HTMLDivElement>(null);
36
+ const icons = typeof arrowIcons === 'undefined' || !arrowIcons ? [
37
+ <><svg width="16px" height="16px" viewBox="0 0 24 24" fill="none"> <path d="M12 5V19M12 5L6 11M12 5L18 11" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg></>,
38
+ <><svg width="16px" height="16px" viewBox="0 0 24 24" fill="none"> <path d="M12 6V18M12 18L7 13M12 18L17 13" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg></>,
39
+ <><svg width="16px" height="16px" viewBox="0 0 24 24" fill="none" transform="translate(0 -2)"> <path d="M6 12H18M6 12L11 7M6 12L11 17" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg></>,
40
+ <><svg width="16px" height="16px" viewBox="0 0 24 24" fill="none" transform="translate(0 -2)"> <path d="M6 12H18M18 12L13 7M18 12L13 17" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg></>
41
+ ] : arrowIcons;
42
+
43
+
44
+ const contentAreaScrollUpdate = throttle(handleScrollEvent, 5);
45
+
46
+
47
+ // Vertical (default)
48
+ //========================================
49
+ const scrollTrackRef = useRef<HTMLDivElement>(null);
50
+ const scrollThumbRef = useRef<HTMLDivElement>(null);
51
+ const scrollBarRef = useRef<HTMLDivElement>(null);
52
+ const observer = useRef<ResizeObserver | null>(null);
53
+ const [thumbHeight, setThumbHeight] = useState<number>(20);
54
+ const [scrollStartPosition, setScrollStartPosition] = useState<number | null>(null);
55
+ const [initialScrollTop, setInitialScrollTop] = useState<number>(0);
56
+ const [isDragging, setIsDragging] = useState<boolean>(false);
57
+
58
+ // Horizontal
59
+ //========================================
60
+ const scrollTrackHorizontalRef = useRef<HTMLDivElement>(null);
61
+ const scrollThumbHorizontalRef = useRef<HTMLDivElement>(null);
62
+ const scrollBarHorizontalRef = useRef<HTMLDivElement>(null);
63
+ const horizontalObserver = useRef<ResizeObserver | null>(null);
64
+ const [thumbWidth, setThumbWidth] = useState<number>(20);
65
+ const [scrollHorizontalStartPosition, setScrollHorizontalStartPosition] = useState<number | null>(null);
66
+ const [initialScrollLeft, setInitialScrollLeft] = useState<number>(0);
67
+ const [isHorizontalDragging, setIsHorizontalDragging] = useState<boolean>(false);
68
+
69
+
70
+
71
+ // common functions
72
+ //========================================
73
+ function handleScrollEvent(e: any) {
74
+ if (typeof e === 'undefined') return;
75
+ const scrollLeft = contentRef.current?.scrollLeft;
76
+ const scrollTop = contentRef.current?.scrollTop;
77
+
78
+ if (onlyHorizontal) {
79
+ const direction = e.deltaY < 0 ? 'left' : 'right';
80
+ horizontalContentScrollTo(direction, false); //do not use `smooth`
81
+ }
82
+
83
+ onMove?.({
84
+ scrollLeft: scrollLeft,
85
+ scrolTop: scrollTop,
86
+ data: e
87
+ });
88
+ }
89
+
90
+
91
+
92
+ // Vertical --> functions
93
+ //========================================
94
+
95
+ function contentScrollTo(dir: string, smooth: boolean = true) {
96
+ const { current } = contentRef;
97
+ if (current) {
98
+ const scrollAmount = dir === 'down' ? 200 : -200;
99
+ current.scrollBy({ top: scrollAmount, behavior: smooth ? 'smooth' : 'auto' });
100
+ }
101
+ }
102
+
103
+
104
+
105
+ function handleResize(ref: HTMLDivElement, trackSize: number) {
106
+ const { clientHeight, scrollHeight } = ref;
107
+ if (clientHeight === 0 || scrollHeight === 0) return;
108
+ setThumbHeight(Math.max((clientHeight / scrollHeight) * trackSize, 20));
109
+
110
+
111
+ // do not need scrolling
112
+ if (clientHeight / scrollHeight === 1) {
113
+ // hide scrollbar
114
+ if (scrollBarRef.current !== null) scrollBarRef.current.classList.add('disabled');
115
+ } else {
116
+ if (scrollBarRef.current !== null) scrollBarRef.current.classList.remove('disabled');
117
+ }
118
+
119
+
120
+ }
121
+
122
+ function handleScrollButton(direction: 'up' | 'down') {
123
+ contentScrollTo(direction);
124
+ }
125
+
126
+ const handleTrackClick = useCallback(
127
+ (e: React.MouseEvent) => {
128
+ e.preventDefault();
129
+ e.stopPropagation();
130
+ const { current: trackCurrent } = scrollTrackRef;
131
+ const { current: contentCurrent } = contentRef;
132
+ if (trackCurrent && contentCurrent) {
133
+ const { clientY } = e;
134
+ const target = e.target as HTMLDivElement;
135
+ const rect = target.getBoundingClientRect();
136
+ const trackTop = rect.top;
137
+ const thumbOffset = -(thumbHeight / 2);
138
+ const clickRatio = (clientY - trackTop + thumbOffset) / trackCurrent.clientHeight;
139
+ const scrollAmount = Math.floor(clickRatio * contentCurrent.scrollHeight);
140
+ contentCurrent.scrollTo({
141
+ top: scrollAmount,
142
+ behavior: 'smooth',
143
+ });
144
+ }
145
+ },
146
+ [thumbHeight]
147
+ );
148
+
149
+ const handleThumbPosition = useCallback(() => {
150
+ if (
151
+ !contentRef.current ||
152
+ !scrollTrackRef.current ||
153
+ !scrollThumbRef.current
154
+ ) {
155
+ return;
156
+ }
157
+ const { scrollTop: contentTop, scrollHeight: contentHeight } = contentRef.current;
158
+ const { clientHeight: trackHeight } = scrollTrackRef.current;
159
+ let newTop = (+contentTop / +contentHeight) * trackHeight;
160
+ newTop = Math.min(newTop, trackHeight - thumbHeight);
161
+ const thumb = scrollThumbRef.current;
162
+ thumb.style.top = `${newTop}px`;
163
+ }, []);
164
+
165
+ const handleThumbMousedown = useCallback((e: React.MouseEvent) => {
166
+ e.preventDefault();
167
+ e.stopPropagation();
168
+ setScrollStartPosition(e.clientY);
169
+ if (contentRef.current) setInitialScrollTop(contentRef.current.scrollTop);
170
+ setIsDragging(true);
171
+ }, []);
172
+
173
+ const handleThumbMouseup = useCallback(
174
+ (e: React.MouseEvent) => {
175
+ e.preventDefault();
176
+ e.stopPropagation();
177
+ if (isDragging) {
178
+ setIsDragging(false);
179
+ }
180
+ },
181
+ [isDragging]
182
+ ) as any;
183
+
184
+ const handleThumbMousemove = useCallback(
185
+ (e: React.MouseEvent) => {
186
+ e.preventDefault();
187
+ e.stopPropagation();
188
+ if (isDragging && contentRef.current && scrollStartPosition) {
189
+ const {
190
+ scrollHeight: contentScrollHeight,
191
+ offsetHeight: contentOffsetHeight,
192
+ } = contentRef.current;
193
+
194
+ const deltaY =
195
+ (e.clientY - scrollStartPosition) *
196
+ (contentOffsetHeight / thumbHeight);
197
+ const newScrollTop = Math.min(
198
+ initialScrollTop + deltaY,
199
+ contentScrollHeight - contentOffsetHeight
200
+ );
201
+
202
+ contentRef.current.scrollTop = newScrollTop;
203
+ }
204
+ },
205
+ [isDragging, scrollStartPosition, thumbHeight]
206
+ ) as any;
207
+
208
+
209
+ // Horizontal --> functions
210
+ //========================================
211
+
212
+ function horizontalContentScrollTo(dir: string, smooth: boolean = true) {
213
+ const { current } = contentRef;
214
+ if (current) {
215
+ const pivot = current.clientWidth/10;
216
+ const speed = pivot >= 200 ? 200 : pivot;
217
+ const scrollAmount = dir === 'right' ? speed : -speed;
218
+ current.scrollBy({ left: scrollAmount, behavior: smooth ? 'smooth' : 'auto' });
219
+ }
220
+ }
221
+
222
+
223
+ function handleHorizontalResize(ref: HTMLDivElement, trackSize: number) {
224
+ const { clientWidth, scrollWidth } = ref;
225
+ if (clientWidth === 0 || scrollWidth === 0) return;
226
+ setThumbWidth(Math.max((clientWidth / scrollWidth) * trackSize, 20));
227
+
228
+ // do not need scrolling
229
+ if (clientWidth / scrollWidth === 1) {
230
+ // hide scrollbar
231
+ if (scrollBarHorizontalRef.current) scrollBarHorizontalRef.current.classList.add('disabled');
232
+ } else {
233
+ if (scrollBarHorizontalRef.current) scrollBarHorizontalRef.current.classList.remove('disabled');
234
+ }
235
+
236
+
237
+ }
238
+
239
+
240
+ function handleHorizontalScrollButton(direction: 'left' | 'right') {
241
+ horizontalContentScrollTo(direction);
242
+ }
243
+
244
+ const handleHorizontalTrackClick = useCallback(
245
+ (e: React.MouseEvent) => {
246
+ e.preventDefault();
247
+ e.stopPropagation();
248
+ const { current: trackCurrent } = scrollTrackHorizontalRef;
249
+ const { current: contentCurrent } = contentRef;
250
+ if (trackCurrent && contentCurrent) {
251
+ const { clientX } = e;
252
+ const target = e.target as HTMLDivElement;
253
+ const rect = target.getBoundingClientRect();
254
+ const trackLeft = rect.left;
255
+ const thumbOffset = -(thumbWidth / 2);
256
+ const clickRatio = (clientX - trackLeft + thumbOffset) / trackCurrent.clientWidth;
257
+ const scrollAmount = Math.floor(clickRatio * contentCurrent.scrollWidth);
258
+ contentCurrent.scrollTo({
259
+ left: scrollAmount,
260
+ behavior: 'smooth',
261
+ });
262
+ }
263
+ },
264
+ [thumbWidth]
265
+ );
266
+
267
+ const handleHorizontalThumbPosition = useCallback(() => {
268
+ if (
269
+ !contentRef.current ||
270
+ !scrollTrackHorizontalRef.current ||
271
+ !scrollThumbHorizontalRef.current
272
+ ) {
273
+ return;
274
+ }
275
+ const { scrollLeft: contentLeft, scrollWidth: contentWidth } = contentRef.current;
276
+ const { clientWidth: trackWidth } = scrollTrackHorizontalRef.current;
277
+
278
+ let newLeft = (+contentLeft / +contentWidth) * trackWidth;
279
+ newLeft = Math.min(newLeft, trackWidth - thumbWidth);
280
+ const thumb = scrollThumbHorizontalRef.current;
281
+ thumb.style.left = `${newLeft}px`;
282
+ }, []);
283
+
284
+ const handleHorizontalThumbMousedown = useCallback((e: React.MouseEvent) => {
285
+ e.preventDefault();
286
+ e.stopPropagation();
287
+ setScrollHorizontalStartPosition(e.clientX);
288
+ if (contentRef.current) setInitialScrollLeft(contentRef.current.scrollLeft);
289
+ setIsHorizontalDragging(true);
290
+ }, []);
291
+
292
+ const handleHorizontalThumbMouseup = useCallback(
293
+ (e: React.MouseEvent) => {
294
+ e.preventDefault();
295
+ e.stopPropagation();
296
+ if (isHorizontalDragging) {
297
+ setIsHorizontalDragging(false);
298
+ }
299
+ },
300
+ [isHorizontalDragging]
301
+ ) as any;
302
+
303
+ const handleHorizontalThumbMousemove = useCallback(
304
+ (e: React.MouseEvent) => {
305
+ e.preventDefault();
306
+ e.stopPropagation();
307
+ if (isHorizontalDragging && contentRef.current && scrollHorizontalStartPosition) {
308
+ const {
309
+ scrollWidth: contentScrollWidth,
310
+ offsetWidth: contentOffsetWidth,
311
+ } = contentRef.current;
312
+
313
+ const deltaY =
314
+ (e.clientX - scrollHorizontalStartPosition) *
315
+ (contentOffsetWidth / thumbWidth);
316
+ const newScrollLeft = Math.min(
317
+ initialScrollLeft + deltaY,
318
+ contentScrollWidth - contentOffsetWidth
319
+ );
320
+
321
+ contentRef.current.scrollLeft = newScrollLeft;
322
+ }
323
+ },
324
+ [isHorizontalDragging, scrollHorizontalStartPosition, thumbWidth]
325
+ ) as any;
326
+
327
+
328
+
329
+ // If the content and the scrollbar track exist, use a ResizeObserver to adjust height of thumb and listen for scroll event to move the thumb
330
+ useEffect(() => {
331
+
332
+
333
+ if (contentRef.current && scrollTrackRef.current && scrollTrackHorizontalRef.current) {
334
+
335
+ // Vertical
336
+ const ref = contentRef.current;
337
+ const { clientHeight: trackSize } = scrollTrackRef.current;
338
+ observer.current = new ResizeObserver(() => {
339
+ handleResize(ref, trackSize);
340
+ });
341
+ observer.current.observe(ref);
342
+ ref.addEventListener('scroll', handleThumbPosition);
343
+
344
+
345
+ //Horizontal
346
+ const horizontalRef = contentRef.current;
347
+ const { clientWidth: trackHorizontalSize } = scrollTrackHorizontalRef.current;
348
+ horizontalObserver.current = new ResizeObserver(() => {
349
+ handleHorizontalResize(horizontalRef, trackHorizontalSize);
350
+ });
351
+ horizontalObserver.current.observe(horizontalRef);
352
+ horizontalRef.addEventListener('scroll', handleHorizontalThumbPosition);
353
+
354
+
355
+ return () => {
356
+ observer.current?.unobserve(ref);
357
+ ref.removeEventListener('scroll', handleThumbPosition);
358
+
359
+ horizontalObserver.current?.unobserve(horizontalRef);
360
+ horizontalRef.removeEventListener('scroll', handleHorizontalThumbPosition);
361
+
362
+ };
363
+ }
364
+
365
+ }, [data]);
366
+
367
+ // Listen for mouse events to handle scrolling by dragging the thumb
368
+ useEffect(() => {
369
+ document.addEventListener('mousemove', handleThumbMousemove);
370
+ document.addEventListener('mouseup', handleThumbMouseup);
371
+ document.addEventListener('mouseleave', handleThumbMouseup);
372
+
373
+ document.addEventListener('mousemove', handleHorizontalThumbMousemove);
374
+ document.addEventListener('mouseup', handleHorizontalThumbMouseup);
375
+ document.addEventListener('mouseleave', handleHorizontalThumbMouseup);
376
+
377
+
378
+ // Add function to the element that should be used as the scrollable area.
379
+ if (contentRef.current) {
380
+ contentRef.current.removeEventListener('wheel', contentAreaScrollUpdate);
381
+ contentRef.current.addEventListener('wheel', contentAreaScrollUpdate);
382
+ contentAreaScrollUpdate();
383
+ }
384
+
385
+
386
+
387
+ return () => {
388
+ document.removeEventListener('mousemove', handleThumbMousemove);
389
+ document.removeEventListener('mouseup', handleThumbMouseup);
390
+ document.removeEventListener('mouseleave', handleThumbMouseup);
391
+
392
+ document.removeEventListener('mousemove', handleHorizontalThumbMousemove);
393
+ document.removeEventListener('mouseup', handleHorizontalThumbMouseup);
394
+ document.removeEventListener('mouseleave', handleHorizontalThumbMouseup);
395
+
396
+ if (contentRef.current) {
397
+ contentRef.current.removeEventListener('wheel', contentAreaScrollUpdate);
398
+ }
399
+
400
+
401
+ };
402
+
403
+
404
+ }, [handleThumbMousemove, handleThumbMouseup, handleHorizontalThumbMousemove, handleHorizontalThumbMouseup]);
405
+
406
+
407
+ return (
408
+ <>
409
+
410
+ <div
411
+ id={idRes}
412
+ ref={rootRef}
413
+ className={`custom-scrollbars__wrapper ${onlyHorizontal ? 'custom-scrollbars__wrapper--horizontal' : ''} ${disableArrow ? 'arrow-disabled' : ''}`}
414
+ >
415
+ <div className="custom-scrollbars__content" ref={contentRef}>
416
+ {children}
417
+ </div>
418
+
419
+ {/* SCROLLBAR */}
420
+ <div ref={scrollBarRef} className="custom-scrollbars__scrollbar" style={onlyHorizontal ? { display: 'none' } : {}}>
421
+ <button
422
+ className={`custom-scrollbars__button ${disableArrow ? 'disabled' : ''}`}
423
+ onClick={() => handleScrollButton('up')}
424
+ >
425
+ {icons[0]}
426
+ </button>
427
+ <div className="custom-scrollbars__track-and-thumb">
428
+ <div
429
+ className="custom-scrollbars__track"
430
+ ref={scrollTrackRef}
431
+ onClick={handleTrackClick}
432
+ style={{ cursor: isDragging ? 'grabbing' : 'pointer' }}
433
+ ></div>
434
+ <div
435
+ className="custom-scrollbars__thumb"
436
+ ref={scrollThumbRef}
437
+ onMouseDown={handleThumbMousedown}
438
+ style={{
439
+ height: `${thumbHeight}px`,
440
+ cursor: isDragging ? 'grabbing' : 'grab',
441
+ }}
442
+ ></div>
443
+ </div>
444
+ <button
445
+ className={`custom-scrollbars__button ${disableArrow ? 'disabled' : ''}`}
446
+ onClick={() => handleScrollButton('down')}
447
+ >
448
+ {icons[1]}
449
+ </button>
450
+ </div>
451
+ {/* /SCROLLBAR */}
452
+
453
+
454
+
455
+
456
+ {/* SCROLLBAR HORIZONTAL */}
457
+ <div ref={scrollBarHorizontalRef} className="custom-scrollbars__scrollbar-horizontal" style={onlyVerticle ? { display: 'none' } : {}}>
458
+ <button
459
+ className={`custom-scrollbars__button ${disableArrow ? 'disabled' : ''}`}
460
+ onClick={() => handleHorizontalScrollButton('left')}
461
+ >
462
+ {icons[2]}
463
+ </button>
464
+ <div className="custom-scrollbars__track-and-thumb">
465
+ <div
466
+ className="custom-scrollbars__track"
467
+ ref={scrollTrackHorizontalRef}
468
+ onClick={handleHorizontalTrackClick}
469
+ style={{ cursor: isHorizontalDragging ? 'grabbing' : 'pointer' }}
470
+ ></div>
471
+ <div
472
+ className="custom-scrollbars__thumb"
473
+ ref={scrollThumbHorizontalRef}
474
+ onMouseDown={handleHorizontalThumbMousedown}
475
+ style={{
476
+ width: `${thumbWidth}px`,
477
+ cursor: isHorizontalDragging ? 'grabbing' : 'grab',
478
+ }}
479
+ ></div>
480
+ </div>
481
+ <button
482
+ className={`custom-scrollbars__button ${disableArrow ? 'disabled' : ''}`}
483
+ onClick={() => handleHorizontalScrollButton('right')}
484
+ >
485
+ {icons[3]}
486
+ </button>
487
+ </div>
488
+ {/* /SCROLLBAR HORIZONTAL */}
489
+
490
+
491
+ </div>
492
+ </>
493
+ )
494
+ };
495
+
496
+
497
+ export default Scrollbar;
@@ -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
+ }