@uniform-ts/core 0.0.2 → 0.0.3
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 +39 -4
- package/dist/index.d.mts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +19 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ UniForm takes a Zod schema and automatically renders a fully customizable form.
|
|
|
14
14
|
- **Per-field `onChange` in `fields` prop** — react to individual field changes inline, with typed values and full form control methods
|
|
15
15
|
- **Per-field custom components** — pass any `React.ComponentType<FieldProps>` directly as `meta.component` (inline, no registry) or register under a custom string key; direct components bypass the registry _and_ the default `ArrayField`/`ObjectField` routing, allowing fully custom multi-value widgets for `array`-typed fields
|
|
16
16
|
- **Layout hooks** — `classNames`, `fieldWrapper`, `layout.formWrapper`, `layout.sectionWrapper`, `layout.submitButton`
|
|
17
|
-
- **Section grouping** — group fields into named sections via `meta.section`
|
|
17
|
+
- **Section grouping** — group fields into named sections via `meta.section`; style or swap individual section wrappers via `layout.sections`
|
|
18
18
|
- **Conditional fields** — show/hide fields based on form values with `meta.condition`; hidden fields automatically reset to their default value
|
|
19
19
|
- **Field ordering** — control render order with `meta.order`
|
|
20
20
|
- **`createAutoForm()` factory** — bake in your design system defaults once, use everywhere
|
|
@@ -178,7 +178,7 @@ const MyAutoForm = createAutoForm({
|
|
|
178
178
|
| -------------- | ---------------------------------------- | ------------------------------------------------ |
|
|
179
179
|
| `components` | `ComponentRegistry` | Deep merge (instance overrides specific keys) |
|
|
180
180
|
| `fieldWrapper` | `React.ComponentType<FieldWrapperProps>` | Instance replaces factory |
|
|
181
|
-
| `layout` | `LayoutSlots` | Shallow merge
|
|
181
|
+
| `layout` | `LayoutSlots` | Shallow merge (except `sections` — deep-merged) |
|
|
182
182
|
| `classNames` | `FormClassNames` | Shallow merge |
|
|
183
183
|
| `disabled` | `boolean` | OR logic (either `true` → disabled) |
|
|
184
184
|
| `coercions` | `CoercionMap` | Shallow merge |
|
|
@@ -307,11 +307,29 @@ type LayoutSlots = {
|
|
|
307
307
|
sectionWrapper?: React.ComponentType<{
|
|
308
308
|
children: React.ReactNode
|
|
309
309
|
title: string
|
|
310
|
+
className?: string
|
|
310
311
|
}>
|
|
311
312
|
submitButton?: React.ComponentType<{ isSubmitting: boolean }>
|
|
312
313
|
arrayRowLayout?: React.ComponentType<ArrayRowLayoutProps>
|
|
313
314
|
/** Shown while async `defaultValues` are resolving. Default: `<p>Loading…</p>` */
|
|
314
315
|
loadingFallback?: React.ReactNode
|
|
316
|
+
/** Per-section styling / component overrides keyed by section title. */
|
|
317
|
+
sections?: Record<string, SectionConfig>
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### `SectionConfig`
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
type SectionConfig = {
|
|
325
|
+
/** CSS class name forwarded to the section wrapper. */
|
|
326
|
+
className?: string
|
|
327
|
+
/** Replace the section wrapper component for this section only. */
|
|
328
|
+
component?: React.ComponentType<{
|
|
329
|
+
children: React.ReactNode
|
|
330
|
+
title: string
|
|
331
|
+
className?: string
|
|
332
|
+
}>
|
|
315
333
|
}
|
|
316
334
|
```
|
|
317
335
|
|
|
@@ -633,8 +651,8 @@ The `span` value is set as `--field-span` CSS custom property on each field wrap
|
|
|
633
651
|
city: { section: 'Address', order: 4 },
|
|
634
652
|
}}
|
|
635
653
|
layout={{
|
|
636
|
-
sectionWrapper: ({ children, title }) => (
|
|
637
|
-
<fieldset>
|
|
654
|
+
sectionWrapper: ({ children, title, className }) => (
|
|
655
|
+
<fieldset className={className}>
|
|
638
656
|
<legend>{title}</legend>
|
|
639
657
|
{children}
|
|
640
658
|
</fieldset>
|
|
@@ -643,6 +661,23 @@ The `span` value is set as `--field-span` CSS custom property on each field wrap
|
|
|
643
661
|
/>
|
|
644
662
|
```
|
|
645
663
|
|
|
664
|
+
Use `layout.sections` to style or swap the wrapper for individual sections:
|
|
665
|
+
|
|
666
|
+
```tsx
|
|
667
|
+
<AutoForm
|
|
668
|
+
form={myForm}
|
|
669
|
+
onSubmit={handleSubmit}
|
|
670
|
+
layout={{
|
|
671
|
+
sections: {
|
|
672
|
+
Personal: { className: 'bg-blue-50 p-4 rounded' },
|
|
673
|
+
Address: { component: AddressCard }, // completely different component
|
|
674
|
+
},
|
|
675
|
+
}}
|
|
676
|
+
/>
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
`className` is forwarded as a prop to the active wrapper (global `sectionWrapper` or the per-section `component`). Factory-level and instance-level `sections` are merged — instance wins on conflicts.
|
|
680
|
+
|
|
646
681
|
### Conditional Fields
|
|
647
682
|
|
|
648
683
|
Show a field only when another field has a specific value. Conditional fields are fully lifecycle-managed:
|
package/dist/index.d.mts
CHANGED
|
@@ -347,6 +347,20 @@ type ArrayRowLayoutProps = {
|
|
|
347
347
|
/** Total number of rows currently in the array. */
|
|
348
348
|
rowCount: number;
|
|
349
349
|
};
|
|
350
|
+
/**
|
|
351
|
+
* Per-section styling overrides forwarded to the `sectionWrapper` component.
|
|
352
|
+
* Keys are section titles; values control how that section wrapper is styled.
|
|
353
|
+
*/
|
|
354
|
+
type SectionConfig = {
|
|
355
|
+
/** CSS class name(s) applied to the section wrapper. */
|
|
356
|
+
className?: string;
|
|
357
|
+
/** Replaces the section wrapper component entirely for this section. */
|
|
358
|
+
component?: React.ComponentType<{
|
|
359
|
+
children: React.ReactNode;
|
|
360
|
+
title: string;
|
|
361
|
+
className?: string;
|
|
362
|
+
}>;
|
|
363
|
+
};
|
|
350
364
|
/**
|
|
351
365
|
* Optional layout slot overrides for top-level structural components of the
|
|
352
366
|
* form. Provide only the slots you want to replace; omitted slots fall back
|
|
@@ -361,6 +375,7 @@ type LayoutSlots = {
|
|
|
361
375
|
sectionWrapper?: React.ComponentType<{
|
|
362
376
|
children: React.ReactNode;
|
|
363
377
|
title: string;
|
|
378
|
+
className?: string;
|
|
364
379
|
}>;
|
|
365
380
|
/** Custom submit button component. */
|
|
366
381
|
submitButton?: React.ComponentType<{
|
|
@@ -374,6 +389,11 @@ type LayoutSlots = {
|
|
|
374
389
|
* Defaults to a simple `<p>Loading…</p>` when not provided.
|
|
375
390
|
*/
|
|
376
391
|
loadingFallback?: React.ReactNode;
|
|
392
|
+
/**
|
|
393
|
+
* Per-section config keyed by section title.
|
|
394
|
+
* Forwarded to the `sectionWrapper` component as a `className` prop.
|
|
395
|
+
*/
|
|
396
|
+
sections?: Record<string, SectionConfig>;
|
|
377
397
|
};
|
|
378
398
|
/**
|
|
379
399
|
* The resolved version of `LayoutSlots` used internally, where all slots are
|
|
@@ -386,6 +406,7 @@ type ResolvedLayoutSlots = {
|
|
|
386
406
|
sectionWrapper: React.ComponentType<{
|
|
387
407
|
children: React.ReactNode;
|
|
388
408
|
title: string;
|
|
409
|
+
className?: string;
|
|
389
410
|
}>;
|
|
390
411
|
submitButton: React.ComponentType<{
|
|
391
412
|
isSubmitting: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -347,6 +347,20 @@ type ArrayRowLayoutProps = {
|
|
|
347
347
|
/** Total number of rows currently in the array. */
|
|
348
348
|
rowCount: number;
|
|
349
349
|
};
|
|
350
|
+
/**
|
|
351
|
+
* Per-section styling overrides forwarded to the `sectionWrapper` component.
|
|
352
|
+
* Keys are section titles; values control how that section wrapper is styled.
|
|
353
|
+
*/
|
|
354
|
+
type SectionConfig = {
|
|
355
|
+
/** CSS class name(s) applied to the section wrapper. */
|
|
356
|
+
className?: string;
|
|
357
|
+
/** Replaces the section wrapper component entirely for this section. */
|
|
358
|
+
component?: React.ComponentType<{
|
|
359
|
+
children: React.ReactNode;
|
|
360
|
+
title: string;
|
|
361
|
+
className?: string;
|
|
362
|
+
}>;
|
|
363
|
+
};
|
|
350
364
|
/**
|
|
351
365
|
* Optional layout slot overrides for top-level structural components of the
|
|
352
366
|
* form. Provide only the slots you want to replace; omitted slots fall back
|
|
@@ -361,6 +375,7 @@ type LayoutSlots = {
|
|
|
361
375
|
sectionWrapper?: React.ComponentType<{
|
|
362
376
|
children: React.ReactNode;
|
|
363
377
|
title: string;
|
|
378
|
+
className?: string;
|
|
364
379
|
}>;
|
|
365
380
|
/** Custom submit button component. */
|
|
366
381
|
submitButton?: React.ComponentType<{
|
|
@@ -374,6 +389,11 @@ type LayoutSlots = {
|
|
|
374
389
|
* Defaults to a simple `<p>Loading…</p>` when not provided.
|
|
375
390
|
*/
|
|
376
391
|
loadingFallback?: React.ReactNode;
|
|
392
|
+
/**
|
|
393
|
+
* Per-section config keyed by section title.
|
|
394
|
+
* Forwarded to the `sectionWrapper` component as a `className` prop.
|
|
395
|
+
*/
|
|
396
|
+
sections?: Record<string, SectionConfig>;
|
|
377
397
|
};
|
|
378
398
|
/**
|
|
379
399
|
* The resolved version of `LayoutSlots` used internally, where all slots are
|
|
@@ -386,6 +406,7 @@ type ResolvedLayoutSlots = {
|
|
|
386
406
|
sectionWrapper: React.ComponentType<{
|
|
387
407
|
children: React.ReactNode;
|
|
388
408
|
title: string;
|
|
409
|
+
className?: string;
|
|
389
410
|
}>;
|
|
390
411
|
submitButton: React.ComponentType<{
|
|
391
412
|
isSubmitting: boolean;
|
package/dist/index.js
CHANGED
|
@@ -398,9 +398,10 @@ function DefaultFormWrapper({ children }) {
|
|
|
398
398
|
}
|
|
399
399
|
function DefaultSectionWrapper({
|
|
400
400
|
children,
|
|
401
|
-
title
|
|
401
|
+
title,
|
|
402
|
+
className
|
|
402
403
|
}) {
|
|
403
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
|
|
404
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className, children: [
|
|
404
405
|
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: title }),
|
|
405
406
|
children
|
|
406
407
|
] });
|
|
@@ -1525,7 +1526,17 @@ function AutoForm(props) {
|
|
|
1525
1526
|
if (section.title === null) {
|
|
1526
1527
|
return /* @__PURE__ */ jsxRuntime.jsx(React3__namespace.Fragment, { children: renderedFields }, "__ungrouped");
|
|
1527
1528
|
}
|
|
1528
|
-
|
|
1529
|
+
const sectionConfig = layout?.sections?.[section.title];
|
|
1530
|
+
const PerSectionWrapper = sectionConfig?.component ?? SectionWrapper;
|
|
1531
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1532
|
+
PerSectionWrapper,
|
|
1533
|
+
{
|
|
1534
|
+
title: section.title,
|
|
1535
|
+
className: sectionConfig?.className,
|
|
1536
|
+
children: renderedFields
|
|
1537
|
+
},
|
|
1538
|
+
section.title
|
|
1539
|
+
);
|
|
1529
1540
|
}),
|
|
1530
1541
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1531
1542
|
SubmitButton,
|
|
@@ -1545,7 +1556,11 @@ function createAutoForm(config) {
|
|
|
1545
1556
|
[props.components]
|
|
1546
1557
|
);
|
|
1547
1558
|
const mergedLayout = React3__namespace.useMemo(
|
|
1548
|
-
() => ({
|
|
1559
|
+
() => ({
|
|
1560
|
+
...config.layout,
|
|
1561
|
+
...props.layout,
|
|
1562
|
+
sections: config.layout?.sections || props.layout?.sections ? { ...config.layout?.sections, ...props.layout?.sections } : void 0
|
|
1563
|
+
}),
|
|
1549
1564
|
[props.layout]
|
|
1550
1565
|
);
|
|
1551
1566
|
const mergedClassNames = React3__namespace.useMemo(
|