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.
Files changed (70) hide show
  1. package/dist/abstract.cjs +2 -2
  2. package/dist/abstract.d.cts +4 -4
  3. package/dist/abstract.d.mts +4 -4
  4. package/dist/abstract.d.ts +4 -4
  5. package/dist/abstract.mjs +2 -2
  6. package/dist/chunks/devtools.cjs +1 -1
  7. package/dist/chunks/devtools.mjs +1 -1
  8. package/dist/chunks/fingerprint2.cjs +1 -1
  9. package/dist/chunks/fingerprint2.mjs +1 -1
  10. package/dist/index.cjs +3 -3
  11. package/dist/index.d.cts +5 -5
  12. package/dist/index.d.mts +5 -5
  13. package/dist/index.d.ts +5 -5
  14. package/dist/index.mjs +3 -3
  15. package/dist/nuxt.d.cts +1 -1
  16. package/dist/nuxt.d.mts +1 -1
  17. package/dist/nuxt.d.ts +1 -1
  18. package/dist/runtime/plugins/attaform.cjs +1 -1
  19. package/dist/runtime/plugins/attaform.mjs +1 -1
  20. package/dist/shared/{attaform.BrVzB14N.mjs → attaform.B1ZYt2Li.mjs} +4 -4
  21. package/dist/shared/{attaform.BrVzB14N.mjs.map → attaform.B1ZYt2Li.mjs.map} +1 -1
  22. package/dist/shared/{attaform.C9arOC5p.cjs → attaform.B9Wyhfro.cjs} +5 -5
  23. package/dist/shared/{attaform.C9arOC5p.cjs.map → attaform.B9Wyhfro.cjs.map} +1 -1
  24. package/dist/shared/{attaform.C925L0WA.d.mts → attaform.BGDV42hI.d.cts} +1 -1
  25. package/dist/shared/{attaform.BBaMFa-k.cjs → attaform.Biytrv0-.cjs} +3 -3
  26. package/dist/shared/{attaform.CgsQZdy1.mjs.map → attaform.Biytrv0-.cjs.map} +1 -1
  27. package/dist/shared/{attaform.DKtXMaJB.d.ts → attaform.CHlJLAiv.d.mts} +18 -2
  28. package/dist/shared/{attaform.CuUx4jWI.mjs → attaform.CKH2lGdL.mjs} +90 -9
  29. package/dist/shared/attaform.CKH2lGdL.mjs.map +1 -0
  30. package/dist/shared/{attaform.a1GnhjVk.mjs → attaform.CoR4mWfv.mjs} +3 -3
  31. package/dist/shared/{attaform.a1GnhjVk.mjs.map → attaform.CoR4mWfv.mjs.map} +1 -1
  32. package/dist/shared/{attaform.CX2NHAFs.cjs → attaform.D6tk90Rc.cjs} +3 -3
  33. package/dist/shared/{attaform.CX2NHAFs.cjs.map → attaform.D6tk90Rc.cjs.map} +1 -1
  34. package/dist/shared/{attaform.B3spfqIA.cjs → attaform.D8kliaKw.cjs} +4 -4
  35. package/dist/shared/{attaform.B3spfqIA.cjs.map → attaform.D8kliaKw.cjs.map} +1 -1
  36. package/dist/shared/{attaform.CnvbLiIO.d.cts → attaform.DLknk9nF.d.mts} +1 -1
  37. package/dist/shared/{attaform.shkNCMEz.mjs → attaform.DMxM2kol.mjs} +4 -4
  38. package/dist/shared/{attaform.shkNCMEz.mjs.map → attaform.DMxM2kol.mjs.map} +1 -1
  39. package/dist/shared/{attaform.CC4KCx_q.mjs → attaform.DYEr28Pt.mjs} +5 -5
  40. package/dist/shared/{attaform.CC4KCx_q.mjs.map → attaform.DYEr28Pt.mjs.map} +1 -1
  41. package/dist/shared/{attaform.DXwJVUNS.cjs → attaform.DdQN7sgN.cjs} +4 -4
  42. package/dist/shared/{attaform.DXwJVUNS.cjs.map → attaform.DdQN7sgN.cjs.map} +1 -1
  43. package/dist/shared/{attaform.BIPSHY5U.d.cts → attaform.DniawQLP.d.cts} +68 -11
  44. package/dist/shared/{attaform.BIPSHY5U.d.mts → attaform.DniawQLP.d.mts} +68 -11
  45. package/dist/shared/{attaform.BIPSHY5U.d.ts → attaform.DniawQLP.d.ts} +68 -11
  46. package/dist/shared/{attaform.Ib2seKUE.cjs → attaform.DrvMscNX.cjs} +90 -9
  47. package/dist/shared/attaform.DrvMscNX.cjs.map +1 -0
  48. package/dist/shared/{attaform.Cjh6b6xU.d.cts → attaform.F2Q8iv38.d.ts} +18 -2
  49. package/dist/shared/{attaform.gj8dtOlx.d.mts → attaform.FmqKO1Ex.d.cts} +18 -2
  50. package/dist/shared/{attaform.C8zVmank.d.ts → attaform.Fp3sCrVs.d.ts} +1 -1
  51. package/dist/shared/{attaform.CgsQZdy1.mjs → attaform.nMaz2C5P.mjs} +3 -3
  52. package/dist/shared/{attaform.BBaMFa-k.cjs.map → attaform.nMaz2C5P.mjs.map} +1 -1
  53. package/dist/zod-v3.cjs +3 -3
  54. package/dist/zod-v3.d.cts +3 -3
  55. package/dist/zod-v3.d.mts +3 -3
  56. package/dist/zod-v3.d.ts +3 -3
  57. package/dist/zod-v3.mjs +3 -3
  58. package/dist/zod-v4.cjs +3 -3
  59. package/dist/zod-v4.d.cts +6 -6
  60. package/dist/zod-v4.d.mts +6 -6
  61. package/dist/zod-v4.d.ts +6 -6
  62. package/dist/zod-v4.mjs +3 -3
  63. package/dist/zod.cjs +3 -3
  64. package/dist/zod.d.cts +3 -3
  65. package/dist/zod.d.mts +3 -3
  66. package/dist/zod.d.ts +3 -3
  67. package/dist/zod.mjs +3 -3
  68. package/package.json +1 -1
  69. package/dist/shared/attaform.CuUx4jWI.mjs.map +0 -1
  70. package/dist/shared/attaform.Ib2seKUE.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { G as GenericForm, F as FormKey, a as UseFormReturnType, a4 as RegisterValue, T as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, S as Path, a9 as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, af as ValidationError, A as AbstractSchema, aj as WriteMeta, h as DeepPartial, ak as WriteShape, as as TransformAbortHolder, ad as ValidateOn, d as AttaformDefaults, at as RegisterModelDynamicCustomDirective } from './attaform.BIPSHY5U.js';
