@rainbow-oh/yee-c 0.4.4 → 0.5.0

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 (272) hide show
  1. package/dist/cjs/Button/button.d.ts +1 -1
  2. package/dist/cjs/CSV/download.d.ts +1 -1
  3. package/dist/cjs/Carousel/carousel.d.ts +2 -2
  4. package/dist/cjs/Carousel/carousel.js +9 -2
  5. package/dist/cjs/Carousel/interface.d.ts +5 -0
  6. package/dist/cjs/Dialog/demo/basic.js +1 -1
  7. package/dist/cjs/Dialog/dialog.js +1 -1
  8. package/dist/cjs/Drawer/style/index.less +4 -0
  9. package/dist/cjs/Field/demo/validation.js +23 -3
  10. package/dist/cjs/Field/field.js +1 -1
  11. package/dist/cjs/FloatButton/float-button.d.ts +1 -1
  12. package/dist/cjs/Form/Field.js +1 -4
  13. package/dist/cjs/Form/Form.js +1 -1
  14. package/dist/cjs/Form/Group.js +36 -14
  15. package/dist/cjs/Form/List.js +23 -3
  16. package/dist/cjs/Form/demo/async-validate.d.ts +3 -0
  17. package/dist/cjs/Form/demo/async-validate.js +108 -0
  18. package/dist/cjs/Form/demo/basic.js +2 -2
  19. package/dist/cjs/Form/demo/virtual.js +1 -1
  20. package/dist/cjs/Form/form-store.d.ts +4 -4
  21. package/dist/cjs/Form/form-store.js +55 -25
  22. package/dist/cjs/Form/interface.d.ts +10 -7
  23. package/dist/cjs/Form/style/index.less +15 -0
  24. package/dist/cjs/Form/utils/validate.d.ts +23 -2
  25. package/dist/cjs/Form/utils/validate.js +59 -10
  26. package/dist/cjs/Input/style/index.less +0 -17
  27. package/dist/cjs/InputNumber/style/index.less +0 -29
  28. package/dist/cjs/PickerPanel/interface.d.ts +44 -2
  29. package/dist/cjs/PickerPanel/panels/DatePanel/DateBody.d.ts +2 -1
  30. package/dist/cjs/PickerPanel/panels/DatePanel/DateHeader.d.ts +2 -1
  31. package/dist/cjs/PickerPanel/panels/DatePanel/DateHeader.js +1 -1
  32. package/dist/cjs/PickerPanel/panels/DatePanel/index.d.ts +2 -1
  33. package/dist/cjs/PickerPanel/panels/DateTimePanel/index.d.ts +7 -1
  34. package/dist/cjs/PickerPanel/panels/DateTimePanel/index.js +18 -4
  35. package/dist/cjs/PickerPanel/panels/DecadePanel/DecadeBody.d.ts +2 -1
  36. package/dist/cjs/PickerPanel/panels/DecadePanel/DecadeBody.js +1 -1
  37. package/dist/cjs/PickerPanel/panels/DecadePanel/DecadeHeader.d.ts +2 -1
  38. package/dist/cjs/PickerPanel/panels/DecadePanel/DecadeHeader.js +1 -1
  39. package/dist/cjs/PickerPanel/panels/DecadePanel/index.d.ts +2 -1
  40. package/dist/cjs/PickerPanel/panels/DecadePanel/index.js +1 -1
  41. package/dist/cjs/PickerPanel/panels/Header.d.ts +13 -1
  42. package/dist/cjs/PickerPanel/panels/MonthPanel/MonthBody.d.ts +2 -1
  43. package/dist/cjs/PickerPanel/panels/MonthPanel/MonthHeader.d.ts +2 -1
  44. package/dist/cjs/PickerPanel/panels/MonthPanel/MonthHeader.js +1 -1
  45. package/dist/cjs/PickerPanel/panels/MonthPanel/index.d.ts +2 -1
  46. package/dist/cjs/PickerPanel/panels/MonthPanel/index.js +1 -1
  47. package/dist/cjs/PickerPanel/panels/PanelBody.d.ts +21 -1
  48. package/dist/cjs/PickerPanel/panels/PanelBody.js +3 -3
  49. package/dist/cjs/PickerPanel/panels/QuarterPanel/QuarterBody.d.ts +2 -1
  50. package/dist/cjs/PickerPanel/panels/QuarterPanel/QuarterHeader.d.ts +2 -1
  51. package/dist/cjs/PickerPanel/panels/QuarterPanel/index.d.ts +2 -1
  52. package/dist/cjs/PickerPanel/panels/RangePanel/index.d.ts +2 -1
  53. package/dist/cjs/PickerPanel/panels/RangePanel/index.js +2 -2
  54. package/dist/cjs/PickerPanel/panels/RangePanel/interface.d.ts +5 -5
  55. package/dist/cjs/PickerPanel/panels/TimePanel/TimeBody.d.ts +17 -1
  56. package/dist/cjs/PickerPanel/panels/TimePanel/TimeCell.d.ts +7 -1
  57. package/dist/cjs/PickerPanel/panels/TimePanel/TimeCell.js +1 -1
  58. package/dist/cjs/PickerPanel/panels/TimePanel/TimeColumn.d.ts +14 -1
  59. package/dist/cjs/PickerPanel/panels/TimePanel/TimeColumn.js +1 -1
  60. package/dist/cjs/PickerPanel/panels/TimePanel/TimeHeader.d.ts +4 -1
  61. package/dist/cjs/PickerPanel/panels/TimePanel/index.d.ts +9 -1
  62. package/dist/cjs/PickerPanel/panels/WeekPanel/WeekBody.d.ts +2 -1
  63. package/dist/cjs/PickerPanel/panels/WeekPanel/WeekHeader.d.ts +2 -1
  64. package/dist/cjs/PickerPanel/panels/WeekPanel/index.d.ts +2 -1
  65. package/dist/cjs/PickerPanel/panels/YearPanel/YearBody.d.ts +2 -1
  66. package/dist/cjs/PickerPanel/panels/YearPanel/YearBody.js +2 -2
  67. package/dist/cjs/PickerPanel/panels/YearPanel/YearHeader.d.ts +2 -1
  68. package/dist/cjs/PickerPanel/panels/YearPanel/YearHeader.js +7 -1
  69. package/dist/cjs/PickerPanel/panels/YearPanel/index.d.ts +2 -1
  70. package/dist/cjs/PickerPanel/panels/YearPanel/index.js +1 -1
  71. package/dist/cjs/PickerPanel/utils/pickerUtils.d.ts +7 -7
  72. package/dist/cjs/PickerPanel/utils/pickerUtils.js +2 -2
  73. package/dist/cjs/Select/demo/basic.js +1 -1
  74. package/dist/cjs/Select/demo/virtual.d.ts +3 -0
  75. package/dist/cjs/Select/demo/virtual.js +55 -0
  76. package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +7 -1
  77. package/dist/cjs/Select/hooks/useSelectKeyboard.js +16 -6
  78. package/dist/cjs/Select/interface.d.ts +44 -0
  79. package/dist/cjs/Select/option.js +3 -2
  80. package/dist/cjs/Select/options.js +115 -15
  81. package/dist/cjs/Select/select.js +20 -4
  82. package/dist/cjs/Select/style/index.less +17 -13
  83. package/dist/cjs/Selector/style/index.less +2 -137
  84. package/dist/cjs/Table/style/index.less +19 -0
  85. package/dist/cjs/TextArea/style/index.less +0 -17
  86. package/dist/cjs/hooks/index.d.ts +2 -1
  87. package/dist/cjs/hooks/index.js +3 -1
  88. package/dist/cjs/hooks/useEvent.js +4 -1
  89. package/dist/cjs/hooks/useVirtualList.d.ts +71 -0
  90. package/dist/cjs/hooks/useVirtualList.js +117 -0
  91. package/dist/cjs/style/size.css +1 -0
  92. package/dist/cjs/style/themes.css +1 -1
  93. package/dist/cjs/style/variables.css +2 -1
  94. package/dist/cjs/version.d.ts +1 -1
  95. package/dist/cjs/version.js +1 -1
  96. package/dist/esm/Button/button.d.ts +1 -1
  97. package/dist/esm/CSV/download.d.ts +1 -1
  98. package/dist/esm/Carousel/carousel.d.ts +2 -2
  99. package/dist/esm/Carousel/carousel.js +5 -1
  100. package/dist/esm/Carousel/interface.d.ts +5 -0
  101. package/dist/esm/Dialog/demo/basic.js +1 -1
  102. package/dist/esm/Dialog/dialog.js +1 -1
  103. package/dist/esm/Drawer/style/index.less +4 -0
  104. package/dist/esm/Field/demo/validation.js +30 -9
  105. package/dist/esm/Field/field.js +2 -1
  106. package/dist/esm/FloatButton/float-button.d.ts +1 -1
  107. package/dist/esm/Form/Field.js +3 -4
  108. package/dist/esm/Form/Form.js +1 -1
  109. package/dist/esm/Form/Group.js +142 -53
  110. package/dist/esm/Form/List.js +61 -42
  111. package/dist/esm/Form/demo/async-validate.d.ts +3 -0
  112. package/dist/esm/Form/demo/async-validate.js +95 -0
  113. package/dist/esm/Form/demo/basic.js +4 -2
  114. package/dist/esm/Form/demo/virtual.js +1 -1
  115. package/dist/esm/Form/form-store.d.ts +4 -4
  116. package/dist/esm/Form/form-store.js +187 -93
  117. package/dist/esm/Form/interface.d.ts +10 -7
  118. package/dist/esm/Form/style/index.less +15 -0
  119. package/dist/esm/Form/utils/validate.d.ts +23 -2
  120. package/dist/esm/Form/utils/validate.js +122 -17
  121. package/dist/esm/Input/style/index.less +0 -17
  122. package/dist/esm/InputNumber/style/index.less +0 -29
  123. package/dist/esm/PickerPanel/interface.d.ts +44 -2
  124. package/dist/esm/PickerPanel/panels/DatePanel/DateBody.d.ts +2 -1
  125. package/dist/esm/PickerPanel/panels/DatePanel/DateHeader.d.ts +2 -1
  126. package/dist/esm/PickerPanel/panels/DatePanel/DateHeader.js +1 -1
  127. package/dist/esm/PickerPanel/panels/DatePanel/index.d.ts +2 -1
  128. package/dist/esm/PickerPanel/panels/DateTimePanel/index.d.ts +7 -1
  129. package/dist/esm/PickerPanel/panels/DateTimePanel/index.js +11 -7
  130. package/dist/esm/PickerPanel/panels/DecadePanel/DecadeBody.d.ts +2 -1
  131. package/dist/esm/PickerPanel/panels/DecadePanel/DecadeBody.js +2 -1
  132. package/dist/esm/PickerPanel/panels/DecadePanel/DecadeHeader.d.ts +2 -1
  133. package/dist/esm/PickerPanel/panels/DecadePanel/DecadeHeader.js +2 -1
  134. package/dist/esm/PickerPanel/panels/DecadePanel/index.d.ts +2 -1
  135. package/dist/esm/PickerPanel/panels/DecadePanel/index.js +1 -1
  136. package/dist/esm/PickerPanel/panels/Header.d.ts +13 -1
  137. package/dist/esm/PickerPanel/panels/MonthPanel/MonthBody.d.ts +2 -1
  138. package/dist/esm/PickerPanel/panels/MonthPanel/MonthHeader.d.ts +2 -1
  139. package/dist/esm/PickerPanel/panels/MonthPanel/MonthHeader.js +2 -1
  140. package/dist/esm/PickerPanel/panels/MonthPanel/index.d.ts +2 -1
  141. package/dist/esm/PickerPanel/panels/MonthPanel/index.js +1 -1
  142. package/dist/esm/PickerPanel/panels/PanelBody.d.ts +21 -1
  143. package/dist/esm/PickerPanel/panels/PanelBody.js +2 -4
  144. package/dist/esm/PickerPanel/panels/QuarterPanel/QuarterBody.d.ts +2 -1
  145. package/dist/esm/PickerPanel/panels/QuarterPanel/QuarterHeader.d.ts +2 -1
  146. package/dist/esm/PickerPanel/panels/QuarterPanel/index.d.ts +2 -1
  147. package/dist/esm/PickerPanel/panels/RangePanel/index.d.ts +2 -1
  148. package/dist/esm/PickerPanel/panels/RangePanel/index.js +2 -2
  149. package/dist/esm/PickerPanel/panels/RangePanel/interface.d.ts +5 -5
  150. package/dist/esm/PickerPanel/panels/TimePanel/TimeBody.d.ts +17 -1
  151. package/dist/esm/PickerPanel/panels/TimePanel/TimeCell.d.ts +7 -1
  152. package/dist/esm/PickerPanel/panels/TimePanel/TimeCell.js +1 -1
  153. package/dist/esm/PickerPanel/panels/TimePanel/TimeColumn.d.ts +14 -1
  154. package/dist/esm/PickerPanel/panels/TimePanel/TimeColumn.js +1 -1
  155. package/dist/esm/PickerPanel/panels/TimePanel/TimeHeader.d.ts +4 -1
  156. package/dist/esm/PickerPanel/panels/TimePanel/index.d.ts +9 -1
  157. package/dist/esm/PickerPanel/panels/WeekPanel/WeekBody.d.ts +2 -1
  158. package/dist/esm/PickerPanel/panels/WeekPanel/WeekHeader.d.ts +2 -1
  159. package/dist/esm/PickerPanel/panels/WeekPanel/index.d.ts +2 -1
  160. package/dist/esm/PickerPanel/panels/YearPanel/YearBody.d.ts +2 -1
  161. package/dist/esm/PickerPanel/panels/YearPanel/YearBody.js +3 -2
  162. package/dist/esm/PickerPanel/panels/YearPanel/YearHeader.d.ts +2 -1
  163. package/dist/esm/PickerPanel/panels/YearPanel/YearHeader.js +2 -1
  164. package/dist/esm/PickerPanel/panels/YearPanel/index.d.ts +2 -1
  165. package/dist/esm/PickerPanel/panels/YearPanel/index.js +1 -1
  166. package/dist/esm/PickerPanel/picker-panel.js +3 -0
  167. package/dist/esm/PickerPanel/utils/pickerUtils.d.ts +7 -7
  168. package/dist/esm/PickerPanel/utils/pickerUtils.js +4 -10
  169. package/dist/esm/Select/demo/basic.js +1 -1
  170. package/dist/esm/Select/demo/virtual.d.ts +3 -0
  171. package/dist/esm/Select/demo/virtual.js +38 -0
  172. package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +7 -1
  173. package/dist/esm/Select/hooks/useSelectKeyboard.js +25 -9
  174. package/dist/esm/Select/interface.d.ts +44 -0
  175. package/dist/esm/Select/option.js +4 -2
  176. package/dist/esm/Select/options.js +132 -13
  177. package/dist/esm/Select/select.js +19 -4
  178. package/dist/esm/Select/style/index.less +17 -13
  179. package/dist/esm/Selector/style/index.less +2 -137
  180. package/dist/esm/Table/style/index.less +19 -0
  181. package/dist/esm/TextArea/style/index.less +0 -17
  182. package/dist/esm/hooks/index.d.ts +2 -1
  183. package/dist/esm/hooks/index.js +2 -1
  184. package/dist/esm/hooks/useVirtualList.d.ts +71 -0
  185. package/dist/esm/hooks/useVirtualList.js +129 -0
  186. package/dist/esm/style/size.css +1 -0
  187. package/dist/esm/style/themes.css +1 -1
  188. package/dist/esm/style/variables.css +2 -1
  189. package/dist/esm/version.d.ts +1 -1
  190. package/dist/esm/version.js +1 -1
  191. package/dist/umd/yee-c.min.css +2 -2
  192. package/dist/umd/yee-c.min.js +1 -1
  193. package/package.json +1 -1
  194. package/src/Carousel/carousel.tsx +14 -3
  195. package/src/Carousel/interface.ts +6 -0
  196. package/src/Cascader/cascader.tsx +3 -1
  197. package/src/Cascader/column.tsx +1 -1
  198. package/src/Dialog/demo/basic.tsx +1 -1
  199. package/src/Dialog/dialog.tsx +1 -1
  200. package/src/Drawer/style/index.less +4 -0
  201. package/src/Dropdown/dropdown.tsx +1 -1
  202. package/src/Field/demo/validation.tsx +2 -2
  203. package/src/Field/field.tsx +2 -1
  204. package/src/Field/index.en-US.md +1 -1
  205. package/src/Field/index.zh-CN.md +1 -1
  206. package/src/FloatButton/float-button.tsx +5 -3
  207. package/src/Form/Field.tsx +3 -4
  208. package/src/Form/Form.tsx +1 -1
  209. package/src/Form/Group.tsx +15 -12
  210. package/src/Form/List.tsx +3 -3
  211. package/src/Form/demo/async-validate.tsx +71 -0
  212. package/src/Form/demo/basic.tsx +2 -2
  213. package/src/Form/demo/virtual.tsx +1 -1
  214. package/src/Form/form-store.ts +37 -21
  215. package/src/Form/index.en-US.md +6 -5
  216. package/src/Form/index.zh-CN.md +6 -5
  217. package/src/Form/interface.ts +13 -7
  218. package/src/Form/style/index.less +15 -0
  219. package/src/Form/utils/validate.ts +65 -13
  220. package/src/Input/style/index.less +0 -17
  221. package/src/InputNumber/style/index.less +0 -29
  222. package/src/PickerPanel/interface.ts +43 -2
  223. package/src/PickerPanel/panels/DatePanel/DateBody.tsx +2 -1
  224. package/src/PickerPanel/panels/DatePanel/DateHeader.tsx +3 -2
  225. package/src/PickerPanel/panels/DatePanel/index.tsx +2 -1
  226. package/src/PickerPanel/panels/DateTimePanel/index.tsx +12 -5
  227. package/src/PickerPanel/panels/DecadePanel/DecadeBody.tsx +3 -2
  228. package/src/PickerPanel/panels/DecadePanel/DecadeHeader.tsx +3 -2
  229. package/src/PickerPanel/panels/DecadePanel/index.tsx +3 -2
  230. package/src/PickerPanel/panels/Header.tsx +14 -1
  231. package/src/PickerPanel/panels/MonthPanel/MonthBody.tsx +2 -1
  232. package/src/PickerPanel/panels/MonthPanel/MonthHeader.tsx +3 -2
  233. package/src/PickerPanel/panels/MonthPanel/index.tsx +3 -2
  234. package/src/PickerPanel/panels/PanelBody.tsx +27 -5
  235. package/src/PickerPanel/panels/QuarterPanel/QuarterBody.tsx +2 -1
  236. package/src/PickerPanel/panels/QuarterPanel/QuarterHeader.tsx +2 -1
  237. package/src/PickerPanel/panels/QuarterPanel/index.tsx +2 -1
  238. package/src/PickerPanel/panels/RangePanel/index.tsx +11 -9
  239. package/src/PickerPanel/panels/RangePanel/interface.ts +5 -5
  240. package/src/PickerPanel/panels/TimePanel/TimeBody.tsx +21 -5
  241. package/src/PickerPanel/panels/TimePanel/TimeCell.tsx +9 -2
  242. package/src/PickerPanel/panels/TimePanel/TimeColumn.tsx +15 -2
  243. package/src/PickerPanel/panels/TimePanel/TimeHeader.tsx +5 -1
  244. package/src/PickerPanel/panels/TimePanel/index.tsx +10 -1
  245. package/src/PickerPanel/panels/WeekPanel/WeekBody.tsx +2 -1
  246. package/src/PickerPanel/panels/WeekPanel/WeekHeader.tsx +2 -1
  247. package/src/PickerPanel/panels/WeekPanel/index.tsx +2 -1
  248. package/src/PickerPanel/panels/YearPanel/YearBody.tsx +4 -3
  249. package/src/PickerPanel/panels/YearPanel/YearHeader.tsx +9 -3
  250. package/src/PickerPanel/panels/YearPanel/index.tsx +3 -2
  251. package/src/PickerPanel/picker-panel.tsx +5 -3
  252. package/src/PickerPanel/utils/pickerUtils.ts +17 -8
  253. package/src/Select/demo/basic.tsx +1 -1
  254. package/src/Select/demo/virtual.tsx +25 -0
  255. package/src/Select/hooks/useSelectKeyboard.ts +30 -6
  256. package/src/Select/index.en-US.md +4 -0
  257. package/src/Select/index.zh-CN.md +4 -0
  258. package/src/Select/interface.ts +48 -2
  259. package/src/Select/option.tsx +2 -1
  260. package/src/Select/options.tsx +139 -8
  261. package/src/Select/select.tsx +23 -1
  262. package/src/Select/style/index.less +17 -13
  263. package/src/Selector/style/index.less +2 -137
  264. package/src/Splitter/hooks/useMove.ts +2 -2
  265. package/src/Table/style/index.less +19 -0
  266. package/src/TextArea/style/index.less +0 -17
  267. package/src/hooks/index.ts +2 -0
  268. package/src/hooks/useEvent.ts +7 -4
  269. package/src/hooks/useMergedState.ts +6 -4
  270. package/src/hooks/useVirtualList.ts +169 -0
  271. package/src/style/size.css +1 -0
  272. package/src/version.ts +1 -1
