x-ui-design 1.0.31-gamma.1 → 1.0.31

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 (219) hide show
  1. package/.github/workflows/x-ui-design.yml +14 -0
  2. package/README.md +22 -1
  3. package/compile.sh +15 -0
  4. package/dist/{components → esm/types/components}/DatePicker/DatePicker.d.ts +2 -1
  5. package/dist/{components → esm/types/components}/DatePicker/RangePicker/RangePicker.d.ts +2 -1
  6. package/dist/{components → esm/types/components}/Dropdown/Dropdown.d.ts +2 -1
  7. package/dist/{components → esm/types/components}/Empty/Empty.d.ts +2 -1
  8. package/dist/{components → esm/types/components}/Form/Item/Item.d.ts +2 -1
  9. package/dist/esm/types/components/Icons/Icons.d.ts +19 -0
  10. package/dist/esm/types/components/Input/Input.d.ts +16 -0
  11. package/dist/{components → esm/types/components}/Input/Textarea/Textarea.d.ts +2 -1
  12. package/dist/{components → esm/types/components}/Popover/Popover.d.ts +2 -1
  13. package/dist/{components → esm/types/components}/Radio/Button/Button.d.ts +2 -1
  14. package/dist/{components → esm/types/components}/Radio/Group/Group.d.ts +2 -1
  15. package/dist/{components → esm/types/components}/Radio/Radio.d.ts +2 -1
  16. package/dist/{components → esm/types/components}/Result/Result.d.ts +2 -1
  17. package/dist/{components → esm/types/components}/Switch/Switch.d.ts +2 -1
  18. package/dist/{components → esm/types/components}/Upload/Upload.d.ts +2 -1
  19. package/dist/esm/types/index.d.ts +56 -0
  20. package/dist/index.d.ts +150 -1
  21. package/dist/index.esm.js +4209 -3779
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/index.js +4325 -3895
  24. package/dist/index.js.map +1 -1
  25. package/eslint.config.mjs +16 -0
  26. package/lib/components/Button/Button.tsx +136 -0
  27. package/lib/components/Button/index.ts +1 -0
  28. package/lib/components/Button/style.css +197 -0
  29. package/lib/components/Checkbox/Checkbox.tsx +131 -0
  30. package/lib/components/Checkbox/index.ts +1 -0
  31. package/lib/components/Checkbox/style.css +95 -0
  32. package/lib/components/ConditionalWrapper/index.tsx +12 -0
  33. package/lib/components/DatePicker/DatePicker.tsx +526 -0
  34. package/lib/components/DatePicker/RangePicker/RangePicker.tsx +500 -0
  35. package/lib/components/DatePicker/RangePicker/index.ts +1 -0
  36. package/lib/components/DatePicker/RangePicker/style.css +434 -0
  37. package/lib/components/DatePicker/TimePicker/TimePicker.tsx +497 -0
  38. package/lib/components/DatePicker/TimePicker/index.ts +1 -0
  39. package/lib/components/DatePicker/TimePicker/style.css +197 -0
  40. package/lib/components/DatePicker/index.ts +1 -0
  41. package/lib/components/DatePicker/style.css +318 -0
  42. package/lib/components/Dropdown/Dropdown.tsx +234 -0
  43. package/lib/components/Dropdown/index.ts +1 -0
  44. package/lib/components/Dropdown/style.css +124 -0
  45. package/lib/components/Empty/Empty.tsx +45 -0
  46. package/lib/components/Empty/index.ts +1 -0
  47. package/lib/components/Empty/style.css +13 -0
  48. package/lib/components/Form/Form.tsx +130 -0
  49. package/lib/components/Form/Item/Item.tsx +294 -0
  50. package/lib/components/Form/Item/index.ts +1 -0
  51. package/lib/components/Form/Item/style.css +61 -0
  52. package/lib/components/Form/index.ts +1 -0
  53. package/lib/components/Icons/Icons.tsx +433 -0
  54. package/lib/components/Icons/index.ts +15 -0
  55. package/lib/components/Input/Input.tsx +228 -0
  56. package/lib/components/Input/Textarea/Textarea.tsx +110 -0
  57. package/lib/components/Input/Textarea/index.ts +1 -0
  58. package/lib/components/Input/Textarea/style.css +104 -0
  59. package/lib/components/Input/index.ts +1 -0
  60. package/lib/components/Input/style.css +137 -0
  61. package/lib/components/Menu/Item/Item.tsx +65 -0
  62. package/lib/components/Menu/Menu.tsx +261 -0
  63. package/lib/components/Menu/SubMenu/SubMenu.tsx +68 -0
  64. package/lib/components/Menu/index.css +145 -0
  65. package/lib/components/Menu/index.ts +1 -0
  66. package/lib/components/Popover/Popover.tsx +135 -0
  67. package/lib/components/Popover/index.ts +1 -0
  68. package/lib/components/Popover/style.css +82 -0
  69. package/lib/components/Radio/Button/Button.tsx +42 -0
  70. package/lib/components/Radio/Button/index.ts +1 -0
  71. package/lib/components/Radio/Button/style.css +43 -0
  72. package/lib/components/Radio/Group/Group.tsx +105 -0
  73. package/lib/components/Radio/Group/index.ts +1 -0
  74. package/lib/components/Radio/Group/style.css +53 -0
  75. package/lib/components/Radio/Radio.tsx +83 -0
  76. package/lib/components/Radio/index.ts +1 -0
  77. package/lib/components/Radio/style.css +73 -0
  78. package/lib/components/Result/Result.tsx +39 -0
  79. package/lib/components/Result/index.ts +1 -0
  80. package/lib/components/Result/style.css +173 -0
  81. package/lib/components/Select/Option/Option.tsx +49 -0
  82. package/lib/components/Select/Option/index.ts +1 -0
  83. package/lib/components/Select/Option/style.css +50 -0
  84. package/lib/components/Select/Select.tsx +935 -0
  85. package/lib/components/Select/Tag/Tag.tsx +43 -0
  86. package/lib/components/Select/Tag/index.ts +1 -0
  87. package/lib/components/Select/Tag/style.css +87 -0
  88. package/lib/components/Select/index.ts +1 -0
  89. package/lib/components/Select/style.css +186 -0
  90. package/lib/components/Skeleton/Avatar/Avatar.tsx +61 -0
  91. package/lib/components/Skeleton/Avatar/index.ts +1 -0
  92. package/lib/components/Skeleton/Avatar/style.css +27 -0
  93. package/lib/components/Skeleton/Button/Button.tsx +44 -0
  94. package/lib/components/Skeleton/Button/index.ts +1 -0
  95. package/lib/components/Skeleton/Button/style.css +50 -0
  96. package/lib/components/Skeleton/Image/Image.tsx +45 -0
  97. package/lib/components/Skeleton/Image/index.ts +1 -0
  98. package/lib/components/Skeleton/Image/style.css +23 -0
  99. package/lib/components/Skeleton/Input/Input.tsx +42 -0
  100. package/lib/components/Skeleton/Input/index.ts +1 -0
  101. package/lib/components/Skeleton/Input/style.css +56 -0
  102. package/lib/components/Skeleton/Skeleton.tsx +97 -0
  103. package/lib/components/Skeleton/index.ts +1 -0
  104. package/lib/components/Skeleton/style.css +84 -0
  105. package/lib/components/Switch/Switch.tsx +68 -0
  106. package/lib/components/Switch/index.css +50 -0
  107. package/lib/components/Switch/index.ts +1 -0
  108. package/lib/components/Upload/Upload.tsx +291 -0
  109. package/lib/components/Upload/index.ts +1 -0
  110. package/lib/components/Upload/style.css +151 -0
  111. package/lib/global.d.ts +1 -0
  112. package/lib/helpers/flatten.ts +26 -0
  113. package/lib/helpers/index.ts +52 -0
  114. package/lib/helpers/mask.ts +52 -0
  115. package/lib/hooks/useForm.ts +548 -0
  116. package/lib/hooks/usePosition.ts +206 -0
  117. package/lib/hooks/useWatch.ts +41 -0
  118. package/lib/hooks/useWatchError.ts +20 -0
  119. package/lib/index.ts +184 -0
  120. package/lib/styles/global.css +57 -0
  121. package/lib/types/button.ts +83 -0
  122. package/lib/types/checkbox.ts +32 -0
  123. package/lib/types/datepicker.ts +165 -0
  124. package/lib/types/dropdown.ts +41 -0
  125. package/lib/types/empty.ts +8 -0
  126. package/lib/types/form.ts +179 -0
  127. package/lib/types/index.ts +38 -0
  128. package/lib/types/input.ts +72 -0
  129. package/lib/types/menu.ts +55 -0
  130. package/lib/types/popover.ts +16 -0
  131. package/lib/types/radio.ts +69 -0
  132. package/lib/types/result.ts +22 -0
  133. package/lib/types/select.ts +126 -0
  134. package/lib/types/skeleton.ts +62 -0
  135. package/lib/types/switch.ts +22 -0
  136. package/lib/types/upload.ts +67 -0
  137. package/lib/utils/index.ts +37 -0
  138. package/lib/utils/lazy.ts +17 -0
  139. package/next.config.ts +7 -0
  140. package/package.json +20 -22
  141. package/rollup.config.js +71 -0
  142. package/src/app/favicon.ico +0 -0
  143. package/src/app/globals.css +48 -0
  144. package/src/app/layout.d.ts +5 -0
  145. package/src/app/layout.tsx +16 -0
  146. package/src/app/page.d.ts +1 -0
  147. package/src/app/page.tsx +21 -0
  148. package/tsconfig.json +46 -0
  149. package/dist/components/Icons/Icons.d.ts +0 -18
  150. package/dist/components/Input/Input.d.ts +0 -11
  151. /package/dist/{components → esm/types/components}/Button/Button.d.ts +0 -0
  152. /package/dist/{components → esm/types/components}/Button/index.d.ts +0 -0
  153. /package/dist/{components → esm/types/components}/Checkbox/Checkbox.d.ts +0 -0
  154. /package/dist/{components → esm/types/components}/Checkbox/index.d.ts +0 -0
  155. /package/dist/{components → esm/types/components}/ConditionalWrapper/index.d.ts +0 -0
  156. /package/dist/{components → esm/types/components}/DatePicker/RangePicker/index.d.ts +0 -0
  157. /package/dist/{components → esm/types/components}/DatePicker/TimePicker/TimePicker.d.ts +0 -0
  158. /package/dist/{components → esm/types/components}/DatePicker/TimePicker/index.d.ts +0 -0
  159. /package/dist/{components → esm/types/components}/DatePicker/index.d.ts +0 -0
  160. /package/dist/{components → esm/types/components}/Dropdown/index.d.ts +0 -0
  161. /package/dist/{components → esm/types/components}/Empty/index.d.ts +0 -0
  162. /package/dist/{components → esm/types/components}/Form/Form.d.ts +0 -0
  163. /package/dist/{components → esm/types/components}/Form/Item/index.d.ts +0 -0
  164. /package/dist/{components → esm/types/components}/Form/index.d.ts +0 -0
  165. /package/dist/{components → esm/types/components}/Icons/index.d.ts +0 -0
  166. /package/dist/{components → esm/types/components}/Input/Textarea/index.d.ts +0 -0
  167. /package/dist/{components → esm/types/components}/Input/index.d.ts +0 -0
  168. /package/dist/{components → esm/types/components}/Menu/Item/Item.d.ts +0 -0
  169. /package/dist/{components → esm/types/components}/Menu/Menu.d.ts +0 -0
  170. /package/dist/{components → esm/types/components}/Menu/SubMenu/SubMenu.d.ts +0 -0
  171. /package/dist/{components → esm/types/components}/Menu/index.d.ts +0 -0
  172. /package/dist/{components → esm/types/components}/Popover/index.d.ts +0 -0
  173. /package/dist/{components → esm/types/components}/Radio/Button/index.d.ts +0 -0
  174. /package/dist/{components → esm/types/components}/Radio/Group/index.d.ts +0 -0
  175. /package/dist/{components → esm/types/components}/Radio/index.d.ts +0 -0
  176. /package/dist/{components → esm/types/components}/Result/index.d.ts +0 -0
  177. /package/dist/{components → esm/types/components}/Select/Option/Option.d.ts +0 -0
  178. /package/dist/{components → esm/types/components}/Select/Option/index.d.ts +0 -0
  179. /package/dist/{components → esm/types/components}/Select/Select.d.ts +0 -0
  180. /package/dist/{components → esm/types/components}/Select/Tag/Tag.d.ts +0 -0
  181. /package/dist/{components → esm/types/components}/Select/Tag/index.d.ts +0 -0
  182. /package/dist/{components → esm/types/components}/Select/index.d.ts +0 -0
  183. /package/dist/{components → esm/types/components}/Skeleton/Avatar/Avatar.d.ts +0 -0
  184. /package/dist/{components → esm/types/components}/Skeleton/Avatar/index.d.ts +0 -0
  185. /package/dist/{components → esm/types/components}/Skeleton/Button/Button.d.ts +0 -0
  186. /package/dist/{components → esm/types/components}/Skeleton/Button/index.d.ts +0 -0
  187. /package/dist/{components → esm/types/components}/Skeleton/Image/Image.d.ts +0 -0
  188. /package/dist/{components → esm/types/components}/Skeleton/Image/index.d.ts +0 -0
  189. /package/dist/{components → esm/types/components}/Skeleton/Input/Input.d.ts +0 -0
  190. /package/dist/{components → esm/types/components}/Skeleton/Input/index.d.ts +0 -0
  191. /package/dist/{components → esm/types/components}/Skeleton/Skeleton.d.ts +0 -0
  192. /package/dist/{components → esm/types/components}/Skeleton/index.d.ts +0 -0
  193. /package/dist/{components → esm/types/components}/Switch/index.d.ts +0 -0
  194. /package/dist/{components → esm/types/components}/Upload/index.d.ts +0 -0
  195. /package/dist/{helpers → esm/types/helpers}/flatten.d.ts +0 -0
  196. /package/dist/{helpers → esm/types/helpers}/index.d.ts +0 -0
  197. /package/dist/{helpers → esm/types/helpers}/mask.d.ts +0 -0
  198. /package/dist/{hooks → esm/types/hooks}/useForm.d.ts +0 -0
  199. /package/dist/{hooks → esm/types/hooks}/usePosition.d.ts +0 -0
  200. /package/dist/{hooks → esm/types/hooks}/useWatch.d.ts +0 -0
  201. /package/dist/{hooks → esm/types/hooks}/useWatchError.d.ts +0 -0
  202. /package/dist/{types → esm/types/types}/button.d.ts +0 -0
  203. /package/dist/{types → esm/types/types}/checkbox.d.ts +0 -0
  204. /package/dist/{types → esm/types/types}/datepicker.d.ts +0 -0
  205. /package/dist/{types → esm/types/types}/dropdown.d.ts +0 -0
  206. /package/dist/{types → esm/types/types}/empty.d.ts +0 -0
  207. /package/dist/{types → esm/types/types}/form.d.ts +0 -0
  208. /package/dist/{types → esm/types/types}/index.d.ts +0 -0
  209. /package/dist/{types → esm/types/types}/input.d.ts +0 -0
  210. /package/dist/{types → esm/types/types}/menu.d.ts +0 -0
  211. /package/dist/{types → esm/types/types}/popover.d.ts +0 -0
  212. /package/dist/{types → esm/types/types}/radio.d.ts +0 -0
  213. /package/dist/{types → esm/types/types}/result.d.ts +0 -0
  214. /package/dist/{types → esm/types/types}/select.d.ts +0 -0
  215. /package/dist/{types → esm/types/types}/skeleton.d.ts +0 -0
  216. /package/dist/{types → esm/types/types}/switch.d.ts +0 -0
  217. /package/dist/{types → esm/types/types}/upload.d.ts +0 -0
  218. /package/dist/{utils → esm/types/utils}/index.d.ts +0 -0
  219. /package/dist/{utils → esm/types/utils}/lazy.d.ts +0 -0
