@petrarca/sonnet-forms 0.7.0 → 0.8.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 +2 -2
- package/dist/index.d.ts +126 -4
- package/dist/index.js +391 -59
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -91,8 +91,8 @@ actually enforces are in [getting started](docs/usage/getting-started.md).
|
|
|
91
91
|
[templates](docs/usage/templates.md),
|
|
92
92
|
[field components](docs/usage/field-components.md) and
|
|
93
93
|
[change tracking](docs/usage/change-tracking.md).
|
|
94
|
-
- [Design docs](docs/design/index.md) -- form layout, nested fields
|
|
95
|
-
change-tracking model.
|
|
94
|
+
- [Design docs](docs/design/index.md) -- form layout, nested fields, the
|
|
95
|
+
change-tracking model, and custom validation.
|
|
96
96
|
- [Workspace documentation](../../docs/README.md) -- how the packages layer and
|
|
97
97
|
the shared schema extension contract.
|
|
98
98
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React$1 from 'react';
|
|
3
3
|
import React__default, { ComponentType, ReactNode } from 'react';
|
|
4
4
|
import { Input, Textarea, badgeVariants } from '@petrarca/sonnet-ui';
|
|
5
|
-
import { JsonSchemaProperty, UILayoutConfig, JsonSchema, UISchema, UISchemaOptions, FormDiff, RefResolver
|
|
5
|
+
import { JsonSchemaProperty, UILayoutConfig, JsonSchema, UISchema, UISchemaOptions, FieldError, FormDiff, RefResolver } from '@petrarca/sonnet-core/schema';
|
|
6
6
|
export { UISchema, UISchemaOptions } from '@petrarca/sonnet-core/schema';
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
8
|
import { SaveWhenInvalid, ValidationDisplay } from '@petrarca/sonnet-core/config';
|
|
@@ -673,13 +673,18 @@ interface FormActionsProps {
|
|
|
673
673
|
saveLabel?: string;
|
|
674
674
|
cancelLabel?: string;
|
|
675
675
|
showCancel?: boolean;
|
|
676
|
+
/**
|
|
677
|
+
* True while an async validator is in flight. Save is disabled until it
|
|
678
|
+
* settles (validity is not yet known); Cancel stays enabled.
|
|
679
|
+
*/
|
|
680
|
+
isValidating?: boolean;
|
|
676
681
|
/**
|
|
677
682
|
* Optional content rendered left-aligned in the action row (e.g. a secondary
|
|
678
683
|
* action like "Test connection"). Cancel/Save stay grouped on the right.
|
|
679
684
|
*/
|
|
680
685
|
leadingActions?: ReactNode;
|
|
681
686
|
}
|
|
682
|
-
declare function FormActions({ onSave, onCancel, disabled, hasChanges, isValid, saveWhenInvalid, saveLabel, cancelLabel, showCancel, leadingActions, }: FormActionsProps): react_jsx_runtime.JSX.Element;
|
|
687
|
+
declare function FormActions({ onSave, onCancel, disabled, hasChanges, isValid, saveWhenInvalid, saveLabel, cancelLabel, showCancel, isValidating, leadingActions, }: FormActionsProps): react_jsx_runtime.JSX.Element;
|
|
683
688
|
|
|
684
689
|
type Props = {
|
|
685
690
|
properties: Record<string, any>;
|
|
@@ -1026,6 +1031,71 @@ declare function getDefaultWidgetForType(type: string): string;
|
|
|
1026
1031
|
*/
|
|
1027
1032
|
declare const DEFAULT_WIDGETS: WidgetRegistry;
|
|
1028
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Custom validation registry -- named field and form validators referenced from
|
|
1036
|
+
* the schema via `x-ui-validate` and implemented here.
|
|
1037
|
+
*
|
|
1038
|
+
* See the custom-validation design in docs/design/custom-validation.md. `field`
|
|
1039
|
+
* and `form` are synchronous, invoked by `runValidators` every validation pass;
|
|
1040
|
+
* `asyncField` and `asyncForm` return a promise and are invoked only by
|
|
1041
|
+
* `useAsyncValidators`, keyed on their inputs so a remote check fires on
|
|
1042
|
+
* change, never per render.
|
|
1043
|
+
*/
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Context handed to every validator so a rule can reach beyond its immediate
|
|
1047
|
+
* value without the caller wiring it. `formData` is the ROOT form data (carried
|
|
1048
|
+
* like x-ui-rule's rootData), so a validator on a nested field can still compare
|
|
1049
|
+
* against a top-level field.
|
|
1050
|
+
*/
|
|
1051
|
+
interface ValidatorContext {
|
|
1052
|
+
/** The whole current form data (root), for cross-field checks. */
|
|
1053
|
+
formData: Record<string, unknown>;
|
|
1054
|
+
/** RFC 6901 JSON Pointer of the field being validated (field validators). */
|
|
1055
|
+
path: string;
|
|
1056
|
+
/** Per-reference config from the `x-ui-validate` object form (`{ name, args }`). */
|
|
1057
|
+
args?: Record<string, unknown>;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* What a SYNC validator returns. A field validator may use the `string | null`
|
|
1061
|
+
* shorthand (message attached to its own field, or null when valid); a form
|
|
1062
|
+
* validator returns `FieldError[]` because it decides which field each error
|
|
1063
|
+
* lands on (pointer "" = form-level).
|
|
1064
|
+
*/
|
|
1065
|
+
type ValidatorResult = string | null | FieldError[];
|
|
1066
|
+
/** A per-value sync validator: judges one field's value. */
|
|
1067
|
+
type FieldValidator = (value: unknown, ctx: ValidatorContext) => string | null | FieldError[];
|
|
1068
|
+
/** A whole-data sync validator: cross-field / form-level rules. */
|
|
1069
|
+
type FormValidator = (data: Record<string, unknown>, ctx: ValidatorContext) => FieldError[];
|
|
1070
|
+
/**
|
|
1071
|
+
* An async per-value validator -- returns a promise. Declared in its own
|
|
1072
|
+
* namespace (`asyncField`) so the engine knows it is async without calling it,
|
|
1073
|
+
* and invokes it only when its field value changes (never per render). A failed
|
|
1074
|
+
* promise means "could not check", not "invalid".
|
|
1075
|
+
*/
|
|
1076
|
+
type AsyncFieldValidator = (value: unknown, ctx: ValidatorContext) => Promise<string | null | FieldError[]>;
|
|
1077
|
+
/** An async whole-data validator -- returns a promise. See `dependsOn` on the
|
|
1078
|
+
* `x-ui-validate` reference to control when it re-fires. */
|
|
1079
|
+
type AsyncFormValidator = (data: Record<string, unknown>, ctx: ValidatorContext) => Promise<FieldError[]>;
|
|
1080
|
+
/**
|
|
1081
|
+
* The registry: sync and async namespaces, keeping the signatures -- and the
|
|
1082
|
+
* invocation strategy -- apart. Sync validators run on every validation pass;
|
|
1083
|
+
* async validators are invoked only when their inputs change (the engine can
|
|
1084
|
+
* tell them apart because they are declared separately, so a remote check is
|
|
1085
|
+
* never fired speculatively per render). A plain object, like the widget and
|
|
1086
|
+
* template registries -- no factory, no lifecycle.
|
|
1087
|
+
*/
|
|
1088
|
+
interface ValidatorRegistry {
|
|
1089
|
+
/** Sync per-field validators, referenced by `x-ui-validate` on a property. */
|
|
1090
|
+
field?: Record<string, FieldValidator>;
|
|
1091
|
+
/** Sync whole-data validators, referenced by `x-ui-validate` on the root. */
|
|
1092
|
+
form?: Record<string, FormValidator>;
|
|
1093
|
+
/** Async per-field validators (promise), referenced by `x-ui-validate`. */
|
|
1094
|
+
asyncField?: Record<string, AsyncFieldValidator>;
|
|
1095
|
+
/** Async whole-data validators (promise), referenced by `x-ui-validate`. */
|
|
1096
|
+
asyncForm?: Record<string, AsyncFormValidator>;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1029
1099
|
/**
|
|
1030
1100
|
* JsonSchemaFormRenderer - Generic form renderer based on JSON Schema
|
|
1031
1101
|
*
|
|
@@ -1132,6 +1202,22 @@ type JsonSchemaFormRendererProps = {
|
|
|
1132
1202
|
* a partial registry -- unspecified keys fall back to DEFAULT_TEMPLATES.
|
|
1133
1203
|
*/
|
|
1134
1204
|
templates?: TemplateRegistry;
|
|
1205
|
+
/**
|
|
1206
|
+
* Custom validator registry -- named field and form validators referenced
|
|
1207
|
+
* from the schema via `x-ui-validate`, run alongside the built-in schema
|
|
1208
|
+
* validation. `field`/`form` are synchronous; `asyncField`/`asyncForm`
|
|
1209
|
+
* return a promise and gate Save while in flight. See
|
|
1210
|
+
* docs/design/custom-validation.md.
|
|
1211
|
+
*/
|
|
1212
|
+
validators?: ValidatorRegistry;
|
|
1213
|
+
/**
|
|
1214
|
+
* Externally-supplied errors (e.g. a server 422) shown as persistent field
|
|
1215
|
+
* errors, keyed by JSON Pointer. Always displayed regardless of
|
|
1216
|
+
* `validationDisplay`, and block Save. Should be stable/memoized. A
|
|
1217
|
+
* higher-precedence error (built-in or a custom validator) on the same field
|
|
1218
|
+
* wins over an extraError. See the custom-validation design.
|
|
1219
|
+
*/
|
|
1220
|
+
extraErrors?: FieldError[];
|
|
1135
1221
|
/**
|
|
1136
1222
|
* When true, renders with `display: contents` instead of a SimpleStack.
|
|
1137
1223
|
*
|
|
@@ -1248,6 +1334,29 @@ interface UseFormEngineOptions {
|
|
|
1248
1334
|
* synchronous -- pre-load referenced schemas into a map and pass a lookup.
|
|
1249
1335
|
*/
|
|
1250
1336
|
refResolver?: RefResolver;
|
|
1337
|
+
/**
|
|
1338
|
+
* Externally-supplied errors (e.g. a server 422), keyed by JSON Pointer.
|
|
1339
|
+
* Merged into `fieldErrors` (so they affect `isValid` and block Save) with
|
|
1340
|
+
* the lowest precedence -- a built-in error on the same field wins. The
|
|
1341
|
+
* renderer shows them regardless of `validationDisplay`; see the
|
|
1342
|
+
* custom-validation design.
|
|
1343
|
+
*/
|
|
1344
|
+
extraErrors?: FieldError[];
|
|
1345
|
+
/**
|
|
1346
|
+
* Custom validator registry. Field validators (named by `x-ui-validate` on a
|
|
1347
|
+
* property) and, at the root, form/cross-field validators (named on the root
|
|
1348
|
+
* object) run here and merge into `fieldErrors` between built-in errors and
|
|
1349
|
+
* extraErrors. The sync `field`/`form` namespaces run every pass via
|
|
1350
|
+
* `runValidators`; the async `asyncField`/`asyncForm` namespaces are invoked
|
|
1351
|
+
* by `useAsyncValidators` and gate Save (`isValidating`) while in flight.
|
|
1352
|
+
*/
|
|
1353
|
+
validators?: ValidatorRegistry;
|
|
1354
|
+
/**
|
|
1355
|
+
* Whether this engine is the ROOT form (not a nested sub-form). Root-only
|
|
1356
|
+
* concerns -- currently the whole-form/cross-field validators -- run only when
|
|
1357
|
+
* true, so a nested sub-form does not re-run them against its slice.
|
|
1358
|
+
*/
|
|
1359
|
+
isRoot?: boolean;
|
|
1251
1360
|
}
|
|
1252
1361
|
interface UseFormEngineResult {
|
|
1253
1362
|
/** The current working form data (for controlled field values) */
|
|
@@ -1263,6 +1372,8 @@ interface UseFormEngineResult {
|
|
|
1263
1372
|
orderedProperties: [string, JsonSchemaProperty][];
|
|
1264
1373
|
hasChanges: boolean;
|
|
1265
1374
|
isValid: boolean;
|
|
1375
|
+
/** True while any async validator's promise is in flight. */
|
|
1376
|
+
isValidating: boolean;
|
|
1266
1377
|
/**
|
|
1267
1378
|
* Current validation errors, each attributed to its field by JSON Pointer.
|
|
1268
1379
|
* Lets the renderer surface problems for fields that are not on screen.
|
|
@@ -1277,7 +1388,7 @@ interface UseFormEngineResult {
|
|
|
1277
1388
|
handleSave: () => void;
|
|
1278
1389
|
handleCancel: () => void;
|
|
1279
1390
|
}
|
|
1280
|
-
declare function useFormEngine({ schema, data, uiSchema, onChange, onBlur, onValidationChange, onUpdate, onCancel, readOnly, showActions, deferChanges, deferStateUpdates, allowPristineSave, refResolver, }: UseFormEngineOptions): UseFormEngineResult;
|
|
1391
|
+
declare function useFormEngine({ schema, data, uiSchema, onChange, onBlur, onValidationChange, onUpdate, onCancel, readOnly, showActions, deferChanges, deferStateUpdates, allowPristineSave, refResolver, extraErrors, validators, isRoot, }: UseFormEngineOptions): UseFormEngineResult;
|
|
1281
1392
|
|
|
1282
1393
|
/**
|
|
1283
1394
|
* useResolvedSchema - Schema resolution and property ordering
|
|
@@ -1336,6 +1447,17 @@ interface NestedFormContextValue {
|
|
|
1336
1447
|
* it unchanged.
|
|
1337
1448
|
*/
|
|
1338
1449
|
rootData: Record<string, unknown>;
|
|
1450
|
+
/**
|
|
1451
|
+
* Whether the root form's save has been attempted. A nested sub-form is a
|
|
1452
|
+
* separate form-engine instance with its own submit state, which the root's
|
|
1453
|
+
* Save never flips -- so without this a required field inside a nested object
|
|
1454
|
+
* would never reveal its error on submit (only on touch). Carried down so a
|
|
1455
|
+
* nested renderer ORs it into its own error-visibility.
|
|
1456
|
+
*
|
|
1457
|
+
* The outermost renderer sets this to its own submitAttempted; nested
|
|
1458
|
+
* renderers inherit it unchanged.
|
|
1459
|
+
*/
|
|
1460
|
+
submitAttempted: boolean;
|
|
1339
1461
|
}
|
|
1340
1462
|
|
|
1341
1463
|
/**
|
|
@@ -1434,4 +1556,4 @@ declare const DEFAULT_TEMPLATES: TemplateRegistry;
|
|
|
1434
1556
|
*/
|
|
1435
1557
|
declare function groupTemplateKey(variant: string | undefined): string;
|
|
1436
1558
|
|
|
1437
|
-
export { type ArrayHeaderTemplate, type ArrayHeaderTemplateProps, type ArrayItemTemplate, type ArrayItemTemplateProps, ArrayWidget, CheckboxWidget, DEFAULT_TEMPLATES, DEFAULT_WIDGETS, DEFAULT_WIDGET_MAP, DateTimeWidget, DateWidget, DisplayWidget, EntitySelectWidget, ExtraPropertiesCard, type FieldGroup, FormActions, FormCheckbox, FormFieldRenderer, FormFieldWrapper, type FormFieldWrapperProps, FormInput, FormMultiSelect, FormNumberInput, FormQuantityInput, FormSelect, FormTagsInput, FormTextarea, GroupErrorBadge, type GroupLayoutTemplate, type GroupLayoutTemplateProps, JsonEditorWidget, JsonSchemaFormRenderer, type JsonSchemaFormRendererProps, type NestedFormContextValue, type NestedRenderer, NumberWidget, type ObjectContainerTemplate, type ObjectContainerTemplateProps, ObjectWidget, OneOfWidget, type QuantityValue, QuantityWidget, SectionsGroupTemplate, type SelectOption, SelectWidget, StepperGroupTemplate, TabsGroupTemplate, TagsInputWidget, type TemplateRegistry, TextInputWidget, TextareaWidget, TimeWidget, type UnitOption, type UnitPosition, type Widget, type WidgetDescriptor, type WidgetMap, type WidgetProps, type WidgetRegistry, firstError, getAriaDescribedBy, getDefaultWidgetForType, groupTemplateKey, resolveWidget, useFormEngine, useNestedFormContext, useResolvedSchema };
|
|
1559
|
+
export { type ArrayHeaderTemplate, type ArrayHeaderTemplateProps, type ArrayItemTemplate, type ArrayItemTemplateProps, ArrayWidget, type AsyncFieldValidator, type AsyncFormValidator, CheckboxWidget, DEFAULT_TEMPLATES, DEFAULT_WIDGETS, DEFAULT_WIDGET_MAP, DateTimeWidget, DateWidget, DisplayWidget, EntitySelectWidget, ExtraPropertiesCard, type FieldGroup, type FieldValidator, FormActions, FormCheckbox, FormFieldRenderer, FormFieldWrapper, type FormFieldWrapperProps, FormInput, FormMultiSelect, FormNumberInput, FormQuantityInput, FormSelect, FormTagsInput, FormTextarea, type FormValidator, GroupErrorBadge, type GroupLayoutTemplate, type GroupLayoutTemplateProps, JsonEditorWidget, JsonSchemaFormRenderer, type JsonSchemaFormRendererProps, type NestedFormContextValue, type NestedRenderer, NumberWidget, type ObjectContainerTemplate, type ObjectContainerTemplateProps, ObjectWidget, OneOfWidget, type QuantityValue, QuantityWidget, SectionsGroupTemplate, type SelectOption, SelectWidget, StepperGroupTemplate, TabsGroupTemplate, TagsInputWidget, type TemplateRegistry, TextInputWidget, TextareaWidget, TimeWidget, type UnitOption, type UnitPosition, type ValidatorContext, type ValidatorRegistry, type ValidatorResult, type Widget, type WidgetDescriptor, type WidgetMap, type WidgetProps, type WidgetRegistry, firstError, getAriaDescribedBy, getDefaultWidgetForType, groupTemplateKey, resolveWidget, useFormEngine, useNestedFormContext, useResolvedSchema };
|