react-native-mantine 0.6.0 → 0.8.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 (225) hide show
  1. package/README.md +38 -6
  2. package/lib/commonjs/components/Anchor/index.js +6 -1
  3. package/lib/commonjs/components/Anchor/index.js.map +1 -1
  4. package/lib/commonjs/components/Badge/index.js +12 -13
  5. package/lib/commonjs/components/Badge/index.js.map +1 -1
  6. package/lib/commonjs/components/Blockquote/index.js +8 -12
  7. package/lib/commonjs/components/Blockquote/index.js.map +1 -1
  8. package/lib/commonjs/components/Button/index.js +5 -6
  9. package/lib/commonjs/components/Button/index.js.map +1 -1
  10. package/lib/commonjs/components/Chip/index.js +8 -9
  11. package/lib/commonjs/components/Chip/index.js.map +1 -1
  12. package/lib/commonjs/components/Code/index.js +13 -8
  13. package/lib/commonjs/components/Code/index.js.map +1 -1
  14. package/lib/commonjs/components/Group/index.js.map +1 -1
  15. package/lib/commonjs/components/Kbd/index.js +7 -8
  16. package/lib/commonjs/components/Kbd/index.js.map +1 -1
  17. package/lib/commonjs/components/List/index.js +6 -5
  18. package/lib/commonjs/components/List/index.js.map +1 -1
  19. package/lib/commonjs/components/Mark/index.js +4 -0
  20. package/lib/commonjs/components/Mark/index.js.map +1 -1
  21. package/lib/commonjs/components/Menu/index.js +12 -15
  22. package/lib/commonjs/components/Menu/index.js.map +1 -1
  23. package/lib/commonjs/components/NavLink/index.js +7 -11
  24. package/lib/commonjs/components/NavLink/index.js.map +1 -1
  25. package/lib/commonjs/components/Notification/index.js +5 -15
  26. package/lib/commonjs/components/Notification/index.js.map +1 -1
  27. package/lib/commonjs/components/Radio/index.js +62 -6
  28. package/lib/commonjs/components/Radio/index.js.map +1 -1
  29. package/lib/commonjs/components/Table/Table.example.js +131 -0
  30. package/lib/commonjs/components/Table/Table.example.js.map +1 -0
  31. package/lib/commonjs/components/Table/index.js +69 -15
  32. package/lib/commonjs/components/Table/index.js.map +1 -1
  33. package/lib/commonjs/components/Title/index.js +4 -0
  34. package/lib/commonjs/components/Title/index.js.map +1 -1
  35. package/lib/commonjs/hooks/index.js +28 -0
  36. package/lib/commonjs/hooks/index.js.map +1 -0
  37. package/lib/commonjs/hooks/use-form/index.js +40 -0
  38. package/lib/commonjs/hooks/use-form/index.js.map +1 -0
  39. package/lib/commonjs/hooks/use-form/types.js +2 -0
  40. package/lib/commonjs/hooks/use-form/types.js.map +1 -0
  41. package/lib/commonjs/hooks/use-form/useForm.js +418 -0
  42. package/lib/commonjs/hooks/use-form/useForm.js.map +1 -0
  43. package/lib/commonjs/hooks/use-form/validators.js +135 -0
  44. package/lib/commonjs/hooks/use-form/validators.js.map +1 -0
  45. package/lib/commonjs/index.js +11 -0
  46. package/lib/commonjs/index.js.map +1 -1
  47. package/lib/commonjs/theme/utils/index.js +26 -0
  48. package/lib/commonjs/theme/utils/index.js.map +1 -0
  49. package/lib/commonjs/theme/utils/withTextWrapper.js +49 -0
  50. package/lib/commonjs/theme/utils/withTextWrapper.js.map +1 -0
  51. package/lib/module/components/Anchor/index.js +6 -1
  52. package/lib/module/components/Anchor/index.js.map +1 -1
  53. package/lib/module/components/Badge/index.js +5 -6
  54. package/lib/module/components/Badge/index.js.map +1 -1
  55. package/lib/module/components/Blockquote/index.js +6 -10
  56. package/lib/module/components/Blockquote/index.js.map +1 -1
  57. package/lib/module/components/Button/index.js +5 -6
  58. package/lib/module/components/Button/index.js.map +1 -1
  59. package/lib/module/components/Chip/index.js +5 -6
  60. package/lib/module/components/Chip/index.js.map +1 -1
  61. package/lib/module/components/Code/index.js +13 -8
  62. package/lib/module/components/Code/index.js.map +1 -1
  63. package/lib/module/components/Group/index.js.map +1 -1
  64. package/lib/module/components/Kbd/index.js +5 -6
  65. package/lib/module/components/Kbd/index.js.map +1 -1
  66. package/lib/module/components/List/index.js +6 -5
  67. package/lib/module/components/List/index.js.map +1 -1
  68. package/lib/module/components/Mark/index.js +4 -0
  69. package/lib/module/components/Mark/index.js.map +1 -1
  70. package/lib/module/components/Menu/index.js +7 -10
  71. package/lib/module/components/Menu/index.js.map +1 -1
  72. package/lib/module/components/NavLink/index.js +5 -9
  73. package/lib/module/components/NavLink/index.js.map +1 -1
  74. package/lib/module/components/Notification/index.js +5 -15
  75. package/lib/module/components/Notification/index.js.map +1 -1
  76. package/lib/module/components/Radio/index.js +64 -8
  77. package/lib/module/components/Radio/index.js.map +1 -1
  78. package/lib/module/components/Table/Table.example.js +128 -0
  79. package/lib/module/components/Table/Table.example.js.map +1 -0
  80. package/lib/module/components/Table/index.js +70 -16
  81. package/lib/module/components/Table/index.js.map +1 -1
  82. package/lib/module/components/Title/index.js +4 -0
  83. package/lib/module/components/Title/index.js.map +1 -1
  84. package/lib/module/hooks/index.js +5 -0
  85. package/lib/module/hooks/index.js.map +1 -0
  86. package/lib/module/hooks/use-form/index.js +6 -0
  87. package/lib/module/hooks/use-form/index.js.map +1 -0
  88. package/lib/module/hooks/use-form/types.js +2 -0
  89. package/lib/module/hooks/use-form/types.js.map +1 -0
  90. package/lib/module/hooks/use-form/useForm.js +414 -0
  91. package/lib/module/hooks/use-form/useForm.js.map +1 -0
  92. package/lib/module/hooks/use-form/validators.js +122 -0
  93. package/lib/module/hooks/use-form/validators.js.map +1 -0
  94. package/lib/module/index.js +1 -0
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/theme/utils/index.js +5 -0
  97. package/lib/module/theme/utils/index.js.map +1 -0
  98. package/lib/module/theme/utils/withTextWrapper.js +45 -0
  99. package/lib/module/theme/utils/withTextWrapper.js.map +1 -0
  100. package/lib/typescript/commonjs/src/components/Anchor/index.d.ts +2 -1
  101. package/lib/typescript/commonjs/src/components/Anchor/index.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/src/components/Badge/index.d.ts +2 -1
  103. package/lib/typescript/commonjs/src/components/Badge/index.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/src/components/Blockquote/index.d.ts +2 -1
  105. package/lib/typescript/commonjs/src/components/Blockquote/index.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/src/components/Button/index.d.ts +2 -2
  107. package/lib/typescript/commonjs/src/components/Button/index.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/src/components/Chip/index.d.ts +2 -1
  109. package/lib/typescript/commonjs/src/components/Chip/index.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/src/components/Code/index.d.ts +2 -1
  111. package/lib/typescript/commonjs/src/components/Code/index.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/src/components/Group/index.d.ts +1 -0
  113. package/lib/typescript/commonjs/src/components/Group/index.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/src/components/Kbd/index.d.ts +2 -1
  115. package/lib/typescript/commonjs/src/components/Kbd/index.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/src/components/List/index.d.ts +2 -1
  117. package/lib/typescript/commonjs/src/components/List/index.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/src/components/Mark/index.d.ts +2 -1
  119. package/lib/typescript/commonjs/src/components/Mark/index.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/src/components/Menu/index.d.ts +3 -2
  121. package/lib/typescript/commonjs/src/components/Menu/index.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/src/components/NavLink/index.d.ts +2 -1
  123. package/lib/typescript/commonjs/src/components/NavLink/index.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/src/components/Notification/index.d.ts +2 -1
  125. package/lib/typescript/commonjs/src/components/Notification/index.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/src/components/Radio/index.d.ts +22 -1
  127. package/lib/typescript/commonjs/src/components/Radio/index.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/src/components/Table/Table.example.d.ts +3 -0
  129. package/lib/typescript/commonjs/src/components/Table/Table.example.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/src/components/Table/index.d.ts +7 -2
  131. package/lib/typescript/commonjs/src/components/Table/index.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/src/components/Title/index.d.ts +2 -1
  133. package/lib/typescript/commonjs/src/components/Title/index.d.ts.map +1 -1
  134. package/lib/typescript/commonjs/src/hooks/index.d.ts +3 -0
  135. package/lib/typescript/commonjs/src/hooks/index.d.ts.map +1 -0
  136. package/lib/typescript/commonjs/src/hooks/use-form/index.d.ts +4 -0
  137. package/lib/typescript/commonjs/src/hooks/use-form/index.d.ts.map +1 -0
  138. package/lib/typescript/commonjs/src/hooks/use-form/types.d.ts +119 -0
  139. package/lib/typescript/commonjs/src/hooks/use-form/types.d.ts.map +1 -0
  140. package/lib/typescript/commonjs/src/hooks/use-form/useForm.d.ts +30 -0
  141. package/lib/typescript/commonjs/src/hooks/use-form/useForm.d.ts.map +1 -0
  142. package/lib/typescript/commonjs/src/hooks/use-form/validators.d.ts +41 -0
  143. package/lib/typescript/commonjs/src/hooks/use-form/validators.d.ts.map +1 -0
  144. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  145. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  146. package/lib/typescript/commonjs/src/theme/utils/index.d.ts +3 -0
  147. package/lib/typescript/commonjs/src/theme/utils/index.d.ts.map +1 -0
  148. package/lib/typescript/commonjs/src/theme/utils/withTextWrapper.d.ts +41 -0
  149. package/lib/typescript/commonjs/src/theme/utils/withTextWrapper.d.ts.map +1 -0
  150. package/lib/typescript/module/src/components/Anchor/index.d.ts +2 -1
  151. package/lib/typescript/module/src/components/Anchor/index.d.ts.map +1 -1
  152. package/lib/typescript/module/src/components/Badge/index.d.ts +2 -1
  153. package/lib/typescript/module/src/components/Badge/index.d.ts.map +1 -1
  154. package/lib/typescript/module/src/components/Blockquote/index.d.ts +2 -1
  155. package/lib/typescript/module/src/components/Blockquote/index.d.ts.map +1 -1
  156. package/lib/typescript/module/src/components/Button/index.d.ts +2 -2
  157. package/lib/typescript/module/src/components/Button/index.d.ts.map +1 -1
  158. package/lib/typescript/module/src/components/Chip/index.d.ts +2 -1
  159. package/lib/typescript/module/src/components/Chip/index.d.ts.map +1 -1
  160. package/lib/typescript/module/src/components/Code/index.d.ts +2 -1
  161. package/lib/typescript/module/src/components/Code/index.d.ts.map +1 -1
  162. package/lib/typescript/module/src/components/Group/index.d.ts +1 -0
  163. package/lib/typescript/module/src/components/Group/index.d.ts.map +1 -1
  164. package/lib/typescript/module/src/components/Kbd/index.d.ts +2 -1
  165. package/lib/typescript/module/src/components/Kbd/index.d.ts.map +1 -1
  166. package/lib/typescript/module/src/components/List/index.d.ts +2 -1
  167. package/lib/typescript/module/src/components/List/index.d.ts.map +1 -1
  168. package/lib/typescript/module/src/components/Mark/index.d.ts +2 -1
  169. package/lib/typescript/module/src/components/Mark/index.d.ts.map +1 -1
  170. package/lib/typescript/module/src/components/Menu/index.d.ts +3 -2
  171. package/lib/typescript/module/src/components/Menu/index.d.ts.map +1 -1
  172. package/lib/typescript/module/src/components/NavLink/index.d.ts +2 -1
  173. package/lib/typescript/module/src/components/NavLink/index.d.ts.map +1 -1
  174. package/lib/typescript/module/src/components/Notification/index.d.ts +2 -1
  175. package/lib/typescript/module/src/components/Notification/index.d.ts.map +1 -1
  176. package/lib/typescript/module/src/components/Radio/index.d.ts +22 -1
  177. package/lib/typescript/module/src/components/Radio/index.d.ts.map +1 -1
  178. package/lib/typescript/module/src/components/Table/Table.example.d.ts +3 -0
  179. package/lib/typescript/module/src/components/Table/Table.example.d.ts.map +1 -0
  180. package/lib/typescript/module/src/components/Table/index.d.ts +7 -2
  181. package/lib/typescript/module/src/components/Table/index.d.ts.map +1 -1
  182. package/lib/typescript/module/src/components/Title/index.d.ts +2 -1
  183. package/lib/typescript/module/src/components/Title/index.d.ts.map +1 -1
  184. package/lib/typescript/module/src/hooks/index.d.ts +3 -0
  185. package/lib/typescript/module/src/hooks/index.d.ts.map +1 -0
  186. package/lib/typescript/module/src/hooks/use-form/index.d.ts +4 -0
  187. package/lib/typescript/module/src/hooks/use-form/index.d.ts.map +1 -0
  188. package/lib/typescript/module/src/hooks/use-form/types.d.ts +119 -0
  189. package/lib/typescript/module/src/hooks/use-form/types.d.ts.map +1 -0
  190. package/lib/typescript/module/src/hooks/use-form/useForm.d.ts +30 -0
  191. package/lib/typescript/module/src/hooks/use-form/useForm.d.ts.map +1 -0
  192. package/lib/typescript/module/src/hooks/use-form/validators.d.ts +41 -0
  193. package/lib/typescript/module/src/hooks/use-form/validators.d.ts.map +1 -0
  194. package/lib/typescript/module/src/index.d.ts +1 -0
  195. package/lib/typescript/module/src/index.d.ts.map +1 -1
  196. package/lib/typescript/module/src/theme/utils/index.d.ts +3 -0
  197. package/lib/typescript/module/src/theme/utils/index.d.ts.map +1 -0
  198. package/lib/typescript/module/src/theme/utils/withTextWrapper.d.ts +41 -0
  199. package/lib/typescript/module/src/theme/utils/withTextWrapper.d.ts.map +1 -0
  200. package/package.json +1 -1
  201. package/src/components/Anchor/index.tsx +8 -2
  202. package/src/components/Badge/index.tsx +5 -3
  203. package/src/components/Blockquote/index.tsx +6 -5
  204. package/src/components/Button/index.tsx +5 -6
  205. package/src/components/Chip/index.tsx +5 -3
  206. package/src/components/Code/index.tsx +19 -14
  207. package/src/components/Group/index.tsx +1 -0
  208. package/src/components/Kbd/index.tsx +5 -4
  209. package/src/components/List/index.tsx +7 -8
  210. package/src/components/Mark/index.tsx +7 -2
  211. package/src/components/Menu/index.tsx +8 -7
  212. package/src/components/NavLink/index.tsx +6 -4
  213. package/src/components/Notification/index.tsx +6 -15
  214. package/src/components/Radio/index.tsx +99 -8
  215. package/src/components/Table/Table.example.tsx +85 -0
  216. package/src/components/Table/index.tsx +93 -11
  217. package/src/components/Title/index.tsx +7 -2
  218. package/src/hooks/index.ts +2 -0
  219. package/src/hooks/use-form/index.ts +3 -0
  220. package/src/hooks/use-form/types.ts +169 -0
  221. package/src/hooks/use-form/useForm.ts +436 -0
  222. package/src/hooks/use-form/validators.ts +143 -0
  223. package/src/index.tsx +1 -0
  224. package/src/theme/utils/index.ts +2 -0
  225. package/src/theme/utils/withTextWrapper.tsx +58 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Form field validation function