@@ -1,5 +1,12 @@
1
1
  import clsx from 'clsx';
2
- import React, { createContext, forwardRef, useRef } from 'react';
2
+ import React, {
3
+ createContext,
4
+ forwardRef,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ } from 'react';
9
+ import useVirtualList from '../hooks/useVirtualList';
3
10
  import { useLocale } from '../locale';
4
11
  import type { OptionsContextValue, OptionsProps } from './interface';
5
12
  import Option from './option';
@@ -22,27 +29,151 @@ const Options = forwardRef<HTMLDivElement, OptionsProps>(
22
29
  focusedKey,
23
30
  dataTestId,
24
31
  multiple,
32
+ virtual,
33
+ itemHeight = 32,
34
+ listHeight = 200,
35
+ virtualApiRef,
25
36
  onSelect,
26
37
  ...rest
27
38
  } = props;
28
39
 
29
- // eslint-disable-next-line react-hooks/rules-of-hooks
30
- const componentRef = ref || useRef<HTMLDivElement>(null);
40
+ const rootRef = useRef<HTMLDivElement | null>(null);
41
+ // Forward the popup root element to the parent (Select uses it as the
42
+ // keyboard-nav containerRef) while keeping a stable RefObject for the
43
+ // effects below.
44
+ useImperativeHandle(ref, () => rootRef.current as HTMLDivElement, []);
31
45
 
