@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,280 @@
1
+ /**
2
+ * @description select 核心 composable
3
+ * @author 阿怪
4
+ * @date 2026/2/25 14:40
5
+ * @version v1.0.1
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ *
9
+ * 用 computed + ref 替代原 class 继承方案
10
+ * 单选/多选通过 props.multiple 分支处理
11
+ * fetch 通过 IntersectionObserver 观察最后一个选项实现
12
+ * v1.0.1 新增 filterable 支持:开启后 inputReadonly 为 false,可输入过滤 阿怪 2026/2/27
13
+ */
14
+ import { computed, ref, watch, toRef, onBeforeUnmount } from 'vue';
15
+ import { SelectProps } from './props';
16
+ import { useSelectTools } from './useSelectTools';
17
+
18
+ export interface SelectOptionItem {
19
+ /** 原始值 */
20
+ value: any;
21
+ /** 在 options 数组中的索引 */
22
+ index: number;
23
+ /** 是否选中 */
24
+ isSelected: boolean;
25
+ }
26
+
27
+ export function useSelect(props: SelectProps, ctx: any) {
28
+ const tools = useSelectTools(props);
29
+ const optionsRef = toRef(() => props.options ?? []);
30
+ const modelValueRef = toRef(() => props.modelValue);
31
+ const multipleRef = toRef(() => props.multiple ?? false);
32
+
33
+ /**
34
+ * 输入框是否只读:
35
+ * - filterable=true 时强制可编辑(忽略 readonly prop)
36
+ * - 否则沿用 props.readonly(默认 true)
37
+ */
38
+ const inputReadonly = toRef(() => props.filterable ? false : (props.readonly ?? true));
39
+
40
+ // --- 状态 ---
41
+ const inputValue = ref('');
42
+ const isOpen = ref(false);
43
+ const fetchLoading = ref(false);
44
+
45
+ // --- 选中状态 ---
46
+ /** 多选时记录选中的索引集合 */
47
+ const selectedIndices = ref<Set<number>>(new Set());
48
+
49
+ /** 判断某个 option 是否选中 */
50
+ const isSelected = (option: any, index: number): boolean => {
51
+ if (multipleRef.value) {
52
+ return selectedIndices.value.has(index);
53
+ }
54
+ if (props.toMatch) {
55
+ return props.toMatch(option, modelValueRef.value);
56
+ }
57
+ return modelValueRef.value === tools.getModelValue(option);
58
+ };
59
+
60
+ // --- 选项列表 ---
61
+ /** 全量选项(带 isSelected 标记) */
62
+ const allOptions = computed<SelectOptionItem[]>(() => {
63
+ return optionsRef.value.map((o, i) => ({
64
+ value: o,
65
+ index: i,
66
+ isSelected: isSelected(o, i),
67
+ }));
68
+ });
69
+
70
+ /** 过滤后的显示选项 */
71
+ const displayOptions = computed<SelectOptionItem[]>(() => {
72
+ return allOptions.value.filter(o => {
73
+ // 只读模式(非 filterable 且 readonly)直接不过滤
74
+ if (inputReadonly.value) return true;
75
+ // 没有输入值时不过滤
76
+ if (!inputValue.value) return true;
77
+ // 优先使用自定义过滤函数(filterable 或 readonly=false 场景均生效)
78
+ if (props.filter) {
79
+ return props.filter(o.value, inputValue.value);
80
+ }
81
+ // 默认:输入值包含在 optionParam 显示值中(大小写不敏感)
82
+ return tools.getOptionValue(o.value).toLowerCase().includes(inputValue.value.toLowerCase());
83
+ });
84
+ });
85
+
86
+ /** 多选已选标签 */
87
+ const selectedTags = computed<SelectOptionItem[]>(() => {
88
+ if (!multipleRef.value) return [];
89
+ return allOptions.value.filter(o => o.isSelected);
90
+ });
91
+
92
+ // --- 初始化 inputValue ---
93
+ const syncInputFromModelValue = () => {
94
+ if (multipleRef.value) {
95
+ inputValue.value = '';
96
+ return;
97
+ }
98
+ const mv = modelValueRef.value;
99
+ if (mv == null || mv === '') {
100
+ inputValue.value = '';
101
+ return;
102
+ }
103
+ const found = optionsRef.value.find(o => {
104
+ if (props.toMatch) return props.toMatch(o, mv);
105
+ return tools.getModelValue(o) === mv;
106
+ });
107
+ inputValue.value = found ? tools.getInputValue(found) : '';
108
+ };
109
+
110
+ /** 初始化多选选中状态 */
111
+ const syncSelectedFromModelValue = () => {
112
+ if (!multipleRef.value) return;
113
+ const mv = modelValueRef.value;
114
+ const newSet = new Set<number>();
115
+ if (Array.isArray(mv)) {
116
+ optionsRef.value.forEach((o, i) => {
117
+ const val = tools.getModelValue(o);
118
+ if (mv.includes(val)) {
119
+ newSet.add(i);
120
+ }
121
+ if (props.toMatch && mv.some((v: any) => props.toMatch!(o, v))) {
122
+ newSet.add(i);
123
+ }
124
+ });
125
+ }
126
+ selectedIndices.value = newSet;
127
+ };
128
+
129
+ // 初始同步
130
+ syncSelectedFromModelValue();
131
+ syncInputFromModelValue();
132
+
133
+ // --- 事件处理 ---
134
+
135
+ /** 点击选项 */
136
+ const onSelect = (index: number) => {
137
+ const option = optionsRef.value[index];
138
+ if (!option) return;
139
+ if (option.disabled) return;
140
+
141
+ if (multipleRef.value) {
142
+ // 多选:切换选中
143
+ const newSet = new Set(selectedIndices.value);
144
+ if (newSet.has(index)) {
145
+ newSet.delete(index);
146
+ } else {
147
+ newSet.add(index);
148
+ }
149
+ selectedIndices.value = newSet;
150
+ inputValue.value = '';
151
+
152
+ const newModelValue = optionsRef.value
153
+ .filter((_, i) => newSet.has(i))
154
+ .map(o => tools.getModelValue(o));
155
+ ctx.emit('update:modelValue', newModelValue);
156
+ ctx.emit('select', option);
157
+ } else {
158
+ // 单选:选中并关闭
159
+ inputValue.value = tools.getInputValue(option);
160
+ ctx.emit('update:modelValue', tools.getModelValue(option));
161
+ ctx.emit('select', option);
162
+ isOpen.value = false;
163
+ }
164
+ };
165
+
166
+ /** 删除多选标签 */
167
+ const onDeleteTag = (index: number) => {
168
+ const newSet = new Set(selectedIndices.value);
169
+ newSet.delete(index);
170
+ selectedIndices.value = newSet;
171
+
172
+ const newModelValue = optionsRef.value
173
+ .filter((_, i) => newSet.has(i))
174
+ .map(o => tools.getModelValue(o));
175
+ ctx.emit('update:modelValue', newModelValue);
176
+ };
177
+
178
+ /** 输入事件 */
179
+ const onInput = () => {
180
+ ctx.emit('input', inputValue.value);
181
+ };
182
+
183
+ /** 聚焦 */
184
+ const onFocus = (e: FocusEvent) => {
185
+ if (inputReadonly.value) return;
186
+ ctx.emit('focus', e, inputValue.value);
187
+ };
188
+
189
+ /** 失焦 */
190
+ const onBlur = (e: FocusEvent) => {
191
+ if (inputReadonly.value) return;
192
+ ctx.emit('blur', e, inputValue.value);
193
+
194
+ // 单选失焦时,如果输入值不匹配任何选项,清空
195
+ if (!multipleRef.value) {
196
+ const matched = displayOptions.value.find(o => o.isSelected);
197
+ if (matched && inputValue.value === tools.getInputValue(matched.value)) return;
198
+ if (!inputValue.value) {
199
+ ctx.emit('update:modelValue', undefined);
200
+ }
201
+ }
202
+ };
203
+
204
+ // --- Fetch(IntersectionObserver 观察最后一个选项) ---
205
+ const lastOptionRef = ref<HTMLElement | null>(null);
206
+ const optionsContainerRef = ref<HTMLElement | null>(null);
207
+ let fetchObserver: IntersectionObserver | undefined;
208
+
209
+ const cleanupFetchObserver = () => {
210
+ if (fetchObserver) {
211
+ fetchObserver.disconnect();
212
+ fetchObserver = undefined;
213
+ }
214
+ };
215
+
216
+ /** 更新 fetch observer:观察最后一个选项元素 */
217
+ const updateFetchObserver = () => {
218
+ cleanupFetchObserver();
219
+ if (!props.needFetch || !lastOptionRef.value || !optionsContainerRef.value) return;
220
+
221
+ fetchObserver = new IntersectionObserver(
222
+ async (entries) => {
223
+ const entry = entries[0];
224
+ if (entry?.isIntersecting && props.needFetch) {
225
+ fetchLoading.value = true;
226
+ await props.fetch?.();
227
+ fetchLoading.value = false;
228
+ }
229
+ },
230
+ { root: optionsContainerRef.value, threshold: 1 },
231
+ );
232
+ fetchObserver.observe(lastOptionRef.value);
233
+ };
234
+
235
+ // --- Watch ---
236
+
237
+ // modelValue 变化时同步输入框和选中状态
238
+ watch(modelValueRef, () => {
239
+ syncSelectedFromModelValue();
240
+ syncInputFromModelValue();
241
+ });
242
+
243
+ // options 变化时重新同步
244
+ watch(optionsRef, () => {
245
+ syncSelectedFromModelValue();
246
+ syncInputFromModelValue();
247
+ }, { deep: true });
248
+
249
+ // lastOptionRef 变化时更新 fetch observer
250
+ watch(lastOptionRef, () => {
251
+ updateFetchObserver();
252
+ });
253
+
254
+ onBeforeUnmount(cleanupFetchObserver);
255
+
256
+ return {
257
+ // 状态
258
+ inputValue,
259
+ isOpen,
260
+ fetchLoading,
261
+ // 输入框是否只读(filterable=true 时为 false)
262
+ inputReadonly,
263
+ // 列表
264
+ displayOptions,
265
+ selectedTags,
266
+ // DOM refs(fetch 用)
267
+ lastOptionRef,
268
+ optionsContainerRef,
269
+ // 事件
270
+ onSelect,
271
+ onDeleteTag,
272
+ onInput,
273
+ onFocus,
274
+ onBlur,
275
+ // 工具
276
+ tools,
277
+ // fetch
278
+ updateFetchObserver,
279
+ };
280
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @description select 参数映射工具
3
+ * @author 阿怪
4
+ * @date 2026/2/25 14:40
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ *
9
+ * 处理 options 中对象数据的 key 映射
10
+ * inputParam: 选中后输入框显示哪个字段
11
+ * optionParam: 下拉列表中显示哪个字段
12
+ * valueParam: modelValue 绑定哪个字段
13
+ */
14
+ import { SelectProps } from './props';
15
+
16
+ export type SelectParamKeys = 'optionParam' | 'valueParam' | 'inputParam';
17
+
18
+ export function useSelectTools(props: SelectProps) {
19
+
20
+ /** 从 option 中取指定 key 的值 */
21
+ const getInfoWithKey = (option: any, key: SelectParamKeys): any => {
22
+ const paramKey = props[key];
23
+ if (!paramKey) {
24
+ if (option && typeof option === 'object') {
25
+ if ('label' in option) return option.label;
26
+ if ('name' in option) return option.name;
27
+ if ('text' in option) return option.text;
28
+ }
29
+ return option;
30
+ }
31
+ try {
32
+ return option[paramKey];
33
+ } catch {
34
+ return option;
35
+ }
36
+ };
37
+
38
+ /** 输入框显示值 */
39
+ const getInputValue = (option: any): string => {
40
+ return String(getInfoWithKey(option, 'inputParam') ?? '');
41
+ };
42
+
43
+ /** 下拉列表显示值 */
44
+ const getOptionValue = (option: any): string => {
45
+ return String(getInfoWithKey(option, 'optionParam') ?? '');
46
+ };
47
+
48
+ /** modelValue 绑定值 */
49
+ const getModelValue = (option: any): any => {
50
+ if (props.valueParam) {
51
+ return getInfoWithKey(option, 'valueParam');
52
+ }
53
+ if (option && typeof option === 'object' && 'value' in option) {
54
+ return option.value;
55
+ }
56
+ return option;
57
+ };
58
+
59
+ return { getInfoWithKey, getInputValue, getOptionValue, getModelValue };
60
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @description skeleton api
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { MCOPO } from '../../types/props';
10
+ import { SkeletonProps } from './props';
11
+
12
+ export const props: MCOPO<SkeletonProps> = {
13
+ loading: { type: Boolean, default: true },
14
+ rows: { type: Number, default: 3 },
15
+ animated: { type: Boolean, default: true },
16
+ avatar: { type: Boolean, default: false },
17
+ title: { type: Boolean, default: true },
18
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @description skeleton core 导出
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { props } from './api';
10
+
11
+ export const SkeletonCore = {
12
+ props,
13
+ };
14
+
15
+ export type { SkeletonProps } from './props';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @description skeleton api type
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ export declare type SkeletonProps = {
11
+ /**
12
+ * @description 是否显示骨架屏(true=显示骨架,false=显示真实内容)
13
+ * @type boolean
14
+ * @default true
15
+ */
16
+ loading?: boolean,
17
+ /**
18
+ * @description 文本行数
19
+ * @type number
20
+ * @default 3
21
+ */
22
+ rows?: number,
23
+ /**
24
+ * @description 是否开启脉冲动画
25
+ * @type boolean
26
+ * @default true
27
+ */
28
+ animated?: boolean,
29
+ /**
30
+ * @description 是否显示头像占位圆
31
+ * @type boolean
32
+ * @default false
33
+ */
34
+ avatar?: boolean,
35
+ /**
36
+ * @description 是否显示标题占位条(较宽)
37
+ * @type boolean
38
+ * @default true
39
+ */
40
+ title?: boolean,
41
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @description slider 运行时 props
3
+ * @author 阿怪
4
+ * @date 2026/2/25 00:00
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { MCOPO } from '../../types/props';
10
+ import { SliderProps } from './props';
11
+
12
+ export const props: MCOPO<SliderProps> = {
13
+ modelValue: { type: Number, default: 0 },
14
+ min: { type: Number, default: 0 },
15
+ max: { type: Number, default: 100 },
16
+ step: { type: Number, default: 1 },
17
+ disabled: { type: Boolean, default: false },
18
+ showInfo: { type: Boolean, default: false },
19
+ showStops: { type: Boolean, default: false },
20
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @description slider core 导出
3
+ * @author 阿怪
4
+ * @date 2026/2/25 00:00
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { props } from './api.ts';
10
+ import useSlider from './useSlider.ts';
11
+
12
+ export const SliderCore = {
13
+ props,
14
+ useSlider,
15
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @description slider 类型定义
3
+ * @author 阿怪
4
+ * @date 2026/2/25 00:00
5
+ * @version v1.0.0
6
+ *
7
+ * @name m-slider
8
+ * @docDescription Slider component.
9
+ * 滑动条组件。
10
+ * @docUrl https://shuimo.design/slider
11
+ *
12
+ * 江湖的业务千篇一律,复杂的代码好几百行。
13
+ */
14
+
15
+ export declare type SliderProps = {
16
+ /**
17
+ * @description slider value
18
+ * 滑动条绑定值
19
+ * @type number
20
+ * @default 0
21
+ */
22
+ modelValue?: number;
23
+ /**
24
+ * @description slider min
25
+ * 滑动条最小值
26
+ * @type number
27
+ * @default 0
28
+ */
29
+ min?: number;
30
+ /**
31
+ * @description slider max
32
+ * 滑动条最大值
33
+ * @type number
34
+ * @default 100
35
+ */
36
+ max?: number;
37
+ /**
38
+ * @description slider step
39
+ * 滑动条步长
40
+ * @type number
41
+ * @default 1
42
+ */
43
+ step?: number;
44
+ /**
45
+ * @description slider disabled
46
+ * 是否禁用
47
+ * @type boolean
48
+ * @default false
49
+ */
50
+ disabled?: boolean;
51
+ /**
52
+ * @description slider show-info
53
+ * 是否显示信息
54
+ * @type boolean
55
+ * @default false
56
+ */
57
+ showInfo?: boolean;
58
+ /**
59
+ * @description slider show-stops
60
+ * 是否显示刻度点
61
+ * @type boolean
62
+ * @default false
63
+ */
64
+ showStops?: boolean;
65
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @description slider composable
3
+ * @author 阿怪
4
+ * @date 2026/2/25 00:00
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { onMounted, ref } from 'vue';
10
+ import { SliderProps } from './props';
11
+ import { useElementSize } from '../../../compositions/common/useElementSize.ts';
12
+ import useDrag, { DragOption, DragPosition, InteractEvent } from '../../../compositions/common/useDrag.ts';
13
+
14
+ export default function useSlider(props: Required<SliderProps>, ctx: any) {
15
+ const sliderRef = ref<HTMLElement | null>(null);
16
+ const sliderSize = useElementSize(sliderRef);
17
+
18
+ const perRef = ref(0);
19
+
20
+ const option: DragOption = { startAxis: 'x', lockAxis: 'x' };
21
+
22
+ // 按钮宽度固定值,用于计算可拖拽区域
23
+ const btnW = 20;
24
+
25
+ const sub = props.max - props.min;
26
+
27
+ // 处理拖拽移动,计算并约束位置,更新百分比
28
+ const movePositionHandler = (event: InteractEvent, position: DragPosition) => {
29
+ const totalW = sliderSize.w.value - btnW;
30
+
31
+ let positionX = position.x + event.dx;
32
+ if (positionX > totalW) {
33
+ positionX = totalW;
34
+ } else if (positionX < 0) {
35
+ positionX = 0;
36
+ }
37
+ perRef.value = positionX / totalW;
38
+ ctx.emit('update:modelValue', getValue());
39
+ return { x: positionX > 0 ? positionX : 0, y: 0 };
40
+ };
41
+
42
+ // 根据当前百分比和 step 计算实际 value
43
+ const getValue = () => {
44
+ const addStep = Math.round(perRef.value * sub / props.step) * props.step;
45
+ return props.min + addStep;
46
+ };
47
+
48
+ const { init: initDrag, domRef: btnRef } = useDrag({
49
+ direction: 'top-right',
50
+ event: {
51
+ getOption: () => option,
52
+ movePositionHandler,
53
+ },
54
+ });
55
+
56
+ // 根据当前 modelValue 初始化按钮位置
57
+ const init = () => {
58
+ if (btnRef.value) {
59
+ const { max, min, modelValue } = props;
60
+ const per = (modelValue - min) / (max - min);
61
+ if (window && sliderRef.value) {
62
+ const w = Number.parseFloat(window.getComputedStyle(sliderRef.value).width);
63
+
64
+ const totalW = w - btnW;
65
+ perRef.value = per;
66
+ const x = per * totalW;
67
+ btnRef.value.style.transform = `translate(${x}px, 0)`;
68
+
69
+ initDrag({ x });
70
+ }
71
+ }
72
+ };
73
+
74
+ onMounted(() => {
75
+ init();
76
+ });
77
+
78
+ return {
79
+ btnRef,
80
+ sliderRef,
81
+ perRef,
82
+ };
83
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @description space api
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { MCOPO, MPropType } from '../../types/props';
10
+ import { SpaceProps } from './props';
11
+
12
+ export const props: MCOPO<SpaceProps> = {
13
+ direction: { type: String as MPropType<'horizontal' | 'vertical'>, default: 'horizontal' },
14
+ size: { type: [String, Number] as MPropType<'small' | 'medium' | 'large' | number>, default: 'medium' },
15
+ wrap: { type: Boolean, default: false },
16
+ align: { type: String as MPropType<'start' | 'center' | 'end' | 'baseline'>, default: 'center' },
17
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @description space core 导出
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { props } from './api';
10
+
11
+ export const SpaceCore = {
12
+ props,
13
+ };
14
+
15
+ export type { SpaceProps } from './props';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @description space api type
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v1.0.0
6
+ *
7
+ * @name m-space
8
+ * @docDescription Space component with shuimo-ui style.
9
+ * 水墨组件的间距组件。
10
+ *
11
+ * 江湖的业务千篇一律,复杂的代码好几百行。
12
+ */
13
+
14
+ export declare type SpaceProps = {
15
+ /**
16
+ * @description 排列方向
17
+ * @type 'horizontal' | 'vertical'
18
+ * @default 'horizontal'
19
+ */
20
+ direction?: 'horizontal' | 'vertical';
21
+ /**
22
+ * @description 间距大小,支持预设或数字(px)
23
+ * @type 'small' | 'medium' | 'large' | number
24
+ * @default 'medium'
25
+ */
26
+ size?: 'small' | 'medium' | 'large' | number;
27
+ /**
28
+ * @description 是否自动换行(仅水平方向有效)
29
+ * @type boolean
30
+ * @default false
31
+ */
32
+ wrap?: boolean;
33
+ /**
34
+ * @description 交叉轴对齐方式
35
+ * @type 'start' | 'center' | 'end' | 'baseline'
36
+ * @default 'center'
37
+ */
38
+ align?: 'start' | 'center' | 'end' | 'baseline';
39
+ };