@connect-soft/form-generator 1.1.0-alpha7 → 1.1.0-alpha8
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 +125 -1
- package/dist/index.js +66 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +63 -33
- package/dist/index.mjs.map +1 -1
- package/dist/types/components/form/form-generator.d.ts +5 -0
- package/dist/types/components/form/form-generator.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/field-registry.d.ts +10 -10
- package/dist/types/lib/field-registry.d.ts.map +1 -1
- package/dist/types/lib/field-types.d.ts +25 -0
- package/dist/types/lib/field-types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -220,12 +220,13 @@ const fields = [
|
|
|
220
220
|
] as const;
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
Then register the component for your custom field:
|
|
224
224
|
|
|
225
225
|
```typescript
|
|
226
226
|
import { registerField } from '@connect-soft/form-generator';
|
|
227
227
|
import { ColorPicker } from './components/ColorPicker';
|
|
228
228
|
|
|
229
|
+
// Type-safe: 'color-picker' must exist in FieldTypeRegistry
|
|
229
230
|
registerField('color-picker', ({ field, formField }) => (
|
|
230
231
|
<ColorPicker
|
|
231
232
|
value={formField.value}
|
|
@@ -234,6 +235,71 @@ registerField('color-picker', ({ field, formField }) => (
|
|
|
234
235
|
showAlpha={field.showAlpha}
|
|
235
236
|
/>
|
|
236
237
|
));
|
|
238
|
+
|
|
239
|
+
// ❌ TypeScript error: 'unknown-type' is not in FieldTypeRegistry
|
|
240
|
+
// registerField('unknown-type', MyComponent);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
> **Note:** Both `registerField` and `registerFields` enforce that field types must be defined in `FieldTypeRegistry`. This ensures type safety between your type definitions and runtime registrations.
|
|
244
|
+
|
|
245
|
+
### Field Type Validation Helpers
|
|
246
|
+
|
|
247
|
+
Use helper functions for strict type checking without `as const`:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { createField, createArrayField, strictFields } from '@connect-soft/form-generator';
|
|
251
|
+
|
|
252
|
+
// Create a single field with full type checking
|
|
253
|
+
const emailField = createField({
|
|
254
|
+
type: 'email',
|
|
255
|
+
name: 'email',
|
|
256
|
+
label: 'Email',
|
|
257
|
+
placeholder: 'Enter your email' // TypeScript knows this is valid for email
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Create an array field
|
|
261
|
+
const contacts = createArrayField({
|
|
262
|
+
name: 'contacts',
|
|
263
|
+
fields: [
|
|
264
|
+
{ type: 'text', name: 'name', label: 'Name' },
|
|
265
|
+
{ type: 'email', name: 'email', label: 'Email' }
|
|
266
|
+
],
|
|
267
|
+
minItems: 1,
|
|
268
|
+
maxItems: 5
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Create an array of fields with type checking
|
|
272
|
+
const fields = strictFields([
|
|
273
|
+
{ type: 'text', name: 'username', label: 'Username' },
|
|
274
|
+
{ type: 'email', name: 'email', label: 'Email' },
|
|
275
|
+
// { type: 'unknown', name: 'bad' } // TypeScript error!
|
|
276
|
+
]);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Runtime Field Type Validation
|
|
280
|
+
|
|
281
|
+
Enable runtime validation to catch unregistered field types during development:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Warn in console for unregistered types (recommended for development)
|
|
285
|
+
<FormGenerator
|
|
286
|
+
fields={fields}
|
|
287
|
+
onSubmit={handleSubmit}
|
|
288
|
+
validateTypes
|
|
289
|
+
/>
|
|
290
|
+
|
|
291
|
+
// Throw an error for unregistered types
|
|
292
|
+
<FormGenerator
|
|
293
|
+
fields={fields}
|
|
294
|
+
onSubmit={handleSubmit}
|
|
295
|
+
validateTypes={{ throwOnError: true }}
|
|
296
|
+
/>
|
|
297
|
+
|
|
298
|
+
// Manual validation
|
|
299
|
+
import { validateFieldTypes, getRegisteredFieldTypes } from '@connect-soft/form-generator';
|
|
300
|
+
|
|
301
|
+
const registeredTypes = getRegisteredFieldTypes();
|
|
302
|
+
validateFieldTypes(fields, registeredTypes, { throwOnError: true });
|
|
237
303
|
```
|
|
238
304
|
|
|
239
305
|
---
|
|
@@ -574,6 +640,63 @@ function MyForm() {
|
|
|
574
640
|
| `isDirty()` | Check if form has unsaved changes |
|
|
575
641
|
| `form` | Access underlying react-hook-form instance |
|
|
576
642
|
|
|
643
|
+
### Watching Form Values
|
|
644
|
+
|
|
645
|
+
Detect when field values change from the parent component:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
import { useRef, useEffect } from 'react';
|
|
649
|
+
import { FormGenerator, FormGeneratorRef } from '@connect-soft/form-generator';
|
|
650
|
+
|
|
651
|
+
function MyForm() {
|
|
652
|
+
const formRef = useRef<FormGeneratorRef>(null);
|
|
653
|
+
|
|
654
|
+
useEffect(() => {
|
|
655
|
+
// Watch all fields for changes
|
|
656
|
+
const subscription = formRef.current?.form.watch((values, { name, type }) => {
|
|
657
|
+
console.log('Changed field:', name);
|
|
658
|
+
console.log('New values:', values);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
return () => subscription?.unsubscribe();
|
|
662
|
+
}, []);
|
|
663
|
+
|
|
664
|
+
return (
|
|
665
|
+
<FormGenerator
|
|
666
|
+
ref={formRef}
|
|
667
|
+
fields={fields}
|
|
668
|
+
onSubmit={handleSubmit}
|
|
669
|
+
/>
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Or use `useWatch` inside a custom layout:
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
import { FormGenerator, useWatch } from '@connect-soft/form-generator';
|
|
678
|
+
|
|
679
|
+
function ValueWatcher() {
|
|
680
|
+
const email = useWatch({ name: 'email' }); // Watch specific field
|
|
681
|
+
|
|
682
|
+
useEffect(() => {
|
|
683
|
+
console.log('Email changed:', email);
|
|
684
|
+
}, [email]);
|
|
685
|
+
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
<FormGenerator fields={fields} onSubmit={handleSubmit}>
|
|
690
|
+
{({ fields, buttons }) => (
|
|
691
|
+
<>
|
|
692
|
+
{fields.all}
|
|
693
|
+
<ValueWatcher />
|
|
694
|
+
{buttons.submit}
|
|
695
|
+
</>
|
|
696
|
+
)}
|
|
697
|
+
</FormGenerator>
|
|
698
|
+
```
|
|
699
|
+
|
|
577
700
|
---
|
|
578
701
|
|
|
579
702
|
## API Reference
|
|
@@ -595,6 +718,7 @@ function MyForm() {
|
|
|
595
718
|
| `description` | `string` | - | Form description (available in render props) |
|
|
596
719
|
| `showReset` | `boolean` | `false` | Include reset button in `buttons.reset` |
|
|
597
720
|
| `resetText` | `string` | `'Reset'` | Reset button text |
|
|
721
|
+
| `validateTypes` | `boolean \| ValidateTypesOptions` | `false` | Runtime validation of field types |
|
|
598
722
|
|
|
599
723
|
### Field Base Properties
|
|
600
724
|
|
package/dist/index.js
CHANGED
|
@@ -2795,6 +2795,49 @@ function t(r,e){try{var o=r();}catch(r){return e(r)}return o&&o.then?o.then(void
|
|
|
2795
2795
|
function isArrayField(field) {
|
|
2796
2796
|
return field.type === 'array' && 'fields' in field;
|
|
2797
2797
|
}
|
|
2798
|
+
function createField(field) {
|
|
2799
|
+
return field;
|
|
2800
|
+
}
|
|
2801
|
+
function createArrayField(field) {
|
|
2802
|
+
return {
|
|
2803
|
+
...field,
|
|
2804
|
+
type: 'array'
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
function strictFields(fields) {
|
|
2808
|
+
return fields;
|
|
2809
|
+
}
|
|
2810
|
+
function validateFieldType(type, registeredTypes) {
|
|
2811
|
+
if (!registeredTypes.includes(type) && type !== 'array') {
|
|
2812
|
+
throw new Error(`Unregistered field type: "${type}". ` + `Registered types: ${registeredTypes.join(', ')}. ` + `Use registerField("${type}", YourComponent) to register it.`);
|
|
2813
|
+
}
|
|
2814
|
+
return type;
|
|
2815
|
+
}
|
|
2816
|
+
function validateFieldTypes(fields, registeredTypes, options = {}) {
|
|
2817
|
+
const {
|
|
2818
|
+
throwOnError = false,
|
|
2819
|
+
warn = true
|
|
2820
|
+
} = options;
|
|
2821
|
+
const invalidTypes = [];
|
|
2822
|
+
const checkField = field => {
|
|
2823
|
+
if (field.type === 'array' && 'fields' in field) {
|
|
2824
|
+
field.fields.forEach(checkField);
|
|
2825
|
+
} else if (!registeredTypes.includes(field.type) && field.type !== 'array') {
|
|
2826
|
+
invalidTypes.push(field.type);
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
fields.forEach(checkField);
|
|
2830
|
+
if (invalidTypes.length > 0) {
|
|
2831
|
+
const uniqueInvalid = [...new Set(invalidTypes)];
|
|
2832
|
+
const message = `Unregistered field type(s): ${uniqueInvalid.map(t => `"${t}"`).join(', ')}. ` + `Registered types: ${registeredTypes.join(', ')}. ` + 'Register custom types with registerField() or use module augmentation for TypeScript support.';
|
|
2833
|
+
if (throwOnError) {
|
|
2834
|
+
throw new Error(message);
|
|
2835
|
+
} else if (warn && typeof console !== 'undefined') {
|
|
2836
|
+
console.warn(`[FormGenerator] ${message}`);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
return fields;
|
|
2840
|
+
}
|
|
2798
2841
|
|
|
2799
2842
|
const fieldRegistry = new Map();
|
|
2800
2843
|
const componentRegistry = {};
|
|
@@ -2957,27 +3000,15 @@ function createDefaultFieldComponent(type) {
|
|
|
2957
3000
|
};
|
|
2958
3001
|
return FallbackComponent;
|
|
2959
3002
|
}
|
|
2960
|
-
function registerField(type, component, options = {}
|
|
2961
|
-
const {
|
|
2962
|
-
override = true
|
|
2963
|
-
} = config;
|
|
2964
|
-
const exists = fieldRegistry.has(type);
|
|
2965
|
-
if (override === 'only' && !exists) return;
|
|
2966
|
-
if (override === false && exists) return;
|
|
3003
|
+
function registerField(type, component, options = {}) {
|
|
2967
3004
|
fieldRegistry.set(type, {
|
|
2968
3005
|
component,
|
|
2969
3006
|
options
|
|
2970
3007
|
});
|
|
2971
3008
|
}
|
|
2972
|
-
function registerFields(fields
|
|
2973
|
-
const {
|
|
2974
|
-
override = true
|
|
2975
|
-
} = config;
|
|
3009
|
+
function registerFields(fields) {
|
|
2976
3010
|
Object.entries(fields).forEach(([type, definition]) => {
|
|
2977
3011
|
var _a;
|
|
2978
|
-
const exists = fieldRegistry.has(type);
|
|
2979
|
-
if (override === 'only' && !exists) return;
|
|
2980
|
-
if (override === false && exists) return;
|
|
2981
3012
|
if (typeof definition === 'function') {
|
|
2982
3013
|
fieldRegistry.set(type, {
|
|
2983
3014
|
component: definition,
|
|
@@ -3012,24 +3043,12 @@ function unregisterField(type) {
|
|
|
3012
3043
|
function getRegisteredFieldTypes() {
|
|
3013
3044
|
return Array.from(fieldRegistry.keys());
|
|
3014
3045
|
}
|
|
3015
|
-
function registerFormComponents(components
|
|
3016
|
-
const {
|
|
3017
|
-
override = true
|
|
3018
|
-
} = config;
|
|
3046
|
+
function registerFormComponents(components) {
|
|
3019
3047
|
Object.keys(components).forEach(key => {
|
|
3020
|
-
const exists = key in componentRegistry;
|
|
3021
|
-
if (override === 'only' && !exists) return;
|
|
3022
|
-
if (override === false && exists) return;
|
|
3023
3048
|
componentRegistry[key] = components[key];
|
|
3024
3049
|
});
|
|
3025
3050
|
}
|
|
3026
|
-
function registerFormComponent(name, component
|
|
3027
|
-
const {
|
|
3028
|
-
override = true
|
|
3029
|
-
} = config;
|
|
3030
|
-
const exists = name in componentRegistry;
|
|
3031
|
-
if (override === 'only' && !exists) return;
|
|
3032
|
-
if (override === false && exists) return;
|
|
3051
|
+
function registerFormComponent(name, component) {
|
|
3033
3052
|
componentRegistry[name] = component;
|
|
3034
3053
|
}
|
|
3035
3054
|
function getFormComponent(name) {
|
|
@@ -3514,8 +3533,19 @@ function FormGeneratorImpl(props) {
|
|
|
3514
3533
|
showReset = false,
|
|
3515
3534
|
resetText = 'Reset',
|
|
3516
3535
|
children,
|
|
3517
|
-
ref
|
|
3536
|
+
ref,
|
|
3537
|
+
validateTypes
|
|
3518
3538
|
} = props;
|
|
3539
|
+
React.useMemo(() => {
|
|
3540
|
+
if (validateTypes) {
|
|
3541
|
+
const registeredTypes = getRegisteredFieldTypes();
|
|
3542
|
+
const options = typeof validateTypes === 'object' ? validateTypes : {
|
|
3543
|
+
throwOnError: false,
|
|
3544
|
+
warn: true
|
|
3545
|
+
};
|
|
3546
|
+
validateFieldTypes(fields, registeredTypes, options);
|
|
3547
|
+
}
|
|
3548
|
+
}, [fields, validateTypes]);
|
|
3519
3549
|
const userSchema = hasSchema(props) ? props.schema : undefined;
|
|
3520
3550
|
const onSubmit = props.onSubmit;
|
|
3521
3551
|
const formRef = React.useRef(null);
|
|
@@ -3627,10 +3657,10 @@ function FormGeneratorImpl(props) {
|
|
|
3627
3657
|
}
|
|
3628
3658
|
return result;
|
|
3629
3659
|
}, [SubmitButton, disabled, form.formState.isSubmitting, submitText, showReset, resetText, handleReset]);
|
|
3630
|
-
const renderField = React.useCallback((field_1,
|
|
3660
|
+
const renderField = React.useCallback((field_1, options_0) => {
|
|
3631
3661
|
return jsxRuntime.jsx(FieldRenderer, {
|
|
3632
3662
|
field: field_1,
|
|
3633
|
-
namePrefix:
|
|
3663
|
+
namePrefix: options_0 === null || options_0 === void 0 ? void 0 : options_0.namePrefix
|
|
3634
3664
|
});
|
|
3635
3665
|
}, []);
|
|
3636
3666
|
if (!children) {
|
|
@@ -3707,6 +3737,8 @@ exports.FormProvider = FormProvider;
|
|
|
3707
3737
|
exports.clearAllRegistries = clearAllRegistries;
|
|
3708
3738
|
exports.clearFieldRegistry = clearFieldRegistry;
|
|
3709
3739
|
exports.clearFormComponentRegistry = clearFormComponentRegistry;
|
|
3740
|
+
exports.createArrayField = createArrayField;
|
|
3741
|
+
exports.createField = createField;
|
|
3710
3742
|
exports.getFieldComponent = getFieldComponent;
|
|
3711
3743
|
exports.getFormComponent = getFormComponent;
|
|
3712
3744
|
exports.getFormComponents = getFormComponents;
|
|
@@ -3719,10 +3751,13 @@ exports.registerFields = registerFields;
|
|
|
3719
3751
|
exports.registerFormComponent = registerFormComponent;
|
|
3720
3752
|
exports.registerFormComponents = registerFormComponents;
|
|
3721
3753
|
exports.resetFormComponentRegistry = resetFormComponentRegistry;
|
|
3754
|
+
exports.strictFields = strictFields;
|
|
3722
3755
|
exports.unregisterField = unregisterField;
|
|
3723
3756
|
exports.useArrayField = useArrayField;
|
|
3724
3757
|
exports.useFieldArray = useFieldArray;
|
|
3725
3758
|
exports.useForm = useForm;
|
|
3726
3759
|
exports.useFormContext = useFormContext;
|
|
3727
3760
|
exports.useWatch = useWatch;
|
|
3761
|
+
exports.validateFieldType = validateFieldType;
|
|
3762
|
+
exports.validateFieldTypes = validateFieldTypes;
|
|
3728
3763
|
//# sourceMappingURL=index.js.map
|