@webority-technologies/mobile 0.0.21 → 0.0.23

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 (187) hide show
  1. package/lib/commonjs/components/Accordion/Accordion.js +4 -2
  2. package/lib/commonjs/components/Avatar/Avatar.js +4 -2
  3. package/lib/commonjs/components/Badge/Badge.js +5 -5
  4. package/lib/commonjs/components/Banner/Banner.js +8 -4
  5. package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +6 -4
  6. package/lib/commonjs/components/BottomSheet/BottomSheet.js +69 -13
  7. package/lib/commonjs/components/BottomSheet/index.js +6 -0
  8. package/lib/commonjs/components/Box/Box.js +162 -0
  9. package/lib/commonjs/components/Box/index.js +37 -0
  10. package/lib/commonjs/components/Button/Button.js +7 -7
  11. package/lib/commonjs/components/Carousel/Carousel.js +4 -2
  12. package/lib/commonjs/components/Checkbox/Checkbox.js +14 -5
  13. package/lib/commonjs/components/DatePicker/DatePicker.js +9 -7
  14. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +5 -2
  15. package/lib/commonjs/components/Dialog/Dialog.js +2 -2
  16. package/lib/commonjs/components/FieldBase/FieldBase.js +8 -4
  17. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +13 -5
  18. package/lib/commonjs/components/FormField/FormField.js +61 -25
  19. package/lib/commonjs/components/Input/Input.js +41 -29
  20. package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +102 -0
  21. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +13 -0
  22. package/lib/commonjs/components/KeyboardToolbar/KeyboardToolbar.js +130 -0
  23. package/lib/commonjs/components/KeyboardToolbar/index.js +13 -0
  24. package/lib/commonjs/components/Modal/Modal.js +17 -6
  25. package/lib/commonjs/components/NumberInput/NumberInput.js +35 -28
  26. package/lib/commonjs/components/OTPInput/OTPInput.js +33 -18
  27. package/lib/commonjs/components/Radio/Radio.js +7 -5
  28. package/lib/commonjs/components/Radio/RadioGroup.js +10 -3
  29. package/lib/commonjs/components/SearchBar/SearchBar.js +4 -2
  30. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +20 -10
  31. package/lib/commonjs/components/Select/Select.js +33 -32
  32. package/lib/commonjs/components/Skeleton/SkeletonContent.js +5 -2
  33. package/lib/commonjs/components/Slider/Slider.js +42 -26
  34. package/lib/commonjs/components/Spinner/Spinner.js +5 -5
  35. package/lib/commonjs/components/Switch/Switch.js +29 -16
  36. package/lib/commonjs/components/Tabs/Tabs.js +4 -2
  37. package/lib/commonjs/components/Text/Text.js +142 -0
  38. package/lib/commonjs/components/Text/index.js +13 -0
  39. package/lib/commonjs/components/TimePicker/TimePicker.js +10 -7
  40. package/lib/commonjs/components/Toast/Toast.js +22 -10
  41. package/lib/commonjs/components/Tooltip/Tooltip.js +6 -2
  42. package/lib/commonjs/components/index.js +141 -89
  43. package/lib/commonjs/form/FormContext.js +40 -0
  44. package/lib/commonjs/form/index.js +68 -0
  45. package/lib/commonjs/form/path.js +79 -0
  46. package/lib/commonjs/form/rules.js +67 -0
  47. package/lib/commonjs/form/types.js +2 -0
  48. package/lib/commonjs/form/useField.js +54 -0
  49. package/lib/commonjs/form/useForm.js +316 -0
  50. package/lib/commonjs/hooks/index.js +14 -0
  51. package/lib/commonjs/hooks/useControllableState.js +30 -0
  52. package/lib/commonjs/hooks/useReducedMotion.js +31 -0
  53. package/lib/commonjs/index.js +96 -11
  54. package/lib/commonjs/theme/ThemeContext.js +30 -2
  55. package/lib/commonjs/theme/tokens.js +12 -0
  56. package/lib/module/components/Accordion/Accordion.js +4 -2
  57. package/lib/module/components/Avatar/Avatar.js +4 -2
  58. package/lib/module/components/Badge/Badge.js +5 -5
  59. package/lib/module/components/Banner/Banner.js +8 -4
  60. package/lib/module/components/BottomNavigation/BottomNavigation.js +6 -4
  61. package/lib/module/components/BottomSheet/BottomSheet.js +68 -13
  62. package/lib/module/components/BottomSheet/index.js +1 -1
  63. package/lib/module/components/Box/Box.js +156 -0
  64. package/lib/module/components/Box/index.js +4 -0
  65. package/lib/module/components/Button/Button.js +7 -7
  66. package/lib/module/components/Carousel/Carousel.js +4 -2
  67. package/lib/module/components/Checkbox/Checkbox.js +14 -5
  68. package/lib/module/components/DatePicker/DatePicker.js +9 -7
  69. package/lib/module/components/DateRangePicker/DateRangePicker.js +5 -2
  70. package/lib/module/components/Dialog/Dialog.js +2 -2
  71. package/lib/module/components/FieldBase/FieldBase.js +8 -4
  72. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +13 -5
  73. package/lib/module/components/FormField/FormField.js +62 -26
  74. package/lib/module/components/Input/Input.js +41 -29
  75. package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +98 -0
  76. package/lib/module/components/KeyboardAwareScrollView/index.js +4 -0
  77. package/lib/module/components/KeyboardToolbar/KeyboardToolbar.js +125 -0
  78. package/lib/module/components/KeyboardToolbar/index.js +4 -0
  79. package/lib/module/components/Modal/Modal.js +17 -6
  80. package/lib/module/components/NumberInput/NumberInput.js +30 -23
  81. package/lib/module/components/OTPInput/OTPInput.js +30 -15
  82. package/lib/module/components/Radio/Radio.js +7 -5
  83. package/lib/module/components/Radio/RadioGroup.js +10 -3
  84. package/lib/module/components/SearchBar/SearchBar.js +4 -2
  85. package/lib/module/components/SegmentedControl/SegmentedControl.js +20 -10
  86. package/lib/module/components/Select/Select.js +33 -32
  87. package/lib/module/components/Skeleton/SkeletonContent.js +5 -2
  88. package/lib/module/components/Slider/Slider.js +42 -26
  89. package/lib/module/components/Spinner/Spinner.js +5 -5
  90. package/lib/module/components/Switch/Switch.js +29 -16
  91. package/lib/module/components/Tabs/Tabs.js +4 -2
  92. package/lib/module/components/Text/Text.js +138 -0
  93. package/lib/module/components/Text/index.js +4 -0
  94. package/lib/module/components/TimePicker/TimePicker.js +10 -7
  95. package/lib/module/components/Toast/Toast.js +22 -10
  96. package/lib/module/components/Tooltip/Tooltip.js +6 -2
  97. package/lib/module/components/index.js +5 -1
  98. package/lib/module/form/FormContext.js +32 -0
  99. package/lib/module/form/index.js +12 -0
  100. package/lib/module/form/path.js +72 -0
  101. package/lib/module/form/rules.js +52 -0
  102. package/lib/module/form/types.js +2 -0
  103. package/lib/module/form/useField.js +49 -0
  104. package/lib/module/form/useForm.js +312 -0
  105. package/lib/module/hooks/index.js +2 -0
  106. package/lib/module/hooks/useControllableState.js +26 -0
  107. package/lib/module/hooks/useReducedMotion.js +27 -0
  108. package/lib/module/index.js +3 -1
  109. package/lib/module/theme/ThemeContext.js +30 -2
  110. package/lib/module/theme/tokens.js +12 -0
  111. package/lib/typescript/commonjs/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  112. package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +41 -0
  113. package/lib/typescript/commonjs/components/BottomSheet/index.d.ts +2 -2
  114. package/lib/typescript/commonjs/components/Box/Box.d.ts +60 -0
  115. package/lib/typescript/commonjs/components/Box/index.d.ts +3 -0
  116. package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -1
  117. package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +3 -2
  118. package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +3 -3
  119. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +2 -2
  120. package/lib/typescript/commonjs/components/FormField/FormField.d.ts +13 -2
  121. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  122. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/index.d.ts +3 -0
  123. package/lib/typescript/commonjs/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  124. package/lib/typescript/commonjs/components/KeyboardToolbar/index.d.ts +3 -0
  125. package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -2
  126. package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +3 -2
  127. package/lib/typescript/commonjs/components/Radio/Radio.d.ts +2 -2
  128. package/lib/typescript/commonjs/components/Radio/RadioGroup.d.ts +3 -2
  129. package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  130. package/lib/typescript/commonjs/components/Slider/Slider.d.ts +6 -4
  131. package/lib/typescript/commonjs/components/Spinner/Spinner.d.ts +1 -1
  132. package/lib/typescript/commonjs/components/Switch/Switch.d.ts +3 -2
  133. package/lib/typescript/commonjs/components/Text/Text.d.ts +25 -0
  134. package/lib/typescript/commonjs/components/Text/index.d.ts +3 -0
  135. package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +3 -3
  136. package/lib/typescript/commonjs/components/index.d.ts +10 -2
  137. package/lib/typescript/commonjs/form/FormContext.d.ts +17 -0
  138. package/lib/typescript/commonjs/form/index.d.ts +9 -0
  139. package/lib/typescript/commonjs/form/path.d.ts +10 -0
  140. package/lib/typescript/commonjs/form/rules.d.ts +31 -0
  141. package/lib/typescript/commonjs/form/types.d.ts +94 -0
  142. package/lib/typescript/commonjs/form/useField.d.ts +27 -0
  143. package/lib/typescript/commonjs/form/useForm.d.ts +10 -0
  144. package/lib/typescript/commonjs/hooks/index.d.ts +3 -0
  145. package/lib/typescript/commonjs/hooks/useControllableState.d.ts +17 -0
  146. package/lib/typescript/commonjs/hooks/useReducedMotion.d.ts +8 -0
  147. package/lib/typescript/commonjs/index.d.ts +4 -2
  148. package/lib/typescript/commonjs/theme/types.d.ts +15 -0
  149. package/lib/typescript/module/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  150. package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +41 -0
  151. package/lib/typescript/module/components/BottomSheet/index.d.ts +2 -2
  152. package/lib/typescript/module/components/Box/Box.d.ts +60 -0
  153. package/lib/typescript/module/components/Box/index.d.ts +3 -0
  154. package/lib/typescript/module/components/Button/Button.d.ts +1 -1
  155. package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +3 -2
  156. package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +3 -3
  157. package/lib/typescript/module/components/Dialog/Dialog.d.ts +2 -2
  158. package/lib/typescript/module/components/FormField/FormField.d.ts +13 -2
  159. package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  160. package/lib/typescript/module/components/KeyboardAwareScrollView/index.d.ts +3 -0
  161. package/lib/typescript/module/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  162. package/lib/typescript/module/components/KeyboardToolbar/index.d.ts +3 -0
  163. package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -2
  164. package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +3 -2
  165. package/lib/typescript/module/components/Radio/Radio.d.ts +2 -2
  166. package/lib/typescript/module/components/Radio/RadioGroup.d.ts +3 -2
  167. package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  168. package/lib/typescript/module/components/Slider/Slider.d.ts +6 -4
  169. package/lib/typescript/module/components/Spinner/Spinner.d.ts +1 -1
  170. package/lib/typescript/module/components/Switch/Switch.d.ts +3 -2
  171. package/lib/typescript/module/components/Text/Text.d.ts +25 -0
  172. package/lib/typescript/module/components/Text/index.d.ts +3 -0
  173. package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +3 -3
  174. package/lib/typescript/module/components/index.d.ts +10 -2
  175. package/lib/typescript/module/form/FormContext.d.ts +17 -0
  176. package/lib/typescript/module/form/index.d.ts +9 -0
  177. package/lib/typescript/module/form/path.d.ts +10 -0
  178. package/lib/typescript/module/form/rules.d.ts +31 -0
  179. package/lib/typescript/module/form/types.d.ts +94 -0
  180. package/lib/typescript/module/form/useField.d.ts +27 -0
  181. package/lib/typescript/module/form/useForm.d.ts +10 -0
  182. package/lib/typescript/module/hooks/index.d.ts +3 -0
  183. package/lib/typescript/module/hooks/useControllableState.d.ts +17 -0
  184. package/lib/typescript/module/hooks/useReducedMotion.d.ts +8 -0
  185. package/lib/typescript/module/index.d.ts +4 -2
  186. package/lib/typescript/module/theme/types.d.ts +15 -0
  187. package/package.json +1 -1
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { getPath, setPath } from "./path.js";
5
+ // Loosely typed (Values -> any) so the generic schema from any useForm<Values>
6
+ // instance flows in without variance friction; callers re-apply Values at use.
7
+ const rulesFor = (validate, name) => {
8
+ if (!validate || typeof validate === 'function') return [];
9
+ const r = validate[name];
10
+ if (!r) return [];
11
+ return Array.isArray(r) ? r : [r];
12
+ };
13
+
14
+ /**
15
+ * Dependency-free form engine. Tracks values/errors/touched/dirty, runs
16
+ * synchronous and async validation (per-field rule maps or a form-level
17
+ * function), gates error display behind touched/submit, and on a failed submit
18
+ * focuses the first errored field. The returned object is rebuilt every render
19
+ * so `<Form>` consumers re-render on state changes.
20
+ */
21
+ export function useForm(config) {
22
+ const configRef = useRef(config);
23
+ configRef.current = config;
24
+ const initialRef = useRef(config.initialValues);
25
+ const [values, setValuesState] = useState(config.initialValues);
26
+ const [errors, setErrorsState] = useState({});
27
+ const [touched, setTouchedState] = useState({});
28
+ const [isSubmitting, setSubmitting] = useState(false);
29
+ const [isValidating, setValidating] = useState(false);
30
+ const [submitCount, setSubmitCount] = useState(0);
31
+
32
+ // Mirror of the latest state so handlers (incl. async) never read stale
33
+ // closures and sequential setFieldValue calls in one tick compound correctly.
34
+ const ref = useRef({
35
+ values,
36
+ errors,
37
+ touched,
38
+ submitCount
39
+ });
40
+ ref.current = {
41
+ values,
42
+ errors,
43
+ touched,
44
+ submitCount
45
+ };
46
+ const fieldNodes = useRef(new Map());
47
+ const fieldOrder = useRef([]);
48
+ const focusedFieldRef = useRef(null);
49
+ useEffect(() => {
50
+ if (!configRef.current.enableReinitialize) return;
51
+ const next = configRef.current.initialValues;
52
+ initialRef.current = next;
53
+ ref.current.values = next;
54
+ ref.current.errors = {};
55
+ ref.current.touched = {};
56
+ setValuesState(next);
57
+ setErrorsState({});
58
+ setTouchedState({});
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, [JSON.stringify(config.initialValues)]);
61
+ const validateField = useCallback(async name => {
62
+ const validate = configRef.current.validate;
63
+ const allValues = ref.current.values;
64
+ if (typeof validate === 'function') {
65
+ const map = (await validate(allValues)) ?? {};
66
+ return map[name] || undefined;
67
+ }
68
+ const value = getPath(allValues, name);
69
+ for (const rule of rulesFor(validate, name)) {
70
+ const res = await rule(value, allValues);
71
+ if (res) return res;
72
+ }
73
+ return undefined;
74
+ }, []);
75
+ const runAll = useCallback(async vals => {
76
+ const validate = configRef.current.validate;
77
+ if (!validate) return {};
78
+ if (typeof validate === 'function') return (await validate(vals)) ?? {};
79
+ const out = {};
80
+ await Promise.all(Object.keys(validate).map(async name => {
81
+ const value = getPath(vals, name);
82
+ for (const rule of rulesFor(validate, name)) {
83
+ const res = await rule(value, vals);
84
+ if (res) {
85
+ out[name] = res;
86
+ break;
87
+ }
88
+ }
89
+ }));
90
+ return out;
91
+ }, []);
92
+ const revalidateField = useCallback(async name => {
93
+ const err = await validateField(name);
94
+ setErrorsState(prev => {
95
+ if (prev[name] === err) return prev;
96
+ const next = {
97
+ ...prev
98
+ };
99
+ if (err) next[name] = err;else delete next[name];
100
+ ref.current.errors = next;
101
+ return next;
102
+ });
103
+ }, [validateField]);
104
+ const validate = useCallback(async () => {
105
+ setValidating(true);
106
+ const errs = await runAll(ref.current.values);
107
+ setValidating(false);
108
+ ref.current.errors = errs;
109
+ setErrorsState(errs);
110
+ return errs;
111
+ }, [runAll]);
112
+ const setFieldValue = useCallback((name, value, shouldValidate) => {
113
+ const next = setPath(ref.current.values, name, value);
114
+ ref.current.values = next;
115
+ setValuesState(next);
116
+ const cfg = configRef.current;
117
+ const willValidate = shouldValidate ?? (ref.current.touched[name] || ref.current.submitCount > 0 || cfg.validateOn === 'change');
118
+ if (willValidate) void revalidateField(name);
119
+ }, [revalidateField]);
120
+ const handleBlur = useCallback(name => {
121
+ setTouchedState(prev => {
122
+ if (prev[name]) return prev;
123
+ const next = {
124
+ ...prev,
125
+ [name]: true
126
+ };
127
+ ref.current.touched = next;
128
+ return next;
129
+ });
130
+ // Blur is the natural moment to surface a field's error.
131
+ void revalidateField(name);
132
+ }, [revalidateField]);
133
+ const setFieldTouched = useCallback((name, isTouched = true, shouldValidate) => {
134
+ const next = {
135
+ ...ref.current.touched,
136
+ [name]: isTouched
137
+ };
138
+ ref.current.touched = next;
139
+ setTouchedState(next);
140
+ if (shouldValidate ?? isTouched) void revalidateField(name);
141
+ }, [revalidateField]);
142
+ const setFieldError = useCallback((name, error) => {
143
+ setErrorsState(prev => {
144
+ const next = {
145
+ ...prev
146
+ };
147
+ if (error) next[name] = error;else delete next[name];
148
+ ref.current.errors = next;
149
+ return next;
150
+ });
151
+ }, []);
152
+ const setErrors = useCallback(errs => {
153
+ ref.current.errors = errs;
154
+ setErrorsState(errs);
155
+ }, []);
156
+ const setValues = useCallback((next, shouldValidate) => {
157
+ const resolved = typeof next === 'function' ? next(ref.current.values) : {
158
+ ...ref.current.values,
159
+ ...next
160
+ };
161
+ ref.current.values = resolved;
162
+ setValuesState(resolved);
163
+ if (shouldValidate) void validate();
164
+ }, [validate]);
165
+ const reset = useCallback(nextValues => {
166
+ const base = nextValues ? {
167
+ ...initialRef.current,
168
+ ...nextValues
169
+ } : initialRef.current;
170
+ initialRef.current = base;
171
+ ref.current.values = base;
172
+ ref.current.errors = {};
173
+ ref.current.touched = {};
174
+ ref.current.submitCount = 0;
175
+ setValuesState(base);
176
+ setErrorsState({});
177
+ setTouchedState({});
178
+ setSubmitCount(0);
179
+ }, []);
180
+ const registerField = useCallback((name, node) => {
181
+ if (node) {
182
+ fieldNodes.current.set(name, node);
183
+ if (!fieldOrder.current.includes(name)) fieldOrder.current.push(name);
184
+ } else {
185
+ fieldNodes.current.delete(name);
186
+ fieldOrder.current = fieldOrder.current.filter(n => n !== name);
187
+ }
188
+ }, []);
189
+ const handleFocus = useCallback(name => {
190
+ focusedFieldRef.current = name;
191
+ }, []);
192
+ const focusAdjacent = useCallback(direction => {
193
+ const order = fieldOrder.current;
194
+ const current = focusedFieldRef.current;
195
+ const index = current ? order.indexOf(current) : -1;
196
+ const target = order[index + direction];
197
+ if (!target) return;
198
+ focusedFieldRef.current = target;
199
+ fieldNodes.current.get(target)?.focus?.();
200
+ }, []);
201
+ const focusNext = useCallback(() => focusAdjacent(1), [focusAdjacent]);
202
+ const focusPrev = useCallback(() => focusAdjacent(-1), [focusAdjacent]);
203
+ const helpers = useMemo(() => ({
204
+ setErrors,
205
+ setFieldError,
206
+ reset,
207
+ setSubmitting
208
+ }), [setErrors, setFieldError, reset]);
209
+ const submit = useCallback(() => {
210
+ void (async () => {
211
+ const cfg = configRef.current;
212
+ const currentValues = ref.current.values;
213
+ ref.current.submitCount += 1;
214
+ setSubmitCount(c => c + 1);
215
+ setValidating(true);
216
+ const errs = await runAll(currentValues);
217
+ setValidating(false);
218
+ const keys = new Set([...fieldOrder.current, ...Object.keys(errs)]);
219
+ const nextTouched = {
220
+ ...ref.current.touched
221
+ };
222
+ keys.forEach(k => {
223
+ nextTouched[k] = true;
224
+ });
225
+ ref.current.touched = nextTouched;
226
+ ref.current.errors = errs;
227
+ setTouchedState(nextTouched);
228
+ setErrorsState(errs);
229
+ const firstError = fieldOrder.current.find(n => errs[n]) ?? Object.keys(errs).find(n => errs[n]);
230
+ if (firstError) {
231
+ fieldNodes.current.get(firstError)?.focus?.();
232
+ return;
233
+ }
234
+ setSubmitting(true);
235
+ try {
236
+ await cfg.onSubmit(currentValues, helpers);
237
+ } finally {
238
+ setSubmitting(false);
239
+ }
240
+ })();
241
+ }, [runAll, helpers]);
242
+ const uiErrors = useMemo(() => {
243
+ const gated = {};
244
+ for (const k of Object.keys(errors)) {
245
+ if (errors[k] && (touched[k] || submitCount > 0)) gated[k] = errors[k];
246
+ }
247
+ return gated;
248
+ }, [errors, touched, submitCount]);
249
+ const isValid = useMemo(() => {
250
+ const validateCfg = configRef.current.validate;
251
+ if (!validateCfg) return true;
252
+ // Form-level async fn can't run synchronously — fall back to known errors.
253
+ if (typeof validateCfg === 'function') return Object.values(errors).every(e => !e);
254
+ for (const name of Object.keys(validateCfg)) {
255
+ const value = getPath(values, name);
256
+ for (const rule of rulesFor(validateCfg, name)) {
257
+ const res = rule(value, values);
258
+ if (typeof res === 'string' && res) return false; // async rules (Promise) are skipped here
259
+ }
260
+ }
261
+ return true;
262
+ }, [values, errors]);
263
+ const dirty = useMemo(() => JSON.stringify(values) !== JSON.stringify(initialRef.current), [values]);
264
+ return {
265
+ values,
266
+ errors: uiErrors,
267
+ rawErrors: errors,
268
+ touched,
269
+ dirty,
270
+ isValid,
271
+ isSubmitting,
272
+ isValidating,
273
+ submitCount,
274
+ setFieldValue,
275
+ setFieldTouched,
276
+ setFieldError,
277
+ setValues,
278
+ setErrors,
279
+ reset,
280
+ validate,
281
+ validateField,
282
+ submit,
283
+ handleBlur,
284
+ focusNext,
285
+ focusPrev,
286
+ registerField,
287
+ getFieldState: name => {
288
+ const value = getPath(values, name);
289
+ const rawError = errors[name];
290
+ const isTouched = !!touched[name];
291
+ return {
292
+ value,
293
+ error: isTouched || submitCount > 0 ? rawError : undefined,
294
+ rawError,
295
+ touched: isTouched,
296
+ dirty: JSON.stringify(value) !== JSON.stringify(getPath(initialRef.current, name))
297
+ };
298
+ },
299
+ getFieldProps: name => ({
300
+ name,
301
+ value: getPath(values, name),
302
+ error: touched[name] || submitCount > 0 ? errors[name] : undefined,
303
+ touched: !!touched[name],
304
+ onChange: val => setFieldValue(name, val),
305
+ onChangeText: text => setFieldValue(name, text),
306
+ onBlur: () => handleBlur(name),
307
+ onFocus: () => handleFocus(name),
308
+ setValue: (val, sv) => setFieldValue(name, val, sv)
309
+ })
310
+ };
311
+ }
312
+ //# sourceMappingURL=useForm.js.map
@@ -3,4 +3,6 @@
3
3
  export { useToggle } from "./useToggle.js";
4
4
  export { useDebounce } from "./useDebounce.js";
5
5
  export { usePressAnimation } from "./usePressAnimation.js";
6
+ export { useReducedMotion } from "./useReducedMotion.js";
7
+ export { useControllableState } from "./useControllableState.js";
6
8
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useRef, useState } from 'react';
4
+ /**
5
+ * Bridges controlled and uncontrolled usage for a value+onChange pair. When
6
+ * `value` is provided the component is controlled (the hook just forwards it
7
+ * and calls `onChange`); otherwise the hook owns the state, seeded from
8
+ * `defaultValue`. The controlled/uncontrolled decision is locked on first
9
+ * render so a component never flips modes mid-life.
10
+ */
11
+ export function useControllableState({
12
+ value,
13
+ defaultValue,
14
+ onChange
15
+ }) {
16
+ const isControlledRef = useRef(value !== undefined);
17
+ const isControlled = isControlledRef.current;
18
+ const [internal, setInternal] = useState(defaultValue);
19
+ const current = isControlled ? value : internal;
20
+ const setValue = useCallback(next => {
21
+ if (!isControlled) setInternal(next);
22
+ onChange?.(next);
23
+ }, [isControlled, onChange]);
24
+ return [current, setValue];
25
+ }
26
+ //# sourceMappingURL=useControllableState.js.map
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { AccessibilityInfo } from 'react-native';
5
+
6
+ /**
7
+ * Tracks the OS "Reduce Motion" accessibility setting. The library's
8
+ * ThemeProvider uses this to collapse `theme.motion` (durations → 0, springs →
9
+ * near-instant) so animations don't trigger vestibular discomfort; consumers
10
+ * can also read it directly to swap an animated path for a static one.
11
+ */
12
+ export const useReducedMotion = () => {
13
+ const [reduced, setReduced] = useState(false);
14
+ useEffect(() => {
15
+ let mounted = true;
16
+ AccessibilityInfo.isReduceMotionEnabled().then(value => {
17
+ if (mounted) setReduced(value);
18
+ });
19
+ const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', value => setReduced(value));
20
+ return () => {
21
+ mounted = false;
22
+ sub.remove();
23
+ };
24
+ }, []);
25
+ return reduced;
26
+ };
27
+ //# sourceMappingURL=useReducedMotion.js.map
@@ -21,13 +21,15 @@ export { VersionCheck, useVersionCheck } from "./versionCheck/index.js";
21
21
  // Theme
