@atscript/vue-form 0.1.103 → 0.1.105

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 (185) hide show
  1. package/dist/{as-action-lLooKipk.cjs → as-action-B712j9xf.cjs} +4 -0
  2. package/dist/{as-action-BoZtbaXt.mjs → as-action-CCjN5EFP.mjs} +4 -0
  3. package/dist/as-action.cjs +1 -1
  4. package/dist/as-action.d.cts +1 -1
  5. package/dist/as-action.d.mts +1 -1
  6. package/dist/as-action.mjs +1 -1
  7. package/dist/{as-action.vue-BCbopk0S.d.cts → as-action.vue-BL8_a0fE.d.cts} +1 -1
  8. package/dist/{as-action.vue-BtQX09n-.d.mts → as-action.vue-CcOPK2qI.d.mts} +1 -1
  9. package/dist/{as-array-4rQYVD9_.mjs → as-array-BGG38Y1L.mjs} +5 -1
  10. package/dist/{as-array-C6tCzZir.cjs → as-array-DVc9_ood.cjs} +5 -1
  11. package/dist/as-array.cjs +1 -1
  12. package/dist/as-array.d.cts +1 -1
  13. package/dist/as-array.d.mts +1 -1
  14. package/dist/as-array.mjs +1 -1
  15. package/dist/{as-array.vue-Ci4xO292.d.cts → as-array.vue-Be1tDTdH.d.cts} +1 -1
  16. package/dist/{as-array.vue-I42wuF6e.d.mts → as-array.vue-CtBBY2gF.d.mts} +1 -1
  17. package/dist/{as-checkbox-C3i6ZmRk.mjs → as-checkbox-DwLRHmXj.mjs} +5 -1
  18. package/dist/{as-checkbox-oxG8_Isu.cjs → as-checkbox-sgF16SuJ.cjs} +5 -1
  19. package/dist/as-checkbox.cjs +1 -1
  20. package/dist/as-checkbox.d.cts +1 -1
  21. package/dist/as-checkbox.d.mts +1 -1
  22. package/dist/as-checkbox.mjs +1 -1
  23. package/dist/{as-checkbox.vue-BoY72SV8.d.mts → as-checkbox.vue-B6zk5IbU.d.cts} +1 -1
  24. package/dist/{as-checkbox.vue-DqIm0ms2.d.cts → as-checkbox.vue-CXpsn_WG.d.mts} +1 -1
  25. package/dist/as-collapsible.d.cts +1 -18
  26. package/dist/as-collapsible.d.mts +1 -19
  27. package/dist/as-collapsible.vue-34S3TEq5.d.cts +20 -0
  28. package/dist/as-collapsible.vue-CIfMlTQ6.d.mts +20 -0
  29. package/dist/{as-date-BfISLr-e.mjs → as-date-CfCa2Kp1.mjs} +6 -2
  30. package/dist/{as-date-C7pJIpe-.cjs → as-date-hCCTd52o.cjs} +6 -2
  31. package/dist/as-date.cjs +1 -1
  32. package/dist/as-date.d.cts +1 -1
  33. package/dist/as-date.d.mts +1 -1
  34. package/dist/as-date.mjs +1 -1
  35. package/dist/{as-date.vue-BfSVrED7.d.mts → as-date.vue-BZiy1s5s.d.cts} +1 -1
  36. package/dist/{as-date.vue-SdlJH5fn.d.cts → as-date.vue-DWWoojir.d.mts} +1 -1
  37. package/dist/{as-datetime-F3In2A6l.mjs → as-datetime-B1I_zREk.mjs} +6 -2
  38. package/dist/{as-datetime-DFTI8Wcr.cjs → as-datetime-DDlWca2Q.cjs} +6 -2
  39. package/dist/as-datetime.cjs +1 -1
  40. package/dist/as-datetime.d.cts +1 -1
  41. package/dist/as-datetime.d.mts +1 -1
  42. package/dist/as-datetime.mjs +1 -1
  43. package/dist/{as-datetime.vue-B47V1siL.d.mts → as-datetime.vue-DYEHokj_.d.cts} +1 -1
  44. package/dist/{as-datetime.vue-DUp_TDOW.d.cts → as-datetime.vue-NblA-SVE.d.mts} +1 -1
  45. package/dist/{as-decimal-DIO8bD2Y.mjs → as-decimal-CQM5OfqB.mjs} +5 -1
  46. package/dist/{as-decimal-CJAHWYjW.cjs → as-decimal-DM10ycwv.cjs} +5 -1
  47. package/dist/as-decimal.cjs +1 -1
  48. package/dist/as-decimal.d.cts +1 -1
  49. package/dist/as-decimal.d.mts +1 -1
  50. package/dist/as-decimal.mjs +1 -1
  51. package/dist/{as-decimal.vue-Bhq4C1Ix.d.cts → as-decimal.vue-BoNNIhbL.d.mts} +1 -1
  52. package/dist/{as-decimal.vue-DitPZpS_.d.mts → as-decimal.vue-D3eGMvC0.d.cts} +1 -1
  53. package/dist/{as-field-DLW-csEu.mjs → as-field-5rz47opo.mjs} +10 -4
  54. package/dist/{as-field-shell-CW3DDHnq.cjs → as-field-shell-C4epFfvE.cjs} +29 -23
  55. package/dist/{as-field-shell-BHYEjGcn.mjs → as-field-shell-DLvPV8WS.mjs} +29 -23
  56. package/dist/as-field-shell.cjs +1 -1
  57. package/dist/as-field-shell.d.cts +1 -1
  58. package/dist/as-field-shell.d.mts +1 -1
  59. package/dist/as-field-shell.mjs +1 -1
  60. package/dist/{as-field-shell.vue-Ct1ixFqc.d.cts → as-field-shell.vue-CLDHaWN0.d.cts} +1 -1
  61. package/dist/{as-field-shell.vue-K4Flr3gw.d.mts → as-field-shell.vue-DssDDSal.d.mts} +1 -1
  62. package/dist/{as-field-BKa4D63r.cjs → as-field-vktmRs-V.cjs} +9 -3
  63. package/dist/as-field.cjs +1 -1
  64. package/dist/as-field.d.cts +1 -24
  65. package/dist/as-field.d.mts +1 -25
  66. package/dist/as-field.mjs +1 -1
  67. package/dist/as-field.vue-A2cJj-Ih.d.cts +26 -0
  68. package/dist/as-field.vue-CmjIDgQ5.d.mts +26 -0
  69. package/dist/{as-form-CrMA0HdQ.cjs → as-form-B4DKpAVk.cjs} +75 -37
  70. package/dist/{as-form-OsyP7m-A.mjs → as-form-me0zA-52.mjs} +77 -39
  71. package/dist/as-form.cjs +1 -1
  72. package/dist/as-form.d.cts +1 -1
  73. package/dist/as-form.d.mts +1 -1
  74. package/dist/as-form.mjs +1 -1
  75. package/dist/as-form.vue-BEXuZ83-.d.mts +1425 -0
  76. package/dist/as-form.vue-liXf4fgq.d.cts +1425 -0
  77. package/dist/{as-input-CqxNYh5q.cjs → as-input-Bp1Ltp15.cjs} +6 -2
  78. package/dist/{as-input-D7aA22Yx.mjs → as-input-DF--i7kY.mjs} +6 -2
  79. package/dist/{as-input-control-BYo_dfQA.cjs → as-input-control-B7F2q99A.cjs} +4 -0
  80. package/dist/{as-input-control-Bx-isLZN.mjs → as-input-control-BofS4mCo.mjs} +4 -0
  81. package/dist/as-input.cjs +1 -1
  82. package/dist/as-input.d.cts +1 -1
  83. package/dist/as-input.d.mts +1 -1
  84. package/dist/as-input.mjs +1 -1
  85. package/dist/{as-input.vue-BHsVVncq.d.cts → as-input.vue-CCAEWqK8.d.mts} +1 -1
  86. package/dist/{as-input.vue-DgZrW4pc.d.mts → as-input.vue-Nc9Q3RPh.d.cts} +1 -1
  87. package/dist/{as-iterator-DsDKfpTh.cjs → as-iterator-Bd-bKTMY.cjs} +1 -1
  88. package/dist/{as-iterator-DW7giLlz.mjs → as-iterator-BjIF-O-J.mjs} +1 -1
  89. package/dist/as-iterator.cjs +1 -1
  90. package/dist/as-iterator.d.cts +1 -20
  91. package/dist/as-iterator.d.mts +1 -21
  92. package/dist/as-iterator.mjs +1 -1
  93. package/dist/as-iterator.vue-BZeozFr-.d.cts +22 -0
  94. package/dist/as-iterator.vue-C8HW6n25.d.mts +22 -0
  95. package/dist/{as-multi-select-DQUInqwY.cjs → as-multi-select-b1l34vP5.cjs} +5 -1
  96. package/dist/{as-multi-select-Bsf4Y_mv.mjs → as-multi-select-mp-aM3LV.mjs} +5 -1
  97. package/dist/as-multi-select.cjs +1 -1
  98. package/dist/as-multi-select.d.cts +1 -1
  99. package/dist/as-multi-select.d.mts +1 -1
  100. package/dist/as-multi-select.mjs +1 -1
  101. package/dist/{as-multi-select.vue--bavpcN-.d.cts → as-multi-select.vue-D9mseRFm.d.cts} +1 -1
  102. package/dist/{as-multi-select.vue-rsDUkzxa.d.mts → as-multi-select.vue-DKpOsOmM.d.mts} +1 -1
  103. package/dist/{as-number-B8MCg-ph.mjs → as-number-De9zTltV.mjs} +6 -2
  104. package/dist/{as-number-CFdK5xjM.cjs → as-number-joydTMcJ.cjs} +6 -2
  105. package/dist/as-number.cjs +1 -1
  106. package/dist/as-number.d.cts +1 -1
  107. package/dist/as-number.d.mts +1 -1
  108. package/dist/as-number.mjs +1 -1
  109. package/dist/{as-number.vue-D2DrbnQu.d.cts → as-number.vue-CZC0-zs8.d.cts} +1 -1
  110. package/dist/{as-number.vue-Oml2y6Nt.d.mts → as-number.vue-DrvjSOdq.d.mts} +1 -1
  111. package/dist/{as-object-CN3KZFXO.mjs → as-object-CQMTYtcY.mjs} +5 -1
  112. package/dist/{as-object-B4sF9sIh.cjs → as-object-DgeItRdH.cjs} +5 -1
  113. package/dist/as-object.cjs +1 -1
  114. package/dist/as-object.d.cts +1 -1
  115. package/dist/as-object.d.mts +1 -1
  116. package/dist/as-object.mjs +1 -1
  117. package/dist/{as-object.vue-BljKYrMo.d.cts → as-object.vue-BItO19fw.d.mts} +1 -1
  118. package/dist/{as-object.vue-DAq7Kha3.d.mts → as-object.vue-DbHog5M-.d.cts} +1 -1
  119. package/dist/{as-paragraph-GIGj6UgL.mjs → as-paragraph-BP3-0EY4.mjs} +4 -0
  120. package/dist/{as-paragraph-BA-c863H.cjs → as-paragraph-CPhDTH_V.cjs} +4 -0
  121. package/dist/as-paragraph.cjs +1 -1
  122. package/dist/as-paragraph.d.cts +1 -1
  123. package/dist/as-paragraph.d.mts +1 -1
  124. package/dist/as-paragraph.mjs +1 -1
  125. package/dist/{as-paragraph.vue-CBU_olev.d.mts → as-paragraph.vue-BGDpjceO.d.mts} +1 -1
  126. package/dist/{as-paragraph.vue-LxXA7VPD.d.cts → as-paragraph.vue-D36pdOoc.d.cts} +1 -1
  127. package/dist/{as-radio-BeOQdoeq.cjs → as-radio-Dlmxk1vr.cjs} +5 -1
  128. package/dist/{as-radio-CFaDbRPe.mjs → as-radio-SwpgM1xP.mjs} +5 -1
  129. package/dist/as-radio.cjs +1 -1
  130. package/dist/as-radio.d.cts +1 -1
  131. package/dist/as-radio.d.mts +1 -1
  132. package/dist/as-radio.mjs +1 -1
  133. package/dist/{as-radio.vue-Dk4nlz-d.d.cts → as-radio.vue-EfrhKcBF.d.cts} +1 -1
  134. package/dist/{as-radio.vue-TVMliE1e.d.mts → as-radio.vue-WywkI-zY.d.mts} +1 -1
  135. package/dist/{as-ref-CNQgr6zE.mjs → as-ref-8JnEuan0.mjs} +5 -1
  136. package/dist/{as-ref-RawBvR6v.cjs → as-ref-BCpvUq8E.cjs} +5 -1
  137. package/dist/as-ref.cjs +1 -1
  138. package/dist/as-ref.d.cts +1 -1
  139. package/dist/as-ref.d.mts +1 -1
  140. package/dist/as-ref.mjs +1 -1
  141. package/dist/{as-ref.vue-CUxxGqyg.d.mts → as-ref.vue-BD_vsu7I.d.mts} +1 -1
  142. package/dist/{as-ref.vue-CnzqRblA.d.cts → as-ref.vue-Cxg-A5QR.d.cts} +1 -1
  143. package/dist/{as-select-Cr00CPKf.mjs → as-select-C3ukCyGQ.mjs} +5 -1
  144. package/dist/{as-select-LJIvBPiv.cjs → as-select-CtSNZXtC.cjs} +5 -1
  145. package/dist/as-select.cjs +1 -1
  146. package/dist/as-select.d.cts +1 -1
  147. package/dist/as-select.d.mts +1 -1
  148. package/dist/as-select.mjs +1 -1
  149. package/dist/{as-select.vue-CKjAg-Y2.d.mts → as-select.vue-DoAG5njV.d.mts} +1 -1
  150. package/dist/{as-select.vue-ZZtjsmcO.d.cts → as-select.vue-hd94jaH_.d.cts} +1 -1
  151. package/dist/{as-time-CE2FaqZ3.mjs → as-time-QJ9YiVj9.mjs} +6 -2
  152. package/dist/{as-time-CqCF0pKQ.cjs → as-time-nAPtGL3t.cjs} +6 -2
  153. package/dist/as-time.cjs +1 -1
  154. package/dist/as-time.d.cts +1 -1
  155. package/dist/as-time.d.mts +1 -1
  156. package/dist/as-time.mjs +1 -1
  157. package/dist/{as-time.vue-BCEE4uCV.d.mts → as-time.vue-DMy_Au0C.d.cts} +1 -1
  158. package/dist/{as-time.vue-BUAvE5m3.d.cts → as-time.vue-DgW7qSAM.d.mts} +1 -1
  159. package/dist/{as-tuple-B3ysbvxB.cjs → as-tuple-CFn7aZYt.cjs} +5 -1
  160. package/dist/{as-tuple-CeJWgP6l.mjs → as-tuple-CJXpGGKj.mjs} +5 -1
  161. package/dist/as-tuple.cjs +1 -1
  162. package/dist/as-tuple.d.cts +1 -1
  163. package/dist/as-tuple.d.mts +1 -1
  164. package/dist/as-tuple.mjs +1 -1
  165. package/dist/{as-tuple.vue-WN7k43tv.d.mts → as-tuple.vue-BDwz5FeN.d.mts} +1 -1
  166. package/dist/{as-tuple.vue-cy0ThAGl.d.cts → as-tuple.vue-DZ2Soqyw.d.cts} +1 -1
  167. package/dist/{as-union-Djhjo69H.cjs → as-union-BDRNBacb.cjs} +4 -0
  168. package/dist/{as-union-BPlYyGVk.mjs → as-union-DZWgGqqM.mjs} +4 -0
  169. package/dist/as-union.cjs +1 -1
  170. package/dist/as-union.d.cts +1 -1
  171. package/dist/as-union.d.mts +1 -1
  172. package/dist/as-union.mjs +1 -1
  173. package/dist/{as-union.vue-AqH35ozq.d.cts → as-union.vue-D9Mh0M3_.d.mts} +1 -1
  174. package/dist/{as-union.vue-Ew3Bany8.d.mts → as-union.vue-DQKWNuhd.d.cts} +1 -1
  175. package/dist/index.cjs +22 -22
  176. package/dist/index.d.cts +24 -1081
  177. package/dist/index.d.mts +24 -1081
  178. package/dist/index.mjs +22 -22
  179. package/dist/{types-BYfe1QEF.d.cts → types-B8aPXWL5.d.cts} +13 -0
  180. package/dist/{types-CV9ss4UN.d.mts → types-nuUi10Kl.d.mts} +13 -0
  181. package/dist/{use-as-date-XLE8HBbo.mjs → use-as-date-B6RGkjjB.mjs} +4 -0
  182. package/dist/{use-as-date-DY8b1_V4.cjs → use-as-date-CslHBPmQ.cjs} +4 -0
  183. package/package.json +9 -9
  184. package/dist/as-form.vue-2NZSk1F9.d.mts +0 -285
  185. package/dist/as-form.vue-Dg_5GYWG.d.cts +0 -285