3
+ */
4
+ export type FormValidator<T> = (value: T) => string | null;
5
+
6
+ /**
7
+ * Form validation rules
8
+ */
9
+ export type FormValidationRules<Values> = {
10
+ [K in keyof Values]?: FormValidator<Values[K]> | FormValidator<Values[K]>[];
11
+ };
12
+
13
+ /**
14
+ * Form errors
15
+ */
16
+ export type FormErrors<Values = Record<string, any>> = Partial<
17
+ Record<keyof Values, string>
18
+ >;
19
+
20
+ /**
21
+ * Form touched state
22
+ */
23
+ export type FormTouched<Values = Record<string, any>> = Partial<
24
+ Record<keyof Values, boolean>
25
+ >;
26
+
27
+ /**
28
+ * Form dirty state
29
+ */
30
+ export type FormDirty<Values = Record<string, any>> = Partial<
31
+ Record<keyof Values, boolean>
32
+ >;
33
+
34
+ /**
35
+ * Form field status
36
+ */
37
+ export interface FormFieldStatus {
38
+ hasError: boolean;
39
+ isTouched: boolean;
40
+ isDirty: boolean;
41
+ }
42
+
43
+ /**
44
+ * Props to be spread on input components
45
+ */
46
+ export interface FormInputProps<T = any> {
47
+ value: T;
48
+ onChange?: (value: T) => void;
49
+ onChangeText?: (text: string) => void;
50
+ error?: string;
51
+ onBlur?: () => void;
52
+ }
53
+
54
+ /**
55
+ * Form submit handler
56
+ */
57
+ export type FormSubmitHandler<Values> = (
58
+ values: Values,
59
+ event?: any
60
+ ) => void | Promise<void>;
61
+
62
+ /**
63
+ * useForm hook input parameters
64
+ */
65
+ export interface UseFormInput<Values = Record<string, any>> {
66
+ /** Initial form values */
67
+ initialValues?: Values;
68
+
69
+ /** Initial form errors */
70
+ initialErrors?: FormErrors<Values>;
71
+
72
+ /** Initial touched state */
73
+ initialTouched?: FormTouched<Values>;
74
+
75
+ /** Initial dirty state */
76
+ initialDirty?: FormDirty<Values>;
77
+
78
+ /** Form validation rules */
79
+ validate?: FormValidationRules<Values>;
80
+
81
+ /** Clear input error on change */
82
+ clearInputErrorOnChange?: boolean;
83
+
84
+ /** Validate input on change */
85
+ validateInputOnChange?: boolean;
86
+
87
+ /** Validate input on blur */
88
+ validateInputOnBlur?: boolean;
89
+
90
+ /** Transform values before submit */
91
+ transformValues?: (values: Values) => any;
92
+ }
93
+
94
+ /**
95
+ * useForm hook return type
96
+ */
97
+ export interface UseFormReturnType<Values = Record<string, any>> {
98
+ /** Current form values */
99
+ values: Values;
100
+
101
+ /** Current form errors */
102
+ errors: FormErrors<Values>;
103
+
104
+ /** Current touched state */
105
+ touched: FormTouched<Values>;
106
+
107
+ /** Current dirty state */
108
+ dirty: FormDirty<Values>;
109
+
110
+ /** Set a single field value */
111
+ setFieldValue: <K extends keyof Values>(field: K, value: Values[K]) => void;
112
+
113
+ /** Set multiple values */
114
+ setValues: (values: Partial<Values>) => void;
115
+
116
+ /** Set a single field error */
117
+ setFieldError: <K extends keyof Values>(field: K, error: string | null) => void;
118
+
119
+ /** Set multiple errors */
120
+ setErrors: (errors: FormErrors<Values>) => void;
121
+
122
+ /** Clear a specific field error */
123
+ clearFieldError: <K extends keyof Values>(field: K) => void;
124
+
125
+ /** Clear all errors */
126
+ clearErrors: () => void;
127
+
128
+ /** Reset form to initial values */
129
+ reset: () => void;
130
+
131
+ /** Validate a specific field */
132
+ validateField: <K extends keyof Values>(field: K) => boolean;
133
+
134
+ /** Validate entire form */
135
+ validate: () => boolean;
136
+
137
+ /** Check if form is valid */
138
+ isValid: () => boolean;
139
+
140
+ /** Check if form is dirty */
141
+ isDirty: () => boolean;
142
+
143
+ /** Get field status */
144
+ getFieldStatus: <K extends keyof Values>(field: K) => FormFieldStatus;
145
+
146
+ /** Get props to spread on input component */
147
+ getInputProps: <K extends keyof Values>(
148
+ field: K,
149
+ options?: {
150
+ type?: 'input' | 'checkbox' | 'radio' | 'select';
151
+ withError?: boolean;
152
+ withFocus?: boolean;
153
+ }
154
+ ) => FormInputProps<Values[K]>;
155
+
156
+ /** Handle form submit */
157
+ onSubmit: (
158
+ handleSubmit: FormSubmitHandler<Values>
159
+ ) => (event?: any) => void | Promise<void>;
160
+
161
+ /** Reset touched state */
162
+ resetTouched: () => void;
163
+
164
+ /** Reset dirty state */
165
+ resetDirty: () => void;
166
+
167
+ /** Mark field as touched */
168
+ setFieldTouched: <K extends keyof Values>(field: K, touched?: boolean) => void;
169
+ }
@@ -0,0 +1,436 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+ import type {
3
+ FormErrors,
4
+ FormFieldStatus,
5
+ FormInputProps,
6
+ FormSubmitHandler,
7
+ FormTouched,
8
+ FormDirty,
9
+ FormValidationRules,
10
+ UseFormInput,
11
+ UseFormReturnType,
12
+ } from './types';
13
+
14
+ /**
15
+ * Validates a single field value against validation rules
16
+ */
17
+ function validateFieldValue<Values>(
18
+ field: keyof Values,
19
+ value: any,
20
+ rules?: FormValidationRules<Values>
21
+ ): string | null {
22
+ if (!rules || !rules[field]) {
23
+ return null;
24
+ }
25
+
26
+ const validators = rules[field];
27
+ if (!validators) {
28
+ return null;
29
+ }
30
+
31
+ // Handle single validator
32
+ if (typeof validators === 'function') {
33
+ return validators(value);
34
+ }
35
+
36
+ // Handle array of validators
37
+ if (Array.isArray(validators)) {
38
+ for (const validator of validators) {
39
+ const error = validator(value);
40
+ if (error) {
41
+ return error;
42
+ }
43
+ }
44
+ }
45
+
46
+ return null;
47
+ }
48
+
49
+ /**
50
+ * Validates all form values
51
+ */
52
+ function validateAllValues<Values>(
53
+ values: Values,
54
+ rules?: FormValidationRules<Values>
55
+ ): FormErrors<Values> {
56
+ if (!rules) {
57
+ return {};
58
+ }
59
+
60
+ const errors: FormErrors<Values> = {};
61
+
62
+ for (const field in rules) {
63
+ const error = validateFieldValue(field, values[field], rules);
64
+ if (error) {
65
+ errors[field] = error;
66
+ }
67
+ }
68
+
69
+ return errors;
70
+ }
71
+
72
+ /**
73
+ * Form state management hook for React Native
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * const form = useForm({
78
+ * initialValues: {
79
+ * email: '',
80
+ * password: '',
81
+ * },
82
+ * validate: {
83
+ * email: isEmail('Invalid email'),
84
+ * password: minLength(6, 'Password must be at least 6 characters'),
85
+ * },
86
+ * });
87
+ *
88
+ * return (
89
+ * <View>
90
+ * <TextInput {...form.getInputProps('email')} />
91
+ * <TextInput {...form.getInputProps('password')} />
92
+ * <Button onPress={form.onSubmit((values) => console.log(values))}>
93
+ * Submit
94
+ * </Button>
95
+ * </View>
96
+ * );
97
+ * ```
98
+ */
99
+ export function useForm<Values extends Record<string, any> = Record<string, any>>({
100
+ initialValues = {} as Values,
101
+ initialErrors = {},
102
+ initialTouched = {},
103
+ initialDirty = {},
104
+ validate: validationRules,
105
+ clearInputErrorOnChange = true,
106
+ validateInputOnChange = false,
107
+ validateInputOnBlur = false,
108
+ transformValues = (values: Values) => values,
109
+ }: UseFormInput<Values> = {}): UseFormReturnType<Values> {
110
+ // Form state
111
+ const [values, setValuesState] = useState<Values>(initialValues);
112
+ const [errors, setErrorsState] = useState<FormErrors<Values>>(initialErrors);
113
+ const [touched, setTouchedState] = useState<FormTouched<Values>>(initialTouched);
114
+ const [dirty, setDirtyState] = useState<FormDirty<Values>>(initialDirty);
115
+
116
+ // Keep track of initial values for reset
117
+ const initialValuesRef = useRef<Values>(initialValues);
118
+
119
+ /**
120
+ * Set a single field value
121
+ */
122
+ const setFieldValue = useCallback(
123
+ <K extends keyof Values>(field: K, value: Values[K]) => {
124
+ setValuesState((prev) => ({ ...prev, [field]: value }));
125
+ setDirtyState((prev) => ({ ...prev, [field]: true }));
126
+
127
+ // Validate on change if enabled
128
+ if (validateInputOnChange && validationRules) {
129
+ const error = validateFieldValue(field, value, validationRules);
130
+ if (error) {
131
+ setErrorsState((prev) => ({ ...prev, [field]: error }));
132
+ } else {
133
+ setErrorsState((prev) => {
134
+ const newErrors = { ...prev };
135
+ delete newErrors[field];
136
+ return newErrors;
137
+ });
138
+ }
139
+ } else if (clearInputErrorOnChange) {
140
+ // Clear error on change if enabled
141
+ setErrorsState((prev) => {
142
+ const newErrors = { ...prev };
143
+ delete newErrors[field];
144
+ return newErrors;
145
+ });
146
+ }
147
+ },
148
+ [validateInputOnChange, clearInputErrorOnChange, validationRules]
149
+ );
150
+
151
+ /**
152
+ * Set multiple field values
153
+ */
154
+ const setValues = useCallback((newValues: Partial<Values>) => {
155
+ setValuesState((prev) => ({ ...prev, ...newValues }));
156
+
157
+ // Mark all updated fields as dirty
158
+ const dirtyFields: FormDirty<Values> = {};
159
+ for (const key in newValues) {
160
+ dirtyFields[key] = true;
161
+ }
162
+ setDirtyState((prev) => ({ ...prev, ...dirtyFields }));
163
+
164
+ if (clearInputErrorOnChange) {
165
+ setErrorsState((prev) => {
166
+ const newErrors = { ...prev };
167
+ for (const key in newValues) {
168
+ delete newErrors[key];
169
+ }
170
+ return newErrors;
171
+ });
172
+ }
173
+ }, [clearInputErrorOnChange]);
174
+
175
+ /**
176
+ * Set a single field error
177
+ */
178
+ const setFieldError = useCallback(
179
+ <K extends keyof Values>(field: K, error: string | null) => {
180
+ if (error === null) {
181
+ setErrorsState((prev) => {
182
+ const newErrors = { ...prev };
183
+ delete newErrors[field];
184
+ return newErrors;
185
+ });
186
+ } else {
187
+ setErrorsState((prev) => ({ ...prev, [field]: error }));
188
+ }
189
+ },
190
+ []
191
+ );
192
+
193
+ /**
194
+ * Set multiple errors
195
+ */
196
+ const setErrors = useCallback((newErrors: FormErrors<Values>) => {
197
+ setErrorsState(newErrors);
198
+ }, []);
199
+
200
+ /**
201
+ * Clear a specific field error
202
+ */
203
+ const clearFieldError = useCallback(<K extends keyof Values>(field: K) => {
204
+ setErrorsState((prev) => {
205
+ const newErrors = { ...prev };
206
+ delete newErrors[field];
207
+ return newErrors;
208
+ });
209
+ }, []);
210
+
211
+ /**
212
+ * Clear all errors
213
+ */
214
+ const clearErrors = useCallback(() => {
215
+ setErrorsState({});
216
+ }, []);
217
+
218
+ /**
219
+ * Reset form to initial values
220
+ */
221
+ const reset = useCallback(() => {
222
+ setValuesState(initialValuesRef.current);
223
+ setErrorsState({});
224
+ setTouchedState({});
225
+ setDirtyState({});
226
+ }, []);
227
+
228
+ /**
229
+ * Validate a specific field
230
+ */
231
+ const validateField = useCallback(
232
+ <K extends keyof Values>(field: K): boolean => {
233
+ if (!validationRules) {
234
+ return true;
235
+ }
236
+
237
+ const error = validateFieldValue(field, values[field], validationRules);
238
+ if (error) {
239
+ setFieldError(field, error);
240
+ return false;
241
+ }
242
+
243
+ clearFieldError(field);
244
+ return true;
245
+ },
246
+ [values, validationRules, setFieldError, clearFieldError]
247
+ );
248
+
249
+ /**
250
+ * Validate entire form
251
+ */
252
+ const validate = useCallback((): boolean => {
253
+ if (!validationRules) {
254
+ return true;
255
+ }
256
+
257
+ const newErrors = validateAllValues(values, validationRules);
258
+ setErrorsState(newErrors);
259
+ return Object.keys(newErrors).length === 0;
260
+ }, [values, validationRules]);
261
+
262
+ /**
263
+ * Check if form is valid
264
+ */
265
+ const isValid = useCallback((): boolean => {
266
+ if (!validationRules) {
267
+ return true;
268
+ }
269
+
270
+ const validationErrors = validateAllValues(values, validationRules);
271
+ return Object.keys(validationErrors).length === 0;
272
+ }, [values, validationRules]);
273
+
274
+ /**
275
+ * Check if form is dirty
276
+ */
277
+ const isDirty = useCallback((): boolean => {
278
+ return Object.values(dirty).some((isDirty) => isDirty === true);
279
+ }, [dirty]);
280
+
281
+ /**
282
+ * Get field status
283
+ */
284
+ const getFieldStatus = useCallback(
285
+ <K extends keyof Values>(field: K): FormFieldStatus => {
286
+ return {
287
+ hasError: !!errors[field],
288
+ isTouched: !!touched[field],
289
+ isDirty: !!dirty[field],
290
+ };
291
+ },
292
+ [errors, touched, dirty]
293
+ );
294
+
295
+ /**
296
+ * Mark field as touched
297
+ */
298
+ const setFieldTouched = useCallback(
299
+ <K extends keyof Values>(field: K, isTouched = true) => {
300
+ setTouchedState((prev) => ({ ...prev, [field]: isTouched }));
301
+
302
+ // Validate on blur if enabled
303
+ if (isTouched && validateInputOnBlur && validationRules) {
304
+ const error = validateFieldValue(field, values[field], validationRules);
305
+ if (error) {
306
+ setFieldError(field, error);
307
+ } else {
308
+ clearFieldError(field);
309
+ }
310
+ }
311
+ },
312
+ [validateInputOnBlur, validationRules, values, setFieldError, clearFieldError]
313
+ );
314
+
315
+ /**
316
+ * Reset touched state
317
+ */
318
+ const resetTouched = useCallback(() => {
319
+ setTouchedState({});
320
+ }, []);
321
+
322
+ /**
323
+ * Reset dirty state
324
+ */
325
+ const resetDirty = useCallback(() => {
326
+ setDirtyState({});
327
+ }, []);
328
+
329
+ /**
330
+ * Get props to spread on input component
331
+ */
332
+ const getInputProps = useCallback(
333
+ <K extends keyof Values>(
334
+ field: K,
335
+ options: {
336
+ type?: 'input' | 'checkbox' | 'radio' | 'select';
337
+ withError?: boolean;
338
+ withFocus?: boolean;
339
+ } = {}
340
+ ): FormInputProps<Values[K]> => {
341
+ const { type = 'input', withError = true, withFocus = true } = options;
342
+
343
+ const baseProps: FormInputProps<Values[K]> = {
344
+ value: values[field],
345
+ };
346
+
347
+ // Add error if needed
348
+ if (withError && errors[field]) {
349
+ baseProps.error = errors[field];
350
+ }
351
+
352
+ // Add blur handler if focus tracking is enabled
353
+ if (withFocus) {
354
+ baseProps.onBlur = () => {
355
+ setFieldTouched(field, true);
356
+ };
357
+ }
358
+
359
+ // Handle different input types
360
+ if (type === 'checkbox') {
361
+ return {
362
+ ...baseProps,
363
+ onChange: (value: Values[K]) => {
364
+ setFieldValue(field, value);
365
+ },
366
+ };
367
+ }
368
+
369
+ if (type === 'radio' || type === 'select') {
370
+ return {
371
+ ...baseProps,
372
+ onChange: (value: Values[K]) => {
373
+ setFieldValue(field, value);
374
+ },
375
+ };
376
+ }
377
+
378
+ // Default text input
379
+ return {
380
+ ...baseProps,
381
+ onChangeText: (text: string) => {
382
+ setFieldValue(field, text as any);
383
+ },
384
+ };
385
+ },
386
+ [values, errors, setFieldValue, setFieldTouched]
387
+ );
388
+
389
+ /**
390
+ * Handle form submit
391
+ */
392
+ const onSubmit = useCallback(
393
+ (handleSubmit: FormSubmitHandler<Values>) => {
394
+ return async (event?: any) => {
395
+ event?.preventDefault?.();
396
+
397
+ // Validate form
398
+ const isFormValid = validate();
399
+ if (!isFormValid) {
400
+ return;
401
+ }
402
+
403
+ // Transform values if needed
404
+ const transformedValues = transformValues(values);
405
+
406
+ // Call submit handler
407
+ await handleSubmit(transformedValues, event);
408
+ };
409
+ },
410
+ [validate, values, transformValues]
411
+ );
412
+
413
+ return {
414
+ values,
415
+ errors,
416
+ touched,
417
+ dirty,
418
+ setFieldValue,
419
+ setValues,
420
+ setFieldError,
421
+ setErrors,
422
+ clearFieldError,
423
+ clearErrors,
424
+ reset,
425
+ validateField,
426
+ validate,
427
+ isValid,
428
+ isDirty,
429
+ getFieldStatus,
430
+ getInputProps,
431
+ onSubmit,
432
+ resetTouched,
433
+ resetDirty,
434
+ setFieldTouched,
435
+ };
436
+ }