22
22
  export { ThemeProvider, useTheme, useThemeMode, setTheme, setColorMode, getColorMode, getTheme, resetTheme, subscribeTheme, mergeTheme, lightTheme, darkTheme } from "./theme/index.js";
23
23
  // Hooks
24
- export { useToggle, useDebounce, usePressAnimation } from "./hooks/index.js";
24
+ export { useToggle, useDebounce, usePressAnimation, useReducedMotion, useControllableState } from "./hooks/index.js";
25
25
  // Utils
26
26
  export { ScreenDimensions, Breakpoints, Responsive, isScreenSize, isTablet, Spacing, Padding, Margin, triggerHaptic, setHapticImplementation, shadowStyle, compressImage } from "./utils/index.js";
27
27
  // Formatters
28
28
  export { formatDate, formatTime, formatDateTime, formatRelativeTime, formatPhone, normalizePhone, formatCurrency, formatNumber, formatPercent, formatCompactNumber, getInitials } from "./formatters/index.js";
29
29
  // Validators
30
30
  export { isNonEmpty, isEmail, isIndianMobile, isPhone, isUrl, isNumeric, isInteger, isAlphanumeric, isStrongPassword, isIndianPincode, isPan, isGstin, isAadhaar, isIfsc, isHexColor, isBetween, minLength, maxLength } from "./validators/index.js";
