@dynamic-field-kit/react 1.6.0 → 1.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # @dynamic-field-kit/react
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Dirty-baseline rebasing, accessible validation errors, form-level message catalogs, and async field options across React, Vue, and Angular.
8
+ - 9b06e3f: Validation messages can be set once per form via `useDynamicForm({ messages })`,
9
+ or process-wide via `setDefaultMessages`, instead of passing a string to every
10
+ validator on every field. Built-in validators now resolve their message when
11
+ they run rather than when the field description is built, which is what made a
12
+ catalog impossible before. A message passed directly to a validator still wins,
13
+ and the English defaults are unchanged when no catalog is supplied.
14
+
15
+ `ValidationContext` - already `validate`'s fourth argument - gains an optional
16
+ `t` resolver, so a hand-written validator can translate its own messages too.
17
+
18
+ Adds `validators.matches(otherFieldName)` for confirm-password and
19
+ confirm-email fields, which every consumer was hand-writing.
20
+
21
+ No locale bundles ship: the mechanism is here, the translations are yours.
22
+
23
+ - a7358f9: Fix per-field `dirty`, which was measured against a baseline captured at mount
24
+ and never re-based - wrong after `reset(newValues)` on all three adapters, and
25
+ wrong on React and Vue for values that arrive after mount, where every field
26
+ reported dirty forever.
27
+
28
+ Adds `baselineValues` and `getDirtyValues()` to the form store on all three
29
+ adapters, and an `initialProperties` prop to `MultiFieldInput` for re-basing
30
+ without a store. Comparison moves from `!==` to `Object.is`, so a `NaN` numeric
31
+ field no longer reads as permanently dirty.
32
+
33
+ React's `useDynamicForm` no longer validates the same data twice per change.
34
+
35
+ - 53ed45a: `options` can now return a promise, covering both dependent selects
36
+ (`optionsDeps`) and search-remote pickers (`onOptionsQuery`). Renderers receive
37
+ `optionsStatus` and `optionsError` alongside `options`.
38
+
39
+ `debounceMs` was declared on `FieldDescription`, published in the `.d.ts` and
40
+ read by no implementation anywhere - setting it did nothing. It now debounces
41
+ these loads.
42
+
43
+ Debounce, abort of a superseded request, and discarding a response that lands
44
+ out of order all live in core's `createOptionsLoader`, so the three adapters
45
+ share one implementation. Synchronous and static options are untouched and never
46
+ enter a loading state.
47
+
48
+ - e35e876: `ariaDescribedBy` is now `${id}-error` when a field has an error instead of
49
+ being hard-coded `undefined`, and `makeErrorId` is exported so a custom renderer
50
+ can put the matching id on its message element. Without this,
51
+ `focusFirstInvalidField` had nothing to find for anyone following the official
52
+ renderer recipe.
53
+
54
+ Default renderers now render the validation message they were already being
55
+ handed - the one visible change in this release. Custom renderers are untouched,
56
+ so nobody gets two copies of their own message.
57
+
58
+ Development builds now warn when `FieldDescription.props` carries a key the
59
+ renderer prop contract owns, which 1.6.0 made possible to lose silently.
60
+
3
61
  ## 1.6.0
4
62
 
5
63
  ### Minor Changes
package/README.md CHANGED
@@ -43,6 +43,11 @@ both packages:
43
43
  - `validateFields` / `validateFieldsAsync` — a whole schema, returns `ValidationResult`
44
44
  - `collectFieldPaths` — the leaf paths a schema actually has in the data (`contacts[0].email`)
45
45
  - `indexGroupPathMap` — index an error or touched map by repeatable-group item
46
+ - `makeErrorId` — the id a renderer puts on its message element so
47
+ `aria-describedby` resolves
48
+ - `createOptionsLoader` / `isAsyncOptions` — the async options engine
49
+ - `createMessageResolver` / `setDefaultMessages` / `MessageCatalog` — validation
50
+ message catalog
46
51
  - `resolveDisabled` / `resolveReadOnly` / `resolveOptions` — resolve a field's dynamic conditions and options
47
52
  - `validators` — the built-in validator helpers (`required`, `email`, `minLength`, `compose`, …)
48
53
  - `ValidationResult` / `ValidationContext`
@@ -140,6 +145,7 @@ const form = useDynamicForm({
140
145
  initialValues: { country: 'VN' },
141
146
  validateOnBlur: true, // default
142
147
  validateOnChange: false, // default
148
+ messages: { required: 'Bắt buộc' }, // optional; see Validation & conditions
143
149
  });
