react-f0rm 0.9.0 → 0.11.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 +120 -3
- package/dist/devtools/index.cjs.js +1 -1
- package/dist/devtools/index.cjs.js.map +1 -1
- package/dist/devtools/index.mjs +1 -1
- package/dist/devtools/index.mjs.map +1 -1
- package/dist/form-BQsb2CuG.mjs +2 -0
- package/dist/form-BQsb2CuG.mjs.map +1 -0
- package/dist/{form-7sbcY2uT.d.ts → form-DSwruZIg.d.ts} +56 -2
- package/dist/form-DZK8HgZ7.cjs.js +2 -0
- package/dist/form-DZK8HgZ7.cjs.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +65 -6
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +139 -6
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +2 -2
- package/dist/index.umd.min.js.map +1 -1
- package/dist/resolvers/standard-schema.cjs.js +1 -1
- package/dist/resolvers/standard-schema.mjs +1 -1
- package/dist/resolvers/yup.cjs.js +1 -1
- package/dist/resolvers/yup.d.ts +1 -1
- package/dist/resolvers/yup.mjs +1 -1
- package/dist/resolvers/zod.cjs.js +1 -1
- package/dist/resolvers/zod.d.ts +1 -1
- package/dist/resolvers/zod.mjs +1 -1
- package/dist/{validate-DXGZBon4.d.ts → validate-CqRu0f-o.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/form-DbDDJ8bt.cjs.js +0 -2
- package/dist/form-DbDDJ8bt.cjs.js.map +0 -1
- package/dist/form-DsydpBhT.mjs +0 -2
- package/dist/form-DsydpBhT.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
A headless, event-driven React form library with field-level subscriptions.
|
|
9
9
|
|
|
10
|
+
Coming from TanStack Form? [Migrating from TanStack Form](./docs/from-tanstack-form.md) is the one-page concept map — the core mapping table, known differences, and common pitfalls.
|
|
11
|
+
|
|
10
12
|
## Features
|
|
11
13
|
|
|
12
14
|
- **Field-level subscriptions.** Editing one field re-renders exactly that field's component, not the whole form. State is read through `useSyncExternalStore`, so snapshots stay consistent under concurrent rendering (no tearing).
|
|
@@ -70,7 +72,7 @@ react-f0rm vs the established options. react-f0rm figures come from this repo (s
|
|
|
70
72
|
| Async validation | `validateDebounce` per field + `meta.signal` (`AbortSignal`) handed to every validator — superseded rounds cancel their in-flight work; pending debounce counts as validating so submit waits | Async validators supported, but no built-in debounce and no cancellation signal — both are hand-rolled per project | Built in: `asyncDebounceMs` debounces and the validator meta carries an `AbortSignal` | Async `validate` supported; no debounce, no signal |
|
|
71
73
|
| Multiple errors per field | Native: every field holds `FieldError[]`; `getFieldErrors`/`useFieldErrors` read them; resolvers forward every schema issue | `criteriaMode: 'all'` collects all failing rules per field | Errors are arrays of messages per field | — |
|
|
72
74
|
| SSR / hydration | `renderToString` renders initial values out of the box; server snapshot matches the client's first render | SSR-safe | SSR-safe | SSR-safe |
|
|
73
|
-
| React 19 / Server Actions | Bridge pattern: dispatch the action from `onValidSubmit` via `startTransition`/`useActionState`, passing the values object rather than FormData (see the React 19 Server Actions guide); no submit before JS loads | `<Form>` accepts a function `action` prop (server-action-style submit) since v7.84, and ships a `react-server` export | Documented server action integration (`createServerValidate` for server-side validation, Next.js examples) | — |
|
|
75
|
+
| React 19 / Server Actions | Bridge pattern: dispatch the action from `onValidSubmit` via `startTransition`/`useActionState`, passing the values object rather than FormData (see the stance above and the React 19 Server Actions guide); no submit before JS loads — first-class `action` prop support is not on the 0.x roadmap | `<Form>` accepts a function `action` prop (server-action-style submit) since v7.84, and ships a `react-server` export | Documented server action integration (`createServerValidate` for server-side validation, Next.js examples) | — |
|
|
74
76
|
| Bundle size | 11.18 KB gzip (7.1 KB brotli, minified), full core | ~11 KB gzip | ~17.5 KB gzip | ~12.8 KB gzip |
|
|
75
77
|
| Devtools | `<Devtools />` from `react-f0rm/devtools` — separate entry point, tree-shakeable, never lands in the main bundle | `@hookform/devtools` (separate package) | Built-in devtools panel | None (official) |
|
|
76
78
|
| Ecosystem maturity | New, 0.x — small audience, few integrations so far | Most mature: massive adoption, resolvers, UI-kit integrations, abundant examples and answers | Backed by the TanStack family, actively growing | Maintenance mode; the author recommends considering RHF or Final Form for new projects |
|
|
@@ -83,6 +85,8 @@ Bundle-size basis: every column is gzip. react-f0rm is measured on the local bui
|
|
|
83
85
|
|
|
84
86
|
**Pick React Hook Form** when uncontrolled inputs are an option: its raw `register` performs no per-field re-render at all and floors at 21µs/change vs our 113µs (see [Benchmarks](#benchmarks)) — uncontrolled is simply a cheaper rendering model. RHF is also the right call when you need its mature ecosystem of resolvers, UI-library integrations and community answers today. TanStack Form sits in between: choose it when the deepest possible type inference (including validator signatures) matters more to you than bundle size.
|
|
85
87
|
|
|
88
|
+
**Server Actions: bridge, not first-class.** RHF-style `action` prop support, a `react-server` entry point, or a TanStack-style `createServerValidate` helper are **not on the 0.x roadmap** — a deliberate stance, not a gap. react-f0rm's source of truth is the values store, not the DOM: an `action`-prop submit would ship FormData keyed by JSON-stringified path keys, drop every store-only value, and skip the validation gate entirely (the [React 19 Server Actions guide](docs-site/docs/guides/react19-server-actions.md) unpacks all four failure modes). The recommended shape is the bridge — dispatch from `onValidSubmit` via `startTransition`/`useActionState`, passing the values object rather than FormData — which keeps validation gating the action and types/nesting intact. If submitting without JavaScript loaded is a hard requirement, RHF's `action` prop support is the better fit today.
|
|
89
|
+
|
|
86
90
|
## Usage
|
|
87
91
|
|
|
88
92
|
```jsx
|
|
@@ -195,6 +199,57 @@ update(1, 'B'); // overwrite one value, keeping that row's id — no k
|
|
|
195
199
|
|
|
196
200
|
`replace(values)` is the refetch shape — a server response replaces the whole list — while `update(index, value)` rewrites a single row in place.
|
|
197
201
|
|
|
202
|
+
### `useFieldArrayItem`
|
|
203
|
+
|
|
204
|
+
Per-row subscription for large arrays — the counterpart of TanStack Form's field api that `useFieldArray` alone cannot offer. `useFieldArray` subscribes to the whole branch, so any row's edit re-renders the component holding the array (and, without memoization, every row). `useFieldArrayItem` gives one row — identified by the stable `id` from `fields[i].id` — a subscription of its own:
|
|
205
|
+
|
|
206
|
+
```jsx
|
|
207
|
+
import {useFieldArray, useFieldArrayItem} from 'react-f0rm';
|
|
208
|
+
|
|
209
|
+
const Row = React.memo(function Row({id}) {
|
|
210
|
+
const item = useFieldArrayItem({name: 'tags', id});
|
|
211
|
+
return (
|
|
212
|
+
<div>
|
|
213
|
+
<input
|
|
214
|
+
value={item.value ?? ''}
|
|
215
|
+
onChange={e => item.setValue(e.target.value)}
|
|
216
|
+
/>
|
|
217
|
+
{item.error && <span>{item.error}</span>}
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
function Tags() {
|
|
223
|
+
const {fields, append, remove} = useFieldArray({name: 'tags'});
|
|
224
|
+
return (
|
|
225
|
+
<div>
|
|
226
|
+
{fields.map(field => (
|
|
227
|
+
<div key={field.id}>
|
|
228
|
+
<Row id={field.id} />
|
|
229
|
+
<button type="button" onClick={() => remove(field.index)}>
|
|
230
|
+
Remove
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
))}
|
|
234
|
+
<button type="button" onClick={() => append('')}>
|
|
235
|
+
Add Tag
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Editing row K re-renders only row K, and a whole-array rewrite (`update`, `append`) re-renders only rows whose value actually changed — untouched rows' renders stay at zero. Two requirements make that hold:
|
|
243
|
+
|
|
244
|
+
- a `useFieldArray({name})` must be mounted at the same path — it publishes the id table rows resolve against;
|
|
245
|
+
- the row component must be `React.memo` with stable props (`id`, optionally `form`): everything else comes from the hook, so the array component's own re-render cannot drag the rows along.
|
|
246
|
+
|
|
247
|
+
Rows whose index migrates — `remove`/`move`/`swap`/`insert` reshuffles — re-render by design: the row's path contains the index, exactly like TanStack Form's per-field api. `replace` regenerates every id, so every row remounts. The win is single-row edits staying single-row.
|
|
248
|
+
|
|
249
|
+
The hook returns `{value, setValue, errors, error, name, index, form}` — the `useField`-style shape plus `index` and `name` (the row's current path key, e.g. `["tags",0]`) for building nested fields. Value reads and writes live on the array layer — the same layer every `useFieldArray` operation touches — so `value`, `setValue` and `update`/`append`/… always agree with each other; editing through a leaf-path `useField({name: ['tags', i]})` writes a different layer and does not flow into `item.value`.
|
|
250
|
+
|
|
251
|
+
Without a paired `useFieldArray` the row is inert rather than broken: `index` is `-1`, `value` is `undefined`, and `setValue` is a no-op.
|
|
252
|
+
|
|
198
253
|
### `createFormContext`
|
|
199
254
|
|
|
200
255
|
The module-level context serves one form per subtree; nesting two forms (or reusing a component under a different form) makes them fight over it. `createFormContext` builds an isolated bundle of bindings, typed against your values shape:
|
|
@@ -216,7 +271,7 @@ function NameField() {
|
|
|
216
271
|
}
|
|
217
272
|
```
|
|
218
273
|
|
|
219
|
-
Each call returns `{context, FormProvider, useFormContext, useField, useFieldArray}` bound to a private React context — pass the form via `<ProfileForm.FormProvider form={form}>`, and providers from separate instances never see each other's forms.
|
|
274
|
+
Each call returns `{context, FormProvider, useFormContext, useField, useFieldArray, useFieldArrayItem}` bound to a private React context — pass the form via `<ProfileForm.FormProvider form={form}>`, and providers from separate instances never see each other's forms.
|
|
220
275
|
|
|
221
276
|
The bundle also carries its raw React context, so `<Form>` can provide into it while keeping its full submit machinery — validation, submit handling, focus-on-error — instead of you wiring `<FormProvider>` + `handleSubmit` by hand:
|
|
222
277
|
|
|
@@ -596,6 +651,39 @@ const form = useForm({
|
|
|
596
651
|
|
|
597
652
|
The `AbortSignal` fires as soon as the round is superseded — a newer round started, which under a positive `validateDebounce` means a kick landed during the in-flight round's window — so async validators can cancel their underlying work instead of racing a stale result home. Stale results are dropped independently by the round gate, so validators that ignore the signal stay correct too. Without `validateDebounce` (`0`/omitted) the validate runs once per `trigger`/submit exactly as before; it still receives the meta argument, but nothing supersedes an immediate round, so its signal never fires.
|
|
598
653
|
|
|
654
|
+
#### Re-running on dependent field changes (`validateDeps`)
|
|
655
|
+
|
|
656
|
+
By default the form-level `validate` runs on `trigger` and submit only — a cross-field error stays on screen even after the user edits the field that would fix it. `validateDeps` declares the fields whose **user changes re-run the form-level `validate`**:
|
|
657
|
+
|
|
658
|
+
```jsx
|
|
659
|
+
const form = useForm({
|
|
660
|
+
initialValues: {password: '', confirm: ''},
|
|
661
|
+
validate: values =>
|
|
662
|
+
values.password !== values.confirm
|
|
663
|
+
? {confirm: 'Passwords do not match'}
|
|
664
|
+
: {},
|
|
665
|
+
validateDeps: ['password']
|
|
666
|
+
});
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Now the submit-then-fix flow works: submit lands the mismatch on `confirm`, editing `password` re-runs the validate, and the passing round makes the error disappear. The re-run timing rides the same mode matrix as any field validator, evaluated against the changed field's effective `mode` (per-field override included) and the form's `reValidateMode`:
|
|
670
|
+
|
|
671
|
+
| Situation | Dep change re-runs the form validate? |
|
|
672
|
+
|---|---|
|
|
673
|
+
| `mode: 'onChange'` / `'all'` (form or the dep field) | yes, error state or not |
|
|
674
|
+
| `mode: 'onTouched'`, dep field touched | yes |
|
|
675
|
+
| otherwise, the last round's error is live **and** `reValidateMode: 'onChange'` (default) | yes — the submit-then-fix flow |
|
|
676
|
+
| `reValidateMode: 'onBlur'` / `'onSubmit'` | no — a change is not a blur; re-runs wait for their own trigger |
|
|
677
|
+
|
|
678
|
+
Details that fall out of the plumbing:
|
|
679
|
+
|
|
680
|
+
- **User changes only.** The kick rides the mounted field's own change pipeline, so typing and `changeValue` (component-library bridges) both fire it, while programmatic `setValue` does not — exactly like field validators. A dep path with no mounted field never re-runs the validate.
|
|
681
|
+
- **Round-scoped error ownership.** Opting in changes what a re-run may clear: each round first drops the errors the *previous round* wrote, then lands its own result — so a passing re-run clears the stale mismatch. Errors the round never wrote (field validators', `setServerErrors`, manual `setError`) survive it, and a foreign write onto a round-owned path takes the key out of the round's ownership.
|
|
682
|
+
- **`validateDebounce` applies.** Dep-change kicks are ordinary kicks: they merge inside the debounce window like `trigger`/submit kicks do.
|
|
683
|
+
- Forms that don't set `validateDeps` keep the historical behavior untouched — the form validate runs on `trigger`/submit only, and re-runs never clear earlier errors.
|
|
684
|
+
|
|
685
|
+
TanStack Form's counterpart is `onChangeListenTo` (v1) / validator `triggers` (v2 alpha); both re-run a validator when listed fields change. react-f0rm keeps the declaration at the form level (the validate belongs to the form) and gates the re-run by the library's own `mode`/`reValidateMode` semantics instead of adding an always-on listener.
|
|
686
|
+
|
|
599
687
|
### Schema validation
|
|
600
688
|
|
|
601
689
|
Any library implementing [Standard Schema v1](https://standardschema.dev) — zod v3.24+/v4, valibot v1, arktype and more — works through one adapter, imported from its own tree-shakeable entry point:
|
|
@@ -627,6 +715,35 @@ Schema errors come back as `{type: 'standard', message}`.
|
|
|
627
715
|
|
|
628
716
|
On success the adapter returns the schema's parsed output, which the form stores as its `parsedValues` baseline: `getValues()` and submit callbacks (`onSubmit`/`onValidSubmit`) read coerced/transformed values — `z.coerce.number()` hands back a real `number`, not the raw string. The baseline sits between `initialValues` and live edits, so fields the user changes afterwards still win, and dirty state keeps comparing live edits against `initialValues` only — parsing never marks a field dirty. `reset()` and `setInitialValues()` clear the baseline.
|
|
629
717
|
|
|
718
|
+
#### Schema defaults
|
|
719
|
+
|
|
720
|
+
**Standard Schema v1 has no default-value metadata.** The interface carries types and `validate` and nothing else — whether a field declares a default, and how to read it, is vendor territory: zod v3.24 exposes `.getDefault()` per field, zod v4 wraps defaulted fields in a `ZodDefault` whose `.def.defaultValue` is a de-facto-public field rather than a documented accessor, valibot ships a `getDefault` util. None of it is reachable through the standard surface, and react-f0rm reads schemas only through `~standard.validate` — probing `schema.shape`/`.def` internals per vendor is exactly the adapter-per-library tree this library refuses to grow. So `defaultValues` derived from a schema is deliberately **not** a library feature: pass `initialValues` explicitly.
|
|
721
|
+
|
|
722
|
+
What you do get for free: defaults flow through `validate`. A schema's parsed output contains every declared default, so after the first successful validation the `parsedValues` baseline already serves them — `getValues()` reads `z.string().default('anon')` fields as `'anon'` without any seeding. The gap is only the render before the first validation round, and two recipes close it user-side:
|
|
723
|
+
|
|
724
|
+
```jsx
|
|
725
|
+
// 1. Vendor-neutral: one parse of an empty object materializes every
|
|
726
|
+
// default the schema declares (nested ones included).
|
|
727
|
+
const result = schema['~standard'].validate({});
|
|
728
|
+
const initialValues = result.issues ? {} : result.value;
|
|
729
|
+
|
|
730
|
+
const form = useForm({initialValues, validate: standardSchemaFormValidator(schema)});
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
The empty parse succeeds only where defaults cover everything; a required field without a default fails it, and `{}` is the honest seed in that case. For per-field extraction instead of a whole-object parse, do it through the vendor's own API — zod v4:
|
|
734
|
+
|
|
735
|
+
```jsx
|
|
736
|
+
// 2. zod v4: ZodDefault wrappers expose their default on .def
|
|
737
|
+
const defaultValues = Object.fromEntries(
|
|
738
|
+
Object.entries(schema.shape).map(([key, field]) => [
|
|
739
|
+
key,
|
|
740
|
+
field.def?.type === 'default' ? field.def.defaultValue : undefined
|
|
741
|
+
])
|
|
742
|
+
);
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
(zod v3.24: the same loop calling `field.getDefault()`. That this loop is version-specific is the point — it is your schema and your vendor, not the form library's, contract to maintain.)
|
|
746
|
+
|
|
630
747
|
### Delaying error display
|
|
631
748
|
|
|
632
749
|
`delayError` (milliseconds) holds a newly appearing error back from the render for a short window — users typing through a field are not interrupted by an error the next keystroke may already fix:
|
|
@@ -866,7 +983,7 @@ const html = renderToString(<ProfileForm initialValues={{name: 'ada', city: 'lon
|
|
|
866
983
|
|
|
867
984
|
## Migrating
|
|
868
985
|
|
|
869
|
-
Coming from another library? Step-by-step migration guides live in the docs site:
|
|
986
|
+
Coming from another library? [Migrating from TanStack Form](./docs/from-tanstack-form.md) is the repo-level concept map — core mapping table, known differences, common pitfalls. Step-by-step migration guides live in the docs site:
|
|
870
987
|
|
|
871
988
|
- [Migrating from Formik](docs-site/docs/migration/from-formik.md)
|
|
872
989
|
- [Migrating from React Hook Form](docs-site/docs/migration/from-react-hook-form.md)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("../form-DbDDJ8bt.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
|
|
1
|
+
"use strict";var e=require("react"),t=require("../form-DZK8HgZ7.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/shim/index.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js","../../src/hooks/form.tsx","../../src/context.ts","../../src/devtools/JsonTree.tsx","../../src/devtools/styles.ts","../../src/devtools/Devtools.tsx"],"sourcesContent":["'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('../cjs/use-sync-external-store-shim.production.js');\n} else {\n module.exports = require('../cjs/use-sync-external-store-shim.development.js');\n}\n","/**\n * @license React\n * use-sync-external-store-shim.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue;\nfunction useSyncExternalStore$2(subscribe, getSnapshot) {\n var value = getSnapshot(),\n _useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),\n inst = _useState[0].inst,\n forceUpdate = _useState[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n}\nvar shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\nexports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n","/**\n * @license React\n * use-sync-external-store-shim.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n (function () {\n function is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n }\n function useSyncExternalStore$2(subscribe, getSnapshot) {\n didWarnOld18Alpha ||\n void 0 === React.startTransition ||\n ((didWarnOld18Alpha = !0),\n console.error(\n \"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release.\"\n ));\n var value = getSnapshot();\n if (!didWarnUncachedGetSnapshot) {\n var cachedValue = getSnapshot();\n objectIs(value, cachedValue) ||\n (console.error(\n \"The result of getSnapshot should be cached to avoid an infinite loop\"\n ),\n (didWarnUncachedGetSnapshot = !0));\n }\n cachedValue = useState({\n inst: { value: value, getSnapshot: getSnapshot }\n });\n var inst = cachedValue[0].inst,\n forceUpdate = cachedValue[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n }\n function checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n }\n function useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n }\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());\n var React = require(\"react\"),\n objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue,\n didWarnOld18Alpha = !1,\n didWarnUncachedGetSnapshot = !1,\n shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\n exports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());\n })();\n","import {useState, useEffect, useCallback, useRef} from 'react';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim';\nimport {on} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\nimport {onKeyEvent, onPathEvent} from '../subscribe';\nimport createForm, {\n getErrorByPath,\n getFieldErrorsByPath,\n getValueByPath,\n hasTouchedByPath,\n hasErrors,\n isDirty,\n getDirtyFields,\n getTouchedFields,\n setInitialValues\n} from '../form';\nimport type {FieldError, Form, Options, Name} from '../form';\nimport type {FieldPath, PathValueOf} from '../types';\nimport createPath from '../path';\nimport type {Path} from '../path';\nimport {isEqual} from '../util';\n\n/**\n * Create a form instance bound to this component.\n *\n * Beyond {@link Options}, the optional `values` object enables controlled\n * usage: when it genuinely changes it is re-synced into the form with\n * setInitialValues semantics -- uncommitted user edits are discarded\n * (master-detail semantics: selecting another record replaces the draft),\n * while touched flags and errors survive. Change detection is\n * reference-first with a structural fallback, so re-renders that pass an\n * inline literal with equal content never re-sync -- the user's\n * in-progress typing is never clobbered.\n */\nexport default function useForm<T extends Record<string, any> = any>(\n options?: Options<T> & {values?: T}\n): Form<T> {\n // Lazy initialization: createForm runs once per mount and the returned\n // instance is stable across re-renders (and StrictMode double renders),\n // without writing to refs during render. A provided `values` object is\n // seeded synchronously here (createForm does the same for initialValues)\n // so the first paint and SSR already reflect the controlled values.\n const [form] = useState(() => {\n const created = createForm<T>(options);\n if (options && options.values !== undefined) {\n setInitialValues(created, options.values);\n }\n return created;\n });\n const initialValues = options && options.initialValues;\n const values = options && options.values;\n\n // Track which initialValues source object the form was last seeded from.\n // Inline options create a fresh object every render, and re-seeding\n // clears the values Map (setInitialValues semantics), which would revert\n // every committed edit right after each re-render -- on the client and\n // after hydration alike. Memoized callers are covered by the reference\n // check; inline literals by the structural one, so only genuinely new\n // content re-seeds.\n const seededRef = useRef<{done: boolean; source: any} | null>(null);\n if (seededRef.current === null)\n seededRef.current = {done: false, source: undefined};\n\n useEffect(() => {\n const seeded = seededRef.current!;\n if (\n seeded.done &&\n (seeded.source === initialValues || isEqual(seeded.source, initialValues))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = initialValues;\n setInitialValues(form, initialValues);\n }, [form, initialValues]);\n\n // Controlled values: re-sync only when the incoming object genuinely\n // differs from what the form was last seeded from. The reference check\n // is the fast path (memoized callers); inline literals get a fresh\n // object identity every render, so without the structural comparison\n // each re-render would clear the values Map (setInitialValues\n // semantics) and revert the user's uncommitted edits -- same hazard the\n // initialValues seed guard above protects against. Master-detail\n // semantics still apply whenever the content actually changed.\n const controlledRef = useRef<{done: boolean; source: any} | null>(null);\n if (controlledRef.current === null) {\n controlledRef.current = {done: false, source: undefined};\n }\n\n useEffect(() => {\n if (values === undefined) return;\n const seeded = controlledRef.current!;\n if (\n seeded.done &&\n (seeded.source === values || isEqual(seeded.source, values))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = values;\n setInitialValues(form, values);\n }, [form, values]);\n\n return form;\n}\n\n/** Per-hook snapshot cache for {@link useWatch}. */\ninterface WatchCache<T> {\n hasValue: boolean;\n value?: T;\n}\n\n/**\n * Shared core of {@link useWatch} and the path-scoped hooks: a\n * useSyncExternalStore binding over a custom event subscription.\n * `subscribeFactory` receives the invalidate callback (drop the snapshot\n * cache, then notify React) and returns its unsubscribe function, so the\n * core stays identical whether the subscription is global or scoped to\n * one path.\n */\nfunction useWatchCore<T>(\n subscribeFactory: (invalidate: () => void) => () => void,\n getter: () => T\n): T {\n // useSyncExternalStore requires getSnapshot to return the same reference\n // until the store actually changed, otherwise React warns and loops.\n // Cache the snapshot per hook instance and recompute it only on the first\n // read and after the watched event fired.\n const cacheRef = useRef<WatchCache<T> | null>(null);\n if (cacheRef.current === null) cacheRef.current = {hasValue: false};\n const cache = cacheRef.current;\n\n // Hold the latest getter in a ref so getSnapshot keeps a stable identity\n // (callers pass a freshly bound function on every render) while still\n // recomputing with the most recent getter when the cache is invalid.\n const getterRef = useRef(getter);\n getterRef.current = getter;\n\n const getSnapshot = useCallback(() => {\n if (!cache.hasValue) {\n cache.value = getterRef.current();\n cache.hasValue = true;\n }\n return cache.value as T;\n }, [cache]);\n\n const subscribe = useCallback(\n (notify: () => void) => {\n // The form may have changed between render and this subscription, and\n // those events were missed: drop the cache. React's consistency check\n // right after subscribing recomputes and re-renders only when the\n // fresh value differs from the committed snapshot.\n cache.hasValue = false;\n const invalidate = () => {\n cache.hasValue = false;\n notify();\n };\n return subscribeFactory(invalidate);\n },\n [subscribeFactory, cache]\n );\n\n // Form state lives entirely in synchronously readable Map/Set structures\n // seeded from initialValues/values during the lazy useState initializer,\n // so the server snapshot is computed exactly like the client's first\n // render -- pass getSnapshot itself as getServerSnapshot and hydration\n // matches.\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Subscribe to a form event and keep the component's snapshot of `getter()`\n * in sync with the form state.\n *\n * Built on useSyncExternalStore, so snapshots taken while React renders are\n * guaranteed consistent (no tearing under concurrent rendering) and changes\n * emitted before the subscription effect runs are still picked up.\n */\nexport function useWatch<T>(\n emitter: EventEmitter,\n event: string,\n getter: () => T\n): T {\n const subscribeFactory = useCallback(\n (invalidate: () => void) => on(emitter, event, invalidate),\n [emitter, event]\n );\n return useWatchCore(subscribeFactory, getter);\n}\n\n/**\n * Get field value state\n */\nexport function useValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return useValueByPath(form, createPath(name));\n}\n\n/**\n * Get field value state by path\n */\nexport function useValueByPath(form: Form, path: Path): any {\n const {emitter} = form;\n const {key} = path;\n // 'leaf' scope: a leaf read depends only on its own key and its\n // ancestors' (getValueByPath fallback chain), so writes elsewhere --\n // siblings, descendants, string-prefix lookalikes ('[\"a\",\"bX\"]') -- never\n // invalidate the snapshot. Payload-less broadcasts (reset, removeField,\n // setInitialValues) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) =>\n onPathEvent(emitter, 'change', path, 'leaf', invalidate),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are `key` on purpose: useValue creates a fresh Path per render, so the object must stay out of the deps while the key string pins the subscription\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getValueByPath.bind(null, form, path));\n}\n\n/**\n * Get field touched state\n */\nexport function useTouched<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): boolean {\n return useTouchedByPath(form, createPath(name));\n}\n\n/**\n * Get field touched state by path\n */\nexport function useTouchedByPath(form: Form, path: Path): boolean {\n const {emitter} = form;\n const {key} = path;\n // Touched is stored per exact key, so only this field's own setTouched\n // (now emitted with its path) matters; payload-less broadcasts (reset,\n // removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'touched', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n hasTouchedByPath.bind(null, form, path)\n );\n}\n\n/**\n * Get field error message state\n * @return current error's message string (display text), or undefined\n */\nexport function useError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): string | undefined {\n return useErrorByPath(form, createPath(name))?.message;\n}\n\n/**\n * Get field error state by path\n * @return current FieldError object ({type, message}), or undefined\n */\nexport function useErrorByPath(form: Form, path: Path): FieldError | undefined {\n const {emitter} = form;\n const {key} = path;\n // Errors are stored per exact key, so only writes to this field's error\n // (setErrorByPath now emits with its path) matter; payload-less\n // broadcasts (clearErrors, reset, removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getErrorByPath.bind(null, form, path));\n}\n\n/**\n * Get all field errors\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrors<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError[] {\n return useFieldErrorsByPath(form, createPath(name));\n}\n\n/**\n * Get all field errors by path\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrorsByPath(form: Form, path: Path): FieldError[] {\n const {emitter} = form;\n const {key} = path;\n // Same exact-key subscription and snapshot rules as useErrorByPath: the\n // getter returns the shared empty constant when clean and the stored\n // array by reference otherwise, so the useSyncExternalStore snapshot is\n // reference-stable between unrelated events.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n getFieldErrorsByPath.bind(null, form, path)\n );\n}\n\nexport function useIsDirty(form: Form): boolean {\n // Dirty state is driven by value changes, not touch state: subscribe to\n // 'change' so typing flips this immediately, even before a blur.\n return useWatch(form.emitter, 'change', isDirty.bind(null, form));\n}\n\n/**\n * Get dirty fields state -- object mapping each dirty field's user-facing\n * dotted path ('a.b', 'a.0.c') to true; recalculated after 'change' events\n */\nexport function useDirtyFields(form: Form): Record<string, boolean> {\n return useWatch(form.emitter, 'change', getDirtyFields.bind(null, form));\n}\n\n/**\n * Get touched fields state -- array of touched fields' user-facing dotted\n * paths ('a.b', 'a.0.c'); recalculated after 'touched' events\n */\nexport function useTouchedFields(form: Form): string[] {\n return useWatch(form.emitter, 'touched', getTouchedFields.bind(null, form));\n}\n\nexport function useHasErrors(form: Form): boolean {\n return useWatch(form.emitter, 'errors', hasErrors.bind(null, form));\n}\n\nexport function useIsSubmitting(form: Form): boolean {\n return useWatch(form.emitter, 'submitting', () => form.isSubmitting);\n}\n\n/**\n * Get whether the form accepts a submit right now:\n * `!isSubmitting && !hasErrors`. This is the single flag a submit\n * button's `disabled` prop wants — it is `false` for the whole async\n * `onSubmit` span (not just the validation pass) and whenever any field\n * holds an error (client validation or server backfill), replacing the\n * hand-rolled `useHasErrors(form) || useIsSubmitting(form)` pair.\n * Deliberately no dirty or validating semantics: an untouched-but-clean\n * form can submit.\n */\nexport function useCanSubmit(form: Form): boolean {\n const {emitter} = form;\n // canSubmit folds two events into one boolean: error writes\n // ('errors') and submit-state flips ('submitting'). useWatch subscribes\n // to a single event, so subscribe to both through useWatchCore — the\n // snapshot recomputes on either wake and re-renders only when the\n // boolean itself flips, so unrelated single-field error churn costs no\n // extra render (the same granularity useHasErrors already has).\n const subscribeFactory = useCallback(\n (invalidate: () => void) => {\n const offErrors = on(emitter, 'errors', invalidate);\n const offSubmitting = on(emitter, 'submitting', invalidate);\n return () => {\n offErrors();\n offSubmitting();\n };\n },\n [emitter]\n );\n return useWatchCore(\n subscribeFactory,\n () => !form.isSubmitting && !hasErrors(form)\n );\n}\n\nexport function useSubmitCount(form: Form): number {\n return useWatch(form.emitter, 'submitCount', () => form.submitCount);\n}\n","import {createContext, createElement, useContext, type ReactNode} from 'react';\nimport {\n useFieldCore,\n type UseFieldOptions,\n type UseFieldResult\n} from './hooks/field';\nimport {useFieldArrayCore, type UseFieldArrayResult} from './hooks/fieldArray';\nimport type {Form} from './form';\nimport type {Name} from './path';\nimport type {FieldPath} from './types';\n\nexport const FormContext = createContext<any>(null);\n\nexport const FormProvider = FormContext.Provider;\n\n/**\n * Read the form from the module-level {@link FormContext}. Pass the values\n * shape — `useFormContext<Values>()` — to get a fully typed `Form<Values>`\n * headless API; the `any` default keeps untyped call sites compiling.\n *\n * For multiple forms in one subtree use {@link createFormContext} instead.\n *\n * @throws when no `<FormProvider>` is mounted above the call site.\n */\nexport function useFormContext<T extends Record<string, any> = any>(): Form<T> {\n const form = useContext(FormContext);\n if (!form) throw new Error('no form provided');\n return form;\n}\n\n/**\n * Create an isolated bundle of form-context bindings: its own React context\n * plus `useField` / `useFieldArray` / `useFormContext` hooks that resolve\n * their form from it.\n *\n * Why: the module-level {@link FormContext} works fine for a single form per\n * subtree, but nesting two forms (or reusing a component inside a different\n * form) makes them fight over one context. Calling this factory once per app\n * area — `const Ctx = createFormContext<Values>()` — fixes the value shape\n * (`Ctx.useField({name: 'user.name'})` gets its `name` constrained by\n * `FieldPath<Values>` and its `value` typed accordingly), so call sites stop\n * hand-writing generics, and each instance's Provider scopes a strictly\n * separate form. The bundle also carries its raw React context\n * (`Ctx.context`) so `<Form context={Ctx.context}>` can provide into it.\n */\nexport function createFormContext<TValues extends Record<string, any> = any>() {\n const Context = createContext<Form<TValues> | null>(null);\n\n // A `form`-prop wrapper instead of exposing Context.Provider directly:\n // callers shouldn't have to know about the raw `value` prop shape.\n function FormProvider({\n form,\n children\n }: {\n form: Form<TValues>;\n children: ReactNode;\n }): ReactNode {\n return createElement(Context.Provider, {value: form}, children);\n }\n\n function useFormContext(): Form<TValues> {\n const form = useContext(Context);\n if (!form) throw new Error('no form provided');\n return form;\n }\n\n function useField<TPath extends FieldPath<TValues> | Name = Name>(\n // The bare `{name: TPath}` member keeps `name` a direct inference site\n // for TPath instead of routing it through the mapped Omit type.\n // `form` is omitted on purpose — the form always comes from this\n // factory's own Context.\n options: {name: TPath} & Omit<UseFieldOptions<TValues, TPath>, 'form'>\n ): UseFieldResult<TValues, TPath> {\n return useFieldCore(options as UseFieldOptions<TValues, TPath>, Context);\n }\n\n function useFieldArray(options: {\n name: FieldPath<TValues> | Name;\n }): UseFieldArrayResult {\n return useFieldArrayCore(options as {name: Name}, Context);\n }\n\n // The raw React context, for `<Form context={...}>`: the component keeps\n // its submit machinery while providing into this instance's private\n // context, so the bound hooks above resolve the form it manages.\n return {\n context: Context,\n FormProvider,\n useFormContext,\n useField,\n useFieldArray\n };\n}\n\nexport const CheckboxGroupContext = createContext<any>(null);\n\nexport const CheckboxGroupProvider = CheckboxGroupContext.Provider;\n\nexport function useCheckboxGroupContext(): any {\n const group = useContext(CheckboxGroupContext);\n if (!group) throw new Error('no group provided');\n return group;\n}\n","import * as React from 'react';\nimport {useState} from 'react';\nimport type {ReactNode} from 'react';\n\n/** Nodes deeper than this start collapsed. */\nconst DEFAULT_OPEN_DEPTH = 1;\n\n/**\n * Read-only inspection: the tree never mutates form state, so structural\n * sharing of the inspected value is safe and re-renders stay cheap.\n */\ninterface JsonNodeProps {\n /** Property name (or array index) rendering before the value. */\n name?: string | number;\n /** Value to render. */\n value: unknown;\n /** Current nesting depth (root is 0). */\n depth?: number;\n}\n\n/**\n * One line of the tree: either a collapsible container row\n * (`▸ key: {`) or a leaf (`key: value`).\n */\nfunction JsonNode({name, value, depth = 0}: JsonNodeProps) {\n const [open, setOpen] = useState(depth <= DEFAULT_OPEN_DEPTH);\n\n const label =\n name === undefined ? null : (\n <>\n <span className=\"rf0-dt-key\">{String(name)}</span>\n <span className=\"rf0-dt-punct\">: </span>\n </>\n );\n\n if (value !== null && typeof value === 'object') {\n const isArray = Array.isArray(value);\n const entries: Array<[string | number, unknown]> = isArray\n ? (value as unknown[]).map((v, i) => [i, v])\n : Object.entries(value as Record<string, unknown>);\n const openBracket = isArray ? '[' : '{';\n const closeBracket = isArray ? ']' : '}';\n const summary = open\n ? ''\n : `${openBracket}…${closeBracket} ${entries.length}`;\n\n return (\n <div className=\"rf0-dt-row\" style={{paddingLeft: depth * 12}}>\n <button\n type=\"button\"\n className=\"rf0-dt-node-toggle\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"rf0-dt-caret\">{open ? '▾' : '▸'}</span>\n {label}\n <span className=\"rf0-dt-punct\">{open ? openBracket : summary}</span>\n </button>\n {open && (\n <>\n {entries.map(([k, v]) => (\n <JsonNode key={String(k)} name={k} value={v} depth={depth + 1} />\n ))}\n <span className=\"rf0-dt-punct\" style={{paddingLeft: depth * 12}}>\n {closeBracket}\n </span>\n </>\n )}\n </div>\n );\n }\n\n return (\n <span\n className=\"rf0-dt-row\"\n style={{paddingLeft: depth * 12, display: 'block'}}\n >\n {label}\n <Primitive value={value} />\n </span>\n );\n}\n\n/** Render a primitive leaf with terminal-style type coloring. */\nfunction Primitive({value}: {value: unknown}): ReactNode {\n if (value === undefined)\n return <span className=\"rf0-dt-null\">undefined</span>;\n if (value === null) return <span className=\"rf0-dt-null\">null</span>;\n if (typeof value === 'string')\n return <span className=\"rf0-dt-string\">"{value}"</span>;\n if (typeof value === 'boolean')\n return <span className=\"rf0-dt-boolean\">{String(value)}</span>;\n return <span className=\"rf0-dt-number\">{String(value)}</span>;\n}\n\nexport default JsonNode;\n","/**\n * Stylesheet for the Devtools panel.\n *\n * Zero runtime dependencies by design: a single CSS string injected once\n * into <head> (idempotent across module reloads and multiple bundles).\n *\n * Aesthetic: instrument panel / terminal — near-black layers, monospace\n * stack, dense rows, hairline borders. Semantic colors only: error red,\n * success green, neutral gray, with one dim amber accent for the active\n * tab indicator and the collapsed badge.\n */\n\nconst CSS = `\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n`;\n\nconst STYLE_ID = 'react-f0rm-devtools-style';\n\n/**\n * Inject the panel stylesheet into <head>. Idempotent: repeated calls\n * (module reloads, HMR, multiple Devtools mounts) never duplicate the\n * <style> element. No-ops outside a DOM environment (SSR).\n */\nexport function injectDevtoolsStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\ninjectDevtoolsStyles();\n","import * as React from 'react';\nimport {useContext, useId, useState} from 'react';\nimport type {KeyboardEvent} from 'react';\nimport {FormContext} from '../context';\nimport {getErrors, getValues, reset, trigger} from '../form';\nimport type {FieldErrorEntry, Form} from '../form';\nimport {\n useDirtyFields,\n useIsSubmitting,\n useSubmitCount,\n useTouchedFields,\n useWatch\n} from '../hooks/form';\nimport JsonTree from './JsonTree';\nimport './styles';\n\n/** Corner the panel docks to. */\nexport type DevtoolsPosition =\n 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left';\n\n/** Props for {@link Devtools}. */\nexport interface DevtoolsProps<T extends Record<string, any> = any> {\n /**\n * Form instance to inspect. When omitted, the panel reads the closest\n * `<Form>` / FormProvider ancestor and throws if there is none.\n */\n form?: Form<T>;\n /** Corner to dock the panel in. Defaults to `'top-right'`. */\n position?: DevtoolsPosition;\n}\n\ntype TabId = 'values' | 'errors' | 'touched' | 'dirty';\n\nconst TABS: TabId[] = ['values', 'errors', 'touched', 'dirty'];\n\n/** Status chip class for the submit-successful indicator. */\nfunction submitStatusClass(\n isSubmitSuccessful: boolean | undefined\n): string | undefined {\n if (isSubmitSuccessful === undefined) return undefined;\n return isSubmitSuccessful ? 'rf0-dt-ok' : 'rf0-dt-err';\n}\n\n/** Count primitive leaves of an inspected value tree. */\nfunction countLeaves(value: unknown): number {\n if (value === null || typeof value !== 'object') return 1;\n let count = 0;\n for (const v of Object.values(value as Record<string, unknown>)) {\n count += countLeaves(v);\n }\n return count;\n}\n\n/**\n * Live form inspector — a floating instrument panel for development.\n *\n * Renders four tabs (values / errors / touched / dirty), a submit status\n * strip (isSubmitting, submitCount, isSubmitSuccessful) and two actions:\n * Reset and Validate (full `trigger`). All state is read through the\n * library's own watch hooks, so the panel updates in real time without\n * participating in validation or submit flows. Docked at a corner,\n * collapsible to a small badge; fully keyboard operable.\n *\n * Ship it from the dedicated `react-f0rm/devtools` entry — it is never\n * re-exported by the main entry, so production bundles stay untouched.\n */\nexport default function Devtools<T extends Record<string, any> = any>({\n form,\n position = 'top-right'\n}: DevtoolsProps<T>) {\n const contextForm = useContext(FormContext);\n const f = form ?? contextForm;\n if (!f) {\n throw new Error(\n '<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.'\n );\n }\n\n const [open, setOpen] = useState(true);\n const [tab, setTab] = useState<TabId>('values');\n const idPrefix = useId().replace(/[^a-zA-Z0-9-]/g, '');\n\n // Live snapshots, straight through the public watch surface.\n const values = useWatch(f.emitter, 'change', getValues.bind(null, f));\n const errors = useWatch<FieldErrorEntry[]>(\n f.emitter,\n 'errors',\n getErrors.bind(null, f)\n );\n const touched = useTouchedFields(f);\n const dirty = useDirtyFields(f);\n const isSubmitting = useIsSubmitting(f);\n const submitCount = useSubmitCount(f);\n const isSubmitSuccessful = useWatch(\n f.emitter,\n 'submitSuccessful',\n () => f.isSubmitSuccessful\n );\n\n if (!open) {\n return (\n <button\n type=\"button\"\n className={`rf0-dt-badge rf0-dt-badge--${position}${\n errors.length > 0 ? ' rf0-dt-badge--has-errors' : ''\n }`}\n aria-expanded={false}\n aria-label={`Open react-f0rm devtools (${errors.length} errors)`}\n onClick={() => setOpen(true)}\n >\n f0\n <span className=\"rf0-dt-dot\" />\n </button>\n );\n }\n\n const counts: Record<TabId, number> = {\n values: countLeaves(values),\n errors: errors.length,\n touched: touched.length,\n dirty: Object.keys(dirty).length\n };\n\n /** Arrow-key tab navigation (buttons stay click/Enter/Space operable). */\n const onTabKeyDown = (e: KeyboardEvent) => {\n const deltas: Record<string, number> = {\n ArrowRight: 1,\n ArrowLeft: -1\n };\n const delta = deltas[e.key];\n if (!delta) return;\n e.preventDefault();\n const next = TABS[(TABS.indexOf(tab) + delta + TABS.length) % TABS.length];\n setTab(next);\n document.getElementById(`${idPrefix}-tab-${next}`)?.focus();\n };\n\n return (\n <section\n className={`rf0-dt rf0-dt--${position}`}\n aria-label=\"react-f0rm devtools\"\n >\n <header className=\"rf0-dt-header\">\n <span className=\"rf0-dt-title\">react-f0rm</span>\n <button\n type=\"button\"\n className=\"rf0-dt-headerbtn\"\n aria-label=\"Collapse devtools\"\n onClick={() => setOpen(false)}\n >\n –\n </button>\n </header>\n\n <div\n className=\"rf0-dt-tablist\"\n role=\"tablist\"\n aria-label=\"Form state\"\n tabIndex={-1}\n onKeyDown={onTabKeyDown}\n >\n {TABS.map(id => (\n <button\n key={id}\n id={`${idPrefix}-tab-${id}`}\n type=\"button\"\n role=\"tab\"\n className={`rf0-dt-tab${id === 'errors' ? ' rf0-dt-tab--danger' : ''}`}\n aria-selected={tab === id}\n aria-controls={`${idPrefix}-panel-${id}`}\n tabIndex={tab === id ? 0 : -1}\n onClick={() => setTab(id)}\n >\n {id}\n <span className=\"rf0-dt-tab-count\">{counts[id]}</span>\n </button>\n ))}\n </div>\n\n <div\n id={`${idPrefix}-panel-${tab}`}\n role=\"tabpanel\"\n aria-labelledby={`${idPrefix}-tab-${tab}`}\n className=\"rf0-dt-panel\"\n >\n {tab === 'values' && <JsonTree value={values} />}\n {tab === 'errors' &&\n (errors.length === 0 ? (\n <p className=\"rf0-dt-empty\">no errors</p>\n ) : (\n errors.map(({path, type, message}, index) => (\n // Same path can hold several errors now; index keeps keys\n // unique without changing what is rendered (messages may\n // legitimately repeat for one path).\n\n <div key={`${path}:${index}`} className=\"rf0-dt-item\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg\">{message}</span>\n <span className=\"rf0-dt-item-tag\">{type}</span>\n </div>\n ))\n ))}\n {tab === 'touched' &&\n (touched.length === 0 ? (\n <p className=\"rf0-dt-empty\">no touched fields</p>\n ) : (\n touched.map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--touched\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n </div>\n ))\n ))}\n {tab === 'dirty' &&\n (Object.keys(dirty).length === 0 ? (\n <p className=\"rf0-dt-empty\">no dirty fields</p>\n ) : (\n Object.keys(dirty).map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--dirty\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg rf0-dt-item-msg--ok\">\n changed\n </span>\n </div>\n ))\n ))}\n </div>\n\n <p className=\"rf0-dt-status\" aria-live=\"polite\">\n <span className={isSubmitting ? 'rf0-dt-on' : undefined}>\n submitting <b>{String(isSubmitting)}</b>\n </span>\n <span>\n submits <b>{submitCount}</b>\n </span>\n <span className={submitStatusClass(isSubmitSuccessful)}>\n ok{' '}\n <b>\n {isSubmitSuccessful === undefined\n ? '–'\n : String(isSubmitSuccessful)}\n </b>\n </span>\n </p>\n\n <div className=\"rf0-dt-actions\">\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => reset(f, f.initialValues)}\n >\n Reset\n </button>\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => trigger(f)}\n >\n Validate\n </button>\n </div>\n </section>\n );\n}\n"],"names":["process","env","NODE_ENV","shimModule","exports","React","require$$0","objectIs","Object","is","x","y","useState","useEffect","useLayoutEffect","useDebugValue","checkIfSnapshotChanged","inst","latestGetSnapshot","getSnapshot","value","nextValue","error","shim","window","document","createElement","subscribe","_useState","forceUpdate","useSyncExternalStoreShim_production","useSyncExternalStore","__REACT_DEVTOOLS_GLOBAL_HOOK__","registerInternalModuleStart","Error","didWarnOld18Alpha","didWarnUncachedGetSnapshot","startTransition","console","cachedValue","useSyncExternalStoreShim_development","registerInternalModuleStop","useWatch","emitter","event","getter","subscribeFactory","cacheRef","useRef","current","hasValue","cache","getterRef","useCallback","notify","useWatchCore","invalidate","on","FormContext","createContext","Provider","JsonNode","name","depth","open","setOpen","label","Fragment","className","String","isArray","Array","entries","map","v","i","openBracket","closeBracket","summary","length","style","paddingLeft","type","onClick","k","key","display","Primitive","STYLE_ID","getElementById","id","textContent","head","appendChild","injectDevtoolsStyles","TABS","submitStatusClass","isSubmitSuccessful","countLeaves","count","values","form","position","contextForm","useContext","f","tab","setTab","idPrefix","useId","replace","getValues","bind","errors","getErrors","touched","getTouchedFields","useTouchedFields","dirty","getDirtyFields","useDirtyFields","isSubmitting","useIsSubmitting","submitCount","useSubmitCount","counts","keys","role","tabIndex","onKeyDown","e","delta","ArrowRight","ArrowLeft","preventDefault","next","indexOf","focus","JsonTree","path","message","index","reset","initialValues","trigger"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/shim/index.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js","../../src/hooks/form.tsx","../../src/context.ts","../../src/devtools/JsonTree.tsx","../../src/devtools/styles.ts","../../src/devtools/Devtools.tsx"],"sourcesContent":["'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('../cjs/use-sync-external-store-shim.production.js');\n} else {\n module.exports = require('../cjs/use-sync-external-store-shim.development.js');\n}\n","/**\n * @license React\n * use-sync-external-store-shim.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue;\nfunction useSyncExternalStore$2(subscribe, getSnapshot) {\n var value = getSnapshot(),\n _useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),\n inst = _useState[0].inst,\n forceUpdate = _useState[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n}\nvar shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\nexports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n","/**\n * @license React\n * use-sync-external-store-shim.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n (function () {\n function is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n }\n function useSyncExternalStore$2(subscribe, getSnapshot) {\n didWarnOld18Alpha ||\n void 0 === React.startTransition ||\n ((didWarnOld18Alpha = !0),\n console.error(\n \"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release.\"\n ));\n var value = getSnapshot();\n if (!didWarnUncachedGetSnapshot) {\n var cachedValue = getSnapshot();\n objectIs(value, cachedValue) ||\n (console.error(\n \"The result of getSnapshot should be cached to avoid an infinite loop\"\n ),\n (didWarnUncachedGetSnapshot = !0));\n }\n cachedValue = useState({\n inst: { value: value, getSnapshot: getSnapshot }\n });\n var inst = cachedValue[0].inst,\n forceUpdate = cachedValue[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n }\n function checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n }\n function useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n }\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());\n var React = require(\"react\"),\n objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue,\n didWarnOld18Alpha = !1,\n didWarnUncachedGetSnapshot = !1,\n shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\n exports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());\n })();\n","import {useState, useEffect, useCallback, useRef} from 'react';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim';\nimport {on} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\nimport {onKeyEvent, onPathEvent} from '../subscribe';\nimport createForm, {\n getErrorByPath,\n getFieldErrorsByPath,\n getValueByPath,\n hasTouchedByPath,\n hasErrors,\n isDirty,\n getDirtyFields,\n getTouchedFields,\n setInitialValues\n} from '../form';\nimport type {FieldError, Form, Options, Name} from '../form';\nimport type {FieldPath, PathValueOf} from '../types';\nimport createPath from '../path';\nimport type {Path} from '../path';\nimport {isEqual} from '../util';\n\n/**\n * Create a form instance bound to this component.\n *\n * Beyond {@link Options}, the optional `values` object enables controlled\n * usage: when it genuinely changes it is re-synced into the form with\n * setInitialValues semantics -- uncommitted user edits are discarded\n * (master-detail semantics: selecting another record replaces the draft),\n * while touched flags and errors survive. Change detection is\n * reference-first with a structural fallback, so re-renders that pass an\n * inline literal with equal content never re-sync -- the user's\n * in-progress typing is never clobbered.\n */\nexport default function useForm<T extends Record<string, any> = any>(\n options?: Options<T> & {values?: T}\n): Form<T> {\n // Lazy initialization: createForm runs once per mount and the returned\n // instance is stable across re-renders (and StrictMode double renders),\n // without writing to refs during render. A provided `values` object is\n // seeded synchronously here (createForm does the same for initialValues)\n // so the first paint and SSR already reflect the controlled values.\n const [form] = useState(() => {\n const created = createForm<T>(options);\n if (options && options.values !== undefined) {\n setInitialValues(created, options.values);\n }\n return created;\n });\n const initialValues = options && options.initialValues;\n const values = options && options.values;\n\n // Track which initialValues source object the form was last seeded from.\n // Inline options create a fresh object every render, and re-seeding\n // clears the values Map (setInitialValues semantics), which would revert\n // every committed edit right after each re-render -- on the client and\n // after hydration alike. Memoized callers are covered by the reference\n // check; inline literals by the structural one, so only genuinely new\n // content re-seeds.\n const seededRef = useRef<{done: boolean; source: any} | null>(null);\n if (seededRef.current === null)\n seededRef.current = {done: false, source: undefined};\n\n useEffect(() => {\n const seeded = seededRef.current!;\n if (\n seeded.done &&\n (seeded.source === initialValues || isEqual(seeded.source, initialValues))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = initialValues;\n setInitialValues(form, initialValues);\n }, [form, initialValues]);\n\n // Controlled values: re-sync only when the incoming object genuinely\n // differs from what the form was last seeded from. The reference check\n // is the fast path (memoized callers); inline literals get a fresh\n // object identity every render, so without the structural comparison\n // each re-render would clear the values Map (setInitialValues\n // semantics) and revert the user's uncommitted edits -- same hazard the\n // initialValues seed guard above protects against. Master-detail\n // semantics still apply whenever the content actually changed.\n const controlledRef = useRef<{done: boolean; source: any} | null>(null);\n if (controlledRef.current === null) {\n controlledRef.current = {done: false, source: undefined};\n }\n\n useEffect(() => {\n if (values === undefined) return;\n const seeded = controlledRef.current!;\n if (\n seeded.done &&\n (seeded.source === values || isEqual(seeded.source, values))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = values;\n setInitialValues(form, values);\n }, [form, values]);\n\n return form;\n}\n\n/** Per-hook snapshot cache for {@link useWatch}. */\ninterface WatchCache<T> {\n hasValue: boolean;\n value?: T;\n}\n\n/**\n * Shared core of {@link useWatch} and the path-scoped hooks: a\n * useSyncExternalStore binding over a custom event subscription.\n * `subscribeFactory` receives the invalidate callback (drop the snapshot\n * cache, then notify React) and returns its unsubscribe function, so the\n * core stays identical whether the subscription is global or scoped to\n * one path.\n */\nfunction useWatchCore<T>(\n subscribeFactory: (invalidate: () => void) => () => void,\n getter: () => T\n): T {\n // useSyncExternalStore requires getSnapshot to return the same reference\n // until the store actually changed, otherwise React warns and loops.\n // Cache the snapshot per hook instance and recompute it only on the first\n // read and after the watched event fired.\n const cacheRef = useRef<WatchCache<T> | null>(null);\n if (cacheRef.current === null) cacheRef.current = {hasValue: false};\n const cache = cacheRef.current;\n\n // Hold the latest getter in a ref so getSnapshot keeps a stable identity\n // (callers pass a freshly bound function on every render) while still\n // recomputing with the most recent getter when the cache is invalid.\n const getterRef = useRef(getter);\n getterRef.current = getter;\n\n const getSnapshot = useCallback(() => {\n if (!cache.hasValue) {\n cache.value = getterRef.current();\n cache.hasValue = true;\n }\n return cache.value as T;\n }, [cache]);\n\n const subscribe = useCallback(\n (notify: () => void) => {\n // The form may have changed between render and this subscription, and\n // those events were missed: drop the cache. React's consistency check\n // right after subscribing recomputes and re-renders only when the\n // fresh value differs from the committed snapshot.\n cache.hasValue = false;\n const invalidate = () => {\n cache.hasValue = false;\n notify();\n };\n return subscribeFactory(invalidate);\n },\n [subscribeFactory, cache]\n );\n\n // Form state lives entirely in synchronously readable Map/Set structures\n // seeded from initialValues/values during the lazy useState initializer,\n // so the server snapshot is computed exactly like the client's first\n // render -- pass getSnapshot itself as getServerSnapshot and hydration\n // matches.\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Subscribe to a form event and keep the component's snapshot of `getter()`\n * in sync with the form state.\n *\n * Built on useSyncExternalStore, so snapshots taken while React renders are\n * guaranteed consistent (no tearing under concurrent rendering) and changes\n * emitted before the subscription effect runs are still picked up.\n */\nexport function useWatch<T>(\n emitter: EventEmitter,\n event: string,\n getter: () => T\n): T {\n const subscribeFactory = useCallback(\n (invalidate: () => void) => on(emitter, event, invalidate),\n [emitter, event]\n );\n return useWatchCore(subscribeFactory, getter);\n}\n\n/**\n * Get field value state\n */\nexport function useValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return useValueByPath(form, createPath(name));\n}\n\n/**\n * Get field value state by path\n */\nexport function useValueByPath(form: Form, path: Path): any {\n const {emitter} = form;\n const {key} = path;\n // 'leaf' scope: a leaf read depends only on its own key and its\n // ancestors' (getValueByPath fallback chain), so writes elsewhere --\n // siblings, descendants, string-prefix lookalikes ('[\"a\",\"bX\"]') -- never\n // invalidate the snapshot. Payload-less broadcasts (reset, removeField,\n // setInitialValues) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) =>\n onPathEvent(emitter, 'change', path, 'leaf', invalidate),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are `key` on purpose: useValue creates a fresh Path per render, so the object must stay out of the deps while the key string pins the subscription\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getValueByPath.bind(null, form, path));\n}\n\n/**\n * Get field touched state\n */\nexport function useTouched<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): boolean {\n return useTouchedByPath(form, createPath(name));\n}\n\n/**\n * Get field touched state by path\n */\nexport function useTouchedByPath(form: Form, path: Path): boolean {\n const {emitter} = form;\n const {key} = path;\n // Touched is stored per exact key, so only this field's own setTouched\n // (now emitted with its path) matters; payload-less broadcasts (reset,\n // removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'touched', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n hasTouchedByPath.bind(null, form, path)\n );\n}\n\n/**\n * Get field error message state\n * @return current error's message string (display text), or undefined\n */\nexport function useError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): string | undefined {\n return useErrorByPath(form, createPath(name))?.message;\n}\n\n/**\n * Get field error state by path\n * @return current FieldError object ({type, message}), or undefined\n */\nexport function useErrorByPath(form: Form, path: Path): FieldError | undefined {\n const {emitter} = form;\n const {key} = path;\n // Errors are stored per exact key, so only writes to this field's error\n // (setErrorByPath now emits with its path) matter; payload-less\n // broadcasts (clearErrors, reset, removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getErrorByPath.bind(null, form, path));\n}\n\n/**\n * Get all field errors\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrors<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError[] {\n return useFieldErrorsByPath(form, createPath(name));\n}\n\n/**\n * Get all field errors by path\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrorsByPath(form: Form, path: Path): FieldError[] {\n const {emitter} = form;\n const {key} = path;\n // Same exact-key subscription and snapshot rules as useErrorByPath: the\n // getter returns the shared empty constant when clean and the stored\n // array by reference otherwise, so the useSyncExternalStore snapshot is\n // reference-stable between unrelated events.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n getFieldErrorsByPath.bind(null, form, path)\n );\n}\n\nexport function useIsDirty(form: Form): boolean {\n // Dirty state is driven by value changes, not touch state: subscribe to\n // 'change' so typing flips this immediately, even before a blur.\n return useWatch(form.emitter, 'change', isDirty.bind(null, form));\n}\n\n/**\n * Get dirty fields state -- object mapping each dirty field's user-facing\n * dotted path ('a.b', 'a.0.c') to true; recalculated after 'change' events\n */\nexport function useDirtyFields(form: Form): Record<string, boolean> {\n return useWatch(form.emitter, 'change', getDirtyFields.bind(null, form));\n}\n\n/**\n * Get touched fields state -- array of touched fields' user-facing dotted\n * paths ('a.b', 'a.0.c'); recalculated after 'touched' events\n */\nexport function useTouchedFields(form: Form): string[] {\n return useWatch(form.emitter, 'touched', getTouchedFields.bind(null, form));\n}\n\nexport function useHasErrors(form: Form): boolean {\n return useWatch(form.emitter, 'errors', hasErrors.bind(null, form));\n}\n\nexport function useIsSubmitting(form: Form): boolean {\n return useWatch(form.emitter, 'submitting', () => form.isSubmitting);\n}\n\n/**\n * Get whether the form accepts a submit right now:\n * `!isSubmitting && !hasErrors`. This is the single flag a submit\n * button's `disabled` prop wants — it is `false` for the whole async\n * `onSubmit` span (not just the validation pass) and whenever any field\n * holds an error (client validation or server backfill), replacing the\n * hand-rolled `useHasErrors(form) || useIsSubmitting(form)` pair.\n * Deliberately no dirty or validating semantics: an untouched-but-clean\n * form can submit.\n */\nexport function useCanSubmit(form: Form): boolean {\n const {emitter} = form;\n // canSubmit folds two events into one boolean: error writes\n // ('errors') and submit-state flips ('submitting'). useWatch subscribes\n // to a single event, so subscribe to both through useWatchCore — the\n // snapshot recomputes on either wake and re-renders only when the\n // boolean itself flips, so unrelated single-field error churn costs no\n // extra render (the same granularity useHasErrors already has).\n const subscribeFactory = useCallback(\n (invalidate: () => void) => {\n const offErrors = on(emitter, 'errors', invalidate);\n const offSubmitting = on(emitter, 'submitting', invalidate);\n return () => {\n offErrors();\n offSubmitting();\n };\n },\n [emitter]\n );\n return useWatchCore(\n subscribeFactory,\n () => !form.isSubmitting && !hasErrors(form)\n );\n}\n\nexport function useSubmitCount(form: Form): number {\n return useWatch(form.emitter, 'submitCount', () => form.submitCount);\n}\n","import {createContext, createElement, useContext, type ReactNode} from 'react';\nimport {\n useFieldCore,\n type UseFieldOptions,\n type UseFieldResult\n} from './hooks/field';\nimport {\n useFieldArrayCore,\n useFieldArrayItemCore,\n type UseFieldArrayResult,\n type UseFieldArrayItemResult\n} from './hooks/fieldArray';\nimport type {Form} from './form';\nimport type {Name} from './path';\nimport type {FieldPath} from './types';\n\nexport const FormContext = createContext<any>(null);\n\nexport const FormProvider = FormContext.Provider;\n\n/**\n * Read the form from the module-level {@link FormContext}. Pass the values\n * shape — `useFormContext<Values>()` — to get a fully typed `Form<Values>`\n * headless API; the `any` default keeps untyped call sites compiling.\n *\n * For multiple forms in one subtree use {@link createFormContext} instead.\n *\n * @throws when no `<FormProvider>` is mounted above the call site.\n */\nexport function useFormContext<T extends Record<string, any> = any>(): Form<T> {\n const form = useContext(FormContext);\n if (!form) throw new Error('no form provided');\n return form;\n}\n\n/**\n * Create an isolated bundle of form-context bindings: its own React context\n * plus `useField` / `useFieldArray` / `useFieldArrayItem` /\n * `useFormContext` hooks that resolve their form from it.\n *\n * Why: the module-level {@link FormContext} works fine for a single form per\n * subtree, but nesting two forms (or reusing a component inside a different\n * form) makes them fight over one context. Calling this factory once per app\n * area — `const Ctx = createFormContext<Values>()` — fixes the value shape\n * (`Ctx.useField({name: 'user.name'})` gets its `name` constrained by\n * `FieldPath<Values>` and its `value` typed accordingly), so call sites stop\n * hand-writing generics, and each instance's Provider scopes a strictly\n * separate form. The bundle also carries its raw React context\n * (`Ctx.context`) so `<Form context={Ctx.context}>` can provide into it.\n */\nexport function createFormContext<TValues extends Record<string, any> = any>() {\n const Context = createContext<Form<TValues> | null>(null);\n\n // A `form`-prop wrapper instead of exposing Context.Provider directly:\n // callers shouldn't have to know about the raw `value` prop shape.\n function FormProvider({\n form,\n children\n }: {\n form: Form<TValues>;\n children: ReactNode;\n }): ReactNode {\n return createElement(Context.Provider, {value: form}, children);\n }\n\n function useFormContext(): Form<TValues> {\n const form = useContext(Context);\n if (!form) throw new Error('no form provided');\n return form;\n }\n\n function useField<TPath extends FieldPath<TValues> | Name = Name>(\n // The bare `{name: TPath}` member keeps `name` a direct inference site\n // for TPath instead of routing it through the mapped Omit type.\n // `form` is omitted on purpose — the form always comes from this\n // factory's own Context.\n options: {name: TPath} & Omit<UseFieldOptions<TValues, TPath>, 'form'>\n ): UseFieldResult<TValues, TPath> {\n return useFieldCore(options as UseFieldOptions<TValues, TPath>, Context);\n }\n\n function useFieldArray(options: {\n name: FieldPath<TValues> | Name;\n }): UseFieldArrayResult {\n return useFieldArrayCore(options as {name: Name}, Context);\n }\n\n function useFieldArrayItem<TValue = any>(options: {\n name: FieldPath<TValues> | Name;\n id: string;\n }): UseFieldArrayItemResult<TValue> {\n return useFieldArrayItemCore(options as {name: Name; id: string}, Context);\n }\n\n // The raw React context, for `<Form context={...}>`: the component keeps\n // its submit machinery while providing into this instance's private\n // context, so the bound hooks above resolve the form it manages.\n return {\n context: Context,\n FormProvider,\n useFormContext,\n useField,\n useFieldArray,\n useFieldArrayItem\n };\n}\n\nexport const CheckboxGroupContext = createContext<any>(null);\n\nexport const CheckboxGroupProvider = CheckboxGroupContext.Provider;\n\nexport function useCheckboxGroupContext(): any {\n const group = useContext(CheckboxGroupContext);\n if (!group) throw new Error('no group provided');\n return group;\n}\n","import * as React from 'react';\nimport {useState} from 'react';\nimport type {ReactNode} from 'react';\n\n/** Nodes deeper than this start collapsed. */\nconst DEFAULT_OPEN_DEPTH = 1;\n\n/**\n * Read-only inspection: the tree never mutates form state, so structural\n * sharing of the inspected value is safe and re-renders stay cheap.\n */\ninterface JsonNodeProps {\n /** Property name (or array index) rendering before the value. */\n name?: string | number;\n /** Value to render. */\n value: unknown;\n /** Current nesting depth (root is 0). */\n depth?: number;\n}\n\n/**\n * One line of the tree: either a collapsible container row\n * (`▸ key: {`) or a leaf (`key: value`).\n */\nfunction JsonNode({name, value, depth = 0}: JsonNodeProps) {\n const [open, setOpen] = useState(depth <= DEFAULT_OPEN_DEPTH);\n\n const label =\n name === undefined ? null : (\n <>\n <span className=\"rf0-dt-key\">{String(name)}</span>\n <span className=\"rf0-dt-punct\">: </span>\n </>\n );\n\n if (value !== null && typeof value === 'object') {\n const isArray = Array.isArray(value);\n const entries: Array<[string | number, unknown]> = isArray\n ? (value as unknown[]).map((v, i) => [i, v])\n : Object.entries(value as Record<string, unknown>);\n const openBracket = isArray ? '[' : '{';\n const closeBracket = isArray ? ']' : '}';\n const summary = open\n ? ''\n : `${openBracket}…${closeBracket} ${entries.length}`;\n\n return (\n <div className=\"rf0-dt-row\" style={{paddingLeft: depth * 12}}>\n <button\n type=\"button\"\n className=\"rf0-dt-node-toggle\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"rf0-dt-caret\">{open ? '▾' : '▸'}</span>\n {label}\n <span className=\"rf0-dt-punct\">{open ? openBracket : summary}</span>\n </button>\n {open && (\n <>\n {entries.map(([k, v]) => (\n <JsonNode key={String(k)} name={k} value={v} depth={depth + 1} />\n ))}\n <span className=\"rf0-dt-punct\" style={{paddingLeft: depth * 12}}>\n {closeBracket}\n </span>\n </>\n )}\n </div>\n );\n }\n\n return (\n <span\n className=\"rf0-dt-row\"\n style={{paddingLeft: depth * 12, display: 'block'}}\n >\n {label}\n <Primitive value={value} />\n </span>\n );\n}\n\n/** Render a primitive leaf with terminal-style type coloring. */\nfunction Primitive({value}: {value: unknown}): ReactNode {\n if (value === undefined)\n return <span className=\"rf0-dt-null\">undefined</span>;\n if (value === null) return <span className=\"rf0-dt-null\">null</span>;\n if (typeof value === 'string')\n return <span className=\"rf0-dt-string\">"{value}"</span>;\n if (typeof value === 'boolean')\n return <span className=\"rf0-dt-boolean\">{String(value)}</span>;\n return <span className=\"rf0-dt-number\">{String(value)}</span>;\n}\n\nexport default JsonNode;\n","/**\n * Stylesheet for the Devtools panel.\n *\n * Zero runtime dependencies by design: a single CSS string injected once\n * into <head> (idempotent across module reloads and multiple bundles).\n *\n * Aesthetic: instrument panel / terminal — near-black layers, monospace\n * stack, dense rows, hairline borders. Semantic colors only: error red,\n * success green, neutral gray, with one dim amber accent for the active\n * tab indicator and the collapsed badge.\n */\n\nconst CSS = `\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n`;\n\nconst STYLE_ID = 'react-f0rm-devtools-style';\n\n/**\n * Inject the panel stylesheet into <head>. Idempotent: repeated calls\n * (module reloads, HMR, multiple Devtools mounts) never duplicate the\n * <style> element. No-ops outside a DOM environment (SSR).\n */\nexport function injectDevtoolsStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\ninjectDevtoolsStyles();\n","import * as React from 'react';\nimport {useContext, useId, useState} from 'react';\nimport type {KeyboardEvent} from 'react';\nimport {FormContext} from '../context';\nimport {getErrors, getValues, reset, trigger} from '../form';\nimport type {FieldErrorEntry, Form} from '../form';\nimport {\n useDirtyFields,\n useIsSubmitting,\n useSubmitCount,\n useTouchedFields,\n useWatch\n} from '../hooks/form';\nimport JsonTree from './JsonTree';\nimport './styles';\n\n/** Corner the panel docks to. */\nexport type DevtoolsPosition =\n 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left';\n\n/** Props for {@link Devtools}. */\nexport interface DevtoolsProps<T extends Record<string, any> = any> {\n /**\n * Form instance to inspect. When omitted, the panel reads the closest\n * `<Form>` / FormProvider ancestor and throws if there is none.\n */\n form?: Form<T>;\n /** Corner to dock the panel in. Defaults to `'top-right'`. */\n position?: DevtoolsPosition;\n}\n\ntype TabId = 'values' | 'errors' | 'touched' | 'dirty';\n\nconst TABS: TabId[] = ['values', 'errors', 'touched', 'dirty'];\n\n/** Status chip class for the submit-successful indicator. */\nfunction submitStatusClass(\n isSubmitSuccessful: boolean | undefined\n): string | undefined {\n if (isSubmitSuccessful === undefined) return undefined;\n return isSubmitSuccessful ? 'rf0-dt-ok' : 'rf0-dt-err';\n}\n\n/** Count primitive leaves of an inspected value tree. */\nfunction countLeaves(value: unknown): number {\n if (value === null || typeof value !== 'object') return 1;\n let count = 0;\n for (const v of Object.values(value as Record<string, unknown>)) {\n count += countLeaves(v);\n }\n return count;\n}\n\n/**\n * Live form inspector — a floating instrument panel for development.\n *\n * Renders four tabs (values / errors / touched / dirty), a submit status\n * strip (isSubmitting, submitCount, isSubmitSuccessful) and two actions:\n * Reset and Validate (full `trigger`). All state is read through the\n * library's own watch hooks, so the panel updates in real time without\n * participating in validation or submit flows. Docked at a corner,\n * collapsible to a small badge; fully keyboard operable.\n *\n * Ship it from the dedicated `react-f0rm/devtools` entry — it is never\n * re-exported by the main entry, so production bundles stay untouched.\n */\nexport default function Devtools<T extends Record<string, any> = any>({\n form,\n position = 'top-right'\n}: DevtoolsProps<T>) {\n const contextForm = useContext(FormContext);\n const f = form ?? contextForm;\n if (!f) {\n throw new Error(\n '<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.'\n );\n }\n\n const [open, setOpen] = useState(true);\n const [tab, setTab] = useState<TabId>('values');\n const idPrefix = useId().replace(/[^a-zA-Z0-9-]/g, '');\n\n // Live snapshots, straight through the public watch surface.\n const values = useWatch(f.emitter, 'change', getValues.bind(null, f));\n const errors = useWatch<FieldErrorEntry[]>(\n f.emitter,\n 'errors',\n getErrors.bind(null, f)\n );\n const touched = useTouchedFields(f);\n const dirty = useDirtyFields(f);\n const isSubmitting = useIsSubmitting(f);\n const submitCount = useSubmitCount(f);\n const isSubmitSuccessful = useWatch(\n f.emitter,\n 'submitSuccessful',\n () => f.isSubmitSuccessful\n );\n\n if (!open) {\n return (\n <button\n type=\"button\"\n className={`rf0-dt-badge rf0-dt-badge--${position}${\n errors.length > 0 ? ' rf0-dt-badge--has-errors' : ''\n }`}\n aria-expanded={false}\n aria-label={`Open react-f0rm devtools (${errors.length} errors)`}\n onClick={() => setOpen(true)}\n >\n f0\n <span className=\"rf0-dt-dot\" />\n </button>\n );\n }\n\n const counts: Record<TabId, number> = {\n values: countLeaves(values),\n errors: errors.length,\n touched: touched.length,\n dirty: Object.keys(dirty).length\n };\n\n /** Arrow-key tab navigation (buttons stay click/Enter/Space operable). */\n const onTabKeyDown = (e: KeyboardEvent) => {\n const deltas: Record<string, number> = {\n ArrowRight: 1,\n ArrowLeft: -1\n };\n const delta = deltas[e.key];\n if (!delta) return;\n e.preventDefault();\n const next = TABS[(TABS.indexOf(tab) + delta + TABS.length) % TABS.length];\n setTab(next);\n document.getElementById(`${idPrefix}-tab-${next}`)?.focus();\n };\n\n return (\n <section\n className={`rf0-dt rf0-dt--${position}`}\n aria-label=\"react-f0rm devtools\"\n >\n <header className=\"rf0-dt-header\">\n <span className=\"rf0-dt-title\">react-f0rm</span>\n <button\n type=\"button\"\n className=\"rf0-dt-headerbtn\"\n aria-label=\"Collapse devtools\"\n onClick={() => setOpen(false)}\n >\n –\n </button>\n </header>\n\n <div\n className=\"rf0-dt-tablist\"\n role=\"tablist\"\n aria-label=\"Form state\"\n tabIndex={-1}\n onKeyDown={onTabKeyDown}\n >\n {TABS.map(id => (\n <button\n key={id}\n id={`${idPrefix}-tab-${id}`}\n type=\"button\"\n role=\"tab\"\n className={`rf0-dt-tab${id === 'errors' ? ' rf0-dt-tab--danger' : ''}`}\n aria-selected={tab === id}\n aria-controls={`${idPrefix}-panel-${id}`}\n tabIndex={tab === id ? 0 : -1}\n onClick={() => setTab(id)}\n >\n {id}\n <span className=\"rf0-dt-tab-count\">{counts[id]}</span>\n </button>\n ))}\n </div>\n\n <div\n id={`${idPrefix}-panel-${tab}`}\n role=\"tabpanel\"\n aria-labelledby={`${idPrefix}-tab-${tab}`}\n className=\"rf0-dt-panel\"\n >\n {tab === 'values' && <JsonTree value={values} />}\n {tab === 'errors' &&\n (errors.length === 0 ? (\n <p className=\"rf0-dt-empty\">no errors</p>\n ) : (\n errors.map(({path, type, message}, index) => (\n // Same path can hold several errors now; index keeps keys\n // unique without changing what is rendered (messages may\n // legitimately repeat for one path).\n\n <div key={`${path}:${index}`} className=\"rf0-dt-item\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg\">{message}</span>\n <span className=\"rf0-dt-item-tag\">{type}</span>\n </div>\n ))\n ))}\n {tab === 'touched' &&\n (touched.length === 0 ? (\n <p className=\"rf0-dt-empty\">no touched fields</p>\n ) : (\n touched.map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--touched\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n </div>\n ))\n ))}\n {tab === 'dirty' &&\n (Object.keys(dirty).length === 0 ? (\n <p className=\"rf0-dt-empty\">no dirty fields</p>\n ) : (\n Object.keys(dirty).map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--dirty\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg rf0-dt-item-msg--ok\">\n changed\n </span>\n </div>\n ))\n ))}\n </div>\n\n <p className=\"rf0-dt-status\" aria-live=\"polite\">\n <span className={isSubmitting ? 'rf0-dt-on' : undefined}>\n submitting <b>{String(isSubmitting)}</b>\n </span>\n <span>\n submits <b>{submitCount}</b>\n </span>\n <span className={submitStatusClass(isSubmitSuccessful)}>\n ok{' '}\n <b>\n {isSubmitSuccessful === undefined\n ? '–'\n : String(isSubmitSuccessful)}\n </b>\n </span>\n </p>\n\n <div className=\"rf0-dt-actions\">\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => reset(f, f.initialValues)}\n >\n Reset\n </button>\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => trigger(f)}\n >\n Validate\n </button>\n </div>\n </section>\n );\n}\n"],"names":["process","env","NODE_ENV","shimModule","exports","React","require$$0","objectIs","Object","is","x","y","useState","useEffect","useLayoutEffect","useDebugValue","checkIfSnapshotChanged","inst","latestGetSnapshot","getSnapshot","value","nextValue","error","shim","window","document","createElement","subscribe","_useState","forceUpdate","useSyncExternalStoreShim_production","useSyncExternalStore","__REACT_DEVTOOLS_GLOBAL_HOOK__","registerInternalModuleStart","Error","didWarnOld18Alpha","didWarnUncachedGetSnapshot","startTransition","console","cachedValue","useSyncExternalStoreShim_development","registerInternalModuleStop","useWatch","emitter","event","getter","subscribeFactory","cacheRef","useRef","current","hasValue","cache","getterRef","useCallback","notify","useWatchCore","invalidate","on","FormContext","createContext","Provider","JsonNode","name","depth","open","setOpen","label","Fragment","className","String","isArray","Array","entries","map","v","i","openBracket","closeBracket","summary","length","style","paddingLeft","type","onClick","k","key","display","Primitive","STYLE_ID","getElementById","id","textContent","head","appendChild","injectDevtoolsStyles","TABS","submitStatusClass","isSubmitSuccessful","countLeaves","count","values","form","position","contextForm","useContext","f","tab","setTab","idPrefix","useId","replace","getValues","bind","errors","getErrors","touched","getTouchedFields","useTouchedFields","dirty","getDirtyFields","useDirtyFields","isSubmitting","useIsSubmitting","submitCount","useSubmitCount","counts","keys","role","tabIndex","onKeyDown","e","delta","ArrowRight","ArrowLeft","preventDefault","next","indexOf","focus","JsonTree","path","message","index","reset","initialValues","trigger"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|