taro-uno-ui 0.9.0 → 1.0.1

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 (312) hide show
  1. package/README.md +21 -0
  2. package/dist/js/{index-DffLRSro.js → index-CDFsvu80.js} +15369 -10741
  3. package/dist/js/index-CDFsvu80.js.map +1 -0
  4. package/dist/js/index-DFdcksbe.js.map +1 -1
  5. package/dist/js/index-DXRIkWX1.js.map +1 -1
  6. package/dist/js/{index-6NJ3A1Dn.js → index-JffnTUrv.js} +15430 -10801
  7. package/dist/js/index-JffnTUrv.js.map +1 -0
  8. package/dist/utils/http/request.d.ts +280 -0
  9. package/package.json +14 -10
  10. package/src/components/basic/Button/Button.tsx +53 -13
  11. package/src/components/basic/Button/Button.types.ts +45 -9
  12. package/src/components/basic/Divider/Divider.tsx +60 -29
  13. package/src/components/basic/Icon/Icon.data.ts +474 -0
  14. package/src/components/basic/Icon/Icon.test.tsx +2 -2
  15. package/src/components/basic/Icon/Icon.tsx +48 -35
  16. package/src/components/basic/Icon/IconManager.ts +229 -0
  17. package/src/components/basic/Text/Text.styles.ts +3 -3
  18. package/src/components/basic/Text/Text.types.ts +14 -4
  19. package/src/components/basic/Typography/Typography.styles.ts +10 -9
  20. package/src/components/basic/Typography/Typography.tsx +15 -13
  21. package/src/components/basic/Typography/Typography.types.ts +41 -41
  22. package/src/components/basic/Typography/index.tsx +1 -1
  23. package/src/components/basic/Video/Video.styles.ts +777 -0
  24. package/src/components/basic/Video/Video.test.tsx +490 -0
  25. package/src/components/basic/Video/Video.tsx +1468 -0
  26. package/src/components/basic/Video/Video.types.ts +500 -0
  27. package/src/components/basic/Video/index.tsx +26 -0
  28. package/src/components/basic/index.tsx +13 -15
  29. package/src/components/common/ErrorBoundary.tsx +1 -1
  30. package/src/components/common/LazyComponent.tsx +9 -8
  31. package/src/components/common/SecurityProvider.tsx +2 -14
  32. package/src/components/common/ThemeProvider.tsx +43 -56
  33. package/src/components/common/VirtualList.tsx +187 -205
  34. package/src/components/common/index.tsx +25 -0
  35. package/src/components/display/Avatar/Avatar.styles.ts +1 -1
  36. package/src/components/display/Avatar/Avatar.tsx +6 -19
  37. package/src/components/display/Avatar/Avatar.types.ts +1 -1
  38. package/src/components/display/Avatar/index.ts +1 -1
  39. package/src/components/display/Badge/Badge.tsx +3 -16
  40. package/src/components/display/Badge/Badge.types.ts +1 -1
  41. package/src/components/display/Badge/index.ts +1 -1
  42. package/src/components/display/Calendar/Calendar.styles.ts +36 -36
  43. package/src/components/display/Calendar/Calendar.test.tsx +27 -15
  44. package/src/components/display/Calendar/Calendar.tsx +56 -35
  45. package/src/components/display/Calendar/Calendar.types.ts +1 -1
  46. package/src/components/display/Calendar/index.ts +1 -1
  47. package/src/components/display/Card/Card.styles.ts +2 -2
  48. package/src/components/display/Card/Card.test.tsx +6 -4
  49. package/src/components/display/Card/Card.tsx +1 -1
  50. package/src/components/display/Card/Card.types.ts +4 -4
  51. package/src/components/display/Card/index.ts +1 -1
  52. package/src/components/display/Carousel/Carousel.styles.ts +31 -31
  53. package/src/components/display/Carousel/Carousel.tsx +34 -39
  54. package/src/components/display/Carousel/Carousel.types.ts +1 -1
  55. package/src/components/display/Carousel/index.ts +1 -1
  56. package/src/components/display/List/List.styles.ts +3 -3
  57. package/src/components/display/List/List.tsx +0 -1
  58. package/src/components/display/List/index.ts +1 -1
  59. package/src/components/display/Rate/Rate.styles.ts +5 -17
  60. package/src/components/display/Rate/Rate.tsx +6 -14
  61. package/src/components/display/Rate/Rate.types.ts +4 -3
  62. package/src/components/display/Rate/index.ts +3 -11
  63. package/src/components/display/Table/Table.test.tsx +2 -0
  64. package/src/components/display/Table/Table.tsx +3 -7
  65. package/src/components/display/Table/Table.types.ts +3 -2
  66. package/src/components/display/Tag/Tag.styles.ts +31 -31
  67. package/src/components/display/Tag/Tag.tsx +9 -26
  68. package/src/components/display/Tag/Tag.types.ts +1 -1
  69. package/src/components/display/Tag/index.ts +1 -1
  70. package/src/components/display/Timeline/Timeline.styles.ts +32 -32
  71. package/src/components/display/Timeline/Timeline.tsx +23 -42
  72. package/src/components/display/Timeline/Timeline.types.ts +1 -1
  73. package/src/components/display/Timeline/index.ts +1 -1
  74. package/src/components/display/index.tsx +33 -29
  75. package/src/components/feedback/Loading/Loading.tsx +6 -1
  76. package/src/components/feedback/Loading/index.ts +2 -5
  77. package/src/components/feedback/Message/Message.styles.ts +3 -3
  78. package/src/components/feedback/Message/index.ts +2 -5
  79. package/src/components/feedback/Modal/Modal.styles.ts +1 -1
  80. package/src/components/feedback/Modal/Modal.tsx +9 -31
  81. package/src/components/feedback/Modal/Modal.types.ts +12 -2
  82. package/src/components/feedback/Notification/Notification.styles.ts +49 -39
  83. package/src/components/feedback/Notification/Notification.test.tsx +1 -1
  84. package/src/components/feedback/Notification/Notification.tsx +97 -120
  85. package/src/components/feedback/Notification/Notification.types.ts +11 -8
  86. package/src/components/feedback/Notification/NotificationManager.tsx +135 -106
  87. package/src/components/feedback/Notification/index.ts +10 -3
  88. package/src/components/feedback/Notification/index.tsx +16 -26
  89. package/src/components/feedback/Progress/Progress.styles.ts +23 -14
  90. package/src/components/feedback/Progress/Progress.tsx +93 -113
  91. package/src/components/feedback/Progress/Progress.types.ts +1 -1
  92. package/src/components/feedback/Progress/index.ts +1 -1
  93. package/src/components/feedback/Progress/utils/animation.ts +12 -23
  94. package/src/components/feedback/Progress/utils/index.ts +2 -2
  95. package/src/components/feedback/Progress/utils/progress-calculator.ts +14 -32
  96. package/src/components/feedback/Result/Result.styles.ts +29 -29
  97. package/src/components/feedback/Result/Result.tsx +8 -20
  98. package/src/components/feedback/Result/Result.types.ts +7 -7
  99. package/src/components/feedback/Result/index.tsx +1 -1
  100. package/src/components/feedback/Toast/Toast.styles.ts +1 -1
  101. package/src/components/feedback/Toast/Toast.tsx +25 -13
  102. package/src/components/feedback/Tooltip/Tooltip.examples.tsx +21 -44
  103. package/src/components/feedback/Tooltip/Tooltip.styles.ts +16 -22
  104. package/src/components/feedback/Tooltip/Tooltip.test.tsx +1 -1
  105. package/src/components/feedback/Tooltip/Tooltip.tsx +65 -46
  106. package/src/components/feedback/Tooltip/Tooltip.types.ts +14 -20
  107. package/src/components/feedback/Tooltip/index.ts +1 -1
  108. package/src/components/feedback/Tooltip/index.tsx +12 -24
  109. package/src/components/feedback/index.tsx +54 -42
  110. package/src/components/form/Cascader/Cascader.styles.ts +2 -2
  111. package/src/components/form/Cascader/Cascader.tsx +84 -88
  112. package/src/components/form/Cascader/Cascader.types.ts +49 -50
  113. package/src/components/form/Cascader/hooks/useCascaderFieldNames.ts +11 -8
  114. package/src/components/form/Cascader/hooks/useCascaderOptions.ts +73 -55
  115. package/src/components/form/Cascader/hooks/useCascaderState.ts +31 -25
  116. package/src/components/form/Cascader/index.ts +1 -1
  117. package/src/components/form/Cascader/utils/formatDisplayValue.ts +4 -4
  118. package/src/components/form/Checkbox/Checkbox.styles.ts +83 -84
  119. package/src/components/form/Checkbox/Checkbox.tsx +2 -9
  120. package/src/components/form/Checkbox/CheckboxGroup.tsx +7 -7
  121. package/src/components/form/DatePicker/DatePicker.test.tsx +1 -1
  122. package/src/components/form/DatePicker/DatePicker.tsx +91 -75
  123. package/src/components/form/DatePicker/DatePicker.types.ts +4 -1
  124. package/src/components/form/Form/Form.tsx +66 -504
  125. package/src/components/form/Form/Form.types.ts +16 -1
  126. package/src/components/form/Form/useFormLogic.ts +497 -0
  127. package/src/components/form/Input/Input.styles.ts +8 -1
  128. package/src/components/form/Input/Input.tsx +55 -291
  129. package/src/components/form/Input/Input.types.ts +13 -1
  130. package/src/components/form/Input/useInputLogic.test.ts +82 -0
  131. package/src/components/form/Input/useInputLogic.ts +260 -0
  132. package/src/components/form/InputNumber/InputNumber.styles.ts +76 -25
  133. package/src/components/form/InputNumber/InputNumber.tsx +53 -21
  134. package/src/components/form/InputNumber/InputNumber.types.ts +21 -3
  135. package/src/components/form/InputNumber/components/InputNumberClearButton.tsx +3 -11
  136. package/src/components/form/InputNumber/components/InputNumberControls.tsx +3 -12
  137. package/src/components/form/InputNumber/hooks/index.ts +1 -1
  138. package/src/components/form/InputNumber/hooks/useInputNumberState.ts +7 -9
  139. package/src/components/form/InputNumber/hooks/useInputNumberValidation.ts +18 -17
  140. package/src/components/form/InputNumber/index.ts +7 -7
  141. package/src/components/form/Radio/Radio.styles.ts +1 -8
  142. package/src/components/form/Radio/Radio.tsx +3 -9
  143. package/src/components/form/Radio/Radio.types.ts +5 -1
  144. package/src/components/form/Select/Select.styles.ts +5 -1
  145. package/src/components/form/Select/Select.tsx +15 -15
  146. package/src/components/form/Select/Select.types.ts +2 -1
  147. package/src/components/form/Slider/Slider.styles.ts +13 -13
  148. package/src/components/form/Slider/Slider.tsx +19 -33
  149. package/src/components/form/Slider/Slider.types.ts +14 -12
  150. package/src/components/form/Slider/index.tsx +2 -9
  151. package/src/components/form/Switch/Switch.styles.ts +1 -7
  152. package/src/components/form/Switch/Switch.tsx +7 -13
  153. package/src/components/form/Textarea/Textarea.styles.ts +4 -4
  154. package/src/components/form/Textarea/Textarea.tsx +7 -1
  155. package/src/components/form/Textarea/Textarea.types.ts +4 -1
  156. package/src/components/form/TimePicker/TimePicker.styles.ts +8 -12
  157. package/src/components/form/TimePicker/TimePicker.tsx +122 -100
  158. package/src/components/form/TimePicker/TimePicker.types.ts +2 -2
  159. package/src/components/form/TimePicker/index.ts +1 -1
  160. package/src/components/form/Transfer/Transfer.styles.ts +3 -15
  161. package/src/components/form/Transfer/Transfer.tsx +146 -134
  162. package/src/components/form/Transfer/Transfer.types.ts +34 -26
  163. package/src/components/form/Transfer/components/TransferItem.tsx +55 -62
  164. package/src/components/form/Transfer/components/TransferList.tsx +212 -199
  165. package/src/components/form/Transfer/components/TransferOperations.tsx +52 -55
  166. package/src/components/form/Transfer/components/TransferPagination.tsx +115 -111
  167. package/src/components/form/Transfer/components/TransferSearch.tsx +52 -55
  168. package/src/components/form/Transfer/hooks/useTransferData.ts +91 -81
  169. package/src/components/form/Transfer/hooks/useTransferState.ts +22 -16
  170. package/src/components/form/Transfer/index.ts +2 -8
  171. package/src/components/form/Upload/Upload.styles.ts +21 -21
  172. package/src/components/form/Upload/Upload.tsx +189 -142
  173. package/src/components/form/Upload/Upload.types.ts +31 -31
  174. package/src/components/form/Upload/index.tsx +1 -1
  175. package/src/components/form/index.tsx +60 -29
  176. package/src/components/index.tsx +0 -1
  177. package/src/components/layout/Affix/Affix.styles.ts +16 -11
  178. package/src/components/layout/Affix/Affix.tsx +67 -75
  179. package/src/components/layout/Affix/Affix.types.ts +18 -18
  180. package/src/components/layout/Affix/index.tsx +1 -1
  181. package/src/components/layout/Col/Col.styles.ts +17 -17
  182. package/src/components/layout/Col/Col.test.tsx +7 -5
  183. package/src/components/layout/Col/Col.tsx +3 -21
  184. package/src/components/layout/Col/Col.types.ts +1 -1
  185. package/src/components/layout/Container/Container.styles.ts +3 -1
  186. package/src/components/layout/Container/Container.tsx +2 -11
  187. package/src/components/layout/Grid/Grid.tsx +3 -53
  188. package/src/components/layout/Layout/Content.tsx +24 -32
  189. package/src/components/layout/Layout/Footer.tsx +24 -32
  190. package/src/components/layout/Layout/Header.tsx +24 -32
  191. package/src/components/layout/Layout/Layout.styles.ts +17 -17
  192. package/src/components/layout/Layout/Layout.tsx +14 -25
  193. package/src/components/layout/Layout/Layout.types.ts +29 -29
  194. package/src/components/layout/Layout/Sider.tsx +44 -56
  195. package/src/components/layout/Layout/index.tsx +16 -2
  196. package/src/components/layout/Row/Row.tsx +15 -43
  197. package/src/components/layout/Space/Space.tsx +3 -11
  198. package/src/components/layout/Space/Space.types.ts +1 -1
  199. package/src/components/layout/index.tsx +29 -19
  200. package/src/components/navigation/Menu/Menu.constants.ts +69 -0
  201. package/src/components/navigation/Menu/Menu.stories.tsx +107 -0
  202. package/src/components/navigation/Menu/Menu.styles.ts +25 -37
  203. package/src/components/navigation/Menu/Menu.tsx +8 -11
  204. package/src/components/navigation/Menu/Menu.types.ts +2 -2
  205. package/src/components/navigation/Menu/Menu.utils.ts +17 -17
  206. package/src/components/navigation/Menu/MenuItem.tsx +9 -11
  207. package/src/components/navigation/Menu/SubMenu.tsx +8 -6
  208. package/src/components/navigation/Menu/index.tsx +4 -69
  209. package/src/components/navigation/NavBar/NavBar.styles.ts +1 -1
  210. package/src/components/navigation/NavBar/NavBar.tsx +7 -10
  211. package/src/components/navigation/NavBar/NavBar.types.ts +3 -3
  212. package/src/components/navigation/NavBar/index.tsx +1 -1
  213. package/src/components/navigation/Pagination/Pagination.test.tsx +2 -3
  214. package/src/components/navigation/Pagination/Pagination.tsx +3 -3
  215. package/src/components/navigation/Pagination/Pagination.types.ts +3 -2
  216. package/src/components/navigation/Pagination/index.ts +9 -3
  217. package/src/components/navigation/Steps/Step.tsx +24 -44
  218. package/src/components/navigation/Steps/Steps.styles.ts +28 -13
  219. package/src/components/navigation/Steps/Steps.test.tsx +2 -0
  220. package/src/components/navigation/Steps/Steps.tsx +88 -89
  221. package/src/components/navigation/Steps/Steps.types.ts +30 -30
  222. package/src/components/navigation/Steps/index.tsx +1 -1
  223. package/src/components/navigation/Tabs/Tabs.test.tsx +3 -2
  224. package/src/components/navigation/Tabs/Tabs.types.ts +4 -3
  225. package/src/components/navigation/index.tsx +21 -16
  226. package/src/constants/index.ts +1 -1
  227. package/src/hooks/index.ts +52 -102
  228. package/src/hooks/types.ts +4 -5
  229. package/src/hooks/useAsync.ts +46 -47
  230. package/src/hooks/useClickOutside.ts +52 -0
  231. package/src/hooks/useCounter.ts +87 -0
  232. package/src/hooks/useDebounce.ts +150 -0
  233. package/src/hooks/useDeepCompareEffect.ts +88 -0
  234. package/src/hooks/useEventListener.ts +77 -0
  235. package/src/hooks/useMediaQuery.ts +75 -0
  236. package/src/hooks/useMutation.ts +233 -0
  237. package/src/hooks/usePerformance.ts +1 -64
  238. package/src/hooks/usePlatform.ts +3 -1
  239. package/src/hooks/usePrevious.ts +25 -0
  240. package/src/hooks/useRequest.ts +12 -7
  241. package/src/hooks/useStateManagement.ts +1 -1
  242. package/src/hooks/useStorage.ts +169 -0
  243. package/src/hooks/useStyle.ts +8 -2
  244. package/src/hooks/useToggle.ts +54 -0
  245. package/src/index.ts +34 -9
  246. package/src/theme/ThemeProvider.tsx +3 -7
  247. package/src/theme/ThemeProvider.types.ts +1 -1
  248. package/src/theme/defaults.ts +1 -1
  249. package/src/theme/design-system.ts +2 -2
  250. package/src/theme/design-tokens.ts +85 -99
  251. package/src/theme/generated/dark-theme.scss +1 -1
  252. package/src/theme/generated/tokens.scss +82 -18
  253. package/src/theme/index.ts +8 -29
  254. package/src/theme/responsive.tsx +36 -34
  255. package/src/theme/styles.ts +1 -1
  256. package/src/theme/useThemeUtils.ts +43 -43
  257. package/src/theme/utils.ts +32 -32
  258. package/src/theme/variables.ts +70 -51
  259. package/src/types/accessibility.ts +36 -37
  260. package/src/types/button.ts +25 -27
  261. package/src/types/component-props.ts +6 -1
  262. package/src/types/glob.d.ts +4 -0
  263. package/src/types/index.ts +2 -2
  264. package/src/types/standardized-components.ts +9 -3
  265. package/src/types/utils.ts +13 -23
  266. package/src/utils/__tests__/responsiveUtils.test.ts +5 -4
  267. package/src/utils/abort-controller.ts +48 -0
  268. package/src/utils/cache.ts +2 -6
  269. package/src/utils/createNamespace.ts +4 -4
  270. package/src/utils/environment.ts +26 -6
  271. package/src/utils/error-handler.ts +2 -2
  272. package/src/utils/errorLogger.ts +16 -20
  273. package/src/utils/formatUtils.ts +38 -70
  274. package/src/utils/http/error-codes.ts +314 -0
  275. package/src/utils/http/http-client.test.ts +63 -0
  276. package/src/utils/{network → http}/http-client.ts +45 -35
  277. package/src/utils/http/request-cache.ts +127 -0
  278. package/src/utils/http/request.ts +954 -0
  279. package/src/utils/http/taro-adapter.test.ts +74 -0
  280. package/src/utils/http/taro-adapter.ts +24 -0
  281. package/src/utils/http/types.ts +414 -0
  282. package/src/utils/http/web-adapter.ts +33 -0
  283. package/src/utils/index.ts +5 -8
  284. package/src/utils/inputValidator.ts +17 -14
  285. package/src/utils/performance/performance.ts +60 -71
  286. package/src/utils/responsiveUtils.ts +7 -16
  287. package/src/utils/rtl-support.ts +29 -19
  288. package/src/utils/security/api-security.ts +47 -39
  289. package/src/utils/securityHeaders.ts +61 -67
  290. package/src/utils/typeHelpers.ts +10 -10
  291. package/src/utils/types/dataProcessing.ts +93 -92
  292. package/src/utils/types/typeHelpers.ts +31 -21
  293. package/src/utils/xssProtection.ts +96 -48
  294. package/dist/js/index-6NJ3A1Dn.js.map +0 -1
  295. package/dist/js/index-DffLRSro.js.map +0 -1
  296. package/src/components/form/Input/Input.enhanced.tsx +0 -732
  297. package/src/components/navigation/Menu/__tests__/Menu.test.tsx +0 -687
  298. package/src/components/navigation/Tree/Tree.styles.ts +0 -553
  299. package/src/components/navigation/Tree/Tree.test.basic.tsx +0 -7
  300. package/src/components/navigation/Tree/Tree.test.functional.tsx +0 -496
  301. package/src/components/navigation/Tree/Tree.test.import.check.tsx +0 -6
  302. package/src/components/navigation/Tree/Tree.test.import.tsx +0 -6
  303. package/src/components/navigation/Tree/Tree.test.minimal.tsx +0 -5
  304. package/src/components/navigation/Tree/Tree.test.simple.tsx +0 -30
  305. package/src/components/navigation/Tree/Tree.test.tsx +0 -908
  306. package/src/components/navigation/Tree/Tree.test.working.tsx +0 -673
  307. package/src/components/navigation/Tree/Tree.tsx +0 -600
  308. package/src/components/navigation/Tree/Tree.types.ts +0 -909
  309. package/src/components/navigation/Tree/Tree.utils.ts +0 -452
  310. package/src/components/navigation/Tree/index.ts +0 -33
  311. package/src/components/navigation/Tree/index.tsx +0 -23
  312. package/src/utils/network/http-client.test.ts +0 -18
