@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.
- package/README.md +499 -0
- package/dist/index.d.ts +413 -0
- package/dist/index.js +396 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|