1
+ import { G as GenericForm, F as FormKey, a as UseFormReturnType, a4 as RegisterValue, T as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, S as Path, a9 as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, af as ValidationError, A as AbstractSchema, aj as WriteMeta, h as DeepPartial, ak as WriteShape, as as TransformAbortHolder, ad as ValidateOn, d as AttaformDefaults, at as RegisterModelDynamicCustomDirective } from './attaform.DniawQLP.mjs';
2
2
  import { Ref, ComputedRef, App, InjectionKey, Plugin } from 'vue';
3
3
 
4
4
  /**
@@ -758,6 +758,16 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
758
758
  * does not mutate value / focused / blurred or trigger validation.
759
759
  */
760
760
  touchAtPath(segments: Path): void;
761
+ /**
762
+ * Walk every active-variant leaf under `segments` and flip the full
763
+ * interaction ladder (`touched` / `interacted` /
764
+ * `blurredAfterInteraction`), as though the user had focused,
765
+ * edited, and left each one. Powers `form.interact(path?)`.
766
+ * Idempotent; does not mutate value / focused / blurred. Returns
767
+ * whether any leaf resolved, so the caller can skip validation and
768
+ * dev-warn on an empty path.
769
+ */
770
+ interactAtPath(segments: Path): boolean;
761
771
  /**
762
772
  * SSR-only optimistic mark: flip `connected: true` on the field
763
773
  * record without an actual DOM element. Called by the `vRegisterHint`
@@ -773,8 +783,14 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
773
783
  * (a composite widget, or none found). The directive calls it on mount
774
784
  * (`true`) and unmount (`false`). Distinct from the SSR-only
775
785
  * `markConnectedOptimistically`; idempotent on the leading edge.
786
+ *
787
+ * `hostEl` (the host root) is recorded on connect as this path's
788
+ * focus-first-error anchor, scoped to `formInstanceId` the same way a
789
+ * registered control is. A no-latch host enters no `elements` record, so
790
+ * without this it is absent from `getFirstErrorElement`'s walk; the anchor
791
+ * lets that walk resolve a focus target from the host subtree.
776
792
  */
