attaform 0.27.4 → 0.27.6
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/dist/abstract.cjs +2 -2
- package/dist/abstract.d.cts +4 -4
- package/dist/abstract.d.mts +4 -4
- package/dist/abstract.d.ts +4 -4
- package/dist/abstract.mjs +2 -2
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/fingerprint2.cjs +1 -1
- package/dist/chunks/fingerprint2.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +3 -3
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/plugins/attaform.cjs +1 -1
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.BrVzB14N.mjs → attaform.B1ZYt2Li.mjs} +4 -4
- package/dist/shared/{attaform.BrVzB14N.mjs.map → attaform.B1ZYt2Li.mjs.map} +1 -1
- package/dist/shared/{attaform.C9arOC5p.cjs → attaform.B9Wyhfro.cjs} +5 -5
- package/dist/shared/{attaform.C9arOC5p.cjs.map → attaform.B9Wyhfro.cjs.map} +1 -1
- package/dist/shared/{attaform.C925L0WA.d.mts → attaform.BGDV42hI.d.cts} +1 -1
- package/dist/shared/{attaform.BBaMFa-k.cjs → attaform.Biytrv0-.cjs} +3 -3
- package/dist/shared/{attaform.CgsQZdy1.mjs.map → attaform.Biytrv0-.cjs.map} +1 -1
- package/dist/shared/{attaform.DKtXMaJB.d.ts → attaform.CHlJLAiv.d.mts} +18 -2
- package/dist/shared/{attaform.CuUx4jWI.mjs → attaform.CKH2lGdL.mjs} +90 -9
- package/dist/shared/attaform.CKH2lGdL.mjs.map +1 -0
- package/dist/shared/{attaform.a1GnhjVk.mjs → attaform.CoR4mWfv.mjs} +3 -3
- package/dist/shared/{attaform.a1GnhjVk.mjs.map → attaform.CoR4mWfv.mjs.map} +1 -1
- package/dist/shared/{attaform.CX2NHAFs.cjs → attaform.D6tk90Rc.cjs} +3 -3
- package/dist/shared/{attaform.CX2NHAFs.cjs.map → attaform.D6tk90Rc.cjs.map} +1 -1
- package/dist/shared/{attaform.B3spfqIA.cjs → attaform.D8kliaKw.cjs} +4 -4
- package/dist/shared/{attaform.B3spfqIA.cjs.map → attaform.D8kliaKw.cjs.map} +1 -1
- package/dist/shared/{attaform.CnvbLiIO.d.cts → attaform.DLknk9nF.d.mts} +1 -1
- package/dist/shared/{attaform.shkNCMEz.mjs → attaform.DMxM2kol.mjs} +4 -4
- package/dist/shared/{attaform.shkNCMEz.mjs.map → attaform.DMxM2kol.mjs.map} +1 -1
- package/dist/shared/{attaform.CC4KCx_q.mjs → attaform.DYEr28Pt.mjs} +5 -5
- package/dist/shared/{attaform.CC4KCx_q.mjs.map → attaform.DYEr28Pt.mjs.map} +1 -1
- package/dist/shared/{attaform.DXwJVUNS.cjs → attaform.DdQN7sgN.cjs} +4 -4
- package/dist/shared/{attaform.DXwJVUNS.cjs.map → attaform.DdQN7sgN.cjs.map} +1 -1
- package/dist/shared/{attaform.BIPSHY5U.d.cts → attaform.DniawQLP.d.cts} +68 -11
- package/dist/shared/{attaform.BIPSHY5U.d.mts → attaform.DniawQLP.d.mts} +68 -11
- package/dist/shared/{attaform.BIPSHY5U.d.ts → attaform.DniawQLP.d.ts} +68 -11
- package/dist/shared/{attaform.Ib2seKUE.cjs → attaform.DrvMscNX.cjs} +90 -9
- package/dist/shared/attaform.DrvMscNX.cjs.map +1 -0
- package/dist/shared/{attaform.Cjh6b6xU.d.cts → attaform.F2Q8iv38.d.ts} +18 -2
- package/dist/shared/{attaform.gj8dtOlx.d.mts → attaform.FmqKO1Ex.d.cts} +18 -2
- package/dist/shared/{attaform.C8zVmank.d.ts → attaform.Fp3sCrVs.d.ts} +1 -1
- package/dist/shared/{attaform.CgsQZdy1.mjs → attaform.nMaz2C5P.mjs} +3 -3
- package/dist/shared/{attaform.BBaMFa-k.cjs.map → attaform.nMaz2C5P.mjs.map} +1 -1
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +3 -3
- package/dist/zod-v3.d.mts +3 -3
- package/dist/zod-v3.d.ts +3 -3
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +6 -6
- package/dist/zod-v4.d.mts +6 -6
- package/dist/zod-v4.d.ts +6 -6
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +3 -3
- package/dist/zod.d.cts +3 -3
- package/dist/zod.d.mts +3 -3
- package/dist/zod.d.ts +3 -3
- package/dist/zod.mjs +3 -3
- package/package.json +1 -1
- package/dist/shared/attaform.CuUx4jWI.mjs.map +0 -1
- package/dist/shared/attaform.Ib2seKUE.cjs.map +0 -1
|
@@ -2501,9 +2501,16 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2501
2501
|
* but exposes no single inner control to register -- a composite widget,
|
|
2502
2502
|
* or none found. The directive calls it on mount / unmount; distinct from
|
|
2503
2503
|
* the SSR-only `markConnectedOptimistically`.
|
|
2504
|
+
*
|
|
2505
|
+
* `hostEl` is the host root element, recorded on connect as the field's
|
|
2506
|
+
* focus-first-error anchor: a no-latch host registers no control, so it
|
|
2507
|
+
* would otherwise be invisible to `focusFirstError` / `scrollToFirstError`
|
|
2508
|
+
* (which walk the registered-element set). On an invalid submit the error
|
|
2509
|
+
* walk resolves the host root to its first focusable descendant. The
|
|
2510
|
+
* directive passes the same element on both edges.
|
|
2504
2511
|
* @internal
|
|
2505
2512
|
*/
|
|
2506
|
-
markHostConnected: (connected: boolean) => void;
|
|
2513
|
+
markHostConnected: (connected: boolean, hostEl: HTMLElement) => void;
|
|
2507
2514
|
/**
|
|
2508
2515
|
* Mark this field focused (`true`) or blurred (`false`) for a `v-register`
|
|
2509
2516
|
* component host with no single latched control -- a composite widget whose
|
|
@@ -3630,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
|
|
|
3630
3637
|
*/
|
|
3631
3638
|
<P extends string>(path: P): FieldCallResult<Form, P>;
|
|
3632
3639
|
/**
|
|
3633
|
-
* Tuple-segment form.
|
|
3634
|
-
*
|
|
3635
|
-
*
|
|
3636
|
-
*
|
|
3640
|
+
* Tuple-segment form. The segment-array spelling of the string
|
|
3641
|
+
* call-form above, for a path built from variables — the two resolve
|
|
3642
|
+
* to the same proxy, so `form.fields(['users', i, 'email'])` and
|
|
3643
|
+
* `form.fields(\`users.${i}.email\`)` are interchangeable.
|
|
3644
|
+
*
|
|
3645
|
+
* Resolves to that path's `FieldState`, which at a container is its
|
|
3646
|
+
* rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
|
|
3647
|
+
* exist regardless of the value type), exactly as the string form
|
|
3648
|
+
* does. Note this is the CALL form: it hands back the path's state,
|
|
3649
|
+
* not a drillable sub-proxy. To walk into a container's children use
|
|
3650
|
+
* dot/bracket access (`form.fields.users[i].email`), which is what
|
|
3651
|
+
* descends.
|
|
3637
3652
|
*/
|
|
3638
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)):
|
|
3653
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
|
|
3639
3654
|
/**
|
|
3640
3655
|
* Dynamic-array fallback for callers passing `Path`-typed (runtime)
|
|
3641
3656
|
* segment arrays — e.g. forwarding `RegisterValue.segments` to
|
|
@@ -4576,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4576
4591
|
*/
|
|
4577
4592
|
applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
|
|
4578
4593
|
/**
|
|
4579
|
-
* Programmatically mark fields as touched — the
|
|
4580
|
-
*
|
|
4581
|
-
* the gap when fields are populated without a DOM gesture (post-
|
|
4582
|
-
* import, paste, autofill, server-seeded values you want to
|
|
4583
|
-
* validate immediately).
|
|
4594
|
+
* Programmatically mark fields as `touched` — the descriptive
|
|
4595
|
+
* "this field was visited" flag.
|
|
4584
4596
|
*
|
|
4585
4597
|
* ```ts
|
|
4586
4598
|
* form.touch('email') // one leaf
|
|
@@ -4589,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4589
4601
|
* form.touch() // every leaf in the form
|
|
4590
4602
|
* ```
|
|
4591
4603
|
*
|
|
4604
|
+
* **This does not reveal errors under the default display
|
|
4605
|
+
* heuristic.** `touched` also flips on a bare focus → blur with no
|
|
4606
|
+
* edit, so the library-default gate deliberately ignores it and
|
|
4607
|
+
* reads `blurredAfterInteraction` instead — the stricter bit that
|
|
4608
|
+
* only a blur *following* an edit sets. Reach for `touch()` when a
|
|
4609
|
+
* custom `getDisplayState` reducer or your analytics reads
|
|
4610
|
+
* `touched`; reach for {@link UseFormReturnType.interact} to make
|
|
4611
|
+
* seeded or imported values surface their errors.
|
|
4612
|
+
*
|
|
4592
4613
|
* Pure flag write — does not mutate value, focused, blurred, or
|
|
4593
4614
|
* trigger validation. Idempotent: re-calling on an already-touched
|
|
4594
4615
|
* field is a no-op. Touched is sticky-true; pair with
|
|
4595
4616
|
* `form.reset()` / `form.resetField()` to clear.
|
|
4596
4617
|
*/
|
|
4597
4618
|
touch: (path?: FlatPath<Form> | (string | number)[]) => void;
|
|
4619
|
+
/**
|
|
4620
|
+
* Simulate a complete focus → edit → blur over every leaf under
|
|
4621
|
+
* `path` (the whole form when omitted), so values that arrived
|
|
4622
|
+
* without a DOM gesture reveal their errors under the default
|
|
4623
|
+
* display heuristic.
|
|
4624
|
+
*
|
|
4625
|
+
* ```ts
|
|
4626
|
+
* form.interact('profile') // arm one subtree
|
|
4627
|
+
* await form.interact(['members', 2]) // errors committed on resolve
|
|
4628
|
+
* form.interact() // every leaf in the form
|
|
4629
|
+
* ```
|
|
4630
|
+
*
|
|
4631
|
+
* Flips the full interaction ladder (`touched`, `interacted`, and
|
|
4632
|
+
* `blurredAfterInteraction`) and runs the subtree's validation, so
|
|
4633
|
+
* the default gate — `submissionAttempts > 0 ||
|
|
4634
|
+
* blurredAfterInteraction` — opens through its front door. Use it
|
|
4635
|
+
* to arm one region's errors without a form-wide submit, which
|
|
4636
|
+
* would light up every other field on the page: a field-array row
|
|
4637
|
+
* edited in a modal, a server-seeded section, a pasted import.
|
|
4638
|
+
*
|
|
4639
|
+
* Walks schema leaves, so it reaches fields that are currently
|
|
4640
|
+
* `v-if`'d away or were never mounted; the flags are sticky, so
|
|
4641
|
+
* such a subtree stays revealed when it remounts.
|
|
4642
|
+
*
|
|
4643
|
+
* Flags land synchronously — the returned promise resolves once the
|
|
4644
|
+
* subtree's validation has committed, so an awaiting caller can read
|
|
4645
|
+
* `showErrors` / `errors` immediately after. It never rejects, and
|
|
4646
|
+
* ignoring it is fine.
|
|
4647
|
+
*
|
|
4648
|
+
* No-op on a disabled form, matching every other interaction-
|
|
4649
|
+
* lifecycle write. Does not mutate value, `focused`, or `blurred`:
|
|
4650
|
+
* those are DOM-owned, and the display gate reads none of them.
|
|
4651
|
+
* Sticky like `touched`; pair with `form.reset()` /
|
|
4652
|
+
* `form.resetField()` to clear.
|
|
4653
|
+
*/
|
|
4654
|
+
interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
|
|
4598
4655
|
/**
|
|
4599
4656
|
* Append `value` to the array at `path`.
|
|
4600
4657
|
*
|
|
@@ -2501,9 +2501,16 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2501
2501
|
* but exposes no single inner control to register -- a composite widget,
|
|
2502
2502
|
* or none found. The directive calls it on mount / unmount; distinct from
|
|
2503
2503
|
* the SSR-only `markConnectedOptimistically`.
|
|
2504
|
+
*
|
|
2505
|
+
* `hostEl` is the host root element, recorded on connect as the field's
|
|
2506
|
+
* focus-first-error anchor: a no-latch host registers no control, so it
|
|
2507
|
+
* would otherwise be invisible to `focusFirstError` / `scrollToFirstError`
|
|
2508
|
+
* (which walk the registered-element set). On an invalid submit the error
|
|
2509
|
+
* walk resolves the host root to its first focusable descendant. The
|
|
2510
|
+
* directive passes the same element on both edges.
|
|
2504
2511
|
* @internal
|
|
2505
2512
|
*/
|
|
2506
|
-
markHostConnected: (connected: boolean) => void;
|
|
2513
|
+
markHostConnected: (connected: boolean, hostEl: HTMLElement) => void;
|
|
2507
2514
|
/**
|
|
2508
2515
|
* Mark this field focused (`true`) or blurred (`false`) for a `v-register`
|
|
2509
2516
|
* component host with no single latched control -- a composite widget whose
|
|
@@ -3630,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
|
|
|
3630
3637
|
*/
|
|
3631
3638
|
<P extends string>(path: P): FieldCallResult<Form, P>;
|
|
3632
3639
|
/**
|
|
3633
|
-
* Tuple-segment form.
|
|
3634
|
-
*
|
|
3635
|
-
*
|
|
3636
|
-
*
|
|
3640
|
+
* Tuple-segment form. The segment-array spelling of the string
|
|
3641
|
+
* call-form above, for a path built from variables — the two resolve
|
|
3642
|
+
* to the same proxy, so `form.fields(['users', i, 'email'])` and
|
|
3643
|
+
* `form.fields(\`users.${i}.email\`)` are interchangeable.
|
|
3644
|
+
*
|
|
3645
|
+
* Resolves to that path's `FieldState`, which at a container is its
|
|
3646
|
+
* rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
|
|
3647
|
+
* exist regardless of the value type), exactly as the string form
|
|
3648
|
+
* does. Note this is the CALL form: it hands back the path's state,
|
|
3649
|
+
* not a drillable sub-proxy. To walk into a container's children use
|
|
3650
|
+
* dot/bracket access (`form.fields.users[i].email`), which is what
|
|
3651
|
+
* descends.
|
|
3637
3652
|
*/
|
|
3638
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)):
|
|
3653
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
|
|
3639
3654
|
/**
|
|
3640
3655
|
* Dynamic-array fallback for callers passing `Path`-typed (runtime)
|
|
3641
3656
|
* segment arrays — e.g. forwarding `RegisterValue.segments` to
|
|
@@ -4576,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4576
4591
|
*/
|
|
4577
4592
|
applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
|
|
4578
4593
|
/**
|
|
4579
|
-
* Programmatically mark fields as touched — the
|
|
4580
|
-
*
|
|
4581
|
-
* the gap when fields are populated without a DOM gesture (post-
|
|
4582
|
-
* import, paste, autofill, server-seeded values you want to
|
|
4583
|
-
* validate immediately).
|
|
4594
|
+
* Programmatically mark fields as `touched` — the descriptive
|
|
4595
|
+
* "this field was visited" flag.
|
|
4584
4596
|
*
|
|
4585
4597
|
* ```ts
|
|
4586
4598
|
* form.touch('email') // one leaf
|
|
@@ -4589,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4589
4601
|
* form.touch() // every leaf in the form
|
|
4590
4602
|
* ```
|
|
4591
4603
|
*
|
|
4604
|
+
* **This does not reveal errors under the default display
|
|
4605
|
+
* heuristic.** `touched` also flips on a bare focus → blur with no
|
|
4606
|
+
* edit, so the library-default gate deliberately ignores it and
|
|
4607
|
+
* reads `blurredAfterInteraction` instead — the stricter bit that
|
|
4608
|
+
* only a blur *following* an edit sets. Reach for `touch()` when a
|
|
4609
|
+
* custom `getDisplayState` reducer or your analytics reads
|
|
4610
|
+
* `touched`; reach for {@link UseFormReturnType.interact} to make
|
|
4611
|
+
* seeded or imported values surface their errors.
|
|
4612
|
+
*
|
|
4592
4613
|
* Pure flag write — does not mutate value, focused, blurred, or
|
|
4593
4614
|
* trigger validation. Idempotent: re-calling on an already-touched
|
|
4594
4615
|
* field is a no-op. Touched is sticky-true; pair with
|
|
4595
4616
|
* `form.reset()` / `form.resetField()` to clear.
|
|
4596
4617
|
*/
|
|
4597
4618
|
touch: (path?: FlatPath<Form> | (string | number)[]) => void;
|
|
4619
|
+
/**
|
|
4620
|
+
* Simulate a complete focus → edit → blur over every leaf under
|
|
4621
|
+
* `path` (the whole form when omitted), so values that arrived
|
|
4622
|
+
* without a DOM gesture reveal their errors under the default
|
|
4623
|
+
* display heuristic.
|
|
4624
|
+
*
|
|
4625
|
+
* ```ts
|
|
4626
|
+
* form.interact('profile') // arm one subtree
|
|
4627
|
+
* await form.interact(['members', 2]) // errors committed on resolve
|
|
4628
|
+
* form.interact() // every leaf in the form
|
|
4629
|
+
* ```
|
|
4630
|
+
*
|
|
4631
|
+
* Flips the full interaction ladder (`touched`, `interacted`, and
|
|
4632
|
+
* `blurredAfterInteraction`) and runs the subtree's validation, so
|
|
4633
|
+
* the default gate — `submissionAttempts > 0 ||
|
|
4634
|
+
* blurredAfterInteraction` — opens through its front door. Use it
|
|
4635
|
+
* to arm one region's errors without a form-wide submit, which
|
|
4636
|
+
* would light up every other field on the page: a field-array row
|
|
4637
|
+
* edited in a modal, a server-seeded section, a pasted import.
|
|
4638
|
+
*
|
|
4639
|
+
* Walks schema leaves, so it reaches fields that are currently
|
|
4640
|
+
* `v-if`'d away or were never mounted; the flags are sticky, so
|
|
4641
|
+
* such a subtree stays revealed when it remounts.
|
|
4642
|
+
*
|
|
4643
|
+
* Flags land synchronously — the returned promise resolves once the
|
|
4644
|
+
* subtree's validation has committed, so an awaiting caller can read
|
|
4645
|
+
* `showErrors` / `errors` immediately after. It never rejects, and
|
|
4646
|
+
* ignoring it is fine.
|
|
4647
|
+
*
|
|
4648
|
+
* No-op on a disabled form, matching every other interaction-
|
|
4649
|
+
* lifecycle write. Does not mutate value, `focused`, or `blurred`:
|
|
4650
|
+
* those are DOM-owned, and the display gate reads none of them.
|
|
4651
|
+
* Sticky like `touched`; pair with `form.reset()` /
|
|
4652
|
+
* `form.resetField()` to clear.
|
|
4653
|
+
*/
|
|
4654
|
+
interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
|
|
4598
4655
|
/**
|
|
4599
4656
|
* Append `value` to the array at `path`.
|
|
4600
4657
|
*
|
|
@@ -2501,9 +2501,16 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2501
2501
|
* but exposes no single inner control to register -- a composite widget,
|
|
2502
2502
|
* or none found. The directive calls it on mount / unmount; distinct from
|
|
2503
2503
|
* the SSR-only `markConnectedOptimistically`.
|
|
2504
|
+
*
|
|
2505
|
+
* `hostEl` is the host root element, recorded on connect as the field's
|
|
2506
|
+
* focus-first-error anchor: a no-latch host registers no control, so it
|
|
2507
|
+
* would otherwise be invisible to `focusFirstError` / `scrollToFirstError`
|
|
2508
|
+
* (which walk the registered-element set). On an invalid submit the error
|
|
2509
|
+
* walk resolves the host root to its first focusable descendant. The
|
|
2510
|
+
* directive passes the same element on both edges.
|
|
2504
2511
|
* @internal
|
|
2505
2512
|
*/
|
|
2506
|
-
markHostConnected: (connected: boolean) => void;
|
|
2513
|
+
markHostConnected: (connected: boolean, hostEl: HTMLElement) => void;
|
|
2507
2514
|
/**
|
|
2508
2515
|
* Mark this field focused (`true`) or blurred (`false`) for a `v-register`
|
|
2509
2516
|
* component host with no single latched control -- a composite widget whose
|
|
@@ -3630,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
|
|
|
3630
3637
|
*/
|
|
3631
3638
|
<P extends string>(path: P): FieldCallResult<Form, P>;
|
|
3632
3639
|
/**
|
|
3633
|
-
* Tuple-segment form.
|
|
3634
|
-
*
|
|
3635
|
-
*
|
|
3636
|
-
*
|
|
3640
|
+
* Tuple-segment form. The segment-array spelling of the string
|
|
3641
|
+
* call-form above, for a path built from variables — the two resolve
|
|
3642
|
+
* to the same proxy, so `form.fields(['users', i, 'email'])` and
|
|
3643
|
+
* `form.fields(\`users.${i}.email\`)` are interchangeable.
|
|
3644
|
+
*
|
|
3645
|
+
* Resolves to that path's `FieldState`, which at a container is its
|
|
3646
|
+
* rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
|
|
3647
|
+
* exist regardless of the value type), exactly as the string form
|
|
3648
|
+
* does. Note this is the CALL form: it hands back the path's state,
|
|
3649
|
+
* not a drillable sub-proxy. To walk into a container's children use
|
|
3650
|
+
* dot/bracket access (`form.fields.users[i].email`), which is what
|
|
3651
|
+
* descends.
|
|
3637
3652
|
*/
|
|
3638
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)):
|
|
3653
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
|
|
3639
3654
|
/**
|
|
3640
3655
|
* Dynamic-array fallback for callers passing `Path`-typed (runtime)
|
|
3641
3656
|
* segment arrays — e.g. forwarding `RegisterValue.segments` to
|
|
@@ -4576,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4576
4591
|
*/
|
|
4577
4592
|
applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
|
|
4578
4593
|
/**
|
|
4579
|
-
* Programmatically mark fields as touched — the
|
|
4580
|
-
*
|
|
4581
|
-
* the gap when fields are populated without a DOM gesture (post-
|
|
4582
|
-
* import, paste, autofill, server-seeded values you want to
|
|
4583
|
-
* validate immediately).
|
|
4594
|
+
* Programmatically mark fields as `touched` — the descriptive
|
|
4595
|
+
* "this field was visited" flag.
|
|
4584
4596
|
*
|
|
4585
4597
|
* ```ts
|
|
4586
4598
|
* form.touch('email') // one leaf
|
|
@@ -4589,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4589
4601
|
* form.touch() // every leaf in the form
|
|
4590
4602
|
* ```
|
|
4591
4603
|
*
|
|
4604
|
+
* **This does not reveal errors under the default display
|
|
4605
|
+
* heuristic.** `touched` also flips on a bare focus → blur with no
|
|
4606
|
+
* edit, so the library-default gate deliberately ignores it and
|
|
4607
|
+
* reads `blurredAfterInteraction` instead — the stricter bit that
|
|
4608
|
+
* only a blur *following* an edit sets. Reach for `touch()` when a
|
|
4609
|
+
* custom `getDisplayState` reducer or your analytics reads
|
|
4610
|
+
* `touched`; reach for {@link UseFormReturnType.interact} to make
|
|
4611
|
+
* seeded or imported values surface their errors.
|
|
4612
|
+
*
|
|
4592
4613
|
* Pure flag write — does not mutate value, focused, blurred, or
|
|
4593
4614
|
* trigger validation. Idempotent: re-calling on an already-touched
|
|
4594
4615
|
* field is a no-op. Touched is sticky-true; pair with
|
|
4595
4616
|
* `form.reset()` / `form.resetField()` to clear.
|
|
4596
4617
|
*/
|
|
4597
4618
|
touch: (path?: FlatPath<Form> | (string | number)[]) => void;
|
|
4619
|
+
/**
|
|
4620
|
+
* Simulate a complete focus → edit → blur over every leaf under
|
|
4621
|
+
* `path` (the whole form when omitted), so values that arrived
|
|
4622
|
+
* without a DOM gesture reveal their errors under the default
|
|
4623
|
+
* display heuristic.
|
|
4624
|
+
*
|
|
4625
|
+
* ```ts
|
|
4626
|
+
* form.interact('profile') // arm one subtree
|
|
4627
|
+
* await form.interact(['members', 2]) // errors committed on resolve
|
|
4628
|
+
* form.interact() // every leaf in the form
|
|
4629
|
+
* ```
|
|
4630
|
+
*
|
|
4631
|
+
* Flips the full interaction ladder (`touched`, `interacted`, and
|
|
4632
|
+
* `blurredAfterInteraction`) and runs the subtree's validation, so
|
|
4633
|
+
* the default gate — `submissionAttempts > 0 ||
|
|
4634
|
+
* blurredAfterInteraction` — opens through its front door. Use it
|
|
4635
|
+
* to arm one region's errors without a form-wide submit, which
|
|
4636
|
+
* would light up every other field on the page: a field-array row
|
|
4637
|
+
* edited in a modal, a server-seeded section, a pasted import.
|
|
4638
|
+
*
|
|
4639
|
+
* Walks schema leaves, so it reaches fields that are currently
|
|
4640
|
+
* `v-if`'d away or were never mounted; the flags are sticky, so
|
|
4641
|
+
* such a subtree stays revealed when it remounts.
|
|
4642
|
+
*
|
|
4643
|
+
* Flags land synchronously — the returned promise resolves once the
|
|
4644
|
+
* subtree's validation has committed, so an awaiting caller can read
|
|
4645
|
+
* `showErrors` / `errors` immediately after. It never rejects, and
|
|
4646
|
+
* ignoring it is fine.
|
|
4647
|
+
*
|
|
4648
|
+
* No-op on a disabled form, matching every other interaction-
|
|
4649
|
+
* lifecycle write. Does not mutate value, `focused`, or `blurred`:
|
|
4650
|
+
* those are DOM-owned, and the display gate reads none of them.
|
|
4651
|
+
* Sticky like `touched`; pair with `form.reset()` /
|
|
4652
|
+
* `form.resetField()` to clear.
|
|
4653
|
+
*/
|
|
4654
|
+
interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
|
|
4598
4655
|
/**
|
|
4599
4656
|
* Append `value` to the array at `path`.
|
|
4600
4657
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const vue = require('vue');
|
|
4
|
-
const devtoolsShared = require('./attaform.
|
|
4
|
+
const devtoolsShared = require('./attaform.Biytrv0-.cjs');
|
|
5
5
|
const registerProtocol = require('./attaform.yUrz7C2z.cjs');
|
|
6
6
|
|
|
7
7
|
function safeAssign(target, key, value) {
|
|
@@ -626,7 +626,9 @@ function computeVerdict(field, formMeta) {
|
|
|
626
626
|
(e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
|
|
627
627
|
);
|
|
628
628
|
if (hasOwnError) return "error";
|
|
629
|
-
if (field.valid === true && field.blank !== true && field.dirty === true)
|
|
629
|
+
if (field.valid === true && field.blank !== true && (field.dirty === true || field.interacted === true)) {
|
|
630
|
+
return "success";
|
|
631
|
+
}
|
|
630
632
|
return "idle";
|
|
631
633
|
}
|
|
632
634
|
function earliestNonNull(a, b) {
|
|
@@ -2509,8 +2511,8 @@ function buildRegister(state, formInstanceId, instanceConfig) {
|
|
|
2509
2511
|
markConnectedOptimistically: () => {
|
|
2510
2512
|
state.markConnectedOptimistically(segments);
|
|
2511
2513
|
},
|
|
2512
|
-
markHostConnected: (connected) => {
|
|
2513
|
-
state.markHostConnected(segments, connected);
|
|
2514
|
+
markHostConnected: (connected, hostEl) => {
|
|
2515
|
+
state.markHostConnected(segments, connected, hostEl, formInstanceId);
|
|
2514
2516
|
},
|
|
2515
2517
|
markFocused: (focused) => {
|
|
2516
2518
|
state.markFocused(
|
|
@@ -3314,6 +3316,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3314
3316
|
const segments = pathInput === void 0 ? devtoolsShared.ROOT_PATH : devtoolsShared.canonicalizePath(pathInput).segments;
|
|
3315
3317
|
state.touchAtPath(segments);
|
|
3316
3318
|
}
|
|
3319
|
+
async function interact(pathInput) {
|
|
3320
|
+
const segments = pathInput === void 0 ? devtoolsShared.ROOT_PATH : devtoolsShared.canonicalizePath(pathInput).segments;
|
|
3321
|
+
if (!state.interactAtPath(segments)) return;
|
|
3322
|
+
try {
|
|
3323
|
+
await validateAsyncBuilt(pathInput === void 0 ? void 0 : segments);
|
|
3324
|
+
} catch {
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3317
3327
|
const focusFirstError = (options2) => {
|
|
3318
3328
|
const target = state.getFirstErrorElement(formInstanceId);
|
|
3319
3329
|
if (target === null) return false;
|
|
@@ -3460,6 +3470,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
|
|
|
3460
3470
|
scrollToFirstError: gated(scrollToFirstError),
|
|
3461
3471
|
applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
|
|
3462
3472
|
touch: gated(touch),
|
|
3473
|
+
interact: gated(interact),
|
|
3463
3474
|
get history() {
|
|
3464
3475
|
void state.activate();
|
|
3465
3476
|
return formHistory;
|
|
@@ -4068,6 +4079,25 @@ function mergeObjectKeys(target, source, path, schema) {
|
|
|
4068
4079
|
}
|
|
4069
4080
|
|
|
4070
4081
|
const isContainer = (value) => Array.isArray(value) || isPlainRecord(value);
|
|
4082
|
+
const HOST_FOCUS_TARGET_SELECTOR = [
|
|
4083
|
+
'input:not([type="hidden"])',
|
|
4084
|
+
"select",
|
|
4085
|
+
"textarea",
|
|
4086
|
+
"button",
|
|
4087
|
+
"a[href]",
|
|
4088
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
4089
|
+
'[contenteditable="true"]'
|
|
4090
|
+
].join(", ");
|
|
4091
|
+
function resolveHostFocusTarget(hostRoot) {
|
|
4092
|
+
const candidates = hostRoot.querySelectorAll(HOST_FOCUS_TARGET_SELECTOR);
|
|
4093
|
+
for (const el of candidates) {
|
|
4094
|
+
if (!el.isConnected) continue;
|
|
4095
|
+
if (el.offsetParent === null) continue;
|
|
4096
|
+
if (el.matches(":disabled")) continue;
|
|
4097
|
+
return el;
|
|
4098
|
+
}
|
|
4099
|
+
return hostRoot;
|
|
4100
|
+
}
|
|
4071
4101
|
function isHydratedFieldRecord(value) {
|
|
4072
4102
|
if (typeof value !== "object" || value === null) return false;
|
|
4073
4103
|
const r = value;
|
|
@@ -4253,6 +4283,7 @@ function createFormStore(options) {
|
|
|
4253
4283
|
const fields = vue.reactive(/* @__PURE__ */ new Map());
|
|
4254
4284
|
const elements = vue.reactive(/* @__PURE__ */ new Map());
|
|
4255
4285
|
const elementToFormInstance = /* @__PURE__ */ new WeakMap();
|
|
4286
|
+
const hostTargets = /* @__PURE__ */ new Map();
|
|
4256
4287
|
let sortedRegistrationsCache = null;
|
|
4257
4288
|
const schemaErrors = vue.reactive(/* @__PURE__ */ new Map());
|
|
4258
4289
|
const userErrors = vue.reactive(/* @__PURE__ */ new Map());
|
|
@@ -5125,6 +5156,7 @@ function createFormStore(options) {
|
|
|
5125
5156
|
record.elements.add(raw);
|
|
5126
5157
|
}
|
|
5127
5158
|
elementToFormInstance.set(element, formInstanceId);
|
|
5159
|
+
hostTargets.delete(key);
|
|
5128
5160
|
sortedRegistrationsCache = null;
|
|
5129
5161
|
const current = fields.get(key);
|
|
5130
5162
|
touchFieldRecord(key, path, {
|
|
@@ -5162,10 +5194,14 @@ function createFormStore(options) {
|
|
|
5162
5194
|
blurred: current?.blurred ?? true
|
|
5163
5195
|
});
|
|
5164
5196
|
}
|
|
5165
|
-
function markHostConnected(path, connected) {
|
|
5197
|
+
function markHostConnected(path, connected, hostEl, formInstanceId) {
|
|
5166
5198
|
const { key } = devtoolsShared.canonicalizePath(path);
|
|
5167
5199
|
const current = fields.get(key);
|
|
5168
5200
|
if (connected) {
|
|
5201
|
+
if (!elements.has(key)) {
|
|
5202
|
+
hostTargets.set(key, { path, hostRoot: hostEl, formInstanceId });
|
|
5203
|
+
sortedRegistrationsCache = null;
|
|
5204
|
+
}
|
|
5169
5205
|
if (current?.connected === true) return;
|
|
5170
5206
|
touchFieldRecord(key, path, {
|
|
5171
5207
|
connected: true,
|
|
@@ -5173,6 +5209,7 @@ function createFormStore(options) {
|
|
|
5173
5209
|
blurred: current?.blurred ?? true
|
|
5174
5210
|
});
|
|
5175
5211
|
} else {
|
|
5212
|
+
if (hostTargets.delete(key)) sortedRegistrationsCache = null;
|
|
5176
5213
|
if (current?.connected !== true) return;
|
|
5177
5214
|
touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
|
|
5178
5215
|
}
|
|
@@ -5249,6 +5286,32 @@ function createFormStore(options) {
|
|
|
5249
5286
|
);
|
|
5250
5287
|
}
|
|
5251
5288
|
}
|
|
5289
|
+
function interactAtPath(segments) {
|
|
5290
|
+
if (effectiveDisabled.value) return false;
|
|
5291
|
+
const formValue = form.value;
|
|
5292
|
+
let interactedAny = false;
|
|
5293
|
+
for (const [, entry] of originals) {
|
|
5294
|
+
if (!devtoolsShared.isPathPrefix(segments, entry.segments)) continue;
|
|
5295
|
+
if (!hasAtPath(formValue, entry.segments)) continue;
|
|
5296
|
+
interactedAny = true;
|
|
5297
|
+
const leafKey = devtoolsShared.canonicalizePath(entry.segments).key;
|
|
5298
|
+
const current = fields.get(leafKey);
|
|
5299
|
+
if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
|
|
5300
|
+
continue;
|
|
5301
|
+
}
|
|
5302
|
+
touchFieldRecord(leafKey, entry.segments, {
|
|
5303
|
+
touched: true,
|
|
5304
|
+
interacted: true,
|
|
5305
|
+
blurredAfterInteraction: true
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
if (!interactedAny && devtoolsShared.__DEV__) {
|
|
5309
|
+
console.warn(
|
|
5310
|
+
`[attaform] form.interact(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
|
|
5311
|
+
);
|
|
5312
|
+
}
|
|
5313
|
+
return interactedAny;
|
|
5314
|
+
}
|
|
5252
5315
|
function rehydrate() {
|
|
5253
5316
|
const factory = defaultValuesFactory.value;
|
|
5254
5317
|
if (factory === void 0) {
|
|
@@ -5507,18 +5570,35 @@ function createFormStore(options) {
|
|
|
5507
5570
|
function getFirstErrorElement(formInstanceId) {
|
|
5508
5571
|
sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
|
|
5509
5572
|
for (const entry of sortedRegistrationsCache) {
|
|
5510
|
-
if (
|
|
5573
|
+
if (entry.formInstanceId !== formInstanceId) continue;
|
|
5511
5574
|
if (!entry.element.isConnected) continue;
|
|
5512
5575
|
if (entry.element.offsetParent === null) continue;
|
|
5513
5576
|
if (getErrorsForPath(entry.path).length === 0) continue;
|
|
5514
|
-
|
|
5577
|
+
const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
|
|
5578
|
+
return { path: entry.path, element };
|
|
5515
5579
|
}
|
|
5516
5580
|
return null;
|
|
5517
5581
|
}
|
|
5518
5582
|
function rebuildSortedRegistrations() {
|
|
5519
5583
|
const flat = [];
|
|
5520
5584
|
for (const [, record] of elements) {
|
|
5521
|
-
for (const el of record.elements)
|
|
5585
|
+
for (const el of record.elements) {
|
|
5586
|
+
flat.push({
|
|
5587
|
+
path: record.path,
|
|
5588
|
+
element: el,
|
|
5589
|
+
host: false,
|
|
5590
|
+
formInstanceId: elementToFormInstance.get(el) ?? ""
|
|
5591
|
+
});
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
for (const [key, target] of hostTargets) {
|
|
5595
|
+
if (elements.has(key)) continue;
|
|
5596
|
+
flat.push({
|
|
5597
|
+
path: target.path,
|
|
5598
|
+
element: target.hostRoot,
|
|
5599
|
+
host: true,
|
|
5600
|
+
formInstanceId: target.formInstanceId
|
|
5601
|
+
});
|
|
5522
5602
|
}
|
|
5523
5603
|
flat.sort(
|
|
5524
5604
|
(a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
@@ -5586,6 +5666,7 @@ function createFormStore(options) {
|
|
|
5586
5666
|
markFocused,
|
|
5587
5667
|
markInteracted,
|
|
5588
5668
|
touchAtPath,
|
|
5669
|
+
interactAtPath,
|
|
5589
5670
|
markConnectedOptimistically,
|
|
5590
5671
|
markHostConnected,
|
|
5591
5672
|
isPristineAtPath,
|
|
@@ -7613,4 +7694,4 @@ exports.unset = unset;
|
|
|
7613
7694
|
exports.useAbstractForm = useAbstractForm;
|
|
7614
7695
|
exports.useRegister = useRegister;
|
|
7615
7696
|
exports.useWizard = useWizard;
|
|
7616
|
-
//# sourceMappingURL=attaform.
|
|
7697
|
+
//# sourceMappingURL=attaform.DrvMscNX.cjs.map
|