@kine-design/core 0.0.1-beta.1 → 0.0.1-beta.2

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 (300) hide show
  1. package/assets/style/global.css +1 -0
  2. package/assets/style/var/Wuxing.css +71 -0
  3. package/assets/style/var.css +23 -0
  4. package/components/base/affix/api.ts +16 -0
  5. package/components/base/affix/index.ts +15 -0
  6. package/components/base/affix/props.d.ts +34 -0
  7. package/components/base/affix/useAffix.ts +123 -0
  8. package/components/base/alert/api.ts +18 -0
  9. package/components/base/alert/index.ts +15 -0
  10. package/components/base/alert/props.d.ts +50 -0
  11. package/components/base/anchor/api.ts +20 -0
  12. package/components/base/anchor/index.ts +16 -0
  13. package/components/base/anchor/props.d.ts +46 -0
  14. package/components/base/anchor/useAnchor.ts +83 -0
  15. package/components/base/autoComplete/api.ts +24 -0
  16. package/components/base/autoComplete/index.ts +17 -0
  17. package/components/base/autoComplete/props.d.ts +75 -0
  18. package/components/base/autoComplete/useAutoComplete.ts +149 -0
  19. package/components/base/avatar/api.ts +17 -0
  20. package/components/base/avatar/avatar.css +61 -0
  21. package/components/base/avatar/index.ts +14 -0
  22. package/components/base/avatar/props.d.ts +37 -0
  23. package/components/base/backTop/api.ts +17 -0
  24. package/components/base/backTop/index.ts +15 -0
  25. package/components/base/backTop/props.d.ts +38 -0
  26. package/components/base/backTop/useBackTop.ts +62 -0
  27. package/components/base/badge/api.ts +22 -0
  28. package/components/base/badge/index.ts +15 -0
  29. package/components/base/badge/props.d.ts +50 -0
  30. package/components/base/button/api.ts +28 -0
  31. package/components/base/button/button.css +34 -0
  32. package/components/base/button/index.ts +16 -0
  33. package/components/base/button/props.d.ts +64 -0
  34. package/components/base/button/useButton.ts +37 -0
  35. package/components/base/card/api.ts +17 -0
  36. package/components/base/card/index.ts +15 -0
  37. package/components/base/card/props.d.ts +37 -0
  38. package/components/base/carousel/api.ts +28 -0
  39. package/components/base/carousel/index.ts +17 -0
  40. package/components/base/carousel/props.d.ts +72 -0
  41. package/components/base/carousel/useCarousel.ts +151 -0
  42. package/components/base/cascader/api.ts +23 -0
  43. package/components/base/cascader/index.ts +18 -0
  44. package/components/base/cascader/props.d.ts +103 -0
  45. package/components/base/cascader/useCascader.ts +334 -0
  46. package/components/base/checkbox/api.ts +24 -0
  47. package/components/base/checkbox/checkbox.css +0 -0
  48. package/components/base/checkbox/index.ts +16 -0
  49. package/components/base/checkbox/props.d.ts +77 -0
  50. package/components/base/checkbox/useCheckbox.ts +42 -0
  51. package/components/base/collapse/api.ts +21 -0
  52. package/components/base/collapse/index.ts +16 -0
  53. package/components/base/collapse/props.d.ts +45 -0
  54. package/components/base/collapse/useCollapse.ts +80 -0
  55. package/components/base/datePicker/api.ts +18 -0
  56. package/components/base/datePicker/index.ts +19 -0
  57. package/components/base/datePicker/props.d.ts +60 -0
  58. package/components/base/datePicker/useDatePicker.ts +392 -0
  59. package/components/base/divider/api.ts +15 -0
  60. package/components/base/divider/divider.css +11 -0
  61. package/components/base/divider/index.ts +15 -0
  62. package/components/base/divider/props.d.ts +30 -0
  63. package/components/base/dropdown/api.ts +33 -0
  64. package/components/base/dropdown/index.ts +16 -0
  65. package/components/base/dropdown/props.d.ts +60 -0
  66. package/components/base/dropdown/useDropdown.ts +123 -0
  67. package/components/base/empty/api.ts +15 -0
  68. package/components/base/empty/index.ts +15 -0
  69. package/components/base/empty/props.d.ts +26 -0
  70. package/components/base/image/api.ts +25 -0
  71. package/components/base/image/index.ts +18 -0
  72. package/components/base/image/props.d.ts +67 -0
  73. package/components/base/image/useImage.ts +119 -0
  74. package/components/base/input/api.ts +19 -0
  75. package/components/base/input/index.ts +16 -0
  76. package/components/base/input/input.css +19 -0
  77. package/components/base/input/props.d.ts +60 -0
  78. package/components/base/input/useInput.ts +53 -0
  79. package/components/base/inputNumber/api.ts +21 -0
  80. package/components/base/inputNumber/index.ts +15 -0
  81. package/components/base/inputNumber/props.d.ts +64 -0
  82. package/components/base/inputNumber/useInputNumber.ts +140 -0
  83. package/components/base/li/api.ts +15 -0
  84. package/components/base/li/index.ts +13 -0
  85. package/components/base/li/props.d.ts +30 -0
  86. package/components/base/list/api.ts +16 -0
  87. package/components/base/list/index.ts +15 -0
  88. package/components/base/list/props.d.ts +33 -0
  89. package/components/base/list/useList.ts +36 -0
  90. package/components/base/loading/api.ts +17 -0
  91. package/components/base/loading/index.ts +15 -0
  92. package/components/base/loading/props.d.ts +38 -0
  93. package/components/base/popover/api.ts +28 -0
  94. package/components/base/popover/index.ts +15 -0
  95. package/components/base/popover/props.d.ts +73 -0
  96. package/components/base/popover/usePopover.ts +188 -0
  97. package/components/base/progress/api.ts +18 -0
  98. package/components/base/progress/index.ts +15 -0
  99. package/components/base/progress/props.d.ts +53 -0
  100. package/components/base/progress/useProgress.ts +28 -0
  101. package/components/base/radio/api.ts +19 -0
  102. package/components/base/radio/index.ts +19 -0
  103. package/components/base/radio/props.d.ts +59 -0
  104. package/components/base/radio/useRadio.ts +11 -0
  105. package/components/base/rate/api.ts +18 -0
  106. package/components/base/rate/index.ts +15 -0
  107. package/components/base/rate/props.d.ts +49 -0
  108. package/components/base/rate/useRate.ts +75 -0
  109. package/components/base/result/api.ts +20 -0
  110. package/components/base/result/index.ts +15 -0
  111. package/components/base/result/props.d.ts +36 -0
  112. package/components/base/select/api.ts +31 -0
  113. package/components/base/select/index.ts +18 -0
  114. package/components/base/select/props.d.ts +132 -0
  115. package/components/base/select/select.css +7 -0
  116. package/components/base/select/useSelect.ts +280 -0
  117. package/components/base/select/useSelectTools.ts +60 -0
  118. package/components/base/skeleton/api.ts +18 -0
  119. package/components/base/skeleton/index.ts +15 -0
  120. package/components/base/skeleton/props.d.ts +41 -0
  121. package/components/base/slider/api.ts +20 -0
  122. package/components/base/slider/index.ts +15 -0
  123. package/components/base/slider/props.d.ts +65 -0
  124. package/components/base/slider/useSlider.ts +83 -0
  125. package/components/base/space/api.ts +17 -0
  126. package/components/base/space/index.ts +15 -0
  127. package/components/base/space/props.d.ts +39 -0
  128. package/components/base/steps/api.ts +30 -0
  129. package/components/base/steps/index.ts +22 -0
  130. package/components/base/steps/props.d.ts +88 -0
  131. package/components/base/steps/useSteps.ts +101 -0
  132. package/components/base/switch/api.ts +22 -0
  133. package/components/base/switch/index.ts +17 -0
  134. package/components/base/switch/props.d.ts +66 -0
  135. package/components/base/switch/useSwitch.tsx +79 -0
  136. package/components/base/tabs/api.ts +23 -0
  137. package/components/base/tabs/index.ts +18 -0
  138. package/components/base/tabs/props.d.ts +41 -0
  139. package/components/base/tabs/useTabs.ts +66 -0
  140. package/components/base/tag/api.ts +17 -0
  141. package/components/base/tag/index.ts +13 -0
  142. package/components/base/tag/props.d.ts +49 -0
  143. package/components/base/timePicker/api.ts +21 -0
  144. package/components/base/timePicker/index.ts +18 -0
  145. package/components/base/timePicker/props.d.ts +66 -0
  146. package/components/base/timePicker/useTimePicker.ts +161 -0
  147. package/components/base/timeline/api.ts +24 -0
  148. package/components/base/timeline/index.ts +16 -0
  149. package/components/base/timeline/props.d.ts +60 -0
  150. package/components/base/tooltip/api.ts +19 -0
  151. package/components/base/tooltip/index.ts +17 -0
  152. package/components/base/tooltip/props.d.ts +34 -0
  153. package/components/base/tooltip/useTooltip.ts +89 -0
  154. package/components/base/transfer/api.ts +18 -0
  155. package/components/base/transfer/index.ts +17 -0
  156. package/components/base/transfer/props.d.ts +63 -0
  157. package/components/base/transfer/useTransfer.ts +207 -0
  158. package/components/base/tree/api.ts +47 -0
  159. package/components/base/tree/index.ts +29 -0
  160. package/components/base/tree/props.d.ts +108 -0
  161. package/components/base/tree/tree.ts +263 -0
  162. package/components/base/tree/useTree.ts +114 -0
  163. package/components/message/confirm/api.ts +21 -0
  164. package/components/message/confirm/index.ts +15 -0
  165. package/components/message/confirm/props.d.ts +69 -0
  166. package/components/message/dialog/api.ts +19 -0
  167. package/components/message/dialog/index.ts +15 -0
  168. package/components/message/dialog/props.d.ts +55 -0
  169. package/components/message/drawer/api.ts +32 -0
  170. package/components/message/drawer/index.ts +15 -0
  171. package/components/message/drawer/props.d.ts +73 -0
  172. package/components/message/message/api.ts +27 -0
  173. package/components/message/message/index.ts +20 -0
  174. package/components/message/message/props.d.ts +54 -0
  175. package/components/message/message/useMessage.ts +61 -0
  176. package/components/message/notification/api.ts +23 -0
  177. package/components/message/notification/index.ts +19 -0
  178. package/components/message/notification/props.d.ts +64 -0
  179. package/components/message/notification/useNotification.ts +79 -0
  180. package/components/message/popover/MPopover.tsx +94 -0
  181. package/components/message/popover/api.ts +54 -0
  182. package/components/message/popover/index.ts +17 -0
  183. package/components/message/popover/popover.css +21 -0
  184. package/components/message/popover/props.d.ts +76 -0
  185. package/components/message/popover/usePopover.ts +230 -0
  186. package/components/other/darkMode/api.ts +17 -0
  187. package/components/other/darkMode/index.ts +17 -0
  188. package/components/other/darkMode/props.d.ts +37 -0
  189. package/components/other/darkMode/useDarkMode.ts +129 -0
  190. package/components/template/border/api.ts +18 -0
  191. package/components/template/border/index.ts +15 -0
  192. package/components/template/border/props.d.ts +41 -0
  193. package/components/template/breadcrumb/api.ts +15 -0
  194. package/components/template/breadcrumb/index.ts +15 -0
  195. package/components/template/breadcrumb/props.d.ts +45 -0
  196. package/components/template/descriptions/api.ts +23 -0
  197. package/components/template/descriptions/index.ts +16 -0
  198. package/components/template/descriptions/props.d.ts +54 -0
  199. package/components/template/form/api.ts +23 -0
  200. package/components/template/form/index.ts +20 -0
  201. package/components/template/form/props.d.ts +60 -0
  202. package/components/template/grid/api.ts +20 -0
  203. package/components/template/grid/index.ts +15 -0
  204. package/components/template/grid/props.d.ts +48 -0
  205. package/components/template/menu/api.ts +26 -0
  206. package/components/template/menu/index.ts +19 -0
  207. package/components/template/menu/props.d.ts +93 -0
  208. package/components/template/menu/useMenu.ts +155 -0
  209. package/components/template/pagination/api.ts +22 -0
  210. package/components/template/pagination/index.ts +18 -0
  211. package/components/template/pagination/props.d.ts +65 -0
  212. package/components/template/pagination/usePagination.ts +186 -0
  213. package/components/template/table/api.ts +18 -0
  214. package/components/template/table/index.ts +18 -0
  215. package/components/template/table/props.d.ts +36 -0
  216. package/components/template/table/useTable.ts +138 -0
  217. package/components/template/tableColumn/api.ts +17 -0
  218. package/components/template/tableColumn/index.ts +15 -0
  219. package/components/template/tableColumn/props.d.ts +32 -0
  220. package/components/template/virtualList/api.ts +16 -0
  221. package/components/template/virtualList/index.ts +18 -0
  222. package/components/template/virtualList/props.d.ts +25 -0
  223. package/components/template/virtualList/useVirtualList.ts +237 -0
  224. package/components/types/hook.d.ts +13 -0
  225. package/components/types/props.d.ts +52 -0
  226. package/components/types/template.d.ts +59 -0
  227. package/compositions/common/defineCore.ts +55 -0
  228. package/compositions/common/useDebounceFn.ts +27 -0
  229. package/compositions/common/useDrag.ts +65 -0
  230. package/compositions/common/useElementSize.ts +37 -0
  231. package/compositions/common/useEventListener.ts +48 -0
  232. package/compositions/common/usePopover.ts +45 -0
  233. package/compositions/common/useResizeObserver.ts +43 -0
  234. package/compositions/common/useTeleport.ts +24 -0
  235. package/compositions/input/useBooleanInput.ts +52 -0
  236. package/compositions/modal/useModal.ts +72 -0
  237. package/compositions/popper/useClickAway.ts +41 -0
  238. package/compositions/popper/usePopper.ts +63 -0
  239. package/compositions/utils/filters.ts +135 -0
  240. package/compositions/virtualList/enums.ts +52 -0
  241. package/compositions/virtualList/useContainerObserver.ts +82 -0
  242. package/compositions/virtualList/useEntries.ts +248 -0
  243. package/compositions/virtualList/useHeightCache.ts +83 -0
  244. package/compositions/virtualList/useSentinelObserver.ts +81 -0
  245. package/dist/components/base/affix/useAffix.d.ts +5 -4
  246. package/dist/components/base/anchor/useAnchor.d.ts +1 -1
  247. package/dist/components/base/autoComplete/useAutoComplete.d.ts +12 -4
  248. package/dist/components/base/backTop/useBackTop.d.ts +1 -1
  249. package/dist/components/base/button/index.d.ts +11 -2
  250. package/dist/components/base/button/useButton.d.ts +11 -2
  251. package/dist/components/base/carousel/useCarousel.d.ts +6 -3
  252. package/dist/components/base/cascader/useCascader.d.ts +23 -11
  253. package/dist/components/base/checkbox/useCheckbox.d.ts +4 -3
  254. package/dist/components/base/collapse/useCollapse.d.ts +2 -2
  255. package/dist/components/base/datePicker/useDatePicker.d.ts +140 -8
  256. package/dist/components/base/dropdown/useDropdown.d.ts +11 -5
  257. package/dist/components/base/image/useImage.d.ts +5 -5
  258. package/dist/components/base/input/useInput.d.ts +1 -2
  259. package/dist/components/base/inputNumber/useInputNumber.d.ts +2 -2
  260. package/dist/components/base/popover/usePopover.d.ts +8 -8
  261. package/dist/components/base/progress/useProgress.d.ts +1 -1
  262. package/dist/components/base/rate/useRate.d.ts +1 -1
  263. package/dist/components/base/select/useSelect.d.ts +8 -8
  264. package/dist/components/base/slider/useSlider.d.ts +3 -3
  265. package/dist/components/base/steps/index.d.ts +1 -1
  266. package/dist/components/base/steps/useSteps.d.ts +5 -5
  267. package/dist/components/base/switch/useSwitch.d.ts +8 -3
  268. package/dist/components/base/tabs/useTabs.d.ts +2 -2
  269. package/dist/components/base/timePicker/useTimePicker.d.ts +14 -6
  270. package/dist/components/base/tooltip/useTooltip.d.ts +14 -4
  271. package/dist/components/base/transfer/useTransfer.d.ts +15 -15
  272. package/dist/components/base/tree/index.d.ts +1 -1
  273. package/dist/components/base/tree/useTree.d.ts +2 -1
  274. package/dist/components/message/message/useMessage.d.ts +11 -1
  275. package/dist/components/message/notification/useNotification.d.ts +17 -1
  276. package/dist/components/message/popover/MPopover.d.ts +6 -1
  277. package/dist/components/message/popover/usePopover.d.ts +6 -6
  278. package/dist/components/other/darkMode/useDarkMode.d.ts +1 -1
  279. package/dist/components/template/menu/useMenu.d.ts +1 -0
  280. package/dist/components/template/virtualList/useVirtualList.d.ts +10 -7
  281. package/dist/compositions/common/useDrag.d.ts +1 -1
  282. package/dist/compositions/common/useElementSize.d.ts +2 -2
  283. package/dist/compositions/common/useTeleport.d.ts +4 -2
  284. package/dist/compositions/modal/useModal.d.ts +1 -1
  285. package/dist/core.js +6137 -4186
  286. package/dist/runtime/defineHook.d.ts +1 -1
  287. package/index.css +1 -0
  288. package/index.ts +71 -0
  289. package/package.json +19 -16
  290. package/runtime/defineHook.ts +21 -0
  291. package/tools/empty.ts +81 -0
  292. package/tools/index.ts +15 -0
  293. package/tools/types.ts +11 -0
  294. package/tsconfig.json +8 -0
  295. package/types/common/common.d.ts +25 -0
  296. package/types/common/model.d.ts +25 -0
  297. package/types/index.d.ts +11 -0
  298. package/types/props.d.ts +13 -0
  299. package/vite.config.build.ts +41 -0
  300. package/dist/vite.config.build.d.ts +0 -2
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @description core table hook
3
+ * @author 阿怪
4
+ * @date 2026/2/25
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+
11
+ export function useTable() {
12
+
13
+ // 使用 console.warn 替代 Printer 依赖
14
+ const error = (msg: string) => console.warn(`[水墨UI表格组件] ${msg}`);
15
+
16
+ type SlotRender = any | undefined;
17
+ type StyleType = { width?: string } | undefined;
18
+
19
+ const initTable = <T>(renders: {
20
+ /** 数据为空时渲染的内容 */
21
+ empty: T,
22
+ /** 渲染单个 td 单元格 */
23
+ tbodyTr: (option: {
24
+ data: any | string,
25
+ param: string,
26
+ slot?: SlotRender,
27
+ style?: StyleType,
28
+ slotInfo?: { data: any, index: number }
29
+ }) => T,
30
+ /** 渲染单个 th 表头单元格 */
31
+ theadTh: (option: {
32
+ param?: string,
33
+ label?: string,
34
+ slot?: SlotRender,
35
+ style?: StyleType
36
+ }) => T,
37
+ /** 渲染整个 thead */
38
+ thead: (ths: T[]) => T,
39
+ /** 渲染整个 tbody */
40
+ tbody: (trs: T[]) => T,
41
+ /** 渲染单行 tr */
42
+ tbodyTrs: (tds: T[], i: number) => T,
43
+ /** 从 tableColumn VNode 中提取 body/head 插槽 */
44
+ initSlot: (tableColumn: any) => { body: SlotRender, head: SlotRender } | undefined,
45
+ }, columns: Array<any>, data: any[]) => {
46
+ // 每行对应的 td 列表,按行索引分组
47
+ const tbodyTrList: T[][] = [];
48
+ data.forEach(() => { tbodyTrList.push([]); });
49
+
50
+ /** 从 data[i] 中安全取值 */
51
+ const getData = (i: number, param: string) => {
52
+ if (data[i] && data[i][param]) {
53
+ return data[i][param];
54
+ }
55
+ return '';
56
+ };
57
+
58
+ /** 向每行的 td 列表中追加一列 */
59
+ const pushTd = (param: string | undefined, bodySlot: SlotRender, style: StyleType) => {
60
+ if (!param && !bodySlot) {
61
+ error('param is undefined, column without param or slot will be ignored!');
62
+ return;
63
+ }
64
+ tbodyTrList.forEach((t, i) => {
65
+ t.push(renders.tbodyTr({
66
+ data: param ? getData(i, param) : '',
67
+ style,
68
+ param: param ?? '',
69
+ slot: bodySlot,
70
+ slotInfo: {
71
+ data: data[i],
72
+ index: i,
73
+ },
74
+ }));
75
+ });
76
+ };
77
+
78
+ /** 将列宽 prop 转换为 style 对象 */
79
+ const getStyle = (options?: { width?: string | number }): StyleType => {
80
+ if (!options || !options.width) {
81
+ return {};
82
+ }
83
+ const numberWidth = Number(options.width);
84
+ if (!isNaN(numberWidth)) {
85
+ return { width: numberWidth + 'px' };
86
+ }
87
+ return { width: options.width as string };
88
+ };
89
+
90
+ /**
91
+ * 初始化表头,同时将每列对应的 td 推入 tbodyTrList
92
+ */
93
+ const initTHead = () => {
94
+ const ths = (columns ?? []).filter(column => {
95
+ if (!column.props) {
96
+ error('column.props is undefined, column without param will be ignored!');
97
+ return false;
98
+ }
99
+ return true;
100
+ }).map(column => {
101
+ const slots = renders.initSlot(column);
102
+ let bodySlot: SlotRender | undefined;
103
+ let headSlot: SlotRender | undefined;
104
+ const style = getStyle(column.props);
105
+ if (slots) {
106
+ bodySlot = slots.body;
107
+ headSlot = slots.head;
108
+ }
109
+ pushTd(column.props.param, bodySlot, style);
110
+
111
+ return renders.theadTh({
112
+ label: column.props.label,
113
+ param: column.props.param,
114
+ slot: headSlot,
115
+ style,
116
+ });
117
+ });
118
+ return renders.thead(ths);
119
+ };
120
+
121
+ const thead = initTHead();
122
+ const tbody = tbodyTrList.length > 0
123
+ ? renders.tbody(tbodyTrList.map((tds, i) => renders.tbodyTrs(tds, i)))
124
+ : renders.empty;
125
+
126
+ return {
127
+ thead,
128
+ tbody,
129
+ };
130
+ };
131
+
132
+
133
+ return {
134
+ initTable,
135
+ error,
136
+ };
137
+
138
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @description table column api
3
+ * @author 阿怪
4
+ * @date 2026/2/25
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { MCOPO } from '../../types/props';
10
+ import { TableColumnProps } from './props';
11
+
12
+
13
+ export const props: MCOPO<TableColumnProps> = {
14
+ width: { type: String, default: '' },
15
+ param: { type: String, default: '' },
16
+ label: { type: String, default: '' },
17
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @description tableColumn core 导출
3
+ * @author 阿怪
4
+ * @date 2026/2/25
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { props } from './api';
10
+
11
+ export const TableColumnCore = {
12
+ props,
13
+ };
14
+
15
+ export type { TableColumnProps } from './props';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @description tableColumn 组件类型定义
3
+ * @author 阿怪
4
+ * @date 2026/2/25
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ export declare type TableColumnProps = {
11
+ /**
12
+ * @description table column width
13
+ * 列宽
14
+ * @type string
15
+ * @default ''
16
+ */
17
+ width?: string;
18
+ /**
19
+ * @description table column param
20
+ * 列对应的参数字段名
21
+ * @type string
22
+ * @default ''
23
+ */
24
+ param?: string;
25
+ /**
26
+ * @description table column label
27
+ * 列标题
28
+ * @type string
29
+ * @default ''
30
+ */
31
+ label?: string;
32
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @description 虚拟列表运行时 props
3
+ * @author 阿怪
4
+ * @date 2026/2/25 13:30
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { MCOPO } from '../../types/props';
10
+ import { VirtualListProps } from './props';
11
+
12
+ export const props: MCOPO<VirtualListProps> = {
13
+ list: { type: Array, default: () => [] },
14
+ estimatedHeight: { type: Number, default: 40 },
15
+ overScan: { type: Number, default: 1 },
16
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @description 虚拟列表 core 导出
3
+ * @author 阿怪
4
+ * @date 2026/2/25 14:00
5
+ * @version v2.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { props } from './api';
10
+ import { useVirtualList } from './useVirtualList';
11
+
12
+ export const VirtualListCore = {
13
+ props,
14
+ useVirtualList,
15
+ };
16
+
17
+ export type { VirtualListProps } from './props';
18
+ export { useVirtualList } from './useVirtualList';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @description 虚拟列表 props 类型定义
3
+ * @author 阿怪
4
+ * @date 2026/2/25 13:30
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ export declare type VirtualListProps<T = any> = {
11
+ /**
12
+ * @description 列表数据
13
+ */
14
+ list?: Array<T>;
15
+ /**
16
+ * @description 预估行高,用于初始计算可见数量,避免依赖首次全量渲染
17
+ * @default 40
18
+ */
19
+ estimatedHeight?: number;
20
+ /**
21
+ * @description 过扫描系数,渲染可视区域外的额外行数倍数
22
+ * @default 1
23
+ */
24
+ overScan?: number;
25
+ };
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @description 虚拟列表核心 hook(sentinel 方案)
3
+ * @author 阿怪
4
+ * @date 2026/2/25 14:00
5
+ * @version v3.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ *
9
+ * 基于 sentinel 哨兵元素 + scrollTop 绝对定位
10
+ * 不依赖状态机连续性,快速滚动也能正确定位
11
+ */
12
+ import { computed, nextTick, onMounted, ref, watch, toRef, onBeforeUnmount } from 'vue';
13
+ import { VirtualListProps } from './props';
14
+ import { createHeightCache } from '../../../compositions/virtualList/useHeightCache';
15
+ import { useSentinelObserver } from '../../../compositions/virtualList/useSentinelObserver';
16
+
17
+ export interface UseVirtualListOptions<T> {
18
+ props: VirtualListProps<T>;
19
+ /** 触底事件 */
20
+ onReachBottom?: () => void;
21
+ }
22
+
23
+ export function useVirtualList<T>(options: UseVirtualListOptions<T>) {
24
+ const { props, onReachBottom } = options;
25
+
26
+ const containerRef = ref<HTMLElement | null>(null);
27
+ const wrapperRef = ref<HTMLElement | null>(null);
28
+ const topSentinelRef = ref<HTMLElement | null>(null);
29
+ const bottomSentinelRef = ref<HTMLElement | null>(null);
30
+
31
+ const listRef = toRef(() => props.list ?? []);
32
+ const estimatedHeight = props.estimatedHeight ?? 40;
33
+ const overScan = props.overScan ?? 1;
34
+
35
+ // 高度缓存
36
+ const heightCache = createHeightCache(listRef.value.length, estimatedHeight);
37
+
38
+ // 当前渲染范围
39
+ const renderFrom = ref(0);
40
+ const renderEnd = ref(0);
41
+
42
+ // spacer 总高度
43
+ const totalHeight = ref(0);
44
+ // wrapper 偏移
45
+ const offsetY = ref(0);
46
+
47
+ /** 根据 scrollTop 计算渲染范围 */
48
+ const calcRange = () => {
49
+ const container = containerRef.value;
50
+ if (!container) return;
51
+
52
+ const list = listRef.value;
53
+ const total = list.length;
54
+ if (total === 0) {
55
+ renderFrom.value = 0;
56
+ renderEnd.value = 0;
57
+ totalHeight.value = 0;
58
+ offsetY.value = 0;
59
+ return;
60
+ }
61
+
62
+ const scrollTop = container.scrollTop;
63
+ const containerHeight = container.clientHeight;
64
+
65
+ // 用 scrollTop 直接定位起始索引
66
+ const startIndex = heightCache.findIndex(scrollTop);
67
+ // 计算可见数量
68
+ let visibleEnd = startIndex;
69
+ let accHeight = 0;
70
+ while (visibleEnd < total && accHeight < containerHeight) {
71
+ accHeight += heightCache.getHeight(visibleEnd);
72
+ visibleEnd++;
73
+ }
74
+
75
+ const visibleCount = visibleEnd - startIndex;
76
+ const overScanCount = Math.max(1, Math.floor(visibleCount * overScan));
77
+
78
+ // 加上 overScan 缓冲区
79
+ const from = Math.max(0, startIndex - overScanCount);
80
+ const end = Math.min(total, visibleEnd + overScanCount);
81
+
82
+ renderFrom.value = from;
83
+ renderEnd.value = end;
84
+ totalHeight.value = heightCache.getTotalHeight();
85
+ offsetY.value = heightCache.getOffset(from);
86
+ };
87
+
88
+ /** 当前要渲染的数据切片 */
89
+ const displayList = computed(() => {
90
+ const list = listRef.value;
91
+ return list.slice(renderFrom.value, renderEnd.value)
92
+ .map((d, i) => ({ data: d as T, index: i + renderFrom.value }));
93
+ });
94
+
95
+ /**
96
+ * 测量已渲染项的实际高度并更新缓存
97
+ * 同时进行 scrollTop 补偿:当上方元素的真实高度和预估不同时,
98
+ * 修正 scrollTop 使视觉位置不跳动
99
+ */
100
+ const measureItems = () => {
101
+ const wrapper = wrapperRef.value;
102
+ const container = containerRef.value;
103
+ if (!wrapper || !container) return;
104
+
105
+ const scrollTop = container.scrollTop;
106
+ // 记录当前视口第一个可见项的旧偏移
107
+ const firstVisibleIndex = heightCache.findIndex(scrollTop);
108
+ const oldOffset = heightCache.getOffset(firstVisibleIndex);
109
+
110
+ const children = wrapper.children;
111
+ let hasChange = false;
112
+ // 跳过 top sentinel(第一个子元素)
113
+ for (let i = 1; i < children.length - 1; i++) {
114
+ const child = children[i];
115
+ const index = renderFrom.value + (i - 1); // -1 因为跳过 top sentinel
116
+ const height = child.getBoundingClientRect().height;
117
+ if (height > 0) {
118
+ const oldHeight = heightCache.getHeight(index);
119
+ if (Math.abs(oldHeight - height) > 0.5) {
120
+ heightCache.update(index, height);
121
+ hasChange = true;
122
+ }
123
+ }
124
+ }
125
+
126
+ if (hasChange) {
127
+ // 计算补偿量:上方高度变化导致的偏移差
128
+ const newOffset = heightCache.getOffset(firstVisibleIndex);
129
+ const delta = newOffset - oldOffset;
130
+ if (Math.abs(delta) > 0.5) {
131
+ container.scrollTop = scrollTop + delta;
132
+ }
133
+ }
134
+
135
+ // 更新总高度
136
+ totalHeight.value = heightCache.getTotalHeight();
137
+ };
138
+
139
+ const update = () => {
140
+ measureItems();
141
+ calcRange();
142
+ // 范围变化后,下一帧再测量一次(新渲染的元素)
143
+ nextTick(() => {
144
+ measureItems();
145
+ });
146
+ };
147
+
148
+ // sentinel observer
149
+ const { reobserve } = useSentinelObserver({
150
+ containerRef,
151
+ topSentinelRef,
152
+ bottomSentinelRef,
153
+ onUpdate: update,
154
+ onReachBottom,
155
+ });
156
+
157
+ // scroll 事件驱动:用 rAF 节流,保证滚动过程中持续更新渲染范围
158
+ let rafId: number | null = null;
159
+ let scrollHandler: (() => void) | undefined;
160
+ let scrollendHandler: (() => void) | undefined;
161
+
162
+ const setupScrollListener = () => {
163
+ const container = containerRef.value;
164
+ if (!container) return;
165
+
166
+ // 连续滚动时用 rAF 节流更新
167
+ scrollHandler = () => {
168
+ if (rafId !== null) return;
169
+ rafId = requestAnimationFrame(() => {
170
+ rafId = null;
171
+ calcRange();
172
+ });
173
+ };
174
+ container.addEventListener('scroll', scrollHandler, { passive: true });
175
+
176
+ // scrollend 做最终修正(测量 + 补偿)
177
+ scrollendHandler = () => {
178
+ measureItems();
179
+ calcRange();
180
+ };
181
+ container.addEventListener('scrollend', scrollendHandler);
182
+ };
183
+
184
+ const cleanupScrollListener = () => {
185
+ const container = containerRef.value;
186
+ if (container) {
187
+ if (scrollHandler) container.removeEventListener('scroll', scrollHandler);
188
+ if (scrollendHandler) container.removeEventListener('scrollend', scrollendHandler);
189
+ }
190
+ if (rafId !== null) {
191
+ cancelAnimationFrame(rafId);
192
+ rafId = null;
193
+ }
194
+ };
195
+
196
+ // 初始化
197
+ onMounted(() => {
198
+ const list = listRef.value;
199
+ heightCache.reset(list.length);
200
+
201
+ // 初始渲染
202
+ calcRange();
203
+
204
+ nextTick(() => {
205
+ measureItems();
206
+ calcRange();
207
+ reobserve();
208
+ setupScrollListener();
209
+ });
210
+ });
211
+
212
+ // 响应 list 变化
213
+ watch(listRef, (newList) => {
214
+ heightCache.reset(newList.length);
215
+ if (containerRef.value) {
216
+ containerRef.value.scrollTop = 0;
217
+ }
218
+ calcRange();
219
+ nextTick(() => {
220
+ measureItems();
221
+ calcRange();
222
+ reobserve();
223
+ });
224
+ });
225
+
226
+ onBeforeUnmount(cleanupScrollListener);
227
+
228
+ return {
229
+ displayList,
230
+ containerRef,
231
+ wrapperRef,
232
+ topSentinelRef,
233
+ bottomSentinelRef,
234
+ totalHeight,
235
+ offsetY,
236
+ };
237
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @description
3
+ * @author 阿怪
4
+ * @date 2024/12/16 10:15
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { SlotsType } from '@vue/runtime-core';
10
+
11
+ export type UseHookResult<Props, S extends SlotsType, Return> = Return;
12
+
13
+
@@ -0,0 +1,52 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * @description M Component Object Props Options
4
+ * @author 阿怪
5
+ * @date 2022/12/13 00:01
6
+ * @version v1.0.0
7
+ *
8
+ * 江湖的业务千篇一律,复杂的代码好几百行。
9
+ * M means Shuimo
10
+ */
11
+ export declare type MPropOptions<T = any, D = T> = {
12
+ type?: MPropType<T> | true | null,
13
+ required: true,
14
+ default?: D | DefaultFactory<D> | null | undefined | object,
15
+ enum?: T[],
16
+ }
17
+
18
+ export declare type MPropOptionsWithDefault<T = any, D = T> = {
19
+ type?: MPropType<T> | true | null,
20
+ required?: false,
21
+ default: D | DefaultFactory<D> | null | undefined | object,
22
+ enum?: T[],
23
+ }
24
+
25
+ /**
26
+ * @description MComponentObjectPropsOptions
27
+ */
28
+ export type MCOPO<P, OK = Required<Pick<P, OptionalKeys<P>>>> = {
29
+ [K in keyof OK]: MPropOptionsWithDefault<OK[K]>
30
+ } & {
31
+ [K in keyof Pick<P, RequiredKeys<P>>]-?: MPropOptions<P[K]>
32
+ }
33
+
34
+
35
+ type DefaultFactory<T> = (props: Data) => T;
36
+
37
+ type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
38
+
39
+ type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;
40
+
41
+ type MPropConstructor<T = any> =
42
+ | { new(...args: any[]): T & {} }
43
+ | { (): T }
44
+ | MPropMethod<T>
45
+
46
+ type MPropMethod<T, TConstructor = any> = [T] extends [
47
+ ((...args: any) => any) | undefined
48
+ ] // if is function with args, allowing non-required functions
49
+ ? { new(): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
50
+ : never
51
+
52
+ export type MPropType<T> = MPropConstructor<T> | MPropConstructor<T>[];
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @description
3
+ * @author 阿怪
4
+ * @date 2022/12/12 14:52
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { RenderFunction, SetupContext } from 'vue';
10
+ import { MCOPO } from './props';
11
+
12
+ type WithArray<T> = T | T[]; // todo fix this
13
+
14
+ export type HTMLElementEvent<T extends HTMLElement> = Event & {
15
+ target: T;
16
+ };
17
+
18
+ type MVNodeRenderParams = {
19
+ if?: boolean,
20
+ show?: boolean,
21
+ };
22
+
23
+ interface ElementEventListener {
24
+ (event: HTMLElementEvent<any>): void;
25
+ }
26
+
27
+ export type MNodeProps = Record<string, WithArray<string | number | boolean> | ElementEventListener | Record<string, any>>;
28
+
29
+ export declare type MNodeSlot = MVNodeRenderParams & {
30
+ props?: MNodeProps,
31
+ };
32
+
33
+ export declare type MNodeTemplate = {
34
+ type: string,
35
+ props?: MNodeProps,
36
+ children?: Record<string, MNodeTemplate>,
37
+ slots?: Map<string, MNodeSlot>,
38
+ innerText?: string[],
39
+ initProps?: (templateProps: MCOPO<any>, props: MNodeProps, filter?: string[]) => void,
40
+ } & MVNodeRenderParams;
41
+
42
+
43
+ export declare type MVNode = {
44
+ name: string,
45
+ template?: MNodeTemplate,
46
+ dom?: HTMLElement,
47
+ props?: MNodeTemplate['props'],
48
+ children?: Record<string, MVNode>,
49
+ slots?: MNodeTemplate['slots']
50
+ };
51
+
52
+ export declare type CustomElementParams = {
53
+ name: string,
54
+ style?: string,
55
+ template?: MNodeTemplate,
56
+ props?: MCOPO<any>
57
+ };
58
+
59
+ export declare type WCSetup<T = any> = (slot?: HTMLSlotElement) => (props: Readonly<T>, ctx: SetupContext) => RenderFunction;
@@ -0,0 +1,55 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * @description A tool for normalizing grammatical structures
4
+ * @author 阿怪
5
+ * @date 2023/6/16 11:25
6
+ * @version v1.0.0
7
+ *
8
+ * 江湖的业务千篇一律,复杂的代码好几百行。
9
+ */
10
+
11
+ import { Ref } from 'vue';
12
+
13
+ type OptionsEmpty = undefined;
14
+
15
+
16
+ type IsAllUndefined<T extends any[]> = T extends [infer H, ...infer R]
17
+ ? H extends undefined
18
+ ? IsAllUndefined<R>
19
+ : false
20
+ : true;
21
+
22
+ interface OptionsNotEmpty<P, V, E> {
23
+ props: Required<P>;
24
+ value: { [K in keyof V]: Ref<V[K]>; };
25
+ event: E;
26
+ }
27
+
28
+ type CoreArguments = keyof OptionsNotEmpty<any, any, any>;
29
+ type AnyArguments = {
30
+ [K in CoreArguments]?: any
31
+ }
32
+
33
+ type DefinedKeys<T extends AnyArguments> = { [K in keyof T]: T[K] extends undefined ? never : K }[keyof T];
34
+ type UndefinedKeys<T extends AnyArguments> = Exclude<keyof CoreArguments, DefinedKeys<T>>;
35
+
36
+ export type OptionsKeys<
37
+ A extends AnyArguments,
38
+ P, V, E,
39
+ DK extends string | number | symbol = DefinedKeys<A>,
40
+ UK extends string | number | symbol = UndefinedKeys<A>
41
+ > = {
42
+ [K in DK & keyof OptionsNotEmpty<P, V, E>]: OptionsNotEmpty<P, V, E>[K];
43
+ } & {
44
+ [K in UK & keyof OptionsNotEmpty<P, V, E>]-?: OptionsNotEmpty<P, V, E>[K];
45
+ }
46
+
47
+ export type Options<
48
+ K extends AnyArguments,
49
+ P = K['props'],
50
+ V = K['value'],
51
+ E = K['event'],
52
+ > =
53
+ IsAllUndefined<[P, V, E]> extends true
54
+ ? OptionsEmpty
55
+ : OptionsKeys<K, P, V, E>;