@@ -0,0 +1,548 @@
1
+ 'use client';
2
+
3
+ import { useRef, useState } from 'react';
4
+ import { RuleType, RuleTypes } from '../types';
5
+ import type {
6
+ FieldData,
7
+ FieldError,
8
+ FieldInstancesRef,
9
+ FormInstance,
10
+ RuleObject,
11
+ RuleRender
12
+ } from '../types/form';
13
+
14
+ const useForm = (
15
+ initialValues: Record<string, RuleTypes> = {},
16
+ onFieldsChange?: (changedFields: FieldData[]) => void,
17
+ onValuesChange?: (
18
+ changedValues: Record<string, RuleTypes>,
19
+ allValues: Record<string, RuleTypes>
20
+ ) => void,
21
+ scrollToFirstError?: boolean,
22
+ onFinish?: ((values: Record<string, RuleTypes>) => void) | undefined,
23
+ onFinishFailed?: (errorInfo: {
24
+ values: Record<string, RuleTypes>;
25
+ errorFields: Pick<FieldError, 'errors' | 'name'>[];
26
+ }) => void
27
+ ): FormInstance => {
28
+ const touchedFieldsRef = useRef(new Set<string>());
29
+ const rulesRef = useRef<Record<string, RuleObject[] | RuleRender>>({});
30
+ const warningsRef = useRef<Record<string, string[]>>({});
31
+ const _scrollToFirstError = useRef<boolean>(scrollToFirstError);
32
+ const stepRef = useRef<number>(0);
33
+ const formHandlersRef = useRef<{
34
+ onFinish?: ((values: Record<string, RuleTypes>) => void) | undefined,
35
+ onValuesChange?: (
36
+ changedValues: Record<string, RuleTypes>,
37
+ allValues: Record<string, RuleTypes>
38
+ ) => void
39
+ onFieldsChange?: (changedFields: FieldData[]) => void;
40
+ onFinishFailed?: (errorInfo: {
41
+ values: Record<string, RuleTypes>;
42
+ errorFields: Pick<FieldError, 'errors' | 'name'>[];
43
+ }) => void;
44
+ }>({
45
+ onFinish,
46
+ onValuesChange,
47
+ onFieldsChange,
48
+ onFinishFailed
49
+ })
50
+
51
+ const formRef = useRef<Record<number, Record<string, RuleTypes>>>({ [stepRef.current]: { ...initialValues } });
52
+ const trashFormRef = useRef<Record<string, RuleTypes>>({ ...initialValues });
53
+ const fieldInstancesRef = useRef<Record<string, FieldInstancesRef | null>>({});
54
+
55
+ const [isReseting, setIsReseting] = useState(false);
56
+
57
+ const errorsRef = useRef<Record<string, string[]>>({});
58
+ const errorSubscribers = useRef<
59
+ Record<string, ((errors: string[]) => void)[]>
60
+ >({});
61
+
62
+ const fieldSubscribers = useRef<
63
+ Record<string, ((value: RuleTypes) => void)[]>
64
+ >({});
65
+
66
+ const formSubscribers = useRef<
67
+ ((values: Record<string, RuleTypes>) => void)[]
68
+ >([]);
69
+
70
+ function getFormFields() {
71
+ return Object.assign({}, ...Object.values(formRef.current));
72
+ }
73
+
74
+ function getFieldInstance(name?: string) {
75
+ return name ? fieldInstancesRef.current[name] : fieldInstancesRef.current;
76
+ }
77
+
78
+ function getFieldValue(name: string) {
79
+ const formData = getFormFields();
80
+
81
+ return formData[name]
82
+ }
83
+
84
+ function getFieldsValue(nameList?: string[]) {
85
+ const formData = getFormFields();
86
+
87
+ if (!nameList) {
88
+ return formData;
89
+ }
90
+
91
+ return nameList.reduce((acc, key) => {
92
+ acc[key] = formData[key];
93
+
94
+ return acc;
95
+ }, {} as Record<string, RuleTypes>);
96
+ }
97
+
98
+ function getFieldError(name: string) {
99
+ return errorsRef.current[name] || [];
100
+ }
101
+
102
+ function getFieldWarning(name: string): string[] {
103
+ return warningsRef.current[name] || [];
104
+ }
105
+
106
+ function getFieldsError(): Pick<FieldError, 'errors' | 'name'>[] {
107
+ return Object.entries(errorsRef.current).map(([name, err]) => ({ name, errors: err }));
108
+ }
109
+
110
+ function setFieldValue(
111
+ name: string,
112
+ value: RuleTypes,
113
+ errors?: string[],
114
+ reset: boolean | null | -1 | undefined = undefined,
115
+ touch?: boolean
116
+ ) {
117
+ if (
118
+ !reset && reset !== null &&
119
+ ([undefined, null].includes(value) || formRef.current[stepRef.current][name] === value)
120
+ ) {
121
+ return;
122
+ }
123
+
124
+ let isFieldExist = false;
125
+
126
+ Object.values(formRef.current).forEach((_, step) => {
127
+ if (formRef.current[step].hasOwnProperty(name)) {
128
+ formRef.current[step][name] = value;
129
+
130
+ isFieldExist = true;
131
+
132
+ return;
133
+ }
134
+ })
135
+
136
+ if (!isFieldExist) {
137
+ formRef.current[stepRef.current][name] = value;
138
+ }
139
+
140
+ if (touch) {
141
+ touchedFieldsRef.current.add(name);
142
+ }
143
+
144
+ if (reset === null) {
145
+ errorsRef.current[name] = []
146
+ notifyErrorSubscribers(name);
147
+
148
+ return
149
+ }
150
+
151
+ if (!errors?.length) {
152
+ validateField(name).then(() => {
153
+ const allValues = getFieldsValue();
154
+ fieldSubscribers.current[name]?.forEach(callback => callback(value));
155
+ formSubscribers.current.forEach(callback => callback(allValues));
156
+
157
+ if (formHandlersRef.current.onValuesChange) {
158
+ formHandlersRef.current.onValuesChange({ [name]: value }, allValues);
159
+ }
160
+
161
+ if (formHandlersRef.current.onFieldsChange) {
162
+ formHandlersRef.current.onFieldsChange([{ name, value }]);
163
+ }
164
+ });
165
+ } else {
166
+ if (reset === -1) {
167
+ setTimeout(() => {
168
+ errorsRef.current[name] = errors;
169
+ notifyErrorSubscribers(name);
170
+ }, 0);
171
+ } else {
172
+ errorsRef.current[name] = errors;
173
+ notifyErrorSubscribers(name);
174
+ }
175
+ }
176
+ }
177
+
178
+ function setFieldsValue(values: Partial<Record<string, RuleTypes>>, reset?: boolean | null | undefined) {
179
+ Object.entries(values).forEach(([name, value]) =>
180
+ setFieldValue(name, value as RuleTypes, undefined, reset)
181
+ );
182
+ }
183
+
184
+ function setFields(fields: FieldData[]) {
185
+ fields.forEach(({ name, value, errors }) =>
186
+ setFieldValue(
187
+ Array.isArray(name) ? name[0] : name,
188
+ value ?? getFieldValue(Array.isArray(name) ? name[0] : name),
189
+ errors,
190
+ -1
191
+ )
192
+ );
193
+ }
194
+
195
+ function setFieldInstance(fieldName: string, fieldRef: FieldInstancesRef | null) {
196
+ fieldInstancesRef.current[fieldName] = fieldRef;
197
+ }
198
+
199
+ function isFieldTouched(name: string) {
200
+ return touchedFieldsRef.current.has(name);
201
+ }
202
+
203
+ function isFieldsTouched(nameList?: string[], allFieldsTouched = false) {
204
+ if (!nameList) {
205
+ return touchedFieldsRef.current.size > 0;
206
+ }
207
+
208
+ return allFieldsTouched
209
+ ? nameList.every(name => touchedFieldsRef.current.has(name))
210
+ : nameList.some(name => touchedFieldsRef.current.has(name));
211
+ }
212
+
213
+ function isFieldValidating(name: string): boolean {
214
+ return !!name;
215
+ }
216
+
217
+ function registerField(name: string, rules: RuleObject[] = [], remove: boolean = false) {
218
+ if (remove) {
219
+ trashFormRef.current[name] = formRef.current[stepRef.current]?.[name];
220
+
221
+ delete formRef.current[stepRef.current]?.[name];
222
+ delete rulesRef.current[name];
223
+ delete fieldInstancesRef.current[name];
224
+ } else {
225
+ if (!(name in formRef.current[stepRef.current])) {
226
+ if (trashFormRef.current.hasOwnProperty(name)) {
227
+ formRef.current[stepRef.current][name] = trashFormRef.current[name];
228
+
229
+ delete trashFormRef.current[name];
230
+ } else {
231
+ const existFields: Record<string, RuleType> = {};
232
+
233
+ Object.values(formRef.current).forEach((_, step) => {
234
+ if (formRef.current[step].hasOwnProperty(name)) {
235
+ existFields[name] = formRef.current[step][name]
236
+
237
+ delete formRef.current[step][name];
238
+ }
239
+ })
240
+
241
+ formRef.current[stepRef.current][name] = initialValues?.[name];
242
+
243
+ if (Object.keys(existFields).length) {
244
+ Object.entries(existFields).forEach(([_key, _value]) => {
245
+ formRef.current[stepRef.current][_key] = _value
246
+ })
247
+ }
248
+ }
249
+ }
250
+
251
+ rulesRef.current[name] = rules;
252
+ }
253
+ }
254
+
255
+ async function validateField(name: string) {
256
+ const value = formRef.current[stepRef.current][name];
257
+ const rules = rulesRef.current[name] || [];
258
+ const fieldErrors: string[] = [];
259
+ const fieldWarnings: string[] = [];
260
+
261
+ await Promise.all(
262
+ [rules].flat(1).map(async (rule: RuleTypes) => {
263
+ rule = typeof rule === 'function' ? rule(formInstance) : rule;
264
+
265
+ if (
266
+ rule.required &&
267
+ ((rule.validateBooleanFalse && !value) ||
268
+ value === undefined ||
269
+ value === null ||
270
+ value === '' ||
271
+ (Array.isArray(value) && !value.length))
272
+ ) {
273
+ fieldErrors.push(rule.message || 'This field is required');
274
+ }
275
+
276
+ if (
277
+ (typeof value === 'string' ||
278
+ typeof value === 'number' ||
279
+ Array.isArray(value)) &&
280
+ rule.min !== undefined &&
281
+ String(value).length < rule.min
282
+ ) {
283
+ fieldErrors.push(
284
+ rule.message || `Must be at least ${rule.min} characters`
285
+ );
286
+ }
287
+
288
+ if (
289
+ (typeof value === 'string' ||
290
+ typeof value === 'number' ||
291
+ Array.isArray(value)) &&
292
+ rule.max !== undefined &&
293
+ String(value).length > rule.max
294
+ ) {
295
+ fieldErrors.push(
296
+ rule.message || `Must be at most ${rule.max} characters`
297
+ );
298
+ }
299
+
300
+ if (value !== undefined && rule.pattern && !rule.pattern.test(String(value))) {
301
+ fieldErrors.push(rule.message || 'Invalid format');
302
+ }
303
+
304
+ if (rule.warningPattern && !rule.warningPattern.test(String(value))) {
305
+ fieldWarnings.push(rule.warningMessage || 'Invalid format');
306
+ }
307
+
308
+ if (rule.validator) {
309
+ try {
310
+ await rule.validator(
311
+ rule,
312
+ value,
313
+ (error?: string) => error && fieldErrors.push(error)
314
+ );
315
+ } catch (error) {
316
+ fieldErrors.push(
317
+ error instanceof Error ? error.message : String(error)
318
+ );
319
+ }
320
+ }
321
+ })
322
+ );
323
+
324
+ errorsRef.current[name] = fieldErrors
325
+ warningsRef.current[name] = fieldWarnings;
326
+
327
+ notifyErrorSubscribers(name);
328
+
329
+ return fieldErrors.length === 0;
330
+ }
331
+
332
+ async function validateFields(nameList?: string[]) {
333
+ const fieldsToValidate = nameList || Object.keys(formRef.current[stepRef.current]);
334
+
335
+ const results = await Promise.all(
336
+ fieldsToValidate.map(name => validateField(name))
337
+ );
338
+
339
+ const errorFields = formInstance.getFieldsError().filter(e => e.errors.length);
340
+
341
+ if (errorFields.length) {
342
+ formHandlersRef.current.onFinishFailed?.({ values: formInstance.getFieldsValue(), errorFields })
343
+ }
344
+
345
+ if (_scrollToFirstError.current) {
346
+ const firstErrorContent = document.querySelectorAll('.xUi-form-item-has-error')?.[0];
347
+
348
+ if (firstErrorContent) {
349
+ firstErrorContent.closest('.xUi-form-item')?.scrollIntoView({
350
+ behavior: 'smooth'
351
+ });
352
+ }
353
+ }
354
+
355
+ fieldsToValidate.forEach(name => notifyErrorSubscribers(name));
356
+
357
+ return results.every(valid => valid);
358
+ }
359
+
360
+ function resetFields(nameList?: string[], showError: boolean | null = true) {
361
+ const formData = getFormFields();
362
+
363
+ if (nameList?.length) {
364
+ nameList.forEach((name: string) => {
365
+ formData[name] = initialValues[name];
366
+ trashFormRef.current[name] = initialValues[name]
367
+
368
+ touchedFieldsRef.current.delete(name);
369
+ delete warningsRef.current[name];
370
+
371
+ errorsRef.current[name] = [];
372
+ notifyErrorSubscribers(name);
373
+
374
+ setFieldValue(name, initialValues[name], undefined, showError);
375
+ });
376
+ } else {
377
+ touchedFieldsRef.current.clear();
378
+ warningsRef.current = {};
379
+
380
+ Object.keys({
381
+ ...formData,
382
+ }).forEach(name => {
383
+ setFieldValue(name, initialValues[name], undefined, showError);
384
+ });
385
+ }
386
+
387
+ formSubscribers.current.forEach(callback => callback(getFieldsValue()));
388
+ setIsReseting(prev => !prev);
389
+ }
390
+
391
+ async function submit() {
392
+ const formData = getFormFields();
393
+
394
+ return (await validateFields()) ? (() => {
395
+ formHandlersRef.current.onFinish?.(formData)
396
+
397
+ return formData
398
+ })() : undefined;
399
+ }
400
+
401
+ function subscribeToField(
402
+ name: string,
403
+ callback: (value: RuleTypes) => void
404
+ ) {
405
+ if (!fieldSubscribers.current[name]) {
406
+ fieldSubscribers.current[name] = [];
407
+ }
408
+
409
+ fieldSubscribers.current[name].push(callback);
410
+
411
+ return () => {
412
+ fieldSubscribers.current[name] = fieldSubscribers.current[name].filter(
413
+ cb => cb !== callback
414
+ );
415
+ };
416
+ }
417
+
418
+ function subscribeToForm(
419
+ callback: (values: Record<string, RuleTypes>) => void
420
+ ) {
421
+ formSubscribers.current.push(callback);
422
+
423
+ return () => {
424
+ formSubscribers.current = formSubscribers.current.filter(
425
+ cb => cb !== callback
426
+ );
427
+ };
428
+ }
429
+
430
+ function subscribeToFields(
431
+ names: string[],
432
+ callback: (values: Record<string, RuleTypes>) => void
433
+ ) {
434
+ const fieldCallbacks = names.map(name =>
435
+ subscribeToField(name, () => {
436
+ const updatedValues = getFieldsValue(names);
437
+ callback(updatedValues);
438
+ })
439
+ );
440
+
441
+ return () => {
442
+ fieldCallbacks.forEach(unsubscribe => unsubscribe());
443
+ };
444
+ }
445
+
446
+ function subscribeToError(name: string, callback: (errors: string[]) => void) {
447
+ if (!errorSubscribers.current[name]) {
448
+ errorSubscribers.current[name] = [];
449
+ }
450
+
451
+ errorSubscribers.current[name].push(callback);
452
+
453
+ return () => {
454
+ errorSubscribers.current[name] = errorSubscribers.current[name].filter(
455
+ cb => cb !== callback
456
+ );
457
+ };
458
+ }
459
+
460
+ function notifyErrorSubscribers(name: string) {
461
+ const errors = getFieldError(name);
462
+ errorSubscribers.current[name]?.forEach(cb => cb(errors));
463
+ }
464
+
465
+ function setScrollToFirstError(value: boolean) {
466
+ _scrollToFirstError.current = value;
467
+ }
468
+
469
+ function setOnFieldsChange(
470
+ onFieldsChange?: (changedFields: FieldData[]) => void
471
+ ) {
472
+ formHandlersRef.current.onFieldsChange = onFieldsChange
473
+ }
474
+
475
+ function setOnValuesChange(
476
+ onValuesChange?: (changedValues: Record<string, RuleTypes>, allValues: Record<string, RuleTypes>) => void
477
+ ) {
478
+ formHandlersRef.current.onValuesChange = onValuesChange
479
+ }
480
+
481
+ function setOnFinish(
482
+ onFinish?: ((values: Record<string, RuleTypes>) => void) | undefined
483
+ ) {
484
+ formHandlersRef.current.onFinish = onFinish;
485
+ }
486
+
487
+ function setOnFinishFailed(
488
+ onFinishFailed?: ((errorInfo: {
489
+ values: Record<string, RuleType>;
490
+ errorFields: Pick<FieldError, "errors" | "name">[];
491
+ }) => void) | undefined
492
+ ) {
493
+ formHandlersRef.current.onFinishFailed = onFinishFailed
494
+ }
495
+
496
+ function changeStep(step: number) {
497
+ stepRef.current = step ?? 0;
498
+
499
+ if (!formRef.current[stepRef.current]) {
500
+ formRef.current[stepRef.current] = {};
501
+ }
502
+ }
503
+
504
+ const formInstanceRef = useRef<FormInstance>(null);
505
+ const formInstance: FormInstance = {
506
+ submit,
507
+ setFields,
508
+ resetFields,
509
+ getFieldError,
510
+ registerField,
511
+ setFieldValue,
512
+ getFieldValue,
513
+ validateFields,
514
+ setFieldsValue,
515
+ getFieldsValue,
516
+ isFieldTouched,
517
+ getFieldsError,
518
+ setOnFinishFailed,
519
+ isFieldsTouched,
520
+ getFieldWarning,
521
+ isFieldValidating,
522
+ subscribeToField,
523
+ subscribeToForm,
524
+ onFieldsChange,
525
+ onValuesChange,
526
+ getFieldInstance,
527
+ setFieldInstance,
528
+ subscribeToFields,
529
+ setScrollToFirstError,
530
+ subscribeToError,
531
+ scrollToFirstError,
532
+ isReseting,
533
+ setOnFinish,
534
+ setOnFieldsChange,
535
+ setOnValuesChange,
536
+ changeStep,
537
+ };
538
+
539
+ if (formInstanceRef.current) {
540
+ return formInstanceRef.current
541
+ } else {
542
+ formInstanceRef.current = formInstance;
543
+
544
+ return formInstanceRef.current
545
+ }
546
+ };
547
+
548
+ export { useForm };