144
150
 
145
151
  <form onSubmit={form.handleSubmit((data) => save(data))}>
@@ -163,6 +169,25 @@ const form = useDynamicForm({
163
169
  />
164
170
  ```
165
171
 
172
+ The `form` shorthand also carries `baselineValues`, which is what keeps
173
+ per-field `dirty` correct across a reset. Without a form store, pass the
174
+ baseline yourself:
175
+
176
+ ```tsx
177
+ <MultiFieldInput
178
+ fieldDescriptions={fields}
179
+ properties={data}
180
+ initialProperties={original} // what `dirty` compares against
181
+ />
182
+ ```
183
+
184
+ Omit it and the baseline is the first non-`undefined` `properties` the
185
+ component sees — which is what an edit form wants when its values arrive from a
186
+ fetch after mount. Note that `{}` counts as a real value: a form
187
+ that opens blank cannot be told apart from one still waiting on a fetch, so
188
+ pass `initialProperties` when `properties` starts as `{}` rather than
189
+ `undefined`.
190
+
166
191
  Passing `touched` and `errors` gives the form store ownership of renderer
167
192
  metadata. `touched` is what makes an invalid submit visible: `handleSubmit`
168
193
  marks every field touched before validating, so a renderer that gates its error
@@ -170,28 +195,30 @@ on `touched` shows it even for fields the user never focused. `reset()` clears
170
195
  touched the same way. Individually passed props win over the ones `form`
171
196
  derives, so you can pass `form` and still override one wire.
172
197
 
173
- | Member | Description |
174
- | ----------------------------------- | --------------------------------------------------------------------------------- |
175
- | `data` | Current form data, with `computeValue` fields applied |
176
- | `errors` | `Record<string, string[]>`, keyed like `validateFields` |
177
- | `isValid` / `isDirty` | Current synchronous validity / any value has changed |
178
- | `isValidating` | An async validation pass is in flight |
179
- | `isValidationComplete` | Every applicable validator finished and none is in flight |
180
- | `validationStatus` | `'valid' | 'invalid' | 'pending'`— prefer it over`isValid`alone:`valid` cannot tell "nothing is wrong" from "nothing is wrong yet" |
181
- | `isSubmitting` / `isSubmitted` | In-flight submit / at least one submit attempted |
182
- | `touched` | Fields that have been blurred |
183
- | `handleChange(data)` | Replace the whole form data pass to `MultiFieldInput`'s `onChange` |
184
- | `setFieldValue(name, value)` | Change one field |
185
- | `handleBlur(name)` | Mark touched, and validate when `validateOnBlur` |
186
- | `setFieldTouched(name, value?)` | Set touched explicitly |
187
- | `touchAll()` | Mark every field touched `handleSubmit` already calls it |
188
- | `resetTouched()` | Clear touched only, leaving data/errors/dirty alone |
189
- | `setTouched` | Raw setter for the whole touched map |
190
- | `setData` | Raw state setter, for escape hatches |
191
- | `validate()` | Validate now, returns a boolean |
192
- | `validateAsync()` | Validate now, awaiting Promise-based rules |
193
- | `reset(values?)` | Back to `initialValues` (or the values given), clearing errors/touched/submission |
194
- | `handleSubmit(onValid, onInvalid?)` | Returns a submit handler; calls `preventDefault`, validates, then dispatches |
198
+ | Member | Description |
199
+ | ----------------------------------- | ----------------------------------------------------------------------------------------------- |
200
+ | `data` | Current form data, with `computeValue` fields applied |
201
+ | `errors` | `Record<string, string[]>`, keyed like `validateFields` |
202
+ | `isValid` / `isDirty` | Current synchronous validity / any value has changed |
203
+ | `baselineValues` | The values `dirty` is measured against — `initialValues` until `reset(newValues)` replaces them |
204
+ | `getDirtyValues()` | Only the entries differing from `baselineValues`, for PATCH-style submits |
205
+ | `isValidating` | An async validation pass is in flight |
206
+ | `isValidationComplete` | Every applicable validator finished and none is in flight |
207
+ | `validationStatus` | `'valid' | 'invalid' | 'pending'`— prefer it over`isValid`alone:`valid` cannot tell "nothing is wrong" from "nothing is wrong yet" |
208
+ | `isSubmitting` / `isSubmitted` | In-flight submit / at least one submit attempted |
209
+ | `touched` | Fields that have been blurred |
210
+ | `handleChange(data)` | Replace the whole form data — pass to `MultiFieldInput`'s `onChange` |
211
+ | `setFieldValue(name, value)` | Change one field |
212
+ | `handleBlur(name)` | Mark touched, and validate when `validateOnBlur` |
213
+ | `setFieldTouched(name, value?)` | Set touched explicitly |
214
+ | `touchAll()` | Mark every field touched `handleSubmit` already calls it |
215
+ | `resetTouched()` | Clear touched only, leaving data/errors/dirty alone |
216
+ | `setTouched` | Raw setter for the whole touched map |
217
+ | `setData` | Raw state setter, for escape hatches |
218
+ | `validate()` | Validate now, returns a boolean |
219
+ | `validateAsync()` | Validate now, awaiting Promise-based rules |
220
+ | `reset(values?)` | Back to `initialValues` (or the values given), clearing errors/touched/submission |
221
+ | `handleSubmit(onValid, onInvalid?)` | Returns a submit handler; calls `preventDefault`, validates, then dispatches |
195
222
 
196
223
  Leave `touched` off and `MultiFieldInput` falls back to tracking it internally
197
224
  from blur alone, as it always did. In that mode nothing outside the component
@@ -245,6 +272,13 @@ const Base = getDefaultRenderer('date'); // undefined for an unknown type
245
272
  `file` emits a `File` (or `File[]` when `multiple` is set), `range` and `number`
246
273
  emit numbers, `checkbox` / `switch` emit booleans; everything else emits strings.
247
274
 
275
+ Since 1.7.0 a default renderer also renders its validation message, as
276
+ `<div id="{fieldId}-error" class="dfk-field-error" role="alert">`, which is what
277
+ `aria-describedby` points at. Before that they were handed `error` and dropped
278
+ it, so the form showed nothing. A registered custom renderer is unaffected — the
279
+ node is emitted only where a default was used, so you never get two copies. Hide
280
+ it with `.dfk-field-error { display: none }` if you want the old silence.
281
+
248
282
  ## DevTools
249
283
 
250
284
  ```tsx
@@ -350,6 +384,51 @@ fieldRegistry.register('text', ({ value, onValueChange, error, disabled }) => (
350
384
  ));
351
385
  ```
352
386
 
387
+ Forward `ariaInvalid`, `ariaRequired` and `ariaDescribedBy` too, and put
388
+ `makeErrorId(id)` on whatever element renders the message. `focusFirstInvalidField`
389
+ selects `[aria-invalid="true"]`, so a renderer that drops those props makes that
390
+ helper silently do nothing. See
391
+ [the recipes](../../docs/ui-kit-recipes.md#forward-the-aria-props).
392
+
393
+ ### Validation messages
394
+
395
+ Set the built-in validators' messages once per form instead of on every field:
396
+
397
+ ```tsx
398
+ const form = useDynamicForm({
399
+ fields,
400
+ messages: { required: 'Bắt buộc', minLength: 'Tối thiểu {min} ký tự' },
401
+ });
402
+ ```
403
+
404
+ A message passed straight to a validator still wins, and any key omitted falls
405
+ back to the English default. `setDefaultMessages(catalog)` sets a process-wide
406
+ one for code calling `validateFields` directly. Full key list in the
407
+ [core README](../core/README.md#validation-messages). **No locale bundles
408
+ ship** — the mechanism is here, the translations are yours.
409
+
410
+ ### Async options
411
+
412
+ `options` may return a promise, and the renderer receives `optionsStatus`
413
+ (`'idle' | 'loading' | 'ready' | 'error'`), `optionsError` and `onOptionsQuery`:
414
+
415
+ ```tsx
416
+ {
417
+ name: 'assignee',
418
+ type: 'userPicker',
419
+ options: async (data, _rootData, ctx) =>
420
+ fetch(`/api/users?q=${ctx?.query ?? ''}`, { signal: ctx?.signal })
421
+ .then((r) => r.json()),
422
+ optionsDeps: (data) => [data.team], // reload when this changes; default []
423
+ debounceMs: 300, // collapses rapid reloads into one fetch
424
+ }
425
+ ```
426
+
427
+ Superseded requests are aborted and out-of-order responses discarded, so the
428
+ list always reflects the newest request. Static and synchronous options are
429
+ untouched and never enter a loading state. See the
430
+ [core README](../core/README.md#async-options).
431
+
353
432
  ## Repeatable field groups
354
433
 
355
434
  A field with `fields` renders as a repeatable group: `data[name]` becomes an array of items, each shaped by the nested `fields`, with "Add"/"Remove" controls rendered automatically.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode, ComponentType } from 'react';
2
- import { FieldTypeKey, Properties, FieldDescription, LayoutConfig, ValidationResult, FieldRendererProps, FieldTypeMap } from '@dynamic-field-kit/core';
3
- export { FIELD_RENDERER_PROP_KEYS, FieldDescription, FieldRegistry, FieldRendererProps, FieldTypeKey, LayoutConfig, ValidationContext, ValidationResult, buildFieldRendererProps, collectFieldPaths, indexGroupPathMap, makeFieldId, resolveDisabled, resolveOptions, resolveReadOnly, validateField, validateFieldAsync, validateFields, validateFieldsAsync, validators } from '@dynamic-field-kit/core';
2
+ import { FieldTypeKey, Properties, OptionsStatus, FieldDescription, LayoutConfig, ValidationResult, MessageCatalog, FieldRendererProps, FieldTypeMap } from '@dynamic-field-kit/core';
3
+ export { FIELD_RENDERER_PROP_KEYS, FieldDescription, FieldRegistry, FieldRendererProps, FieldTypeKey, LayoutConfig, ValidationContext, ValidationResult, buildFieldRendererProps, collectFieldPaths, indexGroupPathMap, makeErrorId, makeFieldId, resolveDisabled, resolveOptions, resolveReadOnly, validateField, validateFieldAsync, validateFields, validateFieldsAsync, validators } from '@dynamic-field-kit/core';
4
4
 
5
5
  type LayoutRenderer<C = unknown> = (props: {
6
6
  children: React.ReactNode;
@@ -21,6 +21,8 @@ interface Props$2<T extends FieldTypeKey> {
21
21
  label?: string;
22
22
  placeholder?: string;
23
23
  options?: Properties[];
24
+ optionsStatus?: OptionsStatus;
25
+ optionsError?: unknown;
24
26
  className?: string;
25
27
  description?: ReactNode;
26
28
  disabled?: boolean;
@@ -40,8 +42,10 @@ interface Props$2<T extends FieldTypeKey> {
40
42
  multiple?: boolean;
41
43
  /** Extra, framework-agnostic props forwarded verbatim to the renderer. */
42
44
  extraProps?: Properties;
45
+ /** Renderer-driven refetch for a search-remote field. */
46
+ onOptionsQuery?: (query: string) => void;
43
47
  }
44
- declare const DynamicInputInner: <T extends FieldTypeKey>({ type, onChange, onBlur, extraProps, ...rendererProps }: Props$2<T>) => React.JSX.Element;
48
+ declare const DynamicInputInner: <T extends FieldTypeKey>({ type, onChange, onBlur, extraProps, onOptionsQuery, ...rendererProps }: Props$2<T>) => React.JSX.Element;
45
49
  declare const DynamicInput: typeof DynamicInputInner;
46
50
 
47
51
  interface Props$1 {
@@ -67,6 +71,8 @@ interface DynamicFormBinding {
67
71
  data: Properties;
68
72
  errors: Record<string, string[]>;
69
73
  touched: Record<string, boolean>;
74
+ /** The values per-field `dirty` is measured against. See `useDynamicForm`. */
75
+ baselineValues?: Properties;
70
76
  handleChange: (data: Properties) => void;
71
77
  handleBlur: (fieldName: string) => void;
72
78
  }
@@ -95,6 +101,14 @@ interface Props {
95
101
  * restores the pre-1.6 ids), or set `FieldDescription.id` per field.
96
102
  */
97
103
  idPrefix?: string;
104
+ /**
105
+ * The values per-field `dirty` is measured against. Defaults to the first
106
+ * non-`undefined` `properties` this component sees - which is what an edit
107
+ * form wants when its values arrive from a fetch after mount. Supplied
108
+ * automatically by the `form` shorthand; pass it explicitly to re-base
109
+ * `dirty` without going through a form store.
110
+ */
111
+ initialProperties?: Properties;
98
112
  /**
99
113
  * Top-level form data, threaded down through repeatable groups so a nested
100
114
  * field's `appearCondition`/`computeValue` can read the root form. Omitted at
@@ -150,6 +164,13 @@ interface UseDynamicFormOptions {
150
164
  initialValues?: Properties;
151
165
  validateOnBlur?: boolean;
152
166
  validateOnChange?: boolean;
167
+ /**
168
+ * Messages for the built-in validators, set once for the whole form instead
169
+ * of per field. A message passed directly to a validator still wins, and any
170
+ * key omitted here falls back to the validator's English default. See core's
171
+ * `MessageCatalog`.
172
+ */
173
+ messages?: MessageCatalog;
153
174
  }
154
175
  interface UseDynamicFormResult {
155
176
  data: Properties;
@@ -159,6 +180,18 @@ interface UseDynamicFormResult {
159
180
  isValidationComplete: boolean;
160
181
  validationStatus: ValidationResult['status'];
161
182
  isDirty: boolean;
183
+ /**
184
+ * The values `dirty` is measured against: the `initialValues` option until
185
+ * `reset(newValues)` replaces them. Distinct from that option, which never
186
+ * changes - pass this to `MultiFieldInput` (or use the `form` shorthand) so
187
+ * per-field `dirty` survives a reset.
188
+ */
189
+ baselineValues: Properties;
190
+ /**
191
+ * The entries of `data` that differ from `baselineValues`. Intended for
192
+ * PATCH-style submits that should carry only what the user actually edited.
193
+ */
194
+ getDirtyValues: () => Properties;
162
195
  isSubmitting: boolean;
163
196
  isSubmitted: boolean;
164
197
  touched: Record<string, boolean>;
@@ -183,7 +216,7 @@ interface UseDynamicFormResult {
183
216
  validateAsync: () => Promise<boolean>;
184
217
  handleSubmit: (onValid: (data: Properties) => void | Promise<void>, onInvalid?: (errors: Record<string, string[]>) => void) => (e?: React.FormEvent) => Promise<void>;
185
218
  }
186
- declare function useDynamicForm({ fields, initialValues, validateOnBlur, validateOnChange, }: UseDynamicFormOptions): UseDynamicFormResult;
219
+ declare function useDynamicForm({ fields, initialValues, validateOnBlur, validateOnChange, messages, }: UseDynamicFormOptions): UseDynamicFormResult;
187
220
 
188
221
  declare const defaultRenderersMap: Record<string, React.FC<FieldRendererProps>>;
189
222
  declare function getDefaultRenderer(type: string): React.FC<FieldRendererProps> | undefined;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode, ComponentType } from 'react';
2
- import { FieldTypeKey, Properties, FieldDescription, LayoutConfig, ValidationResult, FieldRendererProps, FieldTypeMap } from '@dynamic-field-kit/core';
3
- export { FIELD_RENDERER_PROP_KEYS, FieldDescription, FieldRegistry, FieldRendererProps, FieldTypeKey, LayoutConfig, ValidationContext, ValidationResult, buildFieldRendererProps, collectFieldPaths, indexGroupPathMap, makeFieldId, resolveDisabled, resolveOptions, resolveReadOnly, validateField, validateFieldAsync, validateFields, validateFieldsAsync, validators } from '@dynamic-field-kit/core';
2
+ import { FieldTypeKey, Properties, OptionsStatus, FieldDescription, LayoutConfig, ValidationResult, MessageCatalog, FieldRendererProps, FieldTypeMap } from '@dynamic-field-kit/core';
3
+ export { FIELD_RENDERER_PROP_KEYS, FieldDescription, FieldRegistry, FieldRendererProps, FieldTypeKey, LayoutConfig, ValidationContext, ValidationResult, buildFieldRendererProps, collectFieldPaths, indexGroupPathMap, makeErrorId, makeFieldId, resolveDisabled, resolveOptions, resolveReadOnly, validateField, validateFieldAsync, validateFields, validateFieldsAsync, validators } from '@dynamic-field-kit/core';
4
4
 
5
5
  type LayoutRenderer<C = unknown> = (props: {
6
6
  children: React.ReactNode;
@@ -21,6 +21,8 @@ interface Props$2<T extends FieldTypeKey> {
21
21
  label?: string;
22
22
  placeholder?: string;
23
23
  options?: Properties[];
24
+ optionsStatus?: OptionsStatus;
25
+ optionsError?: unknown;
24
26
  className?: string;
25
27
  description?: ReactNode;
26
28
  disabled?: boolean;
@@ -40,8 +42,10 @@ interface Props$2<T extends FieldTypeKey> {
40
42
  multiple?: boolean;
41
43
  /** Extra, framework-agnostic props forwarded verbatim to the renderer. */
42
44
  extraProps?: Properties;
45
+ /** Renderer-driven refetch for a search-remote field. */
46
+ onOptionsQuery?: (query: string) => void;
43
47
  }
44
- declare const DynamicInputInner: <T extends FieldTypeKey>({ type, onChange, onBlur, extraProps, ...rendererProps }: Props$2<T>) => React.JSX.Element;
48
+ declare const DynamicInputInner: <T extends FieldTypeKey>({ type, onChange, onBlur, extraProps, onOptionsQuery, ...rendererProps }: Props$2<T>) => React.JSX.Element;
45
49
  declare const DynamicInput: typeof DynamicInputInner;
46
50
 
47
51
  interface Props$1 {
@@ -67,6 +71,8 @@ interface DynamicFormBinding {
67
71
  data: Properties;
68
72
  errors: Record<string, string[]>;
69
73
  touched: Record<string, boolean>;
74
+ /** The values per-field `dirty` is measured against. See `useDynamicForm`. */
75
+ baselineValues?: Properties;
70
76
  handleChange: (data: Properties) => void;
71
77
  handleBlur: (fieldName: string) => void;
72
78
  }
@@ -95,6 +101,14 @@ interface Props {
95
101
  * restores the pre-1.6 ids), or set `FieldDescription.id` per field.
96
102
  */
97
103
  idPrefix?: string;
104
+ /**
105
+ * The values per-field `dirty` is measured against. Defaults to the first
106
+ * non-`undefined` `properties` this component sees - which is what an edit
107
+ * form wants when its values arrive from a fetch after mount. Supplied
108
+ * automatically by the `form` shorthand; pass it explicitly to re-base
109
+ * `dirty` without going through a form store.
110
+ */
111
+ initialProperties?: Properties;
98
112
  /**
99
113
  * Top-level form data, threaded down through repeatable groups so a nested
100
114
  * field's `appearCondition`/`computeValue` can read the root form. Omitted at
@@ -150,6 +164,13 @@ interface UseDynamicFormOptions {
150
164
  initialValues?: Properties;
151
165
  validateOnBlur?: boolean;
152
166
  validateOnChange?: boolean;
167
+ /**
168
+ * Messages for the built-in validators, set once for the whole form instead
169
+ * of per field. A message passed directly to a validator still wins, and any
170
+ * key omitted here falls back to the validator's English default. See core's
171
+ * `MessageCatalog`.
172
+ */
173
+ messages?: MessageCatalog;
153
174
  }
154
175
  interface UseDynamicFormResult {
155
176
  data: Properties;
@@ -159,6 +180,18 @@ interface UseDynamicFormResult {
159
180
  isValidationComplete: boolean;
160
181
  validationStatus: ValidationResult['status'];
161
182
  isDirty: boolean;
183
+ /**
184
+ * The values `dirty` is measured against: the `initialValues` option until
185
+ * `reset(newValues)` replaces them. Distinct from that option, which never
186
+ * changes - pass this to `MultiFieldInput` (or use the `form` shorthand) so
187
+ * per-field `dirty` survives a reset.
188
+ */
189
+ baselineValues: Properties;
190
+ /**
191
+ * The entries of `data` that differ from `baselineValues`. Intended for
192
+ * PATCH-style submits that should carry only what the user actually edited.
193
+ */
194
+ getDirtyValues: () => Properties;
162
195
  isSubmitting: boolean;
163
196
  isSubmitted: boolean;
164
197
  touched: Record<string, boolean>;
@@ -183,7 +216,7 @@ interface UseDynamicFormResult {
183
216
  validateAsync: () => Promise<boolean>;
184
217
  handleSubmit: (onValid: (data: Properties) => void | Promise<void>, onInvalid?: (errors: Record<string, string[]>) => void) => (e?: React.FormEvent) => Promise<void>;
185
218
  }
186
- declare function useDynamicForm({ fields, initialValues, validateOnBlur, validateOnChange, }: UseDynamicFormOptions): UseDynamicFormResult;
219
+ declare function useDynamicForm({ fields, initialValues, validateOnBlur, validateOnChange, messages, }: UseDynamicFormOptions): UseDynamicFormResult;
187
220
 
188
221
  declare const defaultRenderersMap: Record<string, React.FC<FieldRendererProps>>;
189
222
  declare function getDefaultRenderer(type: string): React.FC<FieldRendererProps> | undefined;