31
+ // Form engine (dependency-free; replaces Formik + Yup)
32
+ export { useForm, useField, useFieldArray, Form, useFormContext, useOptionalFormContext, rules, getPath, setPath, deletePath } from "./form/index.js";
31
33
  // Network
32
34
  export { useNetworkStatus, getNetworkStatus, addNetworkStatusListener, NetworkStatusBanner } from "./network/index.js";
33
35
  // Permissions
@@ -4,7 +4,30 @@ import React, { createContext, useContext, useEffect, useMemo, useState } from '
4
4
  import { Appearance } from 'react-native';
5
5
  import { darkTheme, lightTheme } from "./tokens.js";
6
6
  import { mergeTheme } from "./merge.js";
7
+ import { useReducedMotion } from "../hooks/useReducedMotion.js";
7
8
  import { jsx as _jsx } from "react/jsx-runtime";
9
+ const REDUCED_SPRING = {
10
+ damping: 100,
11
+ stiffness: 1000,
12
+ mass: 1
13
+ };
14
+ // Reduce Motion: collapse timed animations to instant and make springs settle in
15
+ // ~1 frame, so vestibular-sensitive users aren't subjected to slides/scales/bounces.
16
+ const collapseMotion = motion => ({
17
+ ...motion,
18
+ duration: {
19
+ instant: 0,
20
+ fast: 0,
21
+ normal: 0,
22
+ slow: 0,
23
+ slower: 0
24
+ },
25
+ spring: {
26
+ gentle: REDUCED_SPRING,
27
+ bouncy: REDUCED_SPRING,
28
+ snappy: REDUCED_SPRING
29
+ }
30
+ });
8
31
  const defaultValue = {
9
32
  theme: lightTheme,
10
33
  mode: 'light',
@@ -30,11 +53,16 @@ export const ThemeProvider = ({
30
53
  return () => sub.remove();
31
54
  }, []);
32
55
  const mode = preference === 'system' ? systemMode : preference;
56
+ const reduceMotion = useReducedMotion();
33
57
  const theme = useMemo(() => {
34
58
  const base = mode === 'dark' ? darkTheme : lightTheme;
35
59
  const overrides = mode === 'dark' ? darkOverrides : lightOverrides;
36
- return overrides ? mergeTheme(base, overrides) : base;
37
- }, [mode, lightOverrides, darkOverrides]);
60
+ const merged = overrides ? mergeTheme(base, overrides) : base;
61
+ return reduceMotion ? {
62
+ ...merged,
63
+ motion: collapseMotion(merged.motion)
64
+ } : merged;
65
+ }, [mode, lightOverrides, darkOverrides, reduceMotion]);
38
66
  const value = useMemo(() => ({
39
67
  theme,
40
68
  mode,
@@ -521,6 +521,12 @@ export const lightTheme = {
521
521
  warning: '#F59E0B',
522
522
  error: '#E5484D',
523
523
  info: '#3B82F6',
524
+ onPrimary: '#FFFFFF',
525
+ onSecondary: '#0F0F1A',
526
+ onSuccess: '#FFFFFF',
527
+ onWarning: '#0F0F1A',
528
+ onError: '#FFFFFF',
529
+ onInfo: '#FFFFFF',
524
530
  text: {
525
531
  primary: '#0F0F1A',
526
532
  secondary: '#52525B',
@@ -576,6 +582,12 @@ export const darkTheme = {
576
582
  warning: '#FBBF24',
577
583
  error: '#F87171',
578
584
  info: '#60A5FA',
585
+ onPrimary: '#0F0F1A',
586
+ onSecondary: '#FAFAFA',
587
+ onSuccess: '#0F0F1A',
588
+ onWarning: '#0F0F1A',
589
+ onError: '#0F0F1A',
590
+ onInfo: '#0F0F1A',
579
591
  text: {
580
592
  primary: '#FAFAFA',
581
593
  secondary: '#B4B4BB',
@@ -20,7 +20,7 @@ export interface TabConfig {
20
20
  export interface BottomNavigationProps {
21
21
  tabs: TabConfig[];
22
22
  activeTab: string;
23
- onTabPress: (tabKey: string) => void;
23
+ onChange: (tabKey: string) => void;
24
24
  haptic?: HapticType | false;
25
25
  showLabels?: boolean;
26
26
  variant?: BottomNavigationVariant;
@@ -56,6 +56,23 @@ export interface BottomSheetProps {
56
56
  mode?: BottomSheetMode;
57
57
  handleIndicatorStyle?: StyleProp<ViewStyle>;
58
58
  containerStyle?: StyleProp<ViewStyle>;
59
+ /**
60
+ * Sticky region rendered between the drag handle and the scrollable content.
61
+ * Does not scroll with `children`. Can call `useBottomSheet()` to read sheet
62
+ * state.
63
+ */
64
+ header?: React.ReactNode;
65
+ /**
66
+ * Sticky region pinned to the bottom of the sheet, above the safe-area
67
+ * inset. Does not scroll with `children`, and rides up with the keyboard
68
+ * when `keyboardBehavior='shift'` (no extra wiring needed — the whole sheet
69
+ * shifts). Typical use: action button rows.
70
+ */
71
+ footer?: React.ReactNode;
72
+ /** Style applied to the header region wrapper. */
73
+ headerStyle?: StyleProp<ViewStyle>;
74
+ /** Style applied to the footer region wrapper. */
75
+ footerStyle?: StyleProp<ViewStyle>;
59
76
  children?: React.ReactNode;
60
77
  accessibilityLabel?: string;
61
78
  accessibilityViewIsModal?: boolean;
@@ -66,6 +83,30 @@ export interface BottomSheetRef {
66
83
  collapse: () => void;
67
84
  close: () => void;
68
85
  }
86
+ /**
87
+ * State + actions exposed to anything rendered inside a `<BottomSheet>` —
88
+ * including the `header` and `footer` slots. Read via `useBottomSheet()`.
89
+ *
90
+ * `snapIndex` is the JS-thread mirror of the current snap point. -1 means the
91
+ * sheet is closed (mid-close-animation included). Use it to drive footer
92
+ * button enable state, conditional headers, etc. If you need per-frame
93
+ * progress (e.g. fade a header in as the sheet expands), reach for the
94
+ * animated value via a future enhancement — not exposed today to keep the
95
+ * surface minimal.
96
+ */
97
+ export interface BottomSheetContextValue {
98
+ snapIndex: number;
99
+ snapPoints: number[];
100
+ expand: (index?: number) => void;
101
+ collapse: () => void;
102
+ close: () => void;
103
+ }
104
+ /**
105
+ * Access the enclosing `<BottomSheet>`'s state and imperative actions.
106
+ * Must be called from a component rendered inside a `<BottomSheet>` (as
107
+ * `children`, `header`, or `footer`).
108
+ */
109
+ export declare const useBottomSheet: () => BottomSheetContextValue;
69
110
  declare const BottomSheet: React.ForwardRefExoticComponent<BottomSheetProps & React.RefAttributes<BottomSheetRef>>;
70
111
  export { BottomSheet };
71
112
  export default BottomSheet;
@@ -1,3 +1,3 @@
1
- export { BottomSheet, default } from './BottomSheet';
2
- export type { BottomSheetProps, BottomSheetRef, SnapPoint } from './BottomSheet';
1
+ export { BottomSheet, default, useBottomSheet } from './BottomSheet';
2
+ export type { BottomSheetContextValue, BottomSheetProps, BottomSheetRef, SnapPoint } from './BottomSheet';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
4
+ import type { ColorPalette, RadiusScale, SpacingScale, Theme } from '../../theme/types';
5
+ export type SpacingToken = keyof SpacingScale;
6
+ export type RadiusToken = keyof RadiusScale;
7
+ export type BackgroundToken = keyof ColorPalette['background'];
8
+ export interface BoxProps extends ViewProps {
9
+ /** padding (all sides) */
10
+ p?: SpacingToken;
11
+ /** paddingHorizontal */
12
+ px?: SpacingToken;
13
+ /** paddingVertical */
14
+ py?: SpacingToken;
15
+ pt?: SpacingToken;
16
+ pr?: SpacingToken;
17
+ pb?: SpacingToken;
18
+ pl?: SpacingToken;
19
+ /** margin (all sides) */
20
+ m?: SpacingToken;
21
+ mx?: SpacingToken;
22
+ my?: SpacingToken;
23
+ mt?: SpacingToken;
24
+ mr?: SpacingToken;
25
+ mb?: SpacingToken;
26
+ ml?: SpacingToken;
27
+ /** Flexbox gap between children (spacing token). */
28
+ gap?: SpacingToken;
29
+ /** Background — a `colors.background.*` token or a raw colour string. */
30
+ bg?: BackgroundToken | (string & {});
31
+ radius?: RadiusToken;
32
+ /** Draw a hairline border in `colors.border.primary`. */
33
+ border?: boolean;
34
+ flex?: number;
35
+ align?: ViewStyle['alignItems'];
36
+ justify?: ViewStyle['justifyContent'];
37
+ direction?: ViewStyle['flexDirection'];
38
+ wrap?: boolean;
39
+ style?: StyleProp<ViewStyle>;
40
+ }
41
+ type LayoutProps = Omit<BoxProps, 'style' | 'children'>;
42
+ export declare const resolveBoxStyle: (theme: Theme, p: LayoutProps) => ViewStyle;
43
+ declare const Box: React.ForwardRefExoticComponent<BoxProps & React.RefAttributes<View>>;
44
+ export type StackProps = Omit<BoxProps, 'direction'>;
45
+ /** Vertical flex container with token spacing between children (default gap `md`). */
46
+ declare const Stack: React.ForwardRefExoticComponent<StackProps & React.RefAttributes<View>>;
47
+ export type RowProps = Omit<BoxProps, 'direction'>;
48
+ /** Horizontal flex container (default `align="center"`, gap `sm`). */
49
+ declare const Row: React.ForwardRefExoticComponent<RowProps & React.RefAttributes<View>>;
50
+ export interface SpacerProps {
51
+ /** Fixed spacer of this spacing token. Omit for a flexible spacer (`flex: 1`). */
52
+ size?: SpacingToken;
53
+ style?: StyleProp<ViewStyle>;
54
+ testID?: string;
55
+ }
56
+ /** Fixed gap (`size`) or a flexible push (`flex: 1`) between siblings. */
57
+ declare const Spacer: React.FC<SpacerProps>;
58
+ export { Box, Stack, Row, Spacer };
59
+ export default Box;
60
+ //# sourceMappingURL=Box.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { Box, Stack, Row, Spacer, resolveBoxStyle } from './Box';
2
+ export type { BoxProps, StackProps, RowProps, SpacerProps } from './Box';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -4,7 +4,7 @@ import type { PressableProps, StyleProp, TextStyle, ViewStyle } from 'react-nati
4
4
  import type { GradientDefinition } from '../../theme/types';
5
5
  import type { HapticType } from '../../utils/hapticUtils';
6
6
  export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'link';
7
- export type ButtonTone = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'neutral';
7
+ export type ButtonTone = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'neutral';
8
8
  export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
9
  export interface ButtonProps extends Omit<PressableProps, 'style' | 'children'> {
10
10
  title?: string;
@@ -5,8 +5,9 @@ import type { HapticType } from '../../utils/hapticUtils';
5
5
  export type CheckboxSize = 'sm' | 'md' | 'lg';
6
6
  export type CheckboxTone = 'primary' | 'success' | 'warning' | 'error';
7
7
  export interface CheckboxProps extends Omit<PressableProps, 'style' | 'children' | 'onPress'> {
8
- checked: boolean;
9
- onChange: (checked: boolean) => void;
8
+ checked?: boolean;
9
+ defaultChecked?: boolean;
10
+ onChange?: (checked: boolean) => void;
10
11
  indeterminate?: boolean;
11
12
  disabled?: boolean;
12
13
  label?: string;
@@ -6,21 +6,21 @@ export type WeekStart = 0 | 1 | 6;
6
6
  /**
7
7
  * DatePicker operates in two modes:
8
8
  *
9
- * 1. **Controlled-modal mode** — pass `visible` (plus `onSelect` / `onClose`)
9
+ * 1. **Controlled-modal mode** — pass `visible` (plus `onChange` / `onClose`)
10
10
  * and own the open state externally. The component renders only the modal.
11
11
  * 2. **Trigger mode** — omit `visible`. The component renders a built-in
12
12
  * PickerTrigger field (label / value / placeholder / chevron / clear /
13
13
  * helper / error / size / variant) and manages its own open state. The
14
14
  * field opens the modal on press and closes it on confirm/cancel.
15
15
  *
16
- * `onSelect` / `onClose` are typed optional to support trigger-only usage
16
+ * `onChange` / `onClose` are typed optional to support trigger-only usage
17
17
  * where the consumer may not need either callback. In controlled-modal mode
18
18
  * they remain semantically required.
19
19
  */
20
20
  export interface DatePickerProps {
21
21
  visible?: boolean;
22
22
  value?: Date | null;
23
- onSelect?: (date: Date) => void;
23
+ onChange?: (date: Date) => void;
24
24
  onClose?: () => void;
25
25
  minDate?: Date;
26
26
  maxDate?: Date;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
3
- export type DialogVariant = 'default' | 'success' | 'warning' | 'danger' | 'info';
4
- export type DialogActionTone = 'primary' | 'neutral' | 'danger';
3
+ export type DialogVariant = 'default' | 'success' | 'warning' | 'error' | 'info';
4
+ export type DialogActionTone = 'primary' | 'neutral' | 'error';
5
5
  export interface DialogAction {
6
6
  label: string;
7
7
  onPress: () => void;