zkit-ui 1.0.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 (267) hide show
  1. package/README.md +267 -0
  2. package/dist/ComponentLibProvider.d.ts +15 -0
  3. package/dist/ComponentLibProvider.d.ts.map +1 -0
  4. package/dist/ComponentLibProvider.js +18 -0
  5. package/dist/assets/animations/spinner.json +1 -0
  6. package/dist/assets/icons/check.svg +4 -0
  7. package/dist/assets/icons/toast/error.webp +0 -0
  8. package/dist/assets/icons/toast/success.webp +0 -0
  9. package/dist/assets/icons/toast/warning.webp +0 -0
  10. package/dist/config.d.ts +16 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +42 -0
  13. package/dist/i18n/I18nProvider.d.ts +11 -0
  14. package/dist/i18n/I18nProvider.d.ts.map +1 -0
  15. package/dist/i18n/I18nProvider.js +84 -0
  16. package/dist/i18n/locale.d.ts +7 -0
  17. package/dist/i18n/locale.d.ts.map +1 -0
  18. package/dist/i18n/locale.js +87 -0
  19. package/dist/i18n/locales/de.d.ts +51 -0
  20. package/dist/i18n/locales/de.d.ts.map +1 -0
  21. package/dist/i18n/locales/de.js +62 -0
  22. package/dist/i18n/locales/en-US.d.ts +51 -0
  23. package/dist/i18n/locales/en-US.d.ts.map +1 -0
  24. package/dist/i18n/locales/en-US.js +62 -0
  25. package/dist/i18n/locales/index.d.ts +9 -0
  26. package/dist/i18n/locales/index.d.ts.map +1 -0
  27. package/dist/i18n/locales/index.js +23 -0
  28. package/dist/i18n/locales/ja.d.ts +51 -0
  29. package/dist/i18n/locales/ja.d.ts.map +1 -0
  30. package/dist/i18n/locales/ja.js +62 -0
  31. package/dist/i18n/locales/zh-CN.d.ts +51 -0
  32. package/dist/i18n/locales/zh-CN.d.ts.map +1 -0
  33. package/dist/i18n/locales/zh-CN.js +62 -0
  34. package/dist/i18n/locales/zh-TW.d.ts +51 -0
  35. package/dist/i18n/locales/zh-TW.d.ts.map +1 -0
  36. package/dist/i18n/locales/zh-TW.js +62 -0
  37. package/dist/i18n/types.d.ts +11 -0
  38. package/dist/i18n/types.d.ts.map +1 -0
  39. package/dist/i18n/types.js +2 -0
  40. package/dist/i18n/useI18n.d.ts +2 -0
  41. package/dist/i18n/useI18n.d.ts.map +1 -0
  42. package/dist/i18n/useI18n.js +41 -0
  43. package/dist/index.d.ts +57 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +90 -0
  46. package/dist/services/ActionDialogService/ActionDialog.d.ts +4 -0
  47. package/dist/services/ActionDialogService/ActionDialog.d.ts.map +1 -0
  48. package/dist/services/ActionDialogService/ActionDialog.js +531 -0
  49. package/dist/services/ActionDialogService/index.d.ts +4 -0
  50. package/dist/services/ActionDialogService/index.d.ts.map +1 -0
  51. package/dist/services/ActionDialogService/index.js +8 -0
  52. package/dist/services/ActionDialogService/service.d.ts +7 -0
  53. package/dist/services/ActionDialogService/service.d.ts.map +1 -0
  54. package/dist/services/ActionDialogService/service.js +309 -0
  55. package/dist/services/ActionDialogService/shared.d.ts +17 -0
  56. package/dist/services/ActionDialogService/shared.d.ts.map +1 -0
  57. package/dist/services/ActionDialogService/shared.js +167 -0
  58. package/dist/services/ActionDialogService/types.d.ts +160 -0
  59. package/dist/services/ActionDialogService/types.d.ts.map +1 -0
  60. package/dist/services/ActionDialogService/types.js +2 -0
  61. package/dist/services/CardToastService/index.d.ts +115 -0
  62. package/dist/services/CardToastService/index.d.ts.map +1 -0
  63. package/dist/services/CardToastService/index.js +899 -0
  64. package/dist/services/ImagePreviewService/index.d.ts +179 -0
  65. package/dist/services/ImagePreviewService/index.d.ts.map +1 -0
  66. package/dist/services/ImagePreviewService/index.js +1383 -0
  67. package/dist/services/LoadingService/index.d.ts +146 -0
  68. package/dist/services/LoadingService/index.d.ts.map +1 -0
  69. package/dist/services/LoadingService/index.js +827 -0
  70. package/dist/services/PermissionPurposeDialogService/index.d.ts +99 -0
  71. package/dist/services/PermissionPurposeDialogService/index.d.ts.map +1 -0
  72. package/dist/services/PermissionPurposeDialogService/index.js +360 -0
  73. package/dist/services/PickerService/index.d.ts +128 -0
  74. package/dist/services/PickerService/index.d.ts.map +1 -0
  75. package/dist/services/PickerService/index.js +250 -0
  76. package/dist/theme/ThemeProvider.d.ts +15 -0
  77. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  78. package/dist/theme/ThemeProvider.js +61 -0
  79. package/dist/theme/defaultTheme.d.ts +3 -0
  80. package/dist/theme/defaultTheme.d.ts.map +1 -0
  81. package/dist/theme/defaultTheme.js +28 -0
  82. package/dist/theme/mergeTheme.d.ts +3 -0
  83. package/dist/theme/mergeTheme.d.ts.map +1 -0
  84. package/dist/theme/mergeTheme.js +19 -0
  85. package/dist/theme/types.d.ts +17 -0
  86. package/dist/theme/types.d.ts.map +1 -0
  87. package/dist/theme/types.js +2 -0
  88. package/dist/theme/useTheme.d.ts +2 -0
  89. package/dist/theme/useTheme.d.ts.map +1 -0
  90. package/dist/theme/useTheme.js +46 -0
  91. package/dist/ui/Accordion/index.d.ts +99 -0
  92. package/dist/ui/Accordion/index.d.ts.map +1 -0
  93. package/dist/ui/Accordion/index.js +777 -0
  94. package/dist/ui/AddressCascader/index.d.ts +58 -0
  95. package/dist/ui/AddressCascader/index.d.ts.map +1 -0
  96. package/dist/ui/AddressCascader/index.js +710 -0
  97. package/dist/ui/BetweenTime/index.d.ts +82 -0
  98. package/dist/ui/BetweenTime/index.d.ts.map +1 -0
  99. package/dist/ui/BetweenTime/index.js +702 -0
  100. package/dist/ui/Button/index.d.ts +93 -0
  101. package/dist/ui/Button/index.d.ts.map +1 -0
  102. package/dist/ui/Button/index.js +832 -0
  103. package/dist/ui/Checkbox/index.d.ts +103 -0
  104. package/dist/ui/Checkbox/index.d.ts.map +1 -0
  105. package/dist/ui/Checkbox/index.js +694 -0
  106. package/dist/ui/DatePicker/index.d.ts +5 -0
  107. package/dist/ui/DatePicker/index.d.ts.map +1 -0
  108. package/dist/ui/DatePicker/index.js +157 -0
  109. package/dist/ui/DatePicker/types.d.ts +99 -0
  110. package/dist/ui/DatePicker/types.d.ts.map +1 -0
  111. package/dist/ui/DatePicker/types.js +2 -0
  112. package/dist/ui/DatePicker/utils.d.ts +32 -0
  113. package/dist/ui/DatePicker/utils.d.ts.map +1 -0
  114. package/dist/ui/DatePicker/utils.js +316 -0
  115. package/dist/ui/LinkedScroll/index.d.ts +78 -0
  116. package/dist/ui/LinkedScroll/index.d.ts.map +1 -0
  117. package/dist/ui/LinkedScroll/index.js +470 -0
  118. package/dist/ui/LoadingSpinner/index.d.ts +8 -0
  119. package/dist/ui/LoadingSpinner/index.d.ts.map +1 -0
  120. package/dist/ui/LoadingSpinner/index.js +122 -0
  121. package/dist/ui/Picker/actionBar.d.ts +14 -0
  122. package/dist/ui/Picker/actionBar.d.ts.map +1 -0
  123. package/dist/ui/Picker/actionBar.js +28 -0
  124. package/dist/ui/Picker/index.d.ts +5 -0
  125. package/dist/ui/Picker/index.d.ts.map +1 -0
  126. package/dist/ui/Picker/index.js +412 -0
  127. package/dist/ui/Picker/types.d.ts +80 -0
  128. package/dist/ui/Picker/types.d.ts.map +1 -0
  129. package/dist/ui/Picker/types.js +2 -0
  130. package/dist/ui/Picker/utils.d.ts +27 -0
  131. package/dist/ui/Picker/utils.d.ts.map +1 -0
  132. package/dist/ui/Picker/utils.js +182 -0
  133. package/dist/ui/Radio/index.d.ts +95 -0
  134. package/dist/ui/Radio/index.d.ts.map +1 -0
  135. package/dist/ui/Radio/index.js +633 -0
  136. package/dist/ui/Sheet/index.d.ts +12 -0
  137. package/dist/ui/Sheet/index.d.ts.map +1 -0
  138. package/dist/ui/Sheet/index.js +832 -0
  139. package/dist/ui/Sheet/nativeBottom.d.ts +52 -0
  140. package/dist/ui/Sheet/nativeBottom.d.ts.map +1 -0
  141. package/dist/ui/Sheet/nativeBottom.js +442 -0
  142. package/dist/ui/Sheet/nativeBottom.web.d.ts +18 -0
  143. package/dist/ui/Sheet/nativeBottom.web.d.ts.map +1 -0
  144. package/dist/ui/Sheet/nativeBottom.web.js +46 -0
  145. package/dist/ui/Sheet/slots.d.ts +5 -0
  146. package/dist/ui/Sheet/slots.d.ts.map +1 -0
  147. package/dist/ui/Sheet/slots.js +40 -0
  148. package/dist/ui/Sheet/types.d.ts +131 -0
  149. package/dist/ui/Sheet/types.d.ts.map +1 -0
  150. package/dist/ui/Sheet/types.js +2 -0
  151. package/dist/ui/SliderCaptcha/index.d.ts +57 -0
  152. package/dist/ui/SliderCaptcha/index.d.ts.map +1 -0
  153. package/dist/ui/SliderCaptcha/index.js +586 -0
  154. package/dist/ui/Switch/index.d.ts +72 -0
  155. package/dist/ui/Switch/index.d.ts.map +1 -0
  156. package/dist/ui/Switch/index.js +671 -0
  157. package/dist/ui/Text/index.d.ts +41 -0
  158. package/dist/ui/Text/index.d.ts.map +1 -0
  159. package/dist/ui/Text/index.js +325 -0
  160. package/dist/ui/TextInput/TextInputPrimitive.d.ts +8 -0
  161. package/dist/ui/TextInput/TextInputPrimitive.d.ts.map +1 -0
  162. package/dist/ui/TextInput/TextInputPrimitive.js +54 -0
  163. package/dist/ui/TextInput/index.d.ts +89 -0
  164. package/dist/ui/TextInput/index.d.ts.map +1 -0
  165. package/dist/ui/TextInput/index.js +545 -0
  166. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts +39 -0
  167. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts.map +1 -0
  168. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.js +60 -0
  169. package/dist/ui/WheelColumn/constants.d.ts +2 -0
  170. package/dist/ui/WheelColumn/constants.d.ts.map +1 -0
  171. package/dist/ui/WheelColumn/constants.js +4 -0
  172. package/dist/ui/WheelColumn/index.d.ts +59 -0
  173. package/dist/ui/WheelColumn/index.d.ts.map +1 -0
  174. package/dist/ui/WheelColumn/index.js +547 -0
  175. package/ios/ZKitWheelPicker.h +18 -0
  176. package/ios/ZKitWheelPicker.mm +230 -0
  177. package/ios/ZKitWheelPickerLabel.h +10 -0
  178. package/ios/ZKitWheelPickerLabel.mm +31 -0
  179. package/ios/ZKitWheelPickerManager.h +5 -0
  180. package/ios/ZKitWheelPickerManager.mm +53 -0
  181. package/package.json +75 -0
  182. package/react-native.config.js +10 -0
  183. package/src/ComponentLibProvider.tsx +56 -0
  184. package/src/assets/animations/spinner.json +1 -0
  185. package/src/assets/icons/check.svg +4 -0
  186. package/src/assets/icons/toast/error.webp +0 -0
  187. package/src/assets/icons/toast/success.webp +0 -0
  188. package/src/assets/icons/toast/warning.webp +0 -0
  189. package/src/config.ts +65 -0
  190. package/src/i18n/I18nProvider.tsx +69 -0
  191. package/src/i18n/locale.ts +108 -0
  192. package/src/i18n/locales/de.ts +67 -0
  193. package/src/i18n/locales/en-US.ts +67 -0
  194. package/src/i18n/locales/index.ts +19 -0
  195. package/src/i18n/locales/ja.ts +67 -0
  196. package/src/i18n/locales/zh-CN.ts +67 -0
  197. package/src/i18n/locales/zh-TW.ts +67 -0
  198. package/src/i18n/types.ts +21 -0
  199. package/src/i18n/useI18n.ts +7 -0
  200. package/src/index.ts +395 -0
  201. package/src/services/ActionDialogService/ActionDialog.tsx +756 -0
  202. package/src/services/ActionDialogService/README.md +146 -0
  203. package/src/services/ActionDialogService/index.tsx +39 -0
  204. package/src/services/ActionDialogService/service.tsx +355 -0
  205. package/src/services/ActionDialogService/shared.ts +204 -0
  206. package/src/services/ActionDialogService/types.ts +197 -0
  207. package/src/services/CardToastService/README.md +128 -0
  208. package/src/services/CardToastService/index.tsx +1284 -0
  209. package/src/services/ImagePreviewService/README.md +67 -0
  210. package/src/services/ImagePreviewService/index.tsx +2148 -0
  211. package/src/services/LoadingService/README.md +128 -0
  212. package/src/services/LoadingService/index.tsx +1113 -0
  213. package/src/services/PermissionPurposeDialogService/README.md +80 -0
  214. package/src/services/PermissionPurposeDialogService/index.tsx +449 -0
  215. package/src/services/PickerService/README.md +154 -0
  216. package/src/services/PickerService/index.tsx +410 -0
  217. package/src/theme/ThemeProvider.tsx +40 -0
  218. package/src/theme/defaultTheme.ts +30 -0
  219. package/src/theme/mergeTheme.ts +16 -0
  220. package/src/theme/types.ts +36 -0
  221. package/src/theme/useTheme.ts +10 -0
  222. package/src/types/svg.d.ts +9 -0
  223. package/src/types/vant-area-data.d.ts +19 -0
  224. package/src/ui/Accordion/README.md +67 -0
  225. package/src/ui/Accordion/index.tsx +1221 -0
  226. package/src/ui/AddressCascader/README.md +144 -0
  227. package/src/ui/AddressCascader/index.tsx +1084 -0
  228. package/src/ui/BetweenTime/README.md +490 -0
  229. package/src/ui/BetweenTime/index.tsx +1001 -0
  230. package/src/ui/Button/README.md +186 -0
  231. package/src/ui/Button/index.tsx +1356 -0
  232. package/src/ui/Checkbox/README.md +211 -0
  233. package/src/ui/Checkbox/index.tsx +1231 -0
  234. package/src/ui/DatePicker/README.md +117 -0
  235. package/src/ui/DatePicker/index.tsx +295 -0
  236. package/src/ui/DatePicker/types.ts +115 -0
  237. package/src/ui/DatePicker/utils.ts +405 -0
  238. package/src/ui/LinkedScroll/README.md +55 -0
  239. package/src/ui/LinkedScroll/index.tsx +809 -0
  240. package/src/ui/LoadingSpinner/index.tsx +104 -0
  241. package/src/ui/Picker/README.md +190 -0
  242. package/src/ui/Picker/actionBar.tsx +70 -0
  243. package/src/ui/Picker/index.tsx +694 -0
  244. package/src/ui/Picker/types.ts +94 -0
  245. package/src/ui/Picker/utils.ts +239 -0
  246. package/src/ui/Radio/README.md +207 -0
  247. package/src/ui/Radio/index.tsx +1117 -0
  248. package/src/ui/Sheet/README.md +82 -0
  249. package/src/ui/Sheet/index.tsx +1163 -0
  250. package/src/ui/Sheet/nativeBottom.tsx +683 -0
  251. package/src/ui/Sheet/nativeBottom.web.tsx +40 -0
  252. package/src/ui/Sheet/slots.tsx +75 -0
  253. package/src/ui/Sheet/types.ts +195 -0
  254. package/src/ui/SliderCaptcha/README.md +61 -0
  255. package/src/ui/SliderCaptcha/index.tsx +816 -0
  256. package/src/ui/Switch/README.md +143 -0
  257. package/src/ui/Switch/index.tsx +1092 -0
  258. package/src/ui/Text/README.md +157 -0
  259. package/src/ui/Text/index.tsx +450 -0
  260. package/src/ui/TextInput/README.md +197 -0
  261. package/src/ui/TextInput/TextInputPrimitive.tsx +44 -0
  262. package/src/ui/TextInput/index.tsx +942 -0
  263. package/src/ui/WheelColumn/README.md +104 -0
  264. package/src/ui/WheelColumn/ZKitWheelPickerNativeComponent.tsx +68 -0
  265. package/src/ui/WheelColumn/constants.ts +1 -0
  266. package/src/ui/WheelColumn/index.tsx +826 -0
  267. package/zkit-ui.podspec +19 -0