@@ -1,16 +1,9 @@
1
- import React, { forwardRef, useState, useCallback, createContext } from 'react';
1
+ import React, { forwardRef, createContext } from 'react';
2
2
  import { Form as TaroForm } from '@tarojs/components';
3
3
  import type { ITouchEvent } from '@tarojs/components';
4
4
  import { formStyles } from './Form.styles';
5
- import type {
6
- FormProps,
7
- FormRef,
8
- FormInstance,
9
- FormContext,
10
- FormValues,
11
- FormErrors,
12
- FormFieldInfo,
13
- } from './Form.types';
5
+ import type { FormProps, FormRef, FormContext, FormErrors } from './Form.types';
6
+ import { useFormLogic } from './useFormLogic';
14
7
 
15
8
  // 创建表单上下文
16
9
  export const FormContextProvider = createContext<FormContext | null>(null);
@@ -20,480 +13,30 @@ export type { FormContext } from './Form.types';
20
13
 
21
14
  /** 表单组件 */
22
15
  export const FormComponent = forwardRef<FormRef, FormProps>((props, ref) => {
23
- const {
24
- initialValues = {},
25
- layout = 'horizontal',
26
- labelAlign = 'right',
27
- size = 'md',
28
- labelWidth = 120,
29
- labelSuffix,
30
- colon = true,
31
- requiredMark = true,
32
- rules = {},
33
- validateTrigger = 'onBlur',
34
- immediate = false,
35
- onSubmit,
36
- onReset,
37
- onValuesChange,
38
- onFieldsChange,
39
- onFinishFailed,
40
- className,
41
- style,
42
- accessible = true,
43
- accessibilityLabel,
44
- accessibilityRole = 'form',
45
- disabled = false,
46
- readonly = false,
47
- showValidateMessage = true,
48
- scrollToFirstError = true,
49
- preserve = true,
50
- children,
51
- ...restProps
52
- } = props;
16
+ const { layout = 'horizontal', size = 'md', className, style, children, ...restProps } = props;
53
17
 
54
18
  // Filter out React DOM event handlers that are incompatible with TaroForm
55
19
  const filteredProps = Object.fromEntries(
56
- Object.entries(restProps).filter(([key]) => !key.startsWith('on') || key.includes('Click') || key.includes('Touch'))
57
- );
58
-
59
-
60
- const [formInstance, setFormInstance] = useState<FormInstance>({
61
- values: { ...initialValues },
62
- errors: {},
63
- touched: {},
64
- validating: {},
65
- fields: {},
66
- rules: { ...rules },
67
- status: 'normal',
68
- disabled,
69
- readonly,
70
- });
71
-
72
- // 更新表单实例
73
- const updateFormInstance = useCallback((updates: Partial<FormInstance>) => {
74
- setFormInstance((prev): FormInstance => ({ ...prev, ...updates }));
75
- }, []);
76
-
77
- // 注册字段
78
- const registerField = useCallback((name: string, info: Partial<FormFieldInfo>) => {
79
- setFormInstance((prev): FormInstance => ({
80
- ...prev,
81
- fields: {
82
- ...prev.fields,
83
- [name]: {
84
- name,
85
- value: prev.values[name] ?? info.value ?? '',
86
- errors: [],
87
- touched: false,
88
- validating: false,
89
- rules: info.rules || [],
90
- ...info,
91
- },
92
- },
93
- }));
94
- }, []);
95
-
96
- // 注销字段
97
- const unregisterField = useCallback(
98
- (name: string) => {
99
- setFormInstance((prev): FormInstance => {
100
- const newFields: Record<string, FormFieldInfo> = { ...prev.fields };
101
- delete newFields[name];
102
-
103
- if (!preserve) {
104
- const newValues: FormValues = { ...prev.values };
105
- delete newValues[name];
106
-
107
- return {
108
- ...prev,
109
- fields: newFields,
110
- values: newValues,
111
- };
112
- }
113
-
114
- return { ...prev, fields: newFields };
115
- });
116
- },
117
- [preserve],
118
- );
119
-
120
- // 更新字段
121
- const updateField = useCallback((name: string, info: Partial<FormFieldInfo>) => {
122
- setFormInstance((prev): FormInstance => ({
123
- ...prev,
124
- fields: {
125
- ...prev.fields,
126
- [name]: {
127
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
128
- ...info,
129
- },
130
- },
131
- }));
132
- }, []);
133
-
134
- // 获取字段
135
- const getField = useCallback(
136
- (name: string): FormFieldInfo | null => {
137
- return formInstance.fields[name] || null;
138
- },
139
- [formInstance.fields],
140
- );
141
-
142
- // 设置字段值
143
- const setFieldValue = useCallback(
144
- (name: string, value: any) => {
145
- setFormInstance((prev): FormInstance => {
146
- const newValues: FormValues = { ...prev.values, [name]: value };
147
- const newFields: Record<string, FormFieldInfo> = {
148
- ...prev.fields,
149
- [name]: {
150
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
151
- value,
152
- },
153
- };
154
-
155
- // 触发值变化事件
156
- if (onValuesChange) {
157
- onValuesChange({ [name]: value }, newValues);
158
- }
159
-
160
- // 触发字段变化事件
161
- if (onFieldsChange) {
162
- const changedField = newFields[name] as FormFieldInfo;
163
- onFieldsChange([changedField], Object.values(newFields));
164
- }
165
-
166
- return {
167
- ...prev,
168
- values: newValues,
169
- fields: newFields,
170
- };
171
- });
172
- },
173
- [onValuesChange, onFieldsChange],
174
- );
175
-
176
- // 获取字段值
177
- const getFieldValue = useCallback(
178
- (name: string): any => {
179
- return formInstance.values[name];
180
- },
181
- [formInstance.values],
182
- );
183
-
184
- // 设置字段错误
185
- const setFieldError = useCallback((name: string, error: string | string[]) => {
186
- setFormInstance((prev): FormInstance => ({
187
- ...prev,
188
- errors: {
189
- ...prev.errors,
190
- [name]: Array.isArray(error) ? error : [error],
191
- },
192
- fields: {
193
- ...prev.fields,
194
- [name]: {
195
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
196
- errors: Array.isArray(error) ? error : [error],
197
- },
198
- },
199
- }));
200
- }, []);
201
-
202
- // 获取字段错误
203
- const getFieldError = useCallback(
204
- (name: string): string[] => {
205
- const errors = formInstance.errors[name];
206
- return Array.isArray(errors) ? errors : (errors ? [errors] : []);
207
- },
208
- [formInstance.errors],
209
- );
210
-
211
- // 设置字段 touched 状态
212
- const setFieldTouched = useCallback((name: string, touched: boolean) => {
213
- setFormInstance((prev): FormInstance => ({
214
- ...prev,
215
- touched: {
216
- ...prev.touched,
217
- [name]: touched,
218
- },
219
- fields: {
220
- ...prev.fields,
221
- [name]: {
222
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
223
- touched,
224
- },
225
- },
226
- }));
227
- }, []);
228
-
229
- // 设置字段 validating 状态
230
- const setFieldValidating = useCallback((name: string, validating: boolean) => {
231
- setFormInstance((prev): FormInstance => ({
232
- ...prev,
233
- validating: {
234
- ...prev.validating,
235
- [name]: validating,
236
- },
237
- fields: {
238
- ...prev.fields,
239
- [name]: {
240
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
241
- validating,
242
- },
243
- },
244
- }));
245
- }, []);
246
-
247
- // 验证单个字段
248
- const validateField = useCallback(
249
- async (name: string): Promise<{ valid: boolean; errors: string[]; value: any }> => {
250
- const field = formInstance.fields[name];
251
- if (!field) {
252
- return { valid: true, errors: [], value: undefined };
253
- }
254
-
255
- setFieldValidating(name, true);
256
- const errors: string[] = [];
257
-
258
- try {
259
- // 验证必填
260
- if (field.rules.some((rule) => rule.required)) {
261
- if (field.value === undefined || field.value === null || field.value === '') {
262
- const requiredRule = field.rules.find((rule) => rule.required);
263
- errors.push(requiredRule?.message || '此字段为必填项');
264
- }
265
- }
266
-
267
- // 验证长度
268
- if (typeof field.value === 'string') {
269
- field.rules.forEach((rule) => {
270
- if (rule.min !== undefined && field.value.length < rule.min) {
271
- errors.push(rule.message || `最少需要${rule.min}个字符`);
272
- }
273
- if (rule.max !== undefined && field.value.length > rule.max) {
274
- errors.push(rule.message || `最多允许${rule.max}个字符`);
275
- }
276
- });
277
- }
278
-
279
- // 验证数值范围
280
- if (typeof field.value === 'number') {
281
- field.rules.forEach((rule) => {
282
- if (rule.minValue !== undefined && field.value < rule.minValue) {
283
- errors.push(rule.message || `最小值为${rule.minValue}`);
284
- }
285
- if (rule.maxValue !== undefined && field.value > rule.maxValue) {
286
- errors.push(rule.message || `最大值为${rule.maxValue}`);
287
- }
288
- });
289
- }
290
-
291
- // 验证正则表达式
292
- field.rules.forEach((rule) => {
293
- if (rule.pattern && typeof field.value === 'string' && !rule.pattern.test(field.value)) {
294
- errors.push(rule.message || '输入格式不正确');
295
- }
296
- });
297
-
298
- // 验证枚举值
299
- field.rules.forEach((rule) => {
300
- if (rule.enum && !rule.enum.includes(field.value)) {
301
- errors.push(rule.message || '输入值不在允许范围内');
302
- }
303
- });
304
-
305
- // 自定义验证函数
306
- for (const rule of field.rules) {
307
- if (rule.validator) {
308
- try {
309
- const result = rule.validator(field.value, formInstance.values);
310
- if (typeof result === 'string') {
311
- errors.push(result);
312
- } else if (!result) {
313
- errors.push(rule.message || '验证失败');
314
- }
315
- } catch (error) {
316
- errors.push(rule.message || '验证失败');
317
- }
318
- }
319
- }
320
-
321
- // 异步验证函数
322
- for (const rule of field.rules) {
323
- if (rule.asyncValidator) {
324
- try {
325
- const result = await rule.asyncValidator(field.value, formInstance.values);
326
- if (typeof result === 'string') {
327
- errors.push(result);
328
- } else if (!result) {
329
- errors.push(rule.message || '验证失败');
330
- }
331
- } catch (error) {
332
- errors.push(rule.message || '验证失败');
333
- }
334
- }
335
- }
336
-
337
- setFieldError(name, errors);
338
- return { valid: errors.length === 0, errors, value: field.value };
339
- } finally {
340
- setFieldValidating(name, false);
341
- }
342
- },
343
- [formInstance, setFieldError, setFieldValidating],
20
+ Object.entries(restProps).filter(
21
+ ([key]) => !key.startsWith('on') || key.includes('Click') || key.includes('Touch'),
22
+ ),
344
23
  );
345
24
 
346
- // 重置字段
347
- const resetField = useCallback((name: string) => {
348
- setFormInstance((prev): FormInstance => {
349
- const field = prev.fields[name];
350
- if (!field) return prev;
351
-
352
- const defaultValue = field.rules.find((rule) => rule.defaultValue !== undefined)?.defaultValue;
353
- const newValues: FormValues = { ...prev.values, [name]: defaultValue };
354
- const newFields: Record<string, FormFieldInfo> = {
355
- ...prev.fields,
356
- [name]: {
357
- ...field,
358
- value: defaultValue,
359
- errors: [],
360
- touched: false,
361
- validating: false,
362
- },
363
- };
364
-
365
- return {
366
- ...prev,
367
- values: newValues,
368
- fields: newFields,
369
- errors: { ...prev.errors, [name]: [] },
370
- touched: { ...prev.touched, [name]: false },
371
- validating: { ...prev.validating, [name]: false },
372
- };
373
- });
374
- }, []);
375
-
376
- // 创建表单上下文
377
- const formContext: FormContext = {
378
- form: formInstance,
379
- config: {
380
- layout,
381
- labelAlign,
382
- size,
383
- labelWidth,
384
- labelSuffix,
385
- colon,
386
- requiredMark,
387
- validateTrigger,
388
- showValidateMessage,
389
- disabled,
390
- readonly,
391
- },
392
- styleConfig: formStyles['getStyleConfig'](),
393
- registerField,
394
- unregisterField,
395
- updateField,
396
- getField,
25
+ const {
26
+ formInstance,
27
+ formContext,
28
+ handleSubmit,
29
+ handleReset,
30
+ updateFormInstance,
397
31
  setFieldValue,
398
- getFieldValue,
399
32
  setFieldError,
400
- getFieldError,
401
33
  setFieldTouched,
402
34
  setFieldValidating,
403
35
  validateField,
404
36
  resetField,
405
- };
406
-
407
- // 处理表单提交
408
- const handleSubmit = useCallback(
409
- async (event: any) => {
410
- event.preventDefault?.();
411
-
412
- // 验证所有字段
413
- const validationResults = await Promise.all(Object.keys(formInstance.fields).map((name) => validateField(name)));
414
-
415
- const hasErrors = validationResults.some((result) => !result.valid);
416
-
417
- if (hasErrors) {
418
- const errors: FormErrors = {};
419
- validationResults.forEach((result, index) => {
420
- const fieldName = Object.keys(formInstance.fields)[index];
421
- if (fieldName && !result.valid) {
422
- errors[fieldName] = result.errors;
423
- }
424
- });
425
-
426
- onFinishFailed?.(errors, formInstance.values);
427
- return;
428
- }
429
-
430
- // 提交表单
431
- try {
432
- // Create a synthetic event for compatibility
433
- const syntheticEvent = {
434
- preventDefault: () => {},
435
- stopPropagation: () => {},
436
- ...event
437
- } as ITouchEvent;
438
-
439
- await onSubmit?.(formInstance.values, syntheticEvent);
440
- } catch (error) {
441
- console.error('Form submission error:', error);
442
- }
443
- },
444
- [formInstance, validateField, onSubmit, onFinishFailed],
445
- );
446
-
447
- // 处理表单重置
448
- const handleReset = useCallback(
449
- (event: any) => {
450
- setFormInstance((prev): FormInstance => {
451
- const newValues: FormValues = {};
452
- const newFields: Record<string, FormFieldInfo> = {};
453
- const newErrors: FormErrors = {};
454
- const newTouched: Record<string, boolean> = {};
455
- const newValidating: Record<string, boolean> = {};
456
-
457
- Object.keys(prev.fields).forEach((name) => {
458
- const field = prev.fields[name];
459
- if (field) {
460
- const defaultValue = field.rules.find((rule) => rule.defaultValue !== undefined)?.defaultValue;
461
- newValues[name] = defaultValue;
462
- newFields[name] = {
463
- name: field.name,
464
- value: defaultValue,
465
- errors: [],
466
- touched: false,
467
- validating: false,
468
- rules: field.rules,
469
- };
470
- newErrors[name] = [];
471
- newTouched[name] = false;
472
- newValidating[name] = false;
473
- }
474
- });
475
-
476
- return {
477
- ...prev,
478
- values: newValues,
479
- fields: newFields,
480
- errors: newErrors,
481
- touched: newTouched,
482
- validating: newValidating,
483
- };
484
- });
485
-
486
- // Create a synthetic event for compatibility
487
- const syntheticEvent = {
488
- preventDefault: () => {},
489
- stopPropagation: () => {},
490
- ...event
491
- } as ITouchEvent;
492
-
493
- onReset?.(formInstance.values, syntheticEvent);
494
- },
495
- [formInstance.values, onReset],
496
- );
37
+ updateField,
38
+ getFieldError,
39
+ } = useFormLogic(props);
497
40
 
498
41
  // 暴露给外部的引用方法
499
42
  React.useImperativeHandle(
@@ -501,10 +44,9 @@ export const FormComponent = forwardRef<FormRef, FormProps>((props, ref) => {
501
44
  () => ({
502
45
  getValues: () => formInstance.values,
503
46
  setValues: (values) => {
504
- setFormInstance((prev): FormInstance => ({
505
- ...prev,
506
- values: { ...prev.values, ...values },
507
- }));
47
+ updateFormInstance({
48
+ values: { ...formInstance.values, ...values },
49
+ });
508
50
  },
509
51
  getFieldValue: (name) => formInstance.values[name],
510
52
  setFieldValue: (name, value) => setFieldValue(name, value),
@@ -531,11 +73,11 @@ export const FormComponent = forwardRef<FormRef, FormProps>((props, ref) => {
531
73
  }
532
74
  });
533
75
 
534
- onFinishFailed?.(errors, formInstance.values);
76
+ props.onFinishFailed?.(errors, formInstance.values);
535
77
  throw errors;
536
78
  }
537
79
 
538
- await onSubmit?.(formInstance.values, {} as ITouchEvent);
80
+ await props.onSubmit?.(formInstance.values, {} as ITouchEvent);
539
81
  },
540
82
  validate: async (fields) => {
541
83
  const fieldNames = fields || Object.keys(formInstance.fields);
@@ -583,17 +125,13 @@ export const FormComponent = forwardRef<FormRef, FormProps>((props, ref) => {
583
125
  if (fields) {
584
126
  fields.forEach((name) => setFieldError(name, []));
585
127
  } else {
586
- setFormInstance((prev): FormInstance => ({
587
- ...prev,
588
- errors: {},
589
- }));
128
+ updateFormInstance({ errors: {} });
590
129
  }
591
130
  },
592
131
  setErrors: (errors) => {
593
- setFormInstance((prev): FormInstance => ({
594
- ...prev,
595
- errors: { ...prev.errors, ...errors },
596
- }));
132
+ updateFormInstance({
133
+ errors: { ...formInstance.errors, ...errors },
134
+ });
597
135
  },
598
136
  getFieldError: (name) => getFieldError(name),
599
137
  getErrors: () => formInstance.errors,
@@ -617,46 +155,70 @@ export const FormComponent = forwardRef<FormRef, FormProps>((props, ref) => {
617
155
  });
618
156
  },
619
157
  addFieldRules: (name, newRules) => {
620
- setFormInstance((prev): FormInstance => ({
621
- ...prev,
158
+ updateFormInstance({
622
159
  rules: {
623
- ...prev.rules,
624
- [name]: [...(prev.rules[name] || []), ...newRules],
160
+ ...formInstance.rules,
161
+ [name]: [...(formInstance.rules[name] || []), ...newRules],
625
162
  },
626
163
  fields: {
627
- ...prev.fields,
164
+ ...formInstance.fields,
628
165
  [name]: {
629
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
630
- rules: [...(prev.fields[name]?.rules || []), ...newRules],
166
+ ...(formInstance.fields[name] || {
167
+ name,
168
+ value: '',
169
+ errors: [],
170
+ touched: false,
171
+ validating: false,
172
+ rules: [],
173
+ }),
174
+ rules: [...(formInstance.fields[name]?.rules || []), ...newRules],
631
175
  },
632
176
  },
633
- }));
177
+ });
634
178
  },
635
179
  removeFieldRules: (name) => {
636
- setFormInstance((prev): FormInstance => ({
637
- ...prev,
638
- rules: { ...prev.rules, [name]: [] },
180
+ updateFormInstance({
181
+ rules: { ...formInstance.rules, [name]: [] },
639
182
  fields: {
640
- ...prev.fields,
183
+ ...formInstance.fields,
641
184
  [name]: {
642
- ...(prev.fields[name] || { name, value: '', errors: [], touched: false, validating: false, rules: [] }),
185
+ ...(formInstance.fields[name] || {
186
+ name,
187
+ value: '',
188
+ errors: [],
189
+ touched: false,
190
+ validating: false,
191
+ rules: [],
192
+ }),
643
193
  rules: [],
644
194
  },
645
195
  },
646
- }));
196
+ });
647
197
  },
648
198
  getFieldRules: (name) => formInstance.rules[name] || [],
649
199
  setStatus: (status) => updateFormInstance({ status }),
650
200
  getStatus: () => formInstance.status,
651
201
  setDisabled: (disabled) => updateFormInstance({ disabled }),
652
202
  setReadonly: (readonly) => updateFormInstance({ readonly }),
653
- scrollToField: (name) => {
203
+ scrollToField: (_name) => {
654
204
  // 在实际实现中,这里会滚动到指定字段
655
- console.log('Scroll to field:', name);
656
205
  },
657
206
  getFormInstance: () => formInstance,
658
207
  }),
659
- [formInstance, validateField, setFieldValue, updateField, updateFormInstance, onFinishFailed, onSubmit],
208
+ [
209
+ formInstance,
210
+ validateField,
211
+ setFieldValue,
212
+ updateField,
213
+ updateFormInstance,
214
+ props.onFinishFailed,
215
+ props.onSubmit,
216
+ setFieldError,
217
+ getFieldError,
218
+ setFieldTouched,
219
+ setFieldValidating,
220
+ resetField,
221
+ ],
660
222
  );
661
223
 
662
224
  // 生成表单样式
@@ -85,7 +85,22 @@ export interface FormFieldInfo {
85
85
  export type FormNativeProps = FormHTMLAttributes<HTMLFormElement>;
86
86
 
87
87
  /** 表单组件属性接口 */
88
- export interface FormProps extends Omit<FormNativeProps, 'onSubmit' | 'onReset' | 'dangerouslySetInnerHTML' | 'onTouchStart' | 'onTouchMove' | 'onTouchEnd' | 'onTouchCancel' | 'onClick' | 'onMouseDown' | 'onMouseUp' | 'onMouseOver' | 'onMouseOut'> {
88
+ export interface FormProps
89
+ extends Omit<
90
+ FormNativeProps,
91
+ | 'onSubmit'
92
+ | 'onReset'
93
+ | 'dangerouslySetInnerHTML'
94
+ | 'onTouchStart'
95
+ | 'onTouchMove'
96
+ | 'onTouchEnd'
97
+ | 'onTouchCancel'
98
+ | 'onClick'
99
+ | 'onMouseDown'
100
+ | 'onMouseUp'
101
+ | 'onMouseOver'
102
+ | 'onMouseOut'
103
+ > {
89
104
  /** 表单初始值 */
90
105
  initialValues?: FormValues;
91
106
  /** 表单布局 */