@@ -1,8 +1,8 @@
1
1
  const require_use_form_context = require("./use-form-context-DpGiGUvJ.cjs");
2
2
  const require_use_as_nested_sections_store = require("./use-as-nested-sections-store--E0Ijpt5.cjs");
3
3
  const require_use_as_value_help = require("./use-as-value-help-BsAriqCI.cjs");
4
- const require_as_field = require("./as-field-BKa4D63r.cjs");
5
- const require_as_iterator = require("./as-iterator-DsDKfpTh.cjs");
4
+ const require_as_field = require("./as-field-vktmRs-V.cjs");
5
+ const require_as_iterator = require("./as-iterator-Bd-bKTMY.cjs");
6
6
  let vue = require("vue");
7
7
  let _atscript_ui = require("@atscript/ui");
8
8
  //#region src/composables/use-as-form-patch.ts
@@ -35,14 +35,22 @@ let _atscript_ui = require("@atscript/ui");
35
35
  * data (`dataRev`) or the baseline (`baselineRev`) changes, and Vue caches the
36
36
  * result between reads.
37
37
  *
38
- * @param def getter for the form's `FormDef`
39
- * @param getData getter for the WRAPPED form-data container `{ value }`. Before
40
- * real domain data arrives `useAsForm` yields the empty internal
41
- * fallback `{}` (no `value` key); the tracker treats that as
42
- * "no data yet" and defers the baseline until a `{ value: … }`
43
- * container appears (the fetch-then-fill flow).
38
+ * @param def getter for the form's `FormDef`
39
+ * @param getData getter for the WRAPPED form-data container `{ value }`. Before
40
+ * real domain data arrives `useAsForm` yields the empty internal
41
+ * fallback `{}` (no `value` key); the tracker treats that as
42
+ * "no data yet" and defers the baseline until a `{ value: … }`
43
+ * container appears (the fetch-then-fill flow). The SAME
44
+ * container reference is also the write target for
45
+ * `rebaseOnto` — mutating its `.value` preserves the bound
46
+ * `:form-data` identity so the deep watch fires once.
47
+ * @param onRebased optional callback fired AFTER `rebaseOnto` rewrites the live
48
+ * data, so the host can force-remount the field subtree (e.g.
49
+ * bump a key) when a union variant changed — the variant picker
50
+ * detects its index once at setup and won't re-detect on a
51
+ * data swap. No-op for `rebase()` (which doesn't move data).
44
52
  */
45
- function createAsFormPatch(def, getData) {
53
+ function createAsFormPatch(def, getData, onRebased) {
46
54
  let baseline;
47
55
  const baselineRev = (0, vue.ref)(0);
48
56
  const dataRev = (0, vue.ref)(0);
@@ -57,7 +65,7 @@ function createAsFormPatch(def, getData) {
57
65
  function snapshot() {
58
66
  const cur = getData();
59
67
  if (cur.value === void 0) return void 0;
60
- return deepClone((0, vue.toRaw)(cur));
68
+ return (0, _atscript_ui.deepClone)(cur, vue.toRaw);
61
69
  }
62
70
  function capture() {
63
71
  baseline = snapshot();
@@ -87,46 +95,62 @@ function createAsFormPatch(def, getData) {
87
95
  });
88
96
  const isDirty = (0, vue.computed)(() => diff.value.isDirty);
89
97
  const changes = (0, vue.computed)(() => diff.value.changes);
98
+ const dirtyPaths = (0, vue.computed)(() => (0, _atscript_ui.collectDirtyPaths)(changes.value));
90
99
  function getPatch(opts) {
91
100
  if (baseline === void 0) return {};
92
- const frozen = deepClone((0, vue.toRaw)(getData()));
101
+ const frozen = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
93
102
  return (0, _atscript_ui.buildFormDiff)(def(), baseline, frozen, opts).patch;
94
103
  }
95
104
  function getChanges() {
96
105
  if (baseline === void 0) return [];
97
- const frozen = deepClone((0, vue.toRaw)(getData()));
106
+ const frozen = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
98
107
  return (0, _atscript_ui.buildFormDiff)(def(), baseline, frozen).changes;
99
108
  }
109
+ function isDirtyPath(path) {
110
+ return dirtyPaths.value.has(path);
111
+ }
100
112
  function rebase() {
101
113
  capture();
102
114
  }
115
+ /**
116
+ * 3-way rebase onto a fresh upstream snapshot (see
117
+ * {@link AsFormPatchHandle.rebaseOnto}). One reactive write, then an explicit
118
+ * re-baseline to the upstream snapshot.
119
+ */
120
+ function rebaseOnto(upstream, opts) {
121
+ const u = (0, _atscript_ui.deepClone)(upstream, vue.toRaw);
122
+ if (baseline === void 0) {
123
+ const before = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
124
+ (0, _atscript_ui.setByPath)(getData(), "", u.value);
125
+ if ((0, _atscript_ui.unionVariantChanged)(def(), before, u)) onRebased?.();
126
+ capture();
127
+ return {
128
+ conflicts: [],
129
+ reapplied: []
130
+ };
131
+ }
132
+ const current = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
133
+ const { next, conflicts, reapplied } = (0, _atscript_ui.buildFormRebase)(def(), baseline, current, u, opts);
134
+ const variantMoved = (0, _atscript_ui.unionVariantChanged)(def(), current, next);
135
+ (0, _atscript_ui.setByPath)(getData(), "", next.value);
136
+ baseline = u;
137
+ baselineRev.value++;
138
+ if (variantMoved) onRebased?.();
139
+ return {
140
+ conflicts,
141
+ reapplied
142
+ };
143
+ }
103
144
  return {
104
145
  isDirty,
105
146
  changes,
106
147
  getPatch,
107
148
  getChanges,
108
- rebase
149
+ isDirtyPath,
150
+ rebase,
151
+ rebaseOnto
109
152
  };
110
153
  }
111
- /**
112
- * Structural deep clone of plain JSON-ish data (objects / arrays / primitives /
113
- * Date). Used to freeze the baseline so later edits to the live form data don't
114
- * mutate it. `structuredClone` would throw on Vue proxies / functions; this
115
- * walks own-enumerable keys and copies leaves by value.
116
- */
117
- function deepClone(value) {
118
- if (value === null || typeof value !== "object") return value;
119
- if (value instanceof Date) return new Date(value.getTime());
120
- if (Array.isArray(value)) {
121
- const out = [];
122
- for (let i = 0; i < value.length; i++) out.push(deepClone((0, vue.toRaw)(value[i])));
123
- return out;
124
- }
125
- const src = value;
126
- const out = {};
127
- for (const k of Object.keys(src)) out[k] = deepClone((0, vue.toRaw)(src[k]));
128
- return out;
129
- }
130
154
  const NOT_TRACKING_MSG = "useAsFormPatch(): change tracking is not enabled on this form. Pass `track-changes` to <AsForm> (or `trackChanges: true` to useAsForm).";
131
155
  /**
132
156
  * Reactive read-only access to the form's change-tracking handle from any
@@ -295,6 +319,7 @@ function useAsState(opts) {
295
319
  const EMPTY_CHANGES = Object.freeze([]);
296
320
  const EMPTY_GET_PATCH = () => ({});
297
321
  const EMPTY_GET_CHANGES = () => [];
322
+ const EMPTY_IS_DIRTY_PATH = () => false;
298
323
  /**
299
324
  * Composable backing `<AsForm>`. Owns the entire form state machine —
300
325
  * data container, internal validator, external-error dismissal, action
@@ -330,7 +355,10 @@ function useAsForm(options) {
330
355
  context: formContext.value ?? {}
331
356
  })
332
357
  });
333
- const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value) : void 0;
358
+ const remountKey = (0, vue.ref)(0);
359
+ const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value, () => {
360
+ remountKey.value++;
361
+ }) : void 0;
334
362
  if (patch) (0, vue.provide)(require_use_form_context.FORM_PATCH_KEY, patch);
335
363
  const reset = patch ? async () => {
336
364
  await resetState();
@@ -386,7 +414,8 @@ function useAsForm(options) {
386
414
  isDirty: patch?.isDirty.value ?? false,
387
415
  changes: patch ? patch.changes.value : EMPTY_CHANGES,
388
416
  getPatch: patch ? patch.getPatch : EMPTY_GET_PATCH,
389
- getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES
417
+ getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES,
418
+ isDirtyPath: patch ? patch.isDirtyPath : EMPTY_IS_DIRTY_PATH
390
419
  }));
391
420
  function supportsAction(def, actionId) {
392
421
  return (0, _atscript_ui.getDeclaredFormActions)(def).some((a) => a.id === actionId);
@@ -446,7 +475,8 @@ function useAsForm(options) {
446
475
  dismissFormError: ext.dismissForm,
447
476
  formContext,
448
477
  handleChange,
449
- patch
478
+ patch,
479
+ remountKey
450
480
  };
451
481
  }
452
482
  //#endregion
@@ -561,7 +591,12 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
561
591
  },
562
592
  getPatch: form.patch ? form.patch.getPatch : () => ({}),
563
593
  getChanges: form.patch ? form.patch.getChanges : () => [],
564
- rebase: form.patch ? form.patch.rebase : () => {}
594
+ isDirtyPath: form.patch ? form.patch.isDirtyPath : () => false,
595
+ rebase: form.patch ? form.patch.rebase : () => {},
596
+ rebaseOnto: form.patch ? form.patch.rebaseOnto : () => ({
597
+ conflicts: [],
598
+ reapplied: []
599
+ })
565
600
  });
566
601
  return (_ctx, _cache) => {
567
602
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("form", {
@@ -571,7 +606,10 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
571
606
  }, [
572
607
  (0, vue.renderSlot)(_ctx.$slots, "form.header", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
573
608
  (0, vue.renderSlot)(_ctx.$slots, "form.before", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
574
- (0, vue.createVNode)(require_as_field.as_field_default, { field: __props.def.rootField }, null, 8, ["field"]),
609
+ ((0, vue.openBlock)(), (0, vue.createBlock)(require_as_field.as_field_default, {
610
+ key: (0, vue.unref)(form).remountKey.value,
611
+ field: __props.def.rootField
612
+ }, null, 8, ["field"])),
575
613
  (0, vue.renderSlot)(_ctx.$slots, "form.after", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
576
614
  (0, vue.unref)(form).formError.value ? (0, vue.renderSlot)(_ctx.$slots, "form.error", (0, vue.mergeProps)({ key: 0 }, (0, vue.unref)(form).slotProps.value, {
577
615
  message: (0, vue.unref)(form).formError.value,
@@ -1,10 +1,10 @@
1
1
  import { _ as TYPES_KEY, a as CHANGE_HANDLER_KEY, c as ERRORS_KEY, d as FORM_PATCH_KEY, f as FORM_STATE_KEY, g as ROOT_DATA_KEY, h as PATH_PREFIX_KEY, i as ACTION_HANDLER_KEY, l as FORM_CONTEXT_KEY, o as COMPONENTS_KEY, p as HIDE_ROOT_TITLE_KEY, s as DISMISS_EXTERNAL_AT_KEY, u as FORM_DATA_KEY } from "./use-form-context-D6J1WTyG.mjs";
2
2
  import { n as provideAsNestedSectionsStore, r as useAsNestedSectionsStore, t as DESCENDANT_ERROR_COUNTS_KEY } from "./use-as-nested-sections-store-DVRmG7V0.mjs";
3
3
  import { t as CLIENT_FACTORY_KEY } from "./use-as-value-help-DrISSxCz.mjs";
4
- import { t as as_field_default } from "./as-field-DLW-csEu.mjs";
5
- import { t as as_iterator_default } from "./as-iterator-DW7giLlz.mjs";
6
- import { computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, inject, mergeProps, nextTick, normalizeProps, openBlock, provide, reactive, ref, renderSlot, toDisplayString, toRaw, toValue, unref, watch, watchEffect, withModifiers } from "vue";
7
- import { META_DESCRIPTION, META_LABEL, UI_FORM_FN_DESCRIPTION, UI_FORM_FN_SUBMIT_DISABLED, UI_FORM_FN_SUBMIT_TEXT, UI_FORM_FN_TITLE, UI_FORM_SUBMIT_TEXT, buildDescendantErrorCounts, buildFormDiff, getDeclaredFormActions, getFormValidator, iteratePathAncestors, mergeErrorMaps, resolveFormProp } from "@atscript/ui";
4
+ import { t as as_field_default } from "./as-field-5rz47opo.mjs";
5
+ import { t as as_iterator_default } from "./as-iterator-BjIF-O-J.mjs";
6
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, inject, mergeProps, nextTick, normalizeProps, openBlock, provide, reactive, ref, renderSlot, toDisplayString, toRaw, toValue, unref, watch, watchEffect, withModifiers } from "vue";
7
+ import { META_DESCRIPTION, META_LABEL, UI_FORM_FN_DESCRIPTION, UI_FORM_FN_SUBMIT_DISABLED, UI_FORM_FN_SUBMIT_TEXT, UI_FORM_FN_TITLE, UI_FORM_SUBMIT_TEXT, buildDescendantErrorCounts, buildFormDiff, buildFormRebase, collectDirtyPaths, deepClone, getDeclaredFormActions, getFormValidator, iteratePathAncestors, mergeErrorMaps, resolveFormProp, setByPath, unionVariantChanged } from "@atscript/ui";
8
8
  //#region src/composables/use-as-form-patch.ts
9
9
  /**
10
10
  * Internal factory invoked by `useAsForm` when `trackChanges` is enabled. Owns
@@ -35,14 +35,22 @@ import { META_DESCRIPTION, META_LABEL, UI_FORM_FN_DESCRIPTION, UI_FORM_FN_SUBMIT
35
35
  * data (`dataRev`) or the baseline (`baselineRev`) changes, and Vue caches the
36
36
  * result between reads.
37
37
  *
38
- * @param def getter for the form's `FormDef`
39
- * @param getData getter for the WRAPPED form-data container `{ value }`. Before
40
- * real domain data arrives `useAsForm` yields the empty internal
41
- * fallback `{}` (no `value` key); the tracker treats that as
42
- * "no data yet" and defers the baseline until a `{ value: … }`
43
- * container appears (the fetch-then-fill flow).
38
+ * @param def getter for the form's `FormDef`
39
+ * @param getData getter for the WRAPPED form-data container `{ value }`. Before
40
+ * real domain data arrives `useAsForm` yields the empty internal
41
+ * fallback `{}` (no `value` key); the tracker treats that as
42
+ * "no data yet" and defers the baseline until a `{ value: … }`
43
+ * container appears (the fetch-then-fill flow). The SAME
44
+ * container reference is also the write target for
45
+ * `rebaseOnto` — mutating its `.value` preserves the bound
46
+ * `:form-data` identity so the deep watch fires once.
47
+ * @param onRebased optional callback fired AFTER `rebaseOnto` rewrites the live
48
+ * data, so the host can force-remount the field subtree (e.g.
49
+ * bump a key) when a union variant changed — the variant picker
50
+ * detects its index once at setup and won't re-detect on a
51
+ * data swap. No-op for `rebase()` (which doesn't move data).
44
52
  */
45
- function createAsFormPatch(def, getData) {
53
+ function createAsFormPatch(def, getData, onRebased) {
46
54
  let baseline;
47
55
  const baselineRev = ref(0);
48
56
  const dataRev = ref(0);
@@ -57,7 +65,7 @@ function createAsFormPatch(def, getData) {
57
65
  function snapshot() {
58
66
  const cur = getData();
59
67
  if (cur.value === void 0) return void 0;
60
- return deepClone(toRaw(cur));
68
+ return deepClone(cur, toRaw);
61
69
  }
62
70
  function capture() {
63
71
  baseline = snapshot();
@@ -87,46 +95,62 @@ function createAsFormPatch(def, getData) {
87
95
  });
88
96
  const isDirty = computed(() => diff.value.isDirty);
89
97
  const changes = computed(() => diff.value.changes);
98
+ const dirtyPaths = computed(() => collectDirtyPaths(changes.value));
90
99
  function getPatch(opts) {
91
100
  if (baseline === void 0) return {};
92
- const frozen = deepClone(toRaw(getData()));
101
+ const frozen = deepClone(getData(), toRaw);
93
102
  return buildFormDiff(def(), baseline, frozen, opts).patch;
94
103
  }
95
104
  function getChanges() {
96
105
  if (baseline === void 0) return [];
97
- const frozen = deepClone(toRaw(getData()));
106
+ const frozen = deepClone(getData(), toRaw);
98
107
  return buildFormDiff(def(), baseline, frozen).changes;
99
108
  }
109
+ function isDirtyPath(path) {
110
+ return dirtyPaths.value.has(path);
111
+ }
100
112
  function rebase() {
101
113
  capture();
102
114
  }
115
+ /**
116
+ * 3-way rebase onto a fresh upstream snapshot (see
117
+ * {@link AsFormPatchHandle.rebaseOnto}). One reactive write, then an explicit
118
+ * re-baseline to the upstream snapshot.
119
+ */
120
+ function rebaseOnto(upstream, opts) {
121
+ const u = deepClone(upstream, toRaw);
122
+ if (baseline === void 0) {
123
+ const before = deepClone(getData(), toRaw);
124
+ setByPath(getData(), "", u.value);
125
+ if (unionVariantChanged(def(), before, u)) onRebased?.();
126
+ capture();
127
+ return {
128
+ conflicts: [],
129
+ reapplied: []
130
+ };
131
+ }
132
+ const current = deepClone(getData(), toRaw);
133
+ const { next, conflicts, reapplied } = buildFormRebase(def(), baseline, current, u, opts);
134
+ const variantMoved = unionVariantChanged(def(), current, next);
135
+ setByPath(getData(), "", next.value);
136
+ baseline = u;
137
+ baselineRev.value++;
138
+ if (variantMoved) onRebased?.();
139
+ return {
140
+ conflicts,
141
+ reapplied
142
+ };
143
+ }
103
144
  return {
104
145
  isDirty,
105
146
  changes,
106
147
  getPatch,
107
148
  getChanges,
108
- rebase
149
+ isDirtyPath,
150
+ rebase,
151
+ rebaseOnto
109
152
  };
110
153
  }
111
- /**
112
- * Structural deep clone of plain JSON-ish data (objects / arrays / primitives /
113
- * Date). Used to freeze the baseline so later edits to the live form data don't
114
- * mutate it. `structuredClone` would throw on Vue proxies / functions; this
115
- * walks own-enumerable keys and copies leaves by value.
116
- */
117
- function deepClone(value) {
118
- if (value === null || typeof value !== "object") return value;
119
- if (value instanceof Date) return new Date(value.getTime());
120
- if (Array.isArray(value)) {
121
- const out = [];
122
- for (let i = 0; i < value.length; i++) out.push(deepClone(toRaw(value[i])));
123
- return out;
124
- }
125
- const src = value;
126
- const out = {};
127
- for (const k of Object.keys(src)) out[k] = deepClone(toRaw(src[k]));
128
- return out;
129
- }
130
154
  const NOT_TRACKING_MSG = "useAsFormPatch(): change tracking is not enabled on this form. Pass `track-changes` to <AsForm> (or `trackChanges: true` to useAsForm).";
131
155
  /**
132
156
  * Reactive read-only access to the form's change-tracking handle from any
@@ -295,6 +319,7 @@ function useAsState(opts) {
295
319
  const EMPTY_CHANGES = Object.freeze([]);
296
320
  const EMPTY_GET_PATCH = () => ({});
297
321
  const EMPTY_GET_CHANGES = () => [];
322
+ const EMPTY_IS_DIRTY_PATH = () => false;
298
323
  /**
299
324
  * Composable backing `<AsForm>`. Owns the entire form state machine —
300
325
  * data container, internal validator, external-error dismissal, action
@@ -330,7 +355,10 @@ function useAsForm(options) {
330
355
  context: formContext.value ?? {}
331
356
  })
332
357
  });
333
- const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value) : void 0;
358
+ const remountKey = ref(0);
359
+ const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value, () => {
360
+ remountKey.value++;
361
+ }) : void 0;
334
362
  if (patch) provide(FORM_PATCH_KEY, patch);
335
363
  const reset = patch ? async () => {
336
364
  await resetState();
@@ -386,7 +414,8 @@ function useAsForm(options) {
386
414
  isDirty: patch?.isDirty.value ?? false,
387
415
  changes: patch ? patch.changes.value : EMPTY_CHANGES,
388
416
  getPatch: patch ? patch.getPatch : EMPTY_GET_PATCH,
389
- getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES
417
+ getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES,
418
+ isDirtyPath: patch ? patch.isDirtyPath : EMPTY_IS_DIRTY_PATH
390
419
  }));
391
420
  function supportsAction(def, actionId) {
392
421
  return getDeclaredFormActions(def).some((a) => a.id === actionId);
@@ -446,7 +475,8 @@ function useAsForm(options) {
446
475
  dismissFormError: ext.dismissForm,
447
476
  formContext,
448
477
  handleChange,
449
- patch
478
+ patch,
479
+ remountKey
450
480
  };
451
481
  }
452
482
  //#endregion
@@ -561,7 +591,12 @@ var as_form_default = /* @__PURE__ */ defineComponent({
561
591
  },
562
592
  getPatch: form.patch ? form.patch.getPatch : () => ({}),
563
593
  getChanges: form.patch ? form.patch.getChanges : () => [],
564
- rebase: form.patch ? form.patch.rebase : () => {}
594
+ isDirtyPath: form.patch ? form.patch.isDirtyPath : () => false,
595
+ rebase: form.patch ? form.patch.rebase : () => {},
596
+ rebaseOnto: form.patch ? form.patch.rebaseOnto : () => ({
597
+ conflicts: [],
598
+ reapplied: []
599
+ })
565
600
  });
566
601
  return (_ctx, _cache) => {
567
602
  return openBlock(), createElementBlock("form", {
@@ -571,7 +606,10 @@ var as_form_default = /* @__PURE__ */ defineComponent({
571
606
  }, [
572
607
  renderSlot(_ctx.$slots, "form.header", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
573
608
  renderSlot(_ctx.$slots, "form.before", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
574
- createVNode(as_field_default, { field: __props.def.rootField }, null, 8, ["field"]),
609
+ (openBlock(), createBlock(as_field_default, {
610
+ key: unref(form).remountKey.value,
611
+ field: __props.def.rootField
612
+ }, null, 8, ["field"])),
575
613
  renderSlot(_ctx.$slots, "form.after", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
576
614
  unref(form).formError.value ? renderSlot(_ctx.$slots, "form.error", mergeProps({ key: 0 }, unref(form).slotProps.value, {
577
615
  message: unref(form).formError.value,
package/dist/as-form.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const require_as_form = require("./as-form-CrMA0HdQ.cjs");
1
+ const require_as_form = require("./as-form-B4DKpAVk.cjs");
2
2
  module.exports = require_as_form.as_form_default;
@@ -1,2 +1,2 @@
1
- import { n as _default, t as Props } from "./as-form.vue-Dg_5GYWG.cjs";
1
+ import { n as _default, t as Props } from "./as-form.vue-liXf4fgq.cjs";
2
2
  export { Props, _default as default };
@@ -1,2 +1,2 @@
1
- import { n as _default, t as Props } from "./as-form.vue-2NZSk1F9.mjs";
1
+ import { n as _default, t as Props } from "./as-form.vue-BEXuZ83-.mjs";
2
2
  export { Props, _default as default };
package/dist/as-form.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as as_form_default } from "./as-form-OsyP7m-A.mjs";
1
+ import { t as as_form_default } from "./as-form-me0zA-52.mjs";
2
2
  export { as_form_default as default };