@@ -0,0 +1,410 @@
1
+ /**
2
+ * @file PickerService - 命令式选择器服务
3
+ * @description 提供 pick、pickDate、pickAddress、pickBetweenTime 等方法
4
+ * @example
5
+ * ```tsx
6
+ * import { pickerService } from 'zkit-ui';
7
+ *
8
+ * const result = await pickerService.pick({ options: [...], value: 'id' });
9
+ * const date = await pickerService.pickDate({ value: '2024-01-01' });
10
+ * ```
11
+ */
12
+
13
+ import * as React from 'react';
14
+ import {
15
+ Picker,
16
+ type PickerConfirmPayload,
17
+ type PickerValue,
18
+ type PickerProps,
19
+ } from '../../ui/Picker';
20
+ import { DatePicker, type DatePickerProps, type DatePickerValue } from '../../ui/DatePicker';
21
+ import {
22
+ AddressCascader,
23
+ type AddressCascaderOption,
24
+ type AddressCascaderProps,
25
+ type AddressCascaderValue,
26
+ } from '../../ui/AddressCascader';
27
+ import { BetweenTime, type BetweenTimeProps } from '../../ui/BetweenTime';
28
+
29
+ // ============ Types ============
30
+
31
+ /** 通用选择器返回结果 */
32
+ export type PickerResult = {
33
+ /** 选中值 */
34
+ value: PickerValue;
35
+ /** 选中值数组(多列时) */
36
+ values: PickerConfirmPayload['values'];
37
+ /** 选中项文本 */
38
+ label: string;
39
+ /** 选中项文本数组(多列时) */
40
+ labels: string[];
41
+ /** 选中项数据 */
42
+ items: PickerConfirmPayload['items'];
43
+ } | null;
44
+
45
+ /** 日期选择器返回结果 */
46
+ export type DatePickerResult = {
47
+ /** 选中日期值 */
48
+ value: DatePickerValue;
49
+ /** 格式化后的日期文本 */
50
+ label: string;
51
+ } | null;
52
+
53
+ /** 地址选择器返回结果 */
54
+ export type AddressPickerResult = {
55
+ /** 选中地址码数组 */
56
+ value: AddressCascaderValue;
57
+ /** 选中地址码数组 */
58
+ values: string[];
59
+ /** 完整地址文本 */
60
+ label: string;
61
+ /** 各级地址文本数组 */
62
+ labels: string[];
63
+ /** 选中项数据 */
64
+ items: AddressCascaderOption[];
65
+ } | null;
66
+
67
+ /** 时间区间选择器返回结果 */
68
+ export type BetweenTimePickerResult = {
69
+ /** 时间区间 [开始, 结束] */
70
+ value: string[];
71
+ } | null;
72
+
73
+ /** 通用选择器配置 */
74
+ export type PickOptions = Omit<
75
+ PickerProps,
76
+ 'open' | 'onOpenChange' | 'onConfirm' | 'onCancel' | 'children' | 'onDismissComplete'
77
+ >;
78
+ /** 日期选择器配置 */
79
+ export type PickDateOptions = Omit<
80
+ DatePickerProps,
81
+ 'open' | 'onOpenChange' | 'onConfirm' | 'onCancel' | 'children' | 'onDismissComplete'
82
+ >;
83
+ /** 地址选择器配置 */
84
+ export type PickAddressOptions = Omit<
85
+ AddressCascaderProps,
86
+ 'open' | 'onOpenChange' | 'onConfirm' | 'onCancel' | 'children' | 'onDismissComplete'
87
+ >;
88
+ /** 时间区间选择器配置 */
89
+ export type PickBetweenTimeOptions = Omit<
90
+ BetweenTimeProps,
91
+ 'open' | 'onOpenChange' | 'onConfirm' | 'onCancel' | 'children' | 'onDismissComplete'
92
+ >;
93
+
94
+ type PickerType = 'picker' | 'date' | 'address' | 'betweenTime';
95
+
96
+ type PickerRequestMap = {
97
+ picker: {
98
+ options: PickOptions;
99
+ result: PickerResult;
100
+ };
101
+ date: {
102
+ options: PickDateOptions;
103
+ result: DatePickerResult;
104
+ };
105
+ address: {
106
+ options: PickAddressOptions;
107
+ result: AddressPickerResult;
108
+ };
109
+ betweenTime: {
110
+ options: PickBetweenTimeOptions;
111
+ result: BetweenTimePickerResult;
112
+ };
113
+ };
114
+
115
+ type PickerRequestResult = PickerRequestMap[PickerType]['result'];
116
+
117
+ type PickerRequest<T extends PickerType = PickerType> = {
118
+ [K in T]: {
119
+ id: string;
120
+ type: K;
121
+ options: PickerRequestMap[K]['options'];
122
+ resolve: ((result: PickerRequestResult) => void) | null;
123
+ };
124
+ }[T];
125
+
126
+ type DatePickerConfirmPayload = Parameters<NonNullable<DatePickerProps['onConfirm']>>[0];
127
+ type AddressCascaderConfirmPayload = Parameters<NonNullable<AddressCascaderProps['onConfirm']>>[0];
128
+ type BetweenTimeConfirmPayload = Parameters<NonNullable<BetweenTimeProps['onConfirm']>>[0];
129
+
130
+ type PickerState = {
131
+ activeRequest: PickerRequest | null;
132
+ queuedRequests: PickerRequest[];
133
+ open: boolean;
134
+ };
135
+
136
+ // ============ Service Class ============
137
+
138
+ /**
139
+ * 选择器服务类
140
+ * @internal
141
+ */
142
+ class PickerServiceClass {
143
+ private setState: React.Dispatch<React.SetStateAction<PickerState>> | null = null;
144
+ private sequence = 0;
145
+
146
+ /** @internal 设置状态更新器 */
147
+ setStateUpdater(updater: React.Dispatch<React.SetStateAction<PickerState>> | null) {
148
+ this.setState = updater;
149
+ }
150
+
151
+ /** @internal 显示选择器 */
152
+ private show<T extends PickerType>(
153
+ type: T,
154
+ options: PickerRequestMap[T]['options']
155
+ ): Promise<PickerRequestMap[T]['result']> {
156
+ return new Promise((resolve) => {
157
+ if (!this.setState) {
158
+ console.warn('[PickerService] Provider not mounted');
159
+ resolve(null as PickerRequestMap[T]['result']);
160
+ return;
161
+ }
162
+ const request = {
163
+ id: `picker-request-${Date.now()}-${++this.sequence}`,
164
+ type,
165
+ options,
166
+ resolve: (result: PickerRequestResult) => {
167
+ resolve(result as PickerRequestMap[T]['result']);
168
+ },
169
+ } as PickerRequest;
170
+ this.setState((prev) => {
171
+ if (!prev.activeRequest) {
172
+ return { ...prev, activeRequest: request, open: true };
173
+ }
174
+ return {
175
+ ...prev,
176
+ queuedRequests: [...prev.queuedRequests, request],
177
+ };
178
+ });
179
+ });
180
+ }
181
+
182
+ /** 打开通用选择器 */
183
+ pick(options: PickOptions): Promise<PickerResult> {
184
+ return this.show('picker', options);
185
+ }
186
+
187
+ /** 打开日期选择器 */
188
+ pickDate(options: PickDateOptions = {}): Promise<DatePickerResult> {
189
+ return this.show('date', options);
190
+ }
191
+
192
+ /** 打开地址选择器 */
193
+ pickAddress(options: PickAddressOptions = {}): Promise<AddressPickerResult> {
194
+ return this.show('address', options);
195
+ }
196
+
197
+ /** 打开时间区间选择器 */
198
+ pickBetweenTime(options: PickBetweenTimeOptions = {}): Promise<BetweenTimePickerResult> {
199
+ return this.show('betweenTime', options);
200
+ }
201
+
202
+ /** 关闭当前选择器 */
203
+ close() {
204
+ this.setState?.((prev) => {
205
+ if (!prev.activeRequest || !prev.open) return prev;
206
+ return { ...prev, open: false };
207
+ });
208
+ }
209
+ }
210
+
211
+ /** 选择器服务实例 */
212
+ export const pickerService = new PickerServiceClass();
213
+
214
+ // ============ Provider Component ============
215
+
216
+ const initialState: PickerState = {
217
+ activeRequest: null,
218
+ queuedRequests: [],
219
+ open: false,
220
+ };
221
+
222
+ /**
223
+ * 选择器服务 Provider
224
+ * @description 需要在应用根组件中包裹,已内置于 ComponentLibProvider
225
+ */
226
+ export function PickerServiceProvider({ children }: { children: React.ReactNode }) {
227
+ const [state, setState] = React.useState<PickerState>(initialState);
228
+ const activeRequestRef = React.useRef<PickerRequest | null>(null);
229
+ const queuedRequestsRef = React.useRef<PickerRequest[]>([]);
230
+ const settledRequestIdsRef = React.useRef(new Set<string>());
231
+ activeRequestRef.current = state.activeRequest;
232
+ queuedRequestsRef.current = state.queuedRequests;
233
+
234
+ /** 同一个请求只允许 settle 一次,关闭动画结束前仍保留 activeRequest 做串行调度。 */
235
+ const settleRequest = React.useCallback((request: PickerRequest | null, result: PickerRequestResult) => {
236
+ if (!request) return;
237
+ if (settledRequestIdsRef.current.has(request.id)) return;
238
+ settledRequestIdsRef.current.add(request.id);
239
+ request.resolve?.(result);
240
+ }, []);
241
+
242
+ React.useEffect(() => {
243
+ pickerService.setStateUpdater(setState);
244
+
245
+ return () => {
246
+ settleRequest(activeRequestRef.current, null);
247
+ for (const request of queuedRequestsRef.current) {
248
+ settleRequest(request, null);
249
+ }
250
+ pickerService.setStateUpdater(null);
251
+ };
252
+ }, [settleRequest]);
253
+
254
+ /** 业务层要求关闭时,先 settle 当前 promise,但不立刻销毁实例。 */
255
+ const handleOpenChange = React.useCallback((open: boolean) => {
256
+ if (open) {
257
+ setState((prev) => {
258
+ if (!prev.activeRequest || prev.open) return prev;
259
+ return { ...prev, open: true };
260
+ });
261
+ return;
262
+ }
263
+
264
+ settleRequest(activeRequestRef.current, null);
265
+ setState((prev) => {
266
+ if (!prev.activeRequest || !prev.open) return prev;
267
+ return { ...prev, open: false };
268
+ });
269
+ }, [settleRequest]);
270
+
271
+ /** 取消时 resolve null */
272
+ const handleCancel = React.useCallback(() => {
273
+ settleRequest(activeRequestRef.current, null);
274
+ }, [settleRequest]);
275
+
276
+ /** 原生 dismiss 真正结束后,再安全切到下一个排队请求。 */
277
+ const handleDismissComplete = React.useCallback(
278
+ (requestId: string) => {
279
+ setState((prev) => {
280
+ if (prev.activeRequest?.id !== requestId) return prev;
281
+
282
+ settleRequest(prev.activeRequest, null);
283
+ settledRequestIdsRef.current.delete(requestId);
284
+
285
+ const [nextRequest, ...restQueue] = prev.queuedRequests;
286
+ if (!nextRequest) {
287
+ return {
288
+ activeRequest: null,
289
+ queuedRequests: restQueue,
290
+ open: false,
291
+ };
292
+ }
293
+
294
+ return {
295
+ activeRequest: nextRequest,
296
+ queuedRequests: restQueue,
297
+ open: true,
298
+ };
299
+ });
300
+ },
301
+ [settleRequest]
302
+ );
303
+
304
+ /** 确认时 resolve 结果 */
305
+ const handlePickerConfirm = React.useCallback(
306
+ (payload: PickerConfirmPayload) => {
307
+ settleRequest(activeRequestRef.current, {
308
+ value: payload.value,
309
+ values: payload.values,
310
+ label: payload.label,
311
+ labels: payload.labels,
312
+ items: payload.items,
313
+ });
314
+ },
315
+ [settleRequest]
316
+ );
317
+
318
+ const handleDateConfirm = React.useCallback(
319
+ (payload: DatePickerConfirmPayload) => {
320
+ settleRequest(activeRequestRef.current, {
321
+ value: payload.value,
322
+ label: payload.label,
323
+ });
324
+ },
325
+ [settleRequest]
326
+ );
327
+
328
+ const handleAddressConfirm = React.useCallback(
329
+ (payload: AddressCascaderConfirmPayload) => {
330
+ settleRequest(activeRequestRef.current, {
331
+ value: payload.value,
332
+ values: payload.values,
333
+ label: payload.label,
334
+ labels: payload.labels,
335
+ items: payload.items,
336
+ });
337
+ },
338
+ [settleRequest]
339
+ );
340
+
341
+ const handleBetweenTimeConfirm = React.useCallback(
342
+ (payload: BetweenTimeConfirmPayload) => {
343
+ settleRequest(activeRequestRef.current, {
344
+ value: payload.value,
345
+ });
346
+ },
347
+ [settleRequest]
348
+ );
349
+
350
+ const activeRequest = state.activeRequest;
351
+ const activeRequestId = activeRequest?.id;
352
+
353
+ return (
354
+ <>
355
+ {children}
356
+ {activeRequest?.type === 'picker' && (
357
+ <Picker
358
+ key={activeRequest.id}
359
+ {...activeRequest.options}
360
+ open={state.open}
361
+ onOpenChange={handleOpenChange}
362
+ onConfirm={handlePickerConfirm}
363
+ onCancel={handleCancel}
364
+ onDismissComplete={() => {
365
+ if (activeRequestId) handleDismissComplete(activeRequestId);
366
+ }}
367
+ />
368
+ )}
369
+ {activeRequest?.type === 'date' && (
370
+ <DatePicker
371
+ key={activeRequest.id}
372
+ {...activeRequest.options}
373
+ open={state.open}
374
+ onOpenChange={handleOpenChange}
375
+ onConfirm={handleDateConfirm}
376
+ onCancel={handleCancel}
377
+ onDismissComplete={() => {
378
+ if (activeRequestId) handleDismissComplete(activeRequestId);
379
+ }}
380
+ />
381
+ )}
382
+ {activeRequest?.type === 'address' && (
383
+ <AddressCascader
384
+ key={activeRequest.id}
385
+ {...activeRequest.options}
386
+ open={state.open}
387
+ onOpenChange={handleOpenChange}
388
+ onConfirm={handleAddressConfirm}
389
+ onCancel={handleCancel}
390
+ onDismissComplete={() => {
391
+ if (activeRequestId) handleDismissComplete(activeRequestId);
392
+ }}
393
+ />
394
+ )}
395
+ {activeRequest?.type === 'betweenTime' && (
396
+ <BetweenTime
397
+ key={activeRequest.id}
398
+ {...activeRequest.options}
399
+ open={state.open}
400
+ onOpenChange={handleOpenChange}
401
+ onConfirm={handleBetweenTimeConfirm}
402
+ onCancel={handleCancel}
403
+ onDismissComplete={() => {
404
+ if (activeRequestId) handleDismissComplete(activeRequestId);
405
+ }}
406
+ />
407
+ )}
408
+ </>
409
+ );
410
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import type { Theme, ThemeOverride } from './types';
3
+ import { mergeTheme } from './mergeTheme';
4
+ import { getDefaultTheme } from '../config';
5
+
6
+ type ThemeContextValue = {
7
+ theme: Theme;
8
+ getTheme: () => Theme;
9
+ };
10
+
11
+ // ThemeContext 用于在组件树中向下传递主题对象。
12
+ // 这里提供默认值(getDefaultTheme),这样即使应用侧没有包 ThemeProvider:
13
+ // - useTheme() 也不会返回 undefined
14
+ // - 组件仍能正常渲染(使用组件库默认主题 + 全局配置覆盖)
15
+ export const ThemeContext = React.createContext<ThemeContextValue>({
16
+ theme: getDefaultTheme(),
17
+ getTheme: getDefaultTheme,
18
+ });
19
+
20
+ export type ThemeProviderProps = {
21
+ // baseTheme 允许应用侧提供“完整主题对象”作为基线(例如某个品牌主题)
22
+ // 然后再用 theme 进行局部覆盖。
23
+ baseTheme?: Theme;
24
+ // theme 是覆盖对象(Partial),只需要传你要改的字段即可
25
+ theme?: ThemeOverride;
26
+ children: React.ReactNode;
27
+ };
28
+
29
+ export function ThemeProvider({ baseTheme, theme, children }: ThemeProviderProps) {
30
+ const value = React.useMemo(() => {
31
+ // 优先级(低 -> 高):
32
+ // 1) defaultTheme
33
+ // 2) configureComponentLib({ theme })
34
+ // 3) ThemeProvider({ baseTheme })
35
+ // 4) ThemeProvider({ theme })
36
+ const finalTheme = mergeTheme(baseTheme ?? getDefaultTheme(), theme);
37
+ return { theme: finalTheme, getTheme: () => finalTheme };
38
+ }, [baseTheme, theme]);
39
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
40
+ }
@@ -0,0 +1,30 @@
1
+ import type { Theme } from './types';
2
+
3
+ // defaultTheme 是组件库的默认主题:
4
+ // - 当应用侧不包 ThemeProvider 时,组件会使用这套默认颜色
5
+ // - 当应用侧包了 ThemeProvider 且只覆盖部分字段时,未覆盖的字段也会从这里补齐
6
+ export const defaultTheme: Theme = {
7
+ colors: {
8
+ // 默认主色(偏深色),适合作为按钮等强调元素背景
9
+ primary: '#111827',
10
+ // 主色上的文字/图标颜色
11
+ onPrimary: '#FFFFFF',
12
+
13
+ // 默认次要色(浅背景)
14
+ secondary: '#EEF2FF',
15
+ // 次要色上的文字/图标颜色
16
+ onSecondary: '#111827',
17
+
18
+ // 默认表面背景(卡片、控件背景)
19
+ surface: '#FFFFFF',
20
+ // 表面上的文字/图标颜色
21
+ onSurface: '#111827',
22
+
23
+ // 默认边框色
24
+ border: '#d9d9d9',
25
+ // 弱化文字色
26
+ muted: '#6B7280',
27
+ // 禁用态颜色(这里复用 muted)
28
+ disabled: '#6B7280',
29
+ },
30
+ };
@@ -0,0 +1,16 @@
1
+ import type { Theme, ThemeOverride } from './types';
2
+
3
+ // mergeTheme 用于实现“部分覆盖”体验:
4
+ // - 业务侧只改 colors.primary 时,只需传 { colors: { primary } }
5
+ // - 未覆盖字段回落到 base
6
+ export function mergeTheme(base: Theme, override?: ThemeOverride): Theme {
7
+ if (!override) return base;
8
+ return {
9
+ ...base,
10
+ ...override,
11
+ colors: {
12
+ ...base.colors,
13
+ ...(override.colors ?? {}),
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,36 @@
1
+ // Theme 是组件库内部约定的“主题对象”结构
2
+ // 目前只包含 colors,后续如果需要也可以增加 spacing / radii / typography 等字段
3
+ export type Theme = {
4
+ colors: {
5
+ // 组件主色,例如按钮、选中态等强调色
6
+ primary: string;
7
+ // 主色上的前景色(通常是文字颜色),要求与 primary 有足够对比度
8
+ onPrimary: string;
9
+
10
+ // 次要色,常用于弱一点的按钮、标签背景等
11
+ secondary: string;
12
+ // 次要色上的前景色
13
+ onSecondary: string;
14
+
15
+ // 一般面的背景色(例如卡片、默认控件背景)
16
+ surface: string;
17
+ // 一般面上的前景色(例如普通文字颜色)
18
+ onSurface: string;
19
+
20
+ // 边框颜色(输入框边框、分割线等)
21
+ border: string;
22
+
23
+ // 次要文字、提示文字等弱一点的颜色
24
+ muted: string;
25
+
26
+ // 禁用态文字/图标颜色
27
+ disabled: string;
28
+ };
29
+ };
30
+
31
+ // ThemeOverride 用于在应用侧覆盖默认主题:
32
+ // - 可以只传入部分字段(Partial),未传入的会回落到 defaultTheme
33
+ // - colors 也是部分覆盖,便于只改 primary 这类场景
34
+ export type ThemeOverride = Partial<Theme> & {
35
+ colors?: Partial<Theme['colors']>;
36
+ };
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { ThemeContext } from './ThemeProvider';
3
+
4
+ // useTheme 用于在组件内部读取当前主题
5
+ // - 如果应用侧包了 ThemeProvider:返回合并后的主题
6
+ // - 如果没有包 ThemeProvider:返回 defaultTheme(由 ThemeContext 默认值提供)
7
+ export function useTheme() {
8
+ const ctx = React.useContext(ThemeContext);
9
+ return ctx.getTheme?.() ?? ctx.theme;
10
+ }
@@ -0,0 +1,9 @@
1
+ // 让 TypeScript 认识 `import Icon from './icon.svg'` 这种写法。
2
+ // 在 React Native + react-native-svg-transformer 的组合下,svg 会被转换成一个 React 组件。
3
+ declare module '*.svg' {
4
+ import * as React from 'react';
5
+ import type { SvgProps } from 'react-native-svg';
6
+ const SvgComponent: React.FC<SvgProps>;
7
+ export default SvgComponent;
8
+ }
9
+
@@ -0,0 +1,19 @@
1
+ declare module '@vant/area-data' {
2
+ export type AreaList = {
3
+ province_list: Record<string, string>;
4
+ city_list: Record<string, string>;
5
+ county_list: Record<string, string>;
6
+ };
7
+
8
+ export const areaList: AreaList;
9
+
10
+ export type CascaderNode = {
11
+ text: string;
12
+ value: string;
13
+ children?: CascaderNode[];
14
+ disabled?: boolean;
15
+ };
16
+
17
+ export function useCascaderAreaData(): CascaderNode[];
18
+ }
19
+
@@ -0,0 +1,67 @@
1
+ # Accordion
2
+
3
+ 组合式手风琴/折叠面板,适合设置项、FAQ、表单分组和信息分层。组件默认使用 `value/defaultValue/onChange` 状态模型,支持受控与非受控;动画由 Reanimated shared value 驱动,展开内容、指示器和按压反馈共享同一节奏。
4
+
5
+ ## 基础用法
6
+
7
+ ```tsx
8
+ import * as React from 'react';
9
+ import {
10
+ Accordion,
11
+ AccordionContent,
12
+ AccordionItem,
13
+ AccordionTrigger,
14
+ Text,
15
+ } from 'zkit-ui';
16
+
17
+ export function DemoAccordion() {
18
+ const [value, setValue] = React.useState<string | null>('profile');
19
+
20
+ return (
21
+ <Accordion value={value} onChange={setValue} variant="card" size="md">
22
+ <AccordionItem value="profile">
23
+ <AccordionTrigger title="账户资料" description="姓名、头像和公开信息" />
24
+ <AccordionContent>
25
+ <Text>这里放账户资料表单。</Text>
26
+ </AccordionContent>
27
+ </AccordionItem>
28
+
29
+ <AccordionItem value="security">
30
+ <AccordionTrigger title="安全设置" />
31
+ <AccordionContent>
32
+ <Text>这里放密码、多因素认证和设备管理。</Text>
33
+ </AccordionContent>
34
+ </AccordionItem>
35
+ </Accordion>
36
+ );
37
+ }
38
+ ```
39
+
40
+ ## 状态模型
41
+
42
+ - `type="single"` 是默认模式,`value` 为当前打开项或 `null`;默认 `collapsible=true`,再次点击已打开项会收起。
43
+ - `type="multiple"` 时 `value` 为数组,任意项都可独立展开/收起。
44
+ - 受控模式只由外部 `value` 驱动视觉状态;非受控模式使用 `defaultValue` 初始化内部 store。
45
+
46
+ ## 组合插槽
47
+
48
+ `AccordionTrigger` 提供 `title`、`description`、`leading`、`trailing`、`indicator` 和 `children`。这些插槽都可以传普通节点,也可以传 render prop:
49
+
50
+ ```tsx
51
+ <AccordionTrigger
52
+ title={({ open }) => (open ? '收起详情' : '展开详情')}
53
+ indicator={({ open }) => <YourIcon rotate={open ? 180 : 0} />}
54
+ />
55
+ ```
56
+
57
+ 传入 `children` 时会完全接管 trigger 主体内容;`indicator={false}` 可隐藏默认箭头。
58
+
59
+ ## 外观与动画
60
+
61
+ - `variant`:`card`、`filled`、`plain`
62
+ - `tone`:`neutral`、`primary`、`success`、`warning`、`danger`、`info`
63
+ - `size`:`sm`、`md`、`lg`
64
+ - `animation`:传 `{ duration, easing, reduceMotion }` 覆盖默认动画;传 `false` 关闭动画。
65
+ - `mountStrategy`:`eager` 默认保持内容挂载,`lazy` 首次展开后保持挂载,`unmountOnExit` 收起动画结束后卸载。
66
+
67
+ 所有内置像素尺寸都通过 `wp(...)` 计算。业务侧传入 `itemGap`、`AccordionIndicator size`、`strokeWidth`、自定义 padding 等像素值时也应使用 `wp(...)`。