46
+ // Hook is always called (rules of hooks). When `virtual` is off the
47
+ // returned viewportRef is never attached, so the observer stays idle.
48
+ const {
49
+ start,
50
+ end,
51
+ offsetY,
52
+ totalHeight,
53
+ viewportRef,
54
+ onScroll,
55
+ scrollToIndex,
56
+ } = useVirtualList({
57
+ itemCount: options.length,
58
+ itemHeight,
59
+ });
60
+
61
+ // Expose the virtual scroll API to the parent so keyboard navigation
62
+ // can drive the virtual viewport (replaces scrollIntoView).
63
+ useEffect(() => {
64
+ if (!virtual || !virtualApiRef) return;
65
+ virtualApiRef.current = { scrollToIndex };
66
+ return () => {
67
+ if (virtualApiRef.current) {
68
+ virtualApiRef.current = null;
69
+ }
70
+ };
71
+ }, [virtual, virtualApiRef, scrollToIndex]);
72
+
73
+ // Reset the scroll position whenever the option set changes (e.g. after
74
+ // filtering via search), otherwise the viewport may sit at an offset
75
+ // where none of the new results are visible.
76
+ useEffect(() => {
77
+ if (virtual) {
78
+ scrollToIndex(0);
79
+ }
80
+ }, [options, virtual, scrollToIndex]);
81
+
82
+ // Bring the focused option into view on open and whenever it changes.
83
+ // - virtual mode: drive the virtual viewport via scrollToIndex.
84
+ // - default mode: scrollIntoView on the option's DOM node.
85
+ // Options owns this (rather than the keyboard hook) so it also covers the
86
+ // initial open, where the keyboard effect can fire before the popup is
87
+ // mounted / the virtual API ref is wired up.
88
+ useEffect(() => {
89
+ if (options.length === 0) return;
90
+ if (focusedKey === '') return;
91
+ const focusedIndex = options.findIndex((o) => o.value === focusedKey);
92
+ if (focusedIndex < 0) return;
93
+
94
+ if (virtual) {
95
+ scrollToIndex(focusedIndex);
96
+ return;
97
+ }
98
+
99
+ const container = rootRef.current;
100
+ if (!container) return;
101
+ const optionElements = container.querySelectorAll('[role="option"]');
102
+ const targetElement = optionElements[focusedIndex] as
103
+ | HTMLElement
104
+ | undefined;
105
+ if (targetElement) {
106
+ targetElement.scrollIntoView({ block: 'nearest' });
107
+ }
108
+ }, [focusedKey, virtual, options, scrollToIndex, rootRef]);
109
+
110
+ const isEmpty = options.length === 0;
111
+ const ctxValue = {
112
+ prefixCls,
113
+ selectedKeys,
114
+ focusedKey,
115
+ multiple,
116
+ onSelect,
117
+ };
118
+
119
+ // ----- Virtual mode: only render the visible window -----
120
+ if (virtual && !isEmpty) {
121
+ const visibleOptions = options.slice(start, end);
122
+ return (
123
+ <div
124
+ {...rest}
125
+ className={clsx(
126
+ `${prefixCls}-popup`,
127
+ `${prefixCls}-popup-virtual`,
128
+ popupClassName,
129
+ )}
130
+ style={popupStyle}
131
+ ref={rootRef}
132
+ tabIndex={-1}
133
+ >
134
+ <OptionsCtx.Provider value={ctxValue}>
135
+ <div
136
+ className={`${prefixCls}-popup-viewport`}
137
+ ref={viewportRef}
138
+ onScroll={onScroll}
139
+ style={{ maxHeight: listHeight, overflowY: 'auto' }}
140
+ >
141
+ <div style={{ height: totalHeight, position: 'relative' }}>
142
+ {visibleOptions.map((option, i) => (
143
+ <Option
144
+ {...option}
145
+ key={option.value}
146
+ dataTestId={`${dataTestId}-${start + i}`}
147
+ style={{
148
+ position: 'absolute',
149
+ top: 0,
150
+ left: 0,
151
+ right: 0,
152
+ height: itemHeight,
153
+ transform: `translateY(${offsetY + i * itemHeight}px)`,
154
+ }}
155
+ />
156
+ ))}
157
+ </div>
158
+ </div>
159
+ </OptionsCtx.Provider>
160
+ </div>
161
+ );
162
+ }
163
+
164
+ // ----- Default mode: render every option (unchanged behavior) -----
32
165
  return (
33
166
  <div
34
167
  {...rest}
35
168
  className={clsx(`${prefixCls}-popup`, popupClassName)}
36
169
  style={popupStyle}
37
- ref={componentRef}
170
+ ref={rootRef}
38
171
  tabIndex={-1}
39
172
  >
40
- {options.length === 0 ? (
173
+ {isEmpty ? (
41
174
  <div className={`${prefixCls}-empty`}>{select.noData}</div>
42
175
  ) : (
43
- <OptionsCtx.Provider
44
- value={{ prefixCls, selectedKeys, focusedKey, multiple, onSelect }}
45
- >
176
+ <OptionsCtx.Provider value={ctxValue}>
46
177
  {options.map((option, index: number) => (
47
178
  <Option
48
179
  {...option}
@@ -1,4 +1,10 @@
1
- import React, { useContext, useMemo, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useContext,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import { GlobalContext } from '../Config-Provider';
3
9
  import Selector from '../Selector';
4
10
  import Trigger from '../Trigger';
@@ -23,6 +29,9 @@ const Select = (baseprops: SelectProps) => {
23
29
  disabled,
24
30
  onChange,
25
31
  onFilter,
32
+ virtual,
33
+ itemHeight,
34
+ listHeight,
26
35
  ...rest
27
36
  } = props;
28
37
 
@@ -50,6 +59,14 @@ const Select = (baseprops: SelectProps) => {
50
59
 
51
60
  const [open, setOpen] = useState(false);
52
61
  const popupRef = useRef<HTMLDivElement>(null);
62
+ // Holds the virtual list API exposed by <Options>, so keyboard navigation
63
+ // can drive the virtual viewport (only populated when `virtual` is on).
64
+ const virtualApiRef = useRef<{
65
+ scrollToIndex: (index: number) => void;
66
+ } | null>(null);
67
+ const handleVirtualScrollToIndex = useCallback((index: number) => {
68
+ virtualApiRef.current?.scrollToIndex(index);
69
+ }, []);
53
70
 
54
71
  const getOptions = (keys: string | number | Array<string | number>) => {
55
72
  if (keys === '' || keys === null) {
@@ -135,6 +152,7 @@ const Select = (baseprops: SelectProps) => {
135
152
  onClose: () => setOpen(false),
136
153
  onOpenChange: handleOpenChange,
137
154
  containerRef: popupRef as React.RefObject<HTMLDivElement>,
155
+ scrollToIndex: virtual ? handleVirtualScrollToIndex : undefined,
138
156
  });
139
157
 
140
158
  const popup = (
@@ -147,6 +165,10 @@ const Select = (baseprops: SelectProps) => {
147
165
  dataTestId={rest['data-testid']}
148
166
  onSelect={handleSelect}
149
167
  ref={popupRef}
168
+ virtual={virtual}
169
+ itemHeight={itemHeight}
170
+ listHeight={listHeight}
171
+ virtualApiRef={virtualApiRef}
150
172
  />
151
173
  );
152
174
 
@@ -8,6 +8,17 @@
8
8
  overflow: auto;
9
9
  }
10
10
 
11
+ // Virtual mode: the popup no longer scrolls itself; the inner viewport does.
12
+ // Without this override there would be two scrollbars.
13
+ &-popup-virtual {
14
+ max-height: none;
15
+ overflow: visible;
16
+ }
17
+
18
+ &-popup-viewport {
19
+ overflow-y: auto;
20
+ }
21
+
11
22
  &-option {
12
23
  display: flex;
13
24
  padding: var(--yee-select-option-padding);
@@ -37,20 +48,13 @@
37
48
  }
38
49
  }
39
50
 
40
- &-empty {
41
- padding: var(--yee-select-option-padding);
42
- }
43
- }
44
-
45
- // form error status
46
- .yee-form-item-error {
47
- @{pls} {
48
- border-color: var(--yee-error-color);
51
+ // In virtual mode each option has a fixed inline height; center its content
52
+ // vertically so it stays aligned regardless of the inherited line-height.
53
+ &-popup-virtual &-option {
54
+ align-items: center;
49
55
  }
50
- }
51
56
 
52
- .yee-form-item-warning {
53
- @{pls} {
54
- border-color: var(--yee-warning-color);
57
+ &-empty {
58
+ padding: var(--yee-select-option-padding);
55
59
  }
56
60
  }
@@ -5,8 +5,7 @@
5
5
  box-sizing: border-box;
6
6
  width: 100%;
7
7
  padding: var(--yee-selector-padding);
8
- border: var(--yee-selector-border-width) solid
9
- var(--yee-selector-border-color);
8
+ border: var(--yee-selector-border-width) solid var(--yee-input-border-color);
10
9
  border-radius: var(--yee-selector-border-radius);
11
10
 
12
11
  &-small {
@@ -145,7 +144,7 @@
145
144
 
146
145
  &:not(@{pls}-disabled) {
147
146
  &:hover {
148
- border-color: var(--yee-selector-hover-border-color);
147
+ border-color: var(--yee-input-hover-border-color);
149
148
  }
150
149
  }
151
150
 
@@ -206,137 +205,3 @@
206
205
  }
207
206
  }
208
207
  }
209
-
210
- // .yee-tags-searchable {
211
- // cursor: text;
212
-
213
- // .yee-tags-input-wrapper {
214
- // width: 100%;
215
- // padding: 0;
216
- // border: none;
217
- // outline: none;
218
-
219
- // &:focus {
220
- // border: none;
221
- // box-shadow: none;
222
- // }
223
- // }
224
-
225
- // .yee-tags-icon {
226
- // width: auto;
227
- // }
228
- // }
229
-
230
- // .yee-tags-single-mode {
231
- // height: var(--yee-tags-height);
232
-
233
- // .yee-tags-input-container {
234
- // position: absolute;
235
- // z-index: -1;
236
- // width: 100%;
237
- // font-size: var(--yee-tags-input-font-size);
238
- // line-height: var(--yee-tags-input-line-height);
239
-
240
- // input {
241
- // width: 100%;
242
- // }
243
- // }
244
-
245
- // &.yee-tags-searchable {
246
- // .yee-tags-single-option {
247
- // width: 100%;
248
- // }
249
-
250
- // .yee-tags-container {
251
- // &:focus-within,
252
- // &:focus {
253
- // .yee-tags-input-container {
254
- // z-index: 1;
255
- // opacity: var(--yee-tags-input-focus-opacity);
256
-
257
- // &.has-value {
258
- // opacity: 1;
259
-
260
- // + .yee-tags-single-option {
261
- // display: none;
262
- // }
263
- // }
264
- // }
265
-
266
- // .yee-tags-single-option {
267
- // opacity: var(--yee-tags-single-option-focus-opacity);
268
- // }
269
- // }
270
- // }
271
- // }
272
- // }
273
-
274
- // .yee-tags-multi-mode {
275
- // height: auto;
276
- // min-height: var(--yee-tags-multi-min-height);
277
-
278
- // &.yee-tags-searchable {
279
- // .yee-tag {
280
- // max-width: var(--yee-tags-multi-tag-max-width);
281
- // height: var(--yee-tags-multi-tag-height);
282
- // }
283
- // }
284
-
285
- // .yee-tags-container {
286
- // flex-wrap: wrap;
287
- // }
288
-
289
- // .yee-tags-input-container {
290
- // display: inline-grid;
291
- // // color: var(--yee-tags-input-container-font-color);
292
- // flex: 1 1 auto;
293
- // grid-area: 1 / 1 / 2 / 3;
294
- // grid-template-columns: 0 min-content;
295
- // box-sizing: border-box;
296
- // height: var(--yee-tags-multi-input-height);
297
-
298
- // &::after {
299
- // grid-area: 1 / 2 / auto / auto;
300
- // min-width: var(--yee-tags-multi-input-after-min-width);
301
- // margin: 0;
302
- // padding: 0;
303
- // font: inherit;
304
- // white-space: pre;
305
- // border: 0;
306
- // outline: 0;
307
- // visibility: hidden;
308
- // content: attr(data-value) ' ';
309
- // }
310
-
311
- // & > input {
312
- // grid-area: 1 / 2 / auto / auto;
313
- // width: 100%;
314
- // min-width: var(--yee-tags-multi-input-min-width);
315
- // height: var(--yee-tags-multi-input-height);
316
- // margin: 0;
317
- // padding: 0;
318
- // font: inherit;
319
- // background-color: transparent;
320
- // border: 0;
321
- // outline: 0;
322
- // opacity: 1;
323
- // }
324
- // }
325
- // }
326
-
327
- // form error status
328
- .yee-form-item-error {
329
- @{pls} {
330
- border-color: var(--yee-error-color);
331
-
332
- &:hover {
333
- border-color: var(--yee-error-color-hover);
334
- }
335
- }
336
- }
337
-
338
- .yee-form-item-warning {
339
- @{pls} {
340
- border-color: var(--yee-warning-color);
341
- }
342
- }
@@ -15,13 +15,13 @@ export default function useMove(
15
15
  React.useEffect(() => {
16
16
  if (!handler.current) return;
17
17
  const ele = handler.current as HTMLElement;
18
- const onMouseDown = (e: any) => {
18
+ const onMouseDown = (e: MouseEvent) => {
19
19
  e.preventDefault();
20
20
  let x = e.pageX;
21
21
  let y = e.pageY;
22
22
  setMoving(true);
23
23
 
24
- const onMouseMove = debounce((e: any) => {
24
+ const onMouseMove = debounce((e: MouseEvent) => {
25
25
  document.body.style.userSelect = 'none';
26
26
  document.body.style.cursor =
27
27
  direction === 'horizontal' ? 'col-resize' : 'row-resize';
@@ -23,13 +23,32 @@
23
23
  var(--yee-table-tbody-td-borderbottom-color);
24
24
  }
25
25
  }
26
+
27
+ @{pls}-cell {
28
+ &::before {
29
+ display: none;
30
+ }
31
+ }
26
32
  }
27
33
 
28
34
  &-thead {
29
35
  color: var(--yee-table-thead-color);
30
36
 
31
37
  @{pls}-cell {
38
+ position: relative;
32
39
  background-color: var(--yee-table-thead-bg-color);
40
+
41
+ &:not(:last-child):not(@{pls}-expand-col) {
42
+ &::before {
43
+ position: absolute;
44
+ content: '';
45
+ width: 1.5px;
46
+ height: 30%;
47
+ top: 35%;
48
+ right: 0;
49
+ background-color: var(--yee-table-head-cell-split-line-color, #eee);
50
+ }
51
+ }
33
52
  }
34
53
 
35
54
  &-cell {
@@ -98,20 +98,3 @@
98
98
  cursor: not-allowed;
99
99
  }
100
100
  }
101
-
102
- // form error status
103
- .yee-form-item-error {
104
- @{pls} {
105
- border-color: var(--yee-error-color);
106
-
107
- &:hover {
108
- border-color: var(--yee-error-color-hover);
109
- }
110
- }
111
- }
112
-
113
- .yee-form-item-warning {
114
- @{pls} {
115
- border-color: var(--yee-warning-color);
116
- }
117
- }
@@ -16,6 +16,7 @@ import useMount from './useMount';
16
16
  import usePrevious from './usePrevious';
17
17
  import useUpdateEffect from './useUpdateEffect';
18
18
  import useVirtualForm from './useVirtualForm';
19
+ import useVirtualList from './useVirtualList';
19
20
 
20
21
  export {
21
22
  useBreakpoint,
@@ -36,4 +37,5 @@ export {
36
37
  usePrevious,
37
38
  useUpdateEffect,
38
39
  useVirtualForm,
40
+ useVirtualList,
39
41
  };
@@ -2,13 +2,16 @@ import React from 'react';
2
2
 
3
3
  // eslint-disable-next-line @typescript-eslint/ban-types
4
4
  export default function useEvent<T extends Function>(callback?: T): T {
5
- const ref = React.useRef<any>(undefined);
5
+ const ref = React.useRef<T | undefined>(undefined);
6
6
  ref.current = callback;
7
7
 
8
- const memoFn = React.useCallback<T>(
9
- ((...args: any) => ref.current?.(...args)) as any,
8
+ const memoFn = React.useCallback(
9
+ (...args: unknown[]) =>
10
+ (ref.current as ((...args: unknown[]) => unknown) | undefined)?.(
11
+ ...args,
12
+ ),
10
13
  [],
11
- );
14
+ ) as unknown as T;
12
15
 
13
16
  return memoFn;
14
17
  }
@@ -17,7 +17,7 @@ enum Source {
17
17
  type ValueRecord<T> = [T, Source, T];
18
18
 
19
19
  /** We only think `undefined` is empty */
20
- function hasValue(value: any) {
20
+ function hasValue<T>(value: T | undefined): value is T {
21
21
  return value !== undefined;
22
22
  }
23
23
 
@@ -50,13 +50,13 @@ export default function useMergedState<T, R = T>(
50
50
  } else if (hasValue(defaultValue)) {
51
51
  finalValue =
52
52
  typeof defaultValue === 'function'
53
- ? (defaultValue as any)()
53
+ ? (defaultValue as () => T)()
54
54
  : defaultValue;
55
55
  source = Source.PROP;
56
56
  } else {
57
57
  finalValue =
58
58
  typeof defaultStateValue === 'function'
59
- ? (defaultStateValue as any)()
59
+ ? (defaultStateValue as () => T)()
60
60
  : defaultStateValue;
61
61
  source = Source.INNER;
62
62
  }
@@ -94,7 +94,9 @@ export default function useMergedState<T, R = T>(
94
94
  const [prevValue, prevSource, prevPrevValue] = prev;
95
95
 
96
96
  const nextValue: T =
97
- typeof updater === 'function' ? (updater as any)(prevValue) : updater;
97
+ typeof updater === 'function'
98
+ ? (updater as (origin: T) => T)(prevValue)
99
+ : updater;
98
100
  // Do nothing if value not change
99
101
  if (nextValue === prevValue) {
100
102
  return prev;