@formos/react 0.1.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.
@@ -0,0 +1,413 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { FormEngineOptions, FormEngine } from '@formos/kernel';
3
+ import { NormalizedSchemaV1 } from '@formos/schema';
4
+
5
+ /**
6
+ * React-specific type definitions
7
+ * These extend kernel types with React integration
8
+ */
9
+
10
+ /**
11
+ * Configuration for FormProvider.
12
+ *
13
+ * Extends {@link FormEngineOptions} with React-specific options.
14
+ *
15
+ * @public
16
+ * @stable
17
+ */
18
+ interface FormProviderProps extends FormEngineOptions {
19
+ /** Normalized schema from @formos/schema */
20
+ schema: NormalizedSchemaV1;
21
+ /** Optional initial values for fields */
22
+ initialValues?: Record<string, unknown>;
23
+ /** Children components */
24
+ children: React.ReactNode;
25
+ }
26
+ /**
27
+ * Options for useField hook.
28
+ *
29
+ * @public
30
+ * @stable
31
+ */
32
+ interface UseFieldOptions {
33
+ /** Custom validation trigger (overrides schema default) */
34
+ validateOnChange?: boolean;
35
+ /** Custom blur trigger (overrides schema default) */
36
+ validateOnBlur?: boolean;
37
+ }
38
+ /**
39
+ * Return type of useField hook.
40
+ *
41
+ * @public
42
+ * @stable
43
+ */
44
+ interface UseFieldResult {
45
+ /** Current field value */
46
+ value: unknown;
47
+ /** Current error message (undefined if valid) */
48
+ error: string | undefined;
49
+ /** Whether field has been touched */
50
+ touched: boolean;
51
+ /** Whether field value differs from initial */
52
+ dirty: boolean;
53
+ /** Set field value */
54
+ setValue: (value: unknown) => Promise<void>;
55
+ /** Set field error */
56
+ setError: (error: string) => void;
57
+ /** Clear field error */
58
+ clearError: () => void;
59
+ /** Mark field as touched */
60
+ markTouched: () => void;
61
+ /** Validate field */
62
+ validate: () => Promise<boolean>;
63
+ }
64
+ /**
65
+ * Return type of useFormState hook.
66
+ *
67
+ * @public
68
+ * @stable
69
+ */
70
+ interface UseFormStateResult {
71
+ /** All form values */
72
+ values: Record<string, unknown>;
73
+ /** All form errors */
74
+ errors: Record<string, string | undefined>;
75
+ /** Whether form is valid */
76
+ isValid: boolean;
77
+ /** Whether any field is dirty */
78
+ isDirty: boolean;
79
+ /** Whether form is currently submitting */
80
+ isSubmitting: boolean;
81
+ }
82
+ /**
83
+ * Return type of useForm hook.
84
+ *
85
+ * Provides form-level operations and state access.
86
+ * All multi-step methods return undefined for single-step forms.
87
+ *
88
+ * @public
89
+ * @stable
90
+ */
91
+ interface UseFormResult {
92
+ /** Submit form (validates all fields, calls onSubmit if valid) */
93
+ submit: () => Promise<boolean>;
94
+ /** Reset form to initial state */
95
+ reset: () => void;
96
+ /** Validate entire form with optional trigger */
97
+ validate: (trigger?: "onChange" | "onBlur" | "onSubmit" | "manual") => Promise<boolean>;
98
+ /** Get all form values */
99
+ getValues: () => Record<string, unknown>;
100
+ /** Get single field value */
101
+ getValue: (fieldName: string) => unknown;
102
+ /** Set field value programmatically (triggers validation) */
103
+ setValue: (fieldName: string, value: unknown) => Promise<void>;
104
+ /** Get all field errors */
105
+ getErrors: () => Record<string, string | undefined>;
106
+ /** Get single field error */
107
+ getError: (fieldName: string) => string | undefined;
108
+ /** Set field error manually (e.g., from server) */
109
+ setError: (fieldName: string, error: string) => void;
110
+ /** Clear field error */
111
+ clearError: (fieldName: string) => void;
112
+ /** Check if form is valid (no errors) */
113
+ isValid: () => boolean;
114
+ /** Check if form/field is dirty (changed from initial) */
115
+ isDirty: (fieldName?: string) => boolean;
116
+ /** Check if field has been touched */
117
+ isTouched: (fieldName: string) => boolean;
118
+ /** Mark field as touched */
119
+ markTouched: (fieldName: string) => void;
120
+ /** Get current step index (0-based) */
121
+ getCurrentStep: () => number | undefined;
122
+ /** Get total number of steps */
123
+ getTotalSteps: () => number | undefined;
124
+ /** Go to next step (validates current step first) */
125
+ nextStep: (() => Promise<boolean>) | undefined;
126
+ /** Go to previous step */
127
+ prevStep: (() => void) | undefined;
128
+ /** Jump to specific step (validates if moving forward) */
129
+ goToStep: ((stepIndex: number) => Promise<boolean>) | undefined;
130
+ /** Check if can navigate to step */
131
+ canGoToStep: ((stepIndex: number) => boolean) | undefined;
132
+ /** Direct access to form engine */
133
+ engine: FormEngine;
134
+ /** Direct access to normalized schema */
135
+ schema: NormalizedSchemaV1;
136
+ }
137
+ /**
138
+ * Return type of useStep hook (for multi-step forms).
139
+ *
140
+ * @public
141
+ * @stable
142
+ */
143
+ interface UseStepResult {
144
+ /** Current step index (0-based) */
145
+ currentStep: number;
146
+ /** Total number of steps */
147
+ totalSteps: number;
148
+ /** Go to next step (validates current step first) */
149
+ nextStep: () => Promise<boolean>;
150
+ /** Go to previous step */
151
+ prevStep: () => void;
152
+ /** Jump to specific step */
153
+ goToStep: (stepIndex: number) => Promise<boolean>;
154
+ /** Check if can navigate to step */
155
+ canGoToStep: (stepIndex: number) => boolean;
156
+ }
157
+
158
+ /**
159
+ * Form provider component that creates and manages a form engine instance.
160
+ *
161
+ * Wrap your form components with this provider to enable form hooks.
162
+ * The provider creates a single form engine instance and makes it available
163
+ * via React context.
164
+ *
165
+ * @public
166
+ * @stable
167
+ *
168
+ * @example
169
+ * ```tsx
170
+ * import { FormProvider } from '@formos/react';
171
+ * import { normalizeSchema } from '@formos/schema';
172
+ *
173
+ * const schema = { version: 'v1', fields: [...] };
174
+ * const normalized = normalizeSchema(schema);
175
+ *
176
+ * function App() {
177
+ * return (
178
+ * <FormProvider
179
+ * schema={normalized}
180
+ * validators={{
181
+ * required: (value) => value ? null : 'Required'
182
+ * }}
183
+ * onSubmit={async (values) => {
184
+ * await api.post('/submit', values);
185
+ * }}
186
+ * >
187
+ * <MyForm />
188
+ * </FormProvider>
189
+ * );
190
+ * }
191
+ * ```
192
+ */
193
+ declare function FormProvider({ schema, initialValues, children, ...engineOptions }: FormProviderProps): react_jsx_runtime.JSX.Element;
194
+
195
+ /**
196
+ * useForm hook - primary hook for form-level operations
197
+ */
198
+
199
+ /**
200
+ * Primary hook for form-level operations.
201
+ *
202
+ * Provides access to form engine and form-level actions like submit,
203
+ * reset, validation, and multi-step navigation. Wraps @formos/kernel
204
+ * with React-idiomatic API.
205
+ *
206
+ * @returns Form operations and state access methods
207
+ * @throws {Error} If used outside FormProvider
208
+ *
209
+ * @public
210
+ * @stable
211
+ *
212
+ * @example
213
+ * Single-step form:
214
+ * ```tsx
215
+ * function LoginForm() {
216
+ * const { submit, reset, isValid, isDirty } = useForm();
217
+ *
218
+ * const handleSubmit = async (e: React.FormEvent) => {
219
+ * e.preventDefault();
220
+ * const success = await submit();
221
+ * if (success) {
222
+ * console.log('Login successful');
223
+ * }
224
+ * };
225
+ *
226
+ * return (
227
+ * <form onSubmit={handleSubmit}>
228
+ * <EmailField />
229
+ * <PasswordField />
230
+ * <button type="submit" disabled={!isValid()}>
231
+ * Submit
232
+ * </button>
233
+ * <button type="button" onClick={reset} disabled={!isDirty()}>
234
+ * Reset
235
+ * </button>
236
+ * </form>
237
+ * );
238
+ * }
239
+ * ```
240
+ *
241
+ * @example
242
+ * Multi-step form:
243
+ * ```tsx
244
+ * function SignupWizard() {
245
+ * const {
246
+ * submit,
247
+ * getCurrentStep,
248
+ * getTotalSteps,
249
+ * nextStep,
250
+ * prevStep,
251
+ * isValid
252
+ * } = useForm();
253
+ *
254
+ * const currentStep = getCurrentStep() ?? 0;
255
+ * const totalSteps = getTotalSteps() ?? 1;
256
+ *
257
+ * const handleNext = async () => {
258
+ * const moved = await nextStep?.();
259
+ * if (!moved) {
260
+ * alert('Fix errors before proceeding');
261
+ * }
262
+ * };
263
+ *
264
+ * return (
265
+ * <div>
266
+ * <div>Step {currentStep + 1} of {totalSteps}</div>
267
+ * <button onClick={prevStep} disabled={currentStep === 0}>
268
+ * Previous
269
+ * </button>
270
+ * <button onClick={handleNext}>
271
+ * {currentStep === totalSteps - 1 ? 'Submit' : 'Next'}
272
+ * </button>
273
+ * </div>
274
+ * );
275
+ * }
276
+ * ```
277
+ */
278
+ declare function useForm(): UseFormResult;
279
+
280
+ /**
281
+ * useField hook - field-level operations and state
282
+ */
283
+
284
+ /**
285
+ * Hook for field-level operations and state management.
286
+ *
287
+ * Provides field value, error, touched/dirty state, and actions.
288
+ * Automatically subscribes to field changes for reactive updates.
289
+ *
290
+ * @param fieldName - Name of the field to manage
291
+ * @param options - Optional configuration for validation triggers
292
+ * @returns Field state and actions
293
+ * @throws {Error} If used outside FormProvider
294
+ *
295
+ * @public
296
+ * @stable
297
+ *
298
+ * @example
299
+ * ```tsx
300
+ * function EmailField() {
301
+ * const {
302
+ * value,
303
+ * error,
304
+ * touched,
305
+ * setValue,
306
+ * markTouched
307
+ * } = useField('email', {
308
+ * validateOnChange: true,
309
+ * validateOnBlur: true
310
+ * });
311
+ *
312
+ * return (
313
+ * <div>
314
+ * <input
315
+ * type="email"
316
+ * value={String(value || '')}
317
+ * onChange={(e) => setValue(e.target.value)}
318
+ * onBlur={() => markTouched()}
319
+ * />
320
+ * {touched && error && <span>{error}</span>}
321
+ * </div>
322
+ * );
323
+ * }
324
+ * ```
325
+ */
326
+ declare function useField(fieldName: string, options?: UseFieldOptions): UseFieldResult;
327
+
328
+ /**
329
+ * useFormState hook - observe form-level state
330
+ */
331
+
332
+ /**
333
+ * Hook for observing form-level state.
334
+ *
335
+ * Returns reactive state for all form values, errors, and validation status.
336
+ * Use this for displaying form-level information like submit button state
337
+ * or global error summaries.
338
+ *
339
+ * @returns Form state object
340
+ * @throws {Error} If used outside FormProvider
341
+ *
342
+ * @public
343
+ * @stable
344
+ *
345
+ * @example
346
+ * ```tsx
347
+ * function SubmitButton() {
348
+ * const { isValid, isDirty, isSubmitting } = useFormState();
349
+ *
350
+ * return (
351
+ * <button
352
+ * type="submit"
353
+ * disabled={!isValid || !isDirty || isSubmitting}
354
+ * >
355
+ * {isSubmitting ? 'Submitting...' : 'Submit'}
356
+ * </button>
357
+ * );
358
+ * }
359
+ * ```
360
+ */
361
+ declare function useFormState(): UseFormStateResult;
362
+
363
+ /**
364
+ * useStep hook - multi-step form navigation
365
+ */
366
+
367
+ /**
368
+ * Hook for multi-step form navigation.
369
+ *
370
+ * Only works with multi-step forms (schemas with `steps` defined).
371
+ * Provides current step information and navigation methods.
372
+ *
373
+ * @returns Step state and navigation methods
374
+ * @throws {Error} If used outside FormProvider or in single-step form
375
+ *
376
+ * @public
377
+ * @stable
378
+ *
379
+ * @example
380
+ * ```tsx
381
+ * function StepNavigation() {
382
+ * const {
383
+ * currentStep,
384
+ * totalSteps,
385
+ * nextStep,
386
+ * prevStep,
387
+ * canGoToStep
388
+ * } = useStep();
389
+ *
390
+ * const handleNext = async () => {
391
+ * const moved = await nextStep();
392
+ * if (!moved) {
393
+ * alert('Please fix errors before proceeding');
394
+ * }
395
+ * };
396
+ *
397
+ * return (
398
+ * <div>
399
+ * <p>Step {currentStep + 1} of {totalSteps}</p>
400
+ * <button onClick={prevStep} disabled={currentStep === 0}>
401
+ * Previous
402
+ * </button>
403
+ * <button onClick={handleNext}>
404
+ * {currentStep === totalSteps - 1 ? 'Submit' : 'Next'}
405
+ * </button>
406
+ * </div>
407
+ * );
408
+ * }
409
+ * ```
410
+ */
411
+ declare function useStep(): UseStepResult;
412
+
413
+ export { FormProvider, type FormProviderProps, type UseFieldOptions, type UseFieldResult, type UseFormResult, type UseFormStateResult, type UseStepResult, useField, useForm, useFormState, useStep };