@formality-ui/react 0.0.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 ADDED
@@ -0,0 +1,441 @@
1
+ # @formality-ui/react
2
+
3
+ React implementation of the Formality form framework. Build powerful, dynamic forms with conditional logic, field dependencies, and auto-save support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @formality-ui/react react-hook-form
9
+ # or
10
+ pnpm add @formality-ui/react react-hook-form
11
+ # or
12
+ yarn add @formality-ui/react react-hook-form
13
+ ```
14
+
15
+ **Peer Dependencies:**
16
+ - `react` >= 18.0.0
17
+ - `react-dom` >= 18.0.0
18
+ - `react-hook-form` >= 7.0.0
19
+
20
+ ## Quick Start
21
+
22
+ ```tsx
23
+ import { FormalityProvider, Form, Field } from '@formality-ui/react';
24
+ import type { InputConfig, FormFieldsConfig } from '@formality-ui/react';
25
+
26
+ // Define your input types
27
+ const inputs: Record<string, InputConfig> = {
28
+ textField: {
29
+ component: ({ value, onChange, label, error, ...props }) => (
30
+ <div>
31
+ <label>{label}</label>
32
+ <input value={value ?? ''} onChange={(e) => onChange(e.target.value)} {...props} />
33
+ {error && <span>{error}</span>}
34
+ </div>
35
+ ),
36
+ defaultValue: '',
37
+ },
38
+ switch: {
39
+ component: ({ value, onChange, label }) => (
40
+ <label>
41
+ <input type="checkbox" checked={value ?? false} onChange={(e) => onChange(e.target.checked)} />
42
+ {label}
43
+ </label>
44
+ ),
45
+ defaultValue: false,
46
+ },
47
+ };
48
+
49
+ // Define your form fields
50
+ const config: FormFieldsConfig = {
51
+ name: { type: 'textField', label: 'Full Name' },
52
+ email: { type: 'textField', label: 'Email Address' },
53
+ subscribed: { type: 'switch', label: 'Subscribe to newsletter' },
54
+ };
55
+
56
+ // Use in your app
57
+ function App() {
58
+ return (
59
+ <FormalityProvider inputs={inputs}>
60
+ <Form config={config} onSubmit={(values) => console.log(values)}>
61
+ {({ methods }) => (
62
+ <form onSubmit={methods.handleSubmit(console.log)}>
63
+ <Field name="name" />
64
+ <Field name="email" />
65
+ <Field name="subscribed" />
66
+ <button type="submit">Submit</button>
67
+ </form>
68
+ )}
69
+ </Form>
70
+ </FormalityProvider>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## Components
76
+
77
+ ### FormalityProvider
78
+
79
+ Global configuration provider. Wrap your app or form section.
80
+
81
+ ```tsx
82
+ <FormalityProvider
83
+ inputs={inputConfigs}
84
+ validators={validatorConfigs}
85
+ formatters={formatterConfigs}
86
+ parsers={parserConfigs}
87
+ errorMessages={errorMessageConfigs}
88
+ >
89
+ {children}
90
+ </FormalityProvider>
91
+ ```
92
+
93
+ **Props:**
94
+ | Prop | Type | Description |
95
+ |------|------|-------------|
96
+ | `inputs` | `Record<string, InputConfig>` | Input component configurations |
97
+ | `validators` | `ValidatorsConfig` | Custom validators |
98
+ | `formatters` | `FormattersConfig` | Custom formatters |
99
+ | `parsers` | `ParsersConfig` | Custom parsers |
100
+ | `errorMessages` | `ErrorMessagesConfig` | Custom error messages |
101
+
102
+ ### Form
103
+
104
+ Form container with React Hook Form integration.
105
+
106
+ ```tsx
107
+ <Form
108
+ config={fieldConfigs}
109
+ formConfig={formLevelConfig}
110
+ record={initialValues}
111
+ onSubmit={handleSubmit}
112
+ autoSave={false}
113
+ debounce={1000}
114
+ >
115
+ {({ methods, formState, unusedFields, resolvedTitle }) => (
116
+ // Render your form
117
+ )}
118
+ </Form>
119
+ ```
120
+
121
+ **Props:**
122
+ | Prop | Type | Description |
123
+ |------|------|-------------|
124
+ | `config` | `FormFieldsConfig` | Field configurations |
125
+ | `formConfig` | `FormConfig` | Form-level configuration |
126
+ | `record` | `Record<string, any>` | Initial values |
127
+ | `onSubmit` | `(values) => void` | Submit handler |
128
+ | `autoSave` | `boolean` | Enable auto-save |
129
+ | `debounce` | `number` | Debounce delay (ms) |
130
+
131
+ **Render API:**
132
+ | Property | Type | Description |
133
+ |----------|------|-------------|
134
+ | `methods` | `UseFormReturn` | React Hook Form methods |
135
+ | `formState` | `FormState` | Form state |
136
+ | `unusedFields` | `string[]` | Fields not yet rendered |
137
+ | `resolvedTitle` | `string` | Resolved form title |
138
+
139
+ ### Field
140
+
141
+ Individual field with automatic configuration resolution.
142
+
143
+ ```tsx
144
+ <Field
145
+ name="fieldName"
146
+ type="textField"
147
+ disabled={false}
148
+ hidden={false}
149
+ label="Custom Label"
150
+ shouldRegister={true}
151
+ >
152
+ {({ fieldState, renderedField, fieldProps, watchers }) => (
153
+ // Custom render
154
+ )}
155
+ </Field>
156
+ ```
157
+
158
+ **Props:**
159
+ | Prop | Type | Description |
160
+ |------|------|-------------|
161
+ | `name` | `string` | Field name (required) |
162
+ | `type` | `string` | Override input type |
163
+ | `disabled` | `boolean` | Override disabled state |
164
+ | `hidden` | `boolean` | Hide field |
165
+ | `label` | `string` | Override label |
166
+ | `shouldRegister` | `boolean` | Register as used field |
167
+
168
+ **Render API:**
169
+ | Property | Type | Description |
170
+ |----------|------|-------------|
171
+ | `fieldState` | `FieldState` | Field state |
172
+ | `renderedField` | `ReactNode` | Rendered input component |
173
+ | `fieldProps` | `object` | Resolved field props |
174
+ | `watchers` | `object` | Watched field values |
175
+
176
+ ### FieldGroup
177
+
178
+ Apply conditions to multiple fields.
179
+
180
+ ```tsx
181
+ const formConfig = {
182
+ groups: {
183
+ signedFields: {
184
+ conditions: [{ when: 'signed', is: true, disabled: false }],
185
+ },
186
+ },
187
+ };
188
+
189
+ <FieldGroup name="signedFields">
190
+ <Field name="creditApp" />
191
+ <Field name="inCarvin" />
192
+ </FieldGroup>
193
+ ```
194
+
195
+ **Props:**
196
+ | Prop | Type | Description |
197
+ |------|------|-------------|
198
+ | `name` | `string` | Group name (must match formConfig.groups key) |
199
+ | `children` | `ReactNode` | Child fields/content |
200
+
201
+ ### UnusedFields
202
+
203
+ Render fields from config not explicitly placed.
204
+
205
+ ```tsx
206
+ <Form config={config}>
207
+ <Field name="name" />
208
+ {/* Other fields from config rendered automatically */}
209
+ <UnusedFields />
210
+ </Form>
211
+ ```
212
+
213
+ **Props:**
214
+ | Prop | Type | Description |
215
+ |------|------|-------------|
216
+ | `exclude` | `string[]` | Field names to exclude |
217
+
218
+ ## Conditions
219
+
220
+ Add conditional logic to fields:
221
+
222
+ ```typescript
223
+ const config: FormFieldsConfig = {
224
+ signed: { type: 'switch' },
225
+ creditApp: {
226
+ type: 'switch',
227
+ conditions: [
228
+ { when: 'signed', is: false, disabled: true },
229
+ { when: 'signed', is: true, visible: true },
230
+ ],
231
+ },
232
+ };
233
+ ```
234
+
235
+ **Condition Properties:**
236
+ | Property | Description |
237
+ |----------|-------------|
238
+ | `when` | Field name to watch |
239
+ | `selectWhen` | Expression to evaluate |
240
+ | `is` | Exact value to match |
241
+ | `truthy` | Truthy/falsy match |
242
+ | `disabled` | Set disabled state when matched |
243
+ | `visible` | Set visibility when matched |
244
+ | `set` | Value to set when matched |
245
+ | `selectSet` | Expression for value to set |
246
+
247
+ ### Condition Merging Logic
248
+
249
+ - **disabled**: OR logic (disabled if ANY group/field is disabled)
250
+ - **visible**: AND logic (visible only if ALL groups/fields are visible)
251
+
252
+ ## Dynamic Props (selectProps)
253
+
254
+ Evaluate props dynamically based on form state:
255
+
256
+ ```typescript
257
+ const config: FormFieldsConfig = {
258
+ client: { type: 'autocomplete' },
259
+ clientContact: {
260
+ type: 'autocomplete',
261
+ selectProps: {
262
+ queryParams: 'client.id',
263
+ disabled: '!client',
264
+ placeholder: 'client.name',
265
+ },
266
+ },
267
+ };
268
+ ```
269
+
270
+ ## Auto-Save
271
+
272
+ Enable automatic form submission on changes:
273
+
274
+ ```tsx
275
+ <Form
276
+ config={config}
277
+ autoSave
278
+ debounce={2000}
279
+ onSubmit={async (values) => {
280
+ await saveToServer(values);
281
+ }}
282
+ >
283
+ {/* Fields */}
284
+ </Form>
285
+ ```
286
+
287
+ ## Hooks
288
+
289
+ ### useFormContext
290
+
291
+ Access form state and methods from any child component:
292
+
293
+ ```typescript
294
+ import { useFormContext } from '@formality-ui/react';
295
+
296
+ function CustomComponent() {
297
+ const { config, methods, record, unusedFields, submitImmediate } = useFormContext();
298
+ // ...
299
+ }
300
+ ```
301
+
302
+ ### useConditions
303
+
304
+ Evaluate conditions manually:
305
+
306
+ ```typescript
307
+ import { useConditions } from '@formality-ui/react';
308
+
309
+ const { disabled, visible, setValue } = useConditions({
310
+ conditions: fieldConfig.conditions,
311
+ });
312
+ ```
313
+
314
+ ### usePropsEvaluation
315
+
316
+ Evaluate dynamic props:
317
+
318
+ ```typescript
319
+ import { usePropsEvaluation } from '@formality-ui/react';
320
+
321
+ const evaluatedProps = usePropsEvaluation(selectProps, watchedValues);
322
+ ```
323
+
324
+ ### useFormState
325
+
326
+ Subscribe to form state changes:
327
+
328
+ ```typescript
329
+ import { useFormState } from '@formality-ui/react';
330
+
331
+ const { methods, formState } = useFormState(options);
332
+ ```
333
+
334
+ ### useSubscriptions
335
+
336
+ Subscribe to field value changes:
337
+
338
+ ```typescript
339
+ import { useSubscriptions } from '@formality-ui/react';
340
+
341
+ const watchedValues = useSubscriptions(fieldNames);
342
+ ```
343
+
344
+ ### useInferredInputs
345
+
346
+ Infer input configurations:
347
+
348
+ ```typescript
349
+ import { useInferredInputs } from '@formality-ui/react';
350
+
351
+ const inputs = useInferredInputs(config);
352
+ ```
353
+
354
+ ## Contexts
355
+
356
+ ### ConfigContext
357
+
358
+ Global configuration context:
359
+
360
+ ```typescript
361
+ import { useConfigContext } from '@formality-ui/react';
362
+
363
+ const { inputs, validators, formatters, parsers, errorMessages } = useConfigContext();
364
+ ```
365
+
366
+ ### FormContext
367
+
368
+ Form-level context:
369
+
370
+ ```typescript
371
+ import { useFormContext } from '@formality-ui/react';
372
+
373
+ const { config, methods, record, formConfig, unusedFields } = useFormContext();
374
+ ```
375
+
376
+ ### GroupContext
377
+
378
+ Group-level context for nested conditions:
379
+
380
+ ```typescript
381
+ import { useGroupContext } from '@formality-ui/react';
382
+
383
+ const groupState = useGroupContext();
384
+ ```
385
+
386
+ ## TypeScript Support
387
+
388
+ All types are exported for full TypeScript support:
389
+
390
+ ```typescript
391
+ import type {
392
+ // Components
393
+ FormalityProviderProps,
394
+ FormProps,
395
+ FormRenderAPI,
396
+ FieldProps,
397
+ FieldRenderAPI,
398
+ FieldGroupProps,
399
+ UnusedFieldsProps,
400
+
401
+ // Contexts
402
+ ConfigContextValue,
403
+ FormContextValue,
404
+ GroupContextValue,
405
+ GroupState,
406
+
407
+ // Core types (re-exported)
408
+ InputConfig,
409
+ FieldConfig,
410
+ FormFieldsConfig,
411
+ FormConfig,
412
+ ConditionDescriptor,
413
+ ValidationResult,
414
+ ValidatorSpec,
415
+
416
+ // React-specific types
417
+ InputTemplateProps,
418
+ CustomFieldState,
419
+ ExtendedFormState,
420
+ UseFormStateOptions,
421
+ WatcherSetterFn,
422
+ DebouncedFunction,
423
+ } from '@formality-ui/react';
424
+ ```
425
+
426
+ ## Utilities
427
+
428
+ ### makeProxyState
429
+
430
+ Create proxy state for efficient subscriptions:
431
+
432
+ ```typescript
433
+ import { makeProxyState, makeDeepProxyState } from '@formality-ui/react';
434
+
435
+ const proxy = makeProxyState(initialState);
436
+ const deepProxy = makeDeepProxyState(initialState);
437
+ ```
438
+
439
+ ## License
440
+
441
+ MIT