777
- markHostConnected(path: Path, connected: boolean): void;
793
+ markHostConnected(path: Path, connected: boolean, hostEl: HTMLElement, formInstanceId: string): void;
778
794
  /**
779
795
  * Leaf-only pristine check. `originals` is populated via
780
796
  * `diffAndApply`'s `added` patches, which fire only on primitive
@@ -1,5 +1,5 @@
1
1
  import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, nextTick, warn, readonly, toRaw, reactive, toValue, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope } from 'vue';
2
- import { n as pathsEqual, k as isPathPrefix, _ as __DEV__, f as canonicalizePath, s as segmentsForPathKey, o as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, q as normalizeErrorInput, w as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, x as normalizeErrorInputs, y as coerceToPathKey, a as InvalidUseFormConfigError, z as ensureAttaformInstalled, u as useRegistry, B as kFormContext, C as kFormInstanceId, d as ReservedFormKeyError, m as kAttaformWizardActiveStepResolver, E as kAttaformAncestorWizard } from './attaform.CgsQZdy1.mjs';
2
+ import { n as pathsEqual, k as isPathPrefix, _ as __DEV__, f as canonicalizePath, s as segmentsForPathKey, o as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, q as normalizeErrorInput, w as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, x as normalizeErrorInputs, y as coerceToPathKey, a as InvalidUseFormConfigError, z as ensureAttaformInstalled, u as useRegistry, B as kFormContext, C as kFormInstanceId, d as ReservedFormKeyError, m as kAttaformWizardActiveStepResolver, E as kAttaformAncestorWizard } from './attaform.nMaz2C5P.mjs';
3
3
  import { R as REGISTER_OWNER_MARKER, b as V_REGISTER_MARKER } from './attaform.KvlEedlF.mjs';
4
4
 
5
5
  function safeAssign(target, key, value) {
@@ -624,7 +624,9 @@ function computeVerdict(field, formMeta) {
624
624
  (e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
625
625
  );
626
626
  if (hasOwnError) return "error";
627
- if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
627
+ if (field.valid === true && field.blank !== true && (field.dirty === true || field.interacted === true)) {
628
+ return "success";
629
+ }
628
630
  return "idle";
629
631
  }
630
632
  function earliestNonNull(a, b) {
@@ -2507,8 +2509,8 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2507
2509
  markConnectedOptimistically: () => {
2508
2510
  state.markConnectedOptimistically(segments);
2509
2511
  },
2510
- markHostConnected: (connected) => {
2511
- state.markHostConnected(segments, connected);
2512
+ markHostConnected: (connected, hostEl) => {
2513
+ state.markHostConnected(segments, connected, hostEl, formInstanceId);
2512
2514
  },
2513
2515
  markFocused: (focused) => {
2514
2516
  state.markFocused(
@@ -3312,6 +3314,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3312
3314
  const segments = pathInput === void 0 ? ROOT_PATH : canonicalizePath(pathInput).segments;
3313
3315
  state.touchAtPath(segments);
3314
3316
  }
3317
+ async function interact(pathInput) {
3318
+ const segments = pathInput === void 0 ? ROOT_PATH : canonicalizePath(pathInput).segments;
3319
+ if (!state.interactAtPath(segments)) return;
3320
+ try {
3321
+ await validateAsyncBuilt(pathInput === void 0 ? void 0 : segments);
3322
+ } catch {
3323
+ }
3324
+ }
3315
3325
  const focusFirstError = (options2) => {
3316
3326
  const target = state.getFirstErrorElement(formInstanceId);
3317
3327
  if (target === null) return false;
@@ -3458,6 +3468,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3458
3468
  scrollToFirstError: gated(scrollToFirstError),
3459
3469
  applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
3460
3470
  touch: gated(touch),
3471
+ interact: gated(interact),
3461
3472
  get history() {
3462
3473
  void state.activate();
3463
3474
  return formHistory;
@@ -4066,6 +4077,25 @@ function mergeObjectKeys(target, source, path, schema) {
4066
4077
  }
4067
4078
 
4068
4079
  const isContainer = (value) => Array.isArray(value) || isPlainRecord(value);
4080
+ const HOST_FOCUS_TARGET_SELECTOR = [
4081
+ 'input:not([type="hidden"])',
4082
+ "select",
4083
+ "textarea",
4084
+ "button",
4085
+ "a[href]",
4086
+ '[tabindex]:not([tabindex="-1"])',
4087
+ '[contenteditable="true"]'
4088
+ ].join(", ");
4089
+ function resolveHostFocusTarget(hostRoot) {
4090
+ const candidates = hostRoot.querySelectorAll(HOST_FOCUS_TARGET_SELECTOR);
4091
+ for (const el of candidates) {
4092
+ if (!el.isConnected) continue;
4093
+ if (el.offsetParent === null) continue;
4094
+ if (el.matches(":disabled")) continue;
4095
+ return el;
4096
+ }
4097
+ return hostRoot;
4098
+ }
4069
4099
  function isHydratedFieldRecord(value) {
4070
4100
  if (typeof value !== "object" || value === null) return false;
4071
4101
  const r = value;
@@ -4251,6 +4281,7 @@ function createFormStore(options) {
4251
4281
  const fields = reactive(/* @__PURE__ */ new Map());
4252
4282
  const elements = reactive(/* @__PURE__ */ new Map());
4253
4283
  const elementToFormInstance = /* @__PURE__ */ new WeakMap();
4284
+ const hostTargets = /* @__PURE__ */ new Map();
4254
4285
  let sortedRegistrationsCache = null;
4255
4286
  const schemaErrors = reactive(/* @__PURE__ */ new Map());
4256
4287
  const userErrors = reactive(/* @__PURE__ */ new Map());
@@ -5123,6 +5154,7 @@ function createFormStore(options) {
5123
5154
  record.elements.add(raw);
5124
5155
  }
5125
5156
  elementToFormInstance.set(element, formInstanceId);
5157
+ hostTargets.delete(key);
5126
5158
  sortedRegistrationsCache = null;
5127
5159
  const current = fields.get(key);
5128
5160
  touchFieldRecord(key, path, {
@@ -5160,10 +5192,14 @@ function createFormStore(options) {
5160
5192
  blurred: current?.blurred ?? true
5161
5193
  });
5162
5194
  }
5163
- function markHostConnected(path, connected) {
5195
+ function markHostConnected(path, connected, hostEl, formInstanceId) {
5164
5196
  const { key } = canonicalizePath(path);
5165
5197
  const current = fields.get(key);
5166
5198
  if (connected) {
5199
+ if (!elements.has(key)) {
5200
+ hostTargets.set(key, { path, hostRoot: hostEl, formInstanceId });
5201
+ sortedRegistrationsCache = null;
5202
+ }
5167
5203
  if (current?.connected === true) return;
5168
5204
  touchFieldRecord(key, path, {
5169
5205
  connected: true,
@@ -5171,6 +5207,7 @@ function createFormStore(options) {
5171
5207
  blurred: current?.blurred ?? true
5172
5208
  });
5173
5209
  } else {
5210
+ if (hostTargets.delete(key)) sortedRegistrationsCache = null;
5174
5211
  if (current?.connected !== true) return;
5175
5212
  touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
5176
5213
  }
@@ -5247,6 +5284,32 @@ function createFormStore(options) {
5247
5284
  );
5248
5285
  }
5249
5286
  }
5287
+ function interactAtPath(segments) {
5288
+ if (effectiveDisabled.value) return false;
5289
+ const formValue = form.value;
5290
+ let interactedAny = false;
5291
+ for (const [, entry] of originals) {
5292
+ if (!isPathPrefix(segments, entry.segments)) continue;
5293
+ if (!hasAtPath(formValue, entry.segments)) continue;
5294
+ interactedAny = true;
5295
+ const leafKey = canonicalizePath(entry.segments).key;
5296
+ const current = fields.get(leafKey);
5297
+ if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
5298
+ continue;
5299
+ }
5300
+ touchFieldRecord(leafKey, entry.segments, {
5301
+ touched: true,
5302
+ interacted: true,
5303
+ blurredAfterInteraction: true
5304
+ });
5305
+ }
5306
+ if (!interactedAny && __DEV__) {
5307
+ console.warn(
5308
+ `[attaform] form.interact(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
5309
+ );
5310
+ }
5311
+ return interactedAny;
5312
+ }
5250
5313
  function rehydrate() {
5251
5314
  const factory = defaultValuesFactory.value;
5252
5315
  if (factory === void 0) {
@@ -5505,18 +5568,35 @@ function createFormStore(options) {
5505
5568
  function getFirstErrorElement(formInstanceId) {
5506
5569
  sortedRegistrationsCache ?? (sortedRegistrationsCache = rebuildSortedRegistrations());
5507
5570
  for (const entry of sortedRegistrationsCache) {
5508
- if (elementToFormInstance.get(entry.element) !== formInstanceId) continue;
5571
+ if (entry.formInstanceId !== formInstanceId) continue;
5509
5572
  if (!entry.element.isConnected) continue;
5510
5573
  if (entry.element.offsetParent === null) continue;
5511
5574
  if (getErrorsForPath(entry.path).length === 0) continue;
5512
- return { path: entry.path, element: entry.element };
5575
+ const element = entry.host ? resolveHostFocusTarget(entry.element) : entry.element;
5576
+ return { path: entry.path, element };
5513
5577
  }
5514
5578
  return null;
5515
5579
  }
5516
5580
  function rebuildSortedRegistrations() {
5517
5581
  const flat = [];
5518
5582
  for (const [, record] of elements) {
5519
- for (const el of record.elements) flat.push({ path: record.path, element: el });
5583
+ for (const el of record.elements) {
5584
+ flat.push({
5585
+ path: record.path,
5586
+ element: el,
5587
+ host: false,
5588
+ formInstanceId: elementToFormInstance.get(el) ?? ""
5589
+ });
5590
+ }
5591
+ }
5592
+ for (const [key, target] of hostTargets) {
5593
+ if (elements.has(key)) continue;
5594
+ flat.push({
5595
+ path: target.path,
5596
+ element: target.hostRoot,
5597
+ host: true,
5598
+ formInstanceId: target.formInstanceId
5599
+ });
5520
5600
  }
5521
5601
  flat.sort(
5522
5602
  (a, b) => a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
@@ -5584,6 +5664,7 @@ function createFormStore(options) {
5584
5664
  markFocused,
5585
5665
  markInteracted,
5586
5666
  touchAtPath,
5667
+ interactAtPath,
5587
5668
  markConnectedOptimistically,
5588
5669
  markHostConnected,
5589
5670
  isPristineAtPath,
@@ -7589,4 +7670,4 @@ function escapeForInlineScript(json) {
7589
7670
  }
7590
7671
 
7591
7672
  export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, escapeForInlineScript as e, isUnset as f, gate as g, useAbstractForm as h, injectForm as i, useRegister as j, useWizard as k, lazy as l, makeDefaultDisplayState as m, isPlainRecord as n, safeAssign as o, slimKindOf as p, humanize as q, getAtPath as r, safeOwnRead as s, setAtPath as t, unset as u };
7592
- //# sourceMappingURL=attaform.CuUx4jWI.mjs.map
7673
+ //# sourceMappingURL=attaform.CKH2lGdL.mjs.map