@atscript/vue-form 0.1.102 → 0.1.104
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/{as-action-lLooKipk.cjs → as-action-B712j9xf.cjs} +4 -0
- package/dist/{as-action-BoZtbaXt.mjs → as-action-CCjN5EFP.mjs} +4 -0
- package/dist/as-action.cjs +1 -1
- package/dist/as-action.d.cts +1 -1
- package/dist/as-action.d.mts +1 -1
- package/dist/as-action.mjs +1 -1
- package/dist/{as-action.vue-BCbopk0S.d.cts → as-action.vue-BL8_a0fE.d.cts} +1 -1
- package/dist/{as-action.vue-BtQX09n-.d.mts → as-action.vue-CcOPK2qI.d.mts} +1 -1
- package/dist/{as-array-B_LjJ0oi.mjs → as-array-BGG38Y1L.mjs} +8 -4
- package/dist/{as-array-DAeDxzYV.cjs → as-array-DVc9_ood.cjs} +8 -4
- package/dist/as-array.cjs +1 -1
- package/dist/as-array.d.cts +1 -1
- package/dist/as-array.d.mts +1 -1
- package/dist/as-array.mjs +1 -1
- package/dist/{as-array.vue-Ci4xO292.d.cts → as-array.vue-Be1tDTdH.d.cts} +1 -1
- package/dist/{as-array.vue-I42wuF6e.d.mts → as-array.vue-CtBBY2gF.d.mts} +1 -1
- package/dist/{as-checkbox-dN1K-rJ_.mjs → as-checkbox-DwLRHmXj.mjs} +5 -1
- package/dist/{as-checkbox-Cva4XHFW.cjs → as-checkbox-sgF16SuJ.cjs} +5 -1
- package/dist/as-checkbox.cjs +1 -1
- package/dist/as-checkbox.d.cts +1 -1
- package/dist/as-checkbox.d.mts +1 -1
- package/dist/as-checkbox.mjs +1 -1
- package/dist/{as-checkbox.vue-BoY72SV8.d.mts → as-checkbox.vue-B6zk5IbU.d.cts} +1 -1
- package/dist/{as-checkbox.vue-DqIm0ms2.d.cts → as-checkbox.vue-CXpsn_WG.d.mts} +1 -1
- package/dist/{as-collapsible-B0rlXU0x.cjs → as-collapsible-BQ-eYR0v.cjs} +2 -2
- package/dist/{as-collapsible-DQOxKGGj.mjs → as-collapsible-DFTzB5Ar.mjs} +2 -2
- package/dist/as-collapsible.cjs +1 -1
- package/dist/as-collapsible.d.cts +1 -18
- package/dist/as-collapsible.d.mts +1 -19
- package/dist/as-collapsible.mjs +1 -1
- package/dist/as-collapsible.vue-34S3TEq5.d.cts +20 -0
- package/dist/as-collapsible.vue-CIfMlTQ6.d.mts +20 -0
- package/dist/{as-date-CsBBqja_.mjs → as-date-CfCa2Kp1.mjs} +6 -2
- package/dist/{as-date-Cizo8gZx.cjs → as-date-hCCTd52o.cjs} +6 -2
- package/dist/as-date.cjs +1 -1
- package/dist/as-date.d.cts +1 -1
- package/dist/as-date.d.mts +1 -1
- package/dist/as-date.mjs +1 -1
- package/dist/{as-date.vue-BfSVrED7.d.mts → as-date.vue-BZiy1s5s.d.cts} +1 -1
- package/dist/{as-date.vue-SdlJH5fn.d.cts → as-date.vue-DWWoojir.d.mts} +1 -1
- package/dist/{as-datetime-D_Wbrj9t.mjs → as-datetime-B1I_zREk.mjs} +6 -2
- package/dist/{as-datetime-Cjbprmxg.cjs → as-datetime-DDlWca2Q.cjs} +6 -2
- package/dist/as-datetime.cjs +1 -1
- package/dist/as-datetime.d.cts +1 -1
- package/dist/as-datetime.d.mts +1 -1
- package/dist/as-datetime.mjs +1 -1
- package/dist/{as-datetime.vue-B47V1siL.d.mts → as-datetime.vue-DYEHokj_.d.cts} +1 -1
- package/dist/{as-datetime.vue-DUp_TDOW.d.cts → as-datetime.vue-NblA-SVE.d.mts} +1 -1
- package/dist/{as-decimal-DtQuZmPA.mjs → as-decimal-CQM5OfqB.mjs} +5 -1
- package/dist/{as-decimal-DhrdOvUQ.cjs → as-decimal-DM10ycwv.cjs} +5 -1
- package/dist/as-decimal.cjs +1 -1
- package/dist/as-decimal.d.cts +1 -1
- package/dist/as-decimal.d.mts +1 -1
- package/dist/as-decimal.mjs +1 -1
- package/dist/{as-decimal.vue-Bhq4C1Ix.d.cts → as-decimal.vue-BoNNIhbL.d.mts} +1 -1
- package/dist/{as-decimal.vue-DitPZpS_.d.mts → as-decimal.vue-D3eGMvC0.d.cts} +1 -1
- package/dist/{as-field-CDB-HIfz.mjs → as-field-5rz47opo.mjs} +10 -4
- package/dist/{as-field-shell-zeno02I1.cjs → as-field-shell-C4epFfvE.cjs} +30 -24
- package/dist/{as-field-shell-D0mNXfQZ.mjs → as-field-shell-DLvPV8WS.mjs} +30 -24
- package/dist/as-field-shell.cjs +1 -1
- package/dist/as-field-shell.d.cts +1 -1
- package/dist/as-field-shell.d.mts +1 -1
- package/dist/as-field-shell.mjs +1 -1
- package/dist/{as-field-shell.vue-Ct1ixFqc.d.cts → as-field-shell.vue-CLDHaWN0.d.cts} +1 -1
- package/dist/{as-field-shell.vue-K4Flr3gw.d.mts → as-field-shell.vue-DssDDSal.d.mts} +1 -1
- package/dist/{as-field-DGnC4dew.cjs → as-field-vktmRs-V.cjs} +10 -4
- package/dist/as-field.cjs +1 -1
- package/dist/as-field.d.cts +1 -24
- package/dist/as-field.d.mts +1 -25
- package/dist/as-field.mjs +1 -1
- package/dist/as-field.vue-A2cJj-Ih.d.cts +26 -0
- package/dist/as-field.vue-CmjIDgQ5.d.mts +26 -0
- package/dist/{as-form-DaAnsFVF.cjs → as-form-B4DKpAVk.cjs} +235 -11
- package/dist/{as-form-8YNdb-49.mjs → as-form-me0zA-52.mjs} +232 -14
- package/dist/as-form.cjs +1 -1
- package/dist/as-form.d.cts +1 -1
- package/dist/as-form.d.mts +1 -1
- package/dist/as-form.mjs +1 -1
- package/dist/as-form.vue-D5d1Y2ff.d.cts +1425 -0
- package/dist/as-form.vue-DH3phgWP.d.mts +1425 -0
- package/dist/{as-input-CwPkK9qR.cjs → as-input-Bp1Ltp15.cjs} +6 -2
- package/dist/{as-input-VOGvI31W.mjs → as-input-DF--i7kY.mjs} +6 -2
- package/dist/{as-input-control-BYo_dfQA.cjs → as-input-control-B7F2q99A.cjs} +4 -0
- package/dist/{as-input-control-Bx-isLZN.mjs → as-input-control-BofS4mCo.mjs} +4 -0
- package/dist/as-input.cjs +1 -1
- package/dist/as-input.d.cts +1 -1
- package/dist/as-input.d.mts +1 -1
- package/dist/as-input.mjs +1 -1
- package/dist/{as-input.vue-BHsVVncq.d.cts → as-input.vue-CCAEWqK8.d.mts} +1 -1
- package/dist/{as-input.vue-DgZrW4pc.d.mts → as-input.vue-Nc9Q3RPh.d.cts} +1 -1
- package/dist/{as-iterator-BWXIzbJF.cjs → as-iterator-Bd-bKTMY.cjs} +2 -2
- package/dist/{as-iterator---gxkT-L.mjs → as-iterator-BjIF-O-J.mjs} +2 -2
- package/dist/as-iterator.cjs +1 -1
- package/dist/as-iterator.d.cts +1 -20
- package/dist/as-iterator.d.mts +1 -21
- package/dist/as-iterator.mjs +1 -1
- package/dist/as-iterator.vue-BZeozFr-.d.cts +22 -0
- package/dist/as-iterator.vue-C8HW6n25.d.mts +22 -0
- package/dist/{as-multi-select-D7XGZS3L.cjs → as-multi-select-b1l34vP5.cjs} +5 -1
- package/dist/{as-multi-select-DGipqqGz.mjs → as-multi-select-mp-aM3LV.mjs} +5 -1
- package/dist/as-multi-select.cjs +1 -1
- package/dist/as-multi-select.d.cts +1 -1
- package/dist/as-multi-select.d.mts +1 -1
- package/dist/as-multi-select.mjs +1 -1
- package/dist/{as-multi-select.vue--bavpcN-.d.cts → as-multi-select.vue-D9mseRFm.d.cts} +1 -1
- package/dist/{as-multi-select.vue-rsDUkzxa.d.mts → as-multi-select.vue-DKpOsOmM.d.mts} +1 -1
- package/dist/{as-number-BIhBVXih.mjs → as-number-De9zTltV.mjs} +6 -2
- package/dist/{as-number-BQWwSipg.cjs → as-number-joydTMcJ.cjs} +6 -2
- package/dist/as-number.cjs +1 -1
- package/dist/as-number.d.cts +1 -1
- package/dist/as-number.d.mts +1 -1
- package/dist/as-number.mjs +1 -1
- package/dist/{as-number.vue-D2DrbnQu.d.cts → as-number.vue-CZC0-zs8.d.cts} +1 -1
- package/dist/{as-number.vue-Oml2y6Nt.d.mts → as-number.vue-DrvjSOdq.d.mts} +1 -1
- package/dist/{as-object-Dr7CttaK.mjs → as-object-CQMTYtcY.mjs} +8 -4
- package/dist/{as-object-BJxJJM13.cjs → as-object-DgeItRdH.cjs} +8 -4
- package/dist/as-object.cjs +1 -1
- package/dist/as-object.d.cts +1 -1
- package/dist/as-object.d.mts +1 -1
- package/dist/as-object.mjs +1 -1
- package/dist/{as-object.vue-BljKYrMo.d.cts → as-object.vue-BItO19fw.d.mts} +1 -1
- package/dist/{as-object.vue-DAq7Kha3.d.mts → as-object.vue-DbHog5M-.d.cts} +1 -1
- package/dist/{as-paragraph-GIGj6UgL.mjs → as-paragraph-BP3-0EY4.mjs} +4 -0
- package/dist/{as-paragraph-BA-c863H.cjs → as-paragraph-CPhDTH_V.cjs} +4 -0
- package/dist/as-paragraph.cjs +1 -1
- package/dist/as-paragraph.d.cts +1 -1
- package/dist/as-paragraph.d.mts +1 -1
- package/dist/as-paragraph.mjs +1 -1
- package/dist/{as-paragraph.vue-CBU_olev.d.mts → as-paragraph.vue-BGDpjceO.d.mts} +1 -1
- package/dist/{as-paragraph.vue-LxXA7VPD.d.cts → as-paragraph.vue-D36pdOoc.d.cts} +1 -1
- package/dist/{as-radio-CdK8gCkN.cjs → as-radio-Dlmxk1vr.cjs} +5 -1
- package/dist/{as-radio-DBW6XzlW.mjs → as-radio-SwpgM1xP.mjs} +5 -1
- package/dist/as-radio.cjs +1 -1
- package/dist/as-radio.d.cts +1 -1
- package/dist/as-radio.d.mts +1 -1
- package/dist/as-radio.mjs +1 -1
- package/dist/{as-radio.vue-Dk4nlz-d.d.cts → as-radio.vue-EfrhKcBF.d.cts} +1 -1
- package/dist/{as-radio.vue-TVMliE1e.d.mts → as-radio.vue-WywkI-zY.d.mts} +1 -1
- package/dist/{as-ref-D6zp_PIG.mjs → as-ref-8JnEuan0.mjs} +6 -2
- package/dist/{as-ref-BOAHVeR-.cjs → as-ref-BCpvUq8E.cjs} +6 -2
- package/dist/as-ref.cjs +1 -1
- package/dist/as-ref.d.cts +1 -1
- package/dist/as-ref.d.mts +1 -1
- package/dist/as-ref.mjs +1 -1
- package/dist/{as-ref.vue-CUxxGqyg.d.mts → as-ref.vue-BD_vsu7I.d.mts} +1 -1
- package/dist/{as-ref.vue-CnzqRblA.d.cts → as-ref.vue-Cxg-A5QR.d.cts} +1 -1
- package/dist/{as-select-BiS6USZW.mjs → as-select-C3ukCyGQ.mjs} +5 -1
- package/dist/{as-select-B2jqgF38.cjs → as-select-CtSNZXtC.cjs} +5 -1
- package/dist/as-select.cjs +1 -1
- package/dist/as-select.d.cts +1 -1
- package/dist/as-select.d.mts +1 -1
- package/dist/as-select.mjs +1 -1
- package/dist/{as-select.vue-CKjAg-Y2.d.mts → as-select.vue-DoAG5njV.d.mts} +1 -1
- package/dist/{as-select.vue-ZZtjsmcO.d.cts → as-select.vue-hd94jaH_.d.cts} +1 -1
- package/dist/{as-time-BN-Vc-iw.mjs → as-time-QJ9YiVj9.mjs} +6 -2
- package/dist/{as-time-DdQnNIQX.cjs → as-time-nAPtGL3t.cjs} +6 -2
- package/dist/as-time.cjs +1 -1
- package/dist/as-time.d.cts +1 -1
- package/dist/as-time.d.mts +1 -1
- package/dist/as-time.mjs +1 -1
- package/dist/{as-time.vue-BCEE4uCV.d.mts → as-time.vue-DMy_Au0C.d.cts} +1 -1
- package/dist/{as-time.vue-BUAvE5m3.d.cts → as-time.vue-DgW7qSAM.d.mts} +1 -1
- package/dist/{as-tuple-DaY_Mw-y.cjs → as-tuple-CFn7aZYt.cjs} +8 -4
- package/dist/{as-tuple-BUzs7nm6.mjs → as-tuple-CJXpGGKj.mjs} +8 -4
- package/dist/as-tuple.cjs +1 -1
- package/dist/as-tuple.d.cts +1 -1
- package/dist/as-tuple.d.mts +1 -1
- package/dist/as-tuple.mjs +1 -1
- package/dist/{as-tuple.vue-WN7k43tv.d.mts → as-tuple.vue-BDwz5FeN.d.mts} +1 -1
- package/dist/{as-tuple.vue-cy0ThAGl.d.cts → as-tuple.vue-DZ2Soqyw.d.cts} +1 -1
- package/dist/{as-union-Cz5cm7BP.cjs → as-union-BDRNBacb.cjs} +6 -2
- package/dist/{as-union-B5OFQLJX.mjs → as-union-DZWgGqqM.mjs} +6 -2
- package/dist/as-union.cjs +1 -1
- package/dist/as-union.d.cts +1 -1
- package/dist/as-union.d.mts +1 -1
- package/dist/as-union.mjs +1 -1
- package/dist/{as-union.vue-AqH35ozq.d.cts → as-union.vue-D9Mh0M3_.d.mts} +1 -1
- package/dist/{as-union.vue-Ew3Bany8.d.mts → as-union.vue-DQKWNuhd.d.cts} +1 -1
- package/dist/index.cjs +28 -27
- package/dist/index.d.cts +24 -996
- package/dist/index.d.mts +24 -996
- package/dist/index.mjs +28 -28
- package/dist/{types-CV9ss4UN.d.mts → types-B8aPXWL5.d.cts} +13 -0
- package/dist/{types-BYfe1QEF.d.cts → types-nuUi10Kl.d.mts} +13 -0
- package/dist/{use-as-date-XLE8HBbo.mjs → use-as-date-B6RGkjjB.mjs} +4 -0
- package/dist/{use-as-date-DY8b1_V4.cjs → use-as-date-CslHBPmQ.cjs} +4 -0
- package/dist/{use-as-optional-add-flow-C85F2VoU.cjs → use-as-optional-add-flow-BXKiK1Kb.cjs} +1 -1
- package/dist/{use-as-optional-add-flow-CQctXLZI.mjs → use-as-optional-add-flow-B_gr8sX-.mjs} +1 -1
- package/dist/{use-form-context-bAj7UoSe.mjs → use-form-context-D6J1WTyG.mjs} +2 -1
- package/dist/{use-form-context-Dwr8Ai1v.cjs → use-form-context-DpGiGUvJ.cjs} +7 -0
- package/package.json +6 -6
- package/dist/as-form.vue-5FyX_1CQ.d.cts +0 -239
- package/dist/as-form.vue-ZykHT4gZ.d.mts +0 -239
- /package/dist/{use-as-nested-sections-store-jdMRxjBE.cjs → use-as-nested-sections-store--E0Ijpt5.cjs} +0 -0
- /package/dist/{use-as-nested-sections-store-lhi0z5z1.mjs → use-as-nested-sections-store-DVRmG7V0.mjs} +0 -0
- /package/dist/{use-as-value-help-uANI3zWa.cjs → use-as-value-help-BsAriqCI.cjs} +0 -0
- /package/dist/{use-as-value-help-CBykDEjZ.mjs → use-as-value-help-DrISSxCz.mjs} +0 -0
|
@@ -1,10 +1,182 @@
|
|
|
1
|
-
const require_use_form_context = require("./use-form-context-
|
|
2
|
-
const require_use_as_nested_sections_store = require("./use-as-nested-sections-store
|
|
3
|
-
const require_use_as_value_help = require("./use-as-value-help-
|
|
4
|
-
const require_as_field = require("./as-field-
|
|
5
|
-
const require_as_iterator = require("./as-iterator-
|
|
1
|
+
const require_use_form_context = require("./use-form-context-DpGiGUvJ.cjs");
|
|
2
|
+
const require_use_as_nested_sections_store = require("./use-as-nested-sections-store--E0Ijpt5.cjs");
|
|
3
|
+
const require_use_as_value_help = require("./use-as-value-help-BsAriqCI.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
|
+
//#region src/composables/use-as-form-patch.ts
|
|
9
|
+
/**
|
|
10
|
+
* Internal factory invoked by `useAsForm` when `trackChanges` is enabled. Owns
|
|
11
|
+
* the deep-clone baseline lifecycle:
|
|
12
|
+
*
|
|
13
|
+
* - captures a DEEP CLONE of the wrapped form-data the moment tracking becomes
|
|
14
|
+
* active (and, if data is not yet available, when it first arrives),
|
|
15
|
+
* - re-baselines on `reset()` (the form's own reset path calls `rebase()`),
|
|
16
|
+
* - exposes `rebase()` so a consumer can re-baseline after a successful save.
|
|
17
|
+
*
|
|
18
|
+
* The clone is mandatory: `buildFormDiff` keeps LIVE references into the
|
|
19
|
+
* supplied baseline, so a shared reference would be mutated by subsequent
|
|
20
|
+
* edits and the diff would always come back empty.
|
|
21
|
+
*
|
|
22
|
+
* Reactivity: a single revert-aware `diff` computed drives `isDirty` /
|
|
23
|
+
* `changes`. `buildFormDiff` reads most reactive leaves of the live container
|
|
24
|
+
* (via `getByPath` + the `deepEqual` walk), so the computed auto-tracks scalar
|
|
25
|
+
* edits, nested-object edits, and `$update` edits to EXISTING keyed-array items.
|
|
26
|
+
* But the `$insert` branch pushes a freshly-added (not-yet-saved) keyed-array
|
|
27
|
+
* element BY REFERENCE without reading its leaves, so editing such a row's
|
|
28
|
+
* non-key leaves (qty/description) would NOT invalidate the computed. To close
|
|
29
|
+
* that blind spot we add a single deep `watch` on the live data — created ONLY
|
|
30
|
+
* when tracking is active, owned by the component scope (it disposes on unmount)
|
|
31
|
+
* — that bumps `dataRev`; the `diff` computed reads `dataRev` so every leaf
|
|
32
|
+
* mutation, including an inserted row's, re-evaluates it. OFF stays zero-cost:
|
|
33
|
+
* `createAsFormPatch` is never called when tracking is disabled, so neither the
|
|
34
|
+
* baseline nor this watcher is ever created. It recomputes only when the live
|
|
35
|
+
* data (`dataRev`) or the baseline (`baselineRev`) changes, and Vue caches the
|
|
36
|
+
* result between reads.
|
|
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). 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).
|
|
52
|
+
*/
|
|
53
|
+
function createAsFormPatch(def, getData, onRebased) {
|
|
54
|
+
let baseline;
|
|
55
|
+
const baselineRev = (0, vue.ref)(0);
|
|
56
|
+
const dataRev = (0, vue.ref)(0);
|
|
57
|
+
/**
|
|
58
|
+
* Deep-clones the current wrapped container, or returns `undefined` when no
|
|
59
|
+
* real domain data exists yet. "No data yet" = the container has no `value`
|
|
60
|
+
* key or its `value` is `undefined` (the `{}` internal fallback, or a
|
|
61
|
+
* `:form-data` ref that has not resolved). Capturing the empty fallback as the
|
|
62
|
+
* baseline would make every field read as dirty once real data replaces it,
|
|
63
|
+
* so we wait for a genuine `{ value: … }` container.
|
|
64
|
+
*/
|
|
65
|
+
function snapshot() {
|
|
66
|
+
const cur = getData();
|
|
67
|
+
if (cur.value === void 0) return void 0;
|
|
68
|
+
return (0, _atscript_ui.deepClone)(cur, vue.toRaw);
|
|
69
|
+
}
|
|
70
|
+
function capture() {
|
|
71
|
+
baseline = snapshot();
|
|
72
|
+
baselineRev.value++;
|
|
73
|
+
}
|
|
74
|
+
capture();
|
|
75
|
+
if (baseline === void 0) {
|
|
76
|
+
const stop = (0, vue.watch)(() => getData().value, () => {
|
|
77
|
+
if (baseline !== void 0) return;
|
|
78
|
+
capture();
|
|
79
|
+
if (baseline !== void 0) stop();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
(0, vue.watch)(() => getData(), () => dataRev.value++, { deep: true });
|
|
83
|
+
const diff = (0, vue.computed)(() => {
|
|
84
|
+
dataRev.value;
|
|
85
|
+
baselineRev.value;
|
|
86
|
+
if (baseline === void 0) return {
|
|
87
|
+
isDirty: false,
|
|
88
|
+
changes: []
|
|
89
|
+
};
|
|
90
|
+
const result = (0, _atscript_ui.buildFormDiff)(def(), baseline, getData());
|
|
91
|
+
return {
|
|
92
|
+
isDirty: result.isDirty,
|
|
93
|
+
changes: result.changes
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
const isDirty = (0, vue.computed)(() => diff.value.isDirty);
|
|
97
|
+
const changes = (0, vue.computed)(() => diff.value.changes);
|
|
98
|
+
const dirtyPaths = (0, vue.computed)(() => (0, _atscript_ui.collectDirtyPaths)(changes.value));
|
|
99
|
+
function getPatch(opts) {
|
|
100
|
+
if (baseline === void 0) return {};
|
|
101
|
+
const frozen = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
|
|
102
|
+
return (0, _atscript_ui.buildFormDiff)(def(), baseline, frozen, opts).patch;
|
|
103
|
+
}
|
|
104
|
+
function getChanges() {
|
|
105
|
+
if (baseline === void 0) return [];
|
|
106
|
+
const frozen = (0, _atscript_ui.deepClone)(getData(), vue.toRaw);
|
|
107
|
+
return (0, _atscript_ui.buildFormDiff)(def(), baseline, frozen).changes;
|
|
108
|
+
}
|
|
109
|
+
function isDirtyPath(path) {
|
|
110
|
+
return dirtyPaths.value.has(path);
|
|
111
|
+
}
|
|
112
|
+
function rebase() {
|
|
113
|
+
capture();
|
|
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
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
isDirty,
|
|
146
|
+
changes,
|
|
147
|
+
getPatch,
|
|
148
|
+
getChanges,
|
|
149
|
+
isDirtyPath,
|
|
150
|
+
rebase,
|
|
151
|
+
rebaseOnto
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const NOT_TRACKING_MSG = "useAsFormPatch(): change tracking is not enabled on this form. Pass `track-changes` to <AsForm> (or `trackChanges: true` to useAsForm).";
|
|
155
|
+
/**
|
|
156
|
+
* Reactive read-only access to the form's change-tracking handle from any
|
|
157
|
+
* descendant of an `<AsForm track-changes>`. Mirrors the `useAsData` /
|
|
158
|
+
* `useAsPath` injector pattern.
|
|
159
|
+
*
|
|
160
|
+
* THROWS when called outside a form, or inside a form that did not enable
|
|
161
|
+
* `track-changes` — fail loud rather than silently report "not dirty".
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```vue
|
|
165
|
+
* <script setup lang="ts">
|
|
166
|
+
* import { useAsFormPatch } from "@atscript/vue-form";
|
|
167
|
+
* const { isDirty, getPatch } = useAsFormPatch();
|
|
168
|
+
* <\/script>
|
|
169
|
+
* <template>
|
|
170
|
+
* <button :disabled="!isDirty" @click="save(getPatch())">Save</button>
|
|
171
|
+
* </template>
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
function useAsFormPatch() {
|
|
175
|
+
const handle = (0, vue.inject)(require_use_form_context.FORM_PATCH_KEY, void 0);
|
|
176
|
+
if (!handle) throw new Error(NOT_TRACKING_MSG);
|
|
177
|
+
return handle;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
8
180
|
//#region src/composables/use-as-external-errors.ts
|
|
9
181
|
/**
|
|
10
182
|
* Local dismissal state for externally-supplied errors.
|
|
@@ -144,6 +316,10 @@ function useAsState(opts) {
|
|
|
144
316
|
}
|
|
145
317
|
//#endregion
|
|
146
318
|
//#region src/composables/use-as-form.ts
|
|
319
|
+
const EMPTY_CHANGES = Object.freeze([]);
|
|
320
|
+
const EMPTY_GET_PATCH = () => ({});
|
|
321
|
+
const EMPTY_GET_CHANGES = () => [];
|
|
322
|
+
const EMPTY_IS_DIRTY_PATH = () => false;
|
|
147
323
|
/**
|
|
148
324
|
* Composable backing `<AsForm>`. Owns the entire form state machine —
|
|
149
325
|
* data container, internal validator, external-error dismissal, action
|
|
@@ -179,6 +355,15 @@ function useAsForm(options) {
|
|
|
179
355
|
context: formContext.value ?? {}
|
|
180
356
|
})
|
|
181
357
|
});
|
|
358
|
+
const remountKey = (0, vue.ref)(0);
|
|
359
|
+
const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value, () => {
|
|
360
|
+
remountKey.value++;
|
|
361
|
+
}) : void 0;
|
|
362
|
+
if (patch) (0, vue.provide)(require_use_form_context.FORM_PATCH_KEY, patch);
|
|
363
|
+
const reset = patch ? async () => {
|
|
364
|
+
await resetState();
|
|
365
|
+
patch.rebase();
|
|
366
|
+
} : resetState;
|
|
182
367
|
(0, vue.provide)(require_use_form_context.ROOT_DATA_KEY, data);
|
|
183
368
|
(0, vue.provide)(require_use_form_context.PATH_PREFIX_KEY, (0, vue.computed)(() => ""));
|
|
184
369
|
(0, vue.provide)(require_use_form_context.TYPES_KEY, (0, vue.computed)(() => options.types()));
|
|
@@ -220,12 +405,17 @@ function useAsForm(options) {
|
|
|
220
405
|
loading: options.loading?.() === true,
|
|
221
406
|
submitText: submitText.value,
|
|
222
407
|
submit: onSubmit,
|
|
223
|
-
reset
|
|
408
|
+
reset,
|
|
224
409
|
clearErrors,
|
|
225
410
|
setErrors,
|
|
226
411
|
dismissError: ext.dismissAt,
|
|
227
412
|
dismissFormError: ext.dismissForm,
|
|
228
|
-
formContext: formContext.value
|
|
413
|
+
formContext: formContext.value,
|
|
414
|
+
isDirty: patch?.isDirty.value ?? false,
|
|
415
|
+
changes: patch ? patch.changes.value : EMPTY_CHANGES,
|
|
416
|
+
getPatch: patch ? patch.getPatch : EMPTY_GET_PATCH,
|
|
417
|
+
getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES,
|
|
418
|
+
isDirtyPath: patch ? patch.isDirtyPath : EMPTY_IS_DIRTY_PATH
|
|
229
419
|
}));
|
|
230
420
|
function supportsAction(def, actionId) {
|
|
231
421
|
return (0, _atscript_ui.getDeclaredFormActions)(def).some((a) => a.id === actionId);
|
|
@@ -271,7 +461,7 @@ function useAsForm(options) {
|
|
|
271
461
|
errors: ext.effective,
|
|
272
462
|
formError: ext.formError,
|
|
273
463
|
internalErrors,
|
|
274
|
-
reset
|
|
464
|
+
reset,
|
|
275
465
|
clearErrors,
|
|
276
466
|
setErrors,
|
|
277
467
|
onSubmit,
|
|
@@ -284,7 +474,9 @@ function useAsForm(options) {
|
|
|
284
474
|
dismissError: ext.dismissAt,
|
|
285
475
|
dismissFormError: ext.dismissForm,
|
|
286
476
|
formContext,
|
|
287
|
-
handleChange
|
|
477
|
+
handleChange,
|
|
478
|
+
patch,
|
|
479
|
+
remountKey
|
|
288
480
|
};
|
|
289
481
|
}
|
|
290
482
|
//#endregion
|
|
@@ -352,6 +544,10 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
352
544
|
loading: {
|
|
353
545
|
type: Boolean,
|
|
354
546
|
required: false
|
|
547
|
+
},
|
|
548
|
+
trackChanges: {
|
|
549
|
+
type: Boolean,
|
|
550
|
+
required: false
|
|
355
551
|
}
|
|
356
552
|
},
|
|
357
553
|
emits: [
|
|
@@ -361,7 +557,7 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
361
557
|
"unsupported-action",
|
|
362
558
|
"change"
|
|
363
559
|
],
|
|
364
|
-
setup(__props, { emit: __emit }) {
|
|
560
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
365
561
|
const props = __props;
|
|
366
562
|
const emit = __emit;
|
|
367
563
|
const form = useAsForm({
|
|
@@ -375,6 +571,7 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
375
571
|
clientFactory: () => props.clientFactory,
|
|
376
572
|
hideRootTitle: () => props.hideRootTitle,
|
|
377
573
|
loading: () => props.loading,
|
|
574
|
+
trackChanges: () => props.trackChanges,
|
|
378
575
|
emits: {
|
|
379
576
|
submit: (data) => emit("submit", data),
|
|
380
577
|
error: (errors) => emit("error", errors),
|
|
@@ -383,6 +580,24 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
383
580
|
change: (type, path, value, formData) => emit("change", type, path, value, formData)
|
|
384
581
|
}
|
|
385
582
|
});
|
|
583
|
+
__expose({
|
|
584
|
+
submit: form.onSubmit,
|
|
585
|
+
reset: form.reset,
|
|
586
|
+
get isDirty() {
|
|
587
|
+
return form.patch?.isDirty.value ?? false;
|
|
588
|
+
},
|
|
589
|
+
get changes() {
|
|
590
|
+
return form.patch?.changes.value ?? [];
|
|
591
|
+
},
|
|
592
|
+
getPatch: form.patch ? form.patch.getPatch : () => ({}),
|
|
593
|
+
getChanges: form.patch ? form.patch.getChanges : () => [],
|
|
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
|
+
})
|
|
600
|
+
});
|
|
386
601
|
return (_ctx, _cache) => {
|
|
387
602
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("form", {
|
|
388
603
|
class: "as-form",
|
|
@@ -391,7 +606,10 @@ var as_form_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
391
606
|
}, [
|
|
392
607
|
(0, vue.renderSlot)(_ctx.$slots, "form.header", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
|
|
393
608
|
(0, vue.renderSlot)(_ctx.$slots, "form.before", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
|
|
394
|
-
(0, vue.
|
|
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"])),
|
|
395
613
|
(0, vue.renderSlot)(_ctx.$slots, "form.after", (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(form).slotProps.value))),
|
|
396
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, {
|
|
397
615
|
message: (0, vue.unref)(form).formError.value,
|
|
@@ -438,6 +656,12 @@ Object.defineProperty(exports, "useAsForm", {
|
|
|
438
656
|
return useAsForm;
|
|
439
657
|
}
|
|
440
658
|
});
|
|
659
|
+
Object.defineProperty(exports, "useAsFormPatch", {
|
|
660
|
+
enumerable: true,
|
|
661
|
+
get: function() {
|
|
662
|
+
return useAsFormPatch;
|
|
663
|
+
}
|
|
664
|
+
});
|
|
441
665
|
Object.defineProperty(exports, "useAsState", {
|
|
442
666
|
enumerable: true,
|
|
443
667
|
get: function() {
|
|
@@ -1,10 +1,182 @@
|
|
|
1
|
-
import { a as CHANGE_HANDLER_KEY, c as ERRORS_KEY, d as
|
|
2
|
-
import { n as provideAsNestedSectionsStore, r as useAsNestedSectionsStore, t as DESCENDANT_ERROR_COUNTS_KEY } from "./use-as-nested-sections-store-
|
|
3
|
-
import { t as CLIENT_FACTORY_KEY } from "./use-as-value-help-
|
|
4
|
-
import { t as as_field_default } from "./as-field-
|
|
5
|
-
import { t as as_iterator_default } from "./as-iterator
|
|
6
|
-
import { computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, 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, getDeclaredFormActions, getFormValidator, iteratePathAncestors, mergeErrorMaps, resolveFormProp } from "@atscript/ui";
|
|
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
|
+
import { n as provideAsNestedSectionsStore, r as useAsNestedSectionsStore, t as DESCENDANT_ERROR_COUNTS_KEY } from "./use-as-nested-sections-store-DVRmG7V0.mjs";
|
|
3
|
+
import { t as CLIENT_FACTORY_KEY } from "./use-as-value-help-DrISSxCz.mjs";
|
|
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
|
+
//#region src/composables/use-as-form-patch.ts
|
|
9
|
+
/**
|
|
10
|
+
* Internal factory invoked by `useAsForm` when `trackChanges` is enabled. Owns
|
|
11
|
+
* the deep-clone baseline lifecycle:
|
|
12
|
+
*
|
|
13
|
+
* - captures a DEEP CLONE of the wrapped form-data the moment tracking becomes
|
|
14
|
+
* active (and, if data is not yet available, when it first arrives),
|
|
15
|
+
* - re-baselines on `reset()` (the form's own reset path calls `rebase()`),
|
|
16
|
+
* - exposes `rebase()` so a consumer can re-baseline after a successful save.
|
|
17
|
+
*
|
|
18
|
+
* The clone is mandatory: `buildFormDiff` keeps LIVE references into the
|
|
19
|
+
* supplied baseline, so a shared reference would be mutated by subsequent
|
|
20
|
+
* edits and the diff would always come back empty.
|
|
21
|
+
*
|
|
22
|
+
* Reactivity: a single revert-aware `diff` computed drives `isDirty` /
|
|
23
|
+
* `changes`. `buildFormDiff` reads most reactive leaves of the live container
|
|
24
|
+
* (via `getByPath` + the `deepEqual` walk), so the computed auto-tracks scalar
|
|
25
|
+
* edits, nested-object edits, and `$update` edits to EXISTING keyed-array items.
|
|
26
|
+
* But the `$insert` branch pushes a freshly-added (not-yet-saved) keyed-array
|
|
27
|
+
* element BY REFERENCE without reading its leaves, so editing such a row's
|
|
28
|
+
* non-key leaves (qty/description) would NOT invalidate the computed. To close
|
|
29
|
+
* that blind spot we add a single deep `watch` on the live data — created ONLY
|
|
30
|
+
* when tracking is active, owned by the component scope (it disposes on unmount)
|
|
31
|
+
* — that bumps `dataRev`; the `diff` computed reads `dataRev` so every leaf
|
|
32
|
+
* mutation, including an inserted row's, re-evaluates it. OFF stays zero-cost:
|
|
33
|
+
* `createAsFormPatch` is never called when tracking is disabled, so neither the
|
|
34
|
+
* baseline nor this watcher is ever created. It recomputes only when the live
|
|
35
|
+
* data (`dataRev`) or the baseline (`baselineRev`) changes, and Vue caches the
|
|
36
|
+
* result between reads.
|
|
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). 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).
|
|
52
|
+
*/
|
|
53
|
+
function createAsFormPatch(def, getData, onRebased) {
|
|
54
|
+
let baseline;
|
|
55
|
+
const baselineRev = ref(0);
|
|
56
|
+
const dataRev = ref(0);
|
|
57
|
+
/**
|
|
58
|
+
* Deep-clones the current wrapped container, or returns `undefined` when no
|
|
59
|
+
* real domain data exists yet. "No data yet" = the container has no `value`
|
|
60
|
+
* key or its `value` is `undefined` (the `{}` internal fallback, or a
|
|
61
|
+
* `:form-data` ref that has not resolved). Capturing the empty fallback as the
|
|
62
|
+
* baseline would make every field read as dirty once real data replaces it,
|
|
63
|
+
* so we wait for a genuine `{ value: … }` container.
|
|
64
|
+
*/
|
|
65
|
+
function snapshot() {
|
|
66
|
+
const cur = getData();
|
|
67
|
+
if (cur.value === void 0) return void 0;
|
|
68
|
+
return deepClone(cur, toRaw);
|
|
69
|
+
}
|
|
70
|
+
function capture() {
|
|
71
|
+
baseline = snapshot();
|
|
72
|
+
baselineRev.value++;
|
|
73
|
+
}
|
|
74
|
+
capture();
|
|
75
|
+
if (baseline === void 0) {
|
|
76
|
+
const stop = watch(() => getData().value, () => {
|
|
77
|
+
if (baseline !== void 0) return;
|
|
78
|
+
capture();
|
|
79
|
+
if (baseline !== void 0) stop();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
watch(() => getData(), () => dataRev.value++, { deep: true });
|
|
83
|
+
const diff = computed(() => {
|
|
84
|
+
dataRev.value;
|
|
85
|
+
baselineRev.value;
|
|
86
|
+
if (baseline === void 0) return {
|
|
87
|
+
isDirty: false,
|
|
88
|
+
changes: []
|
|
89
|
+
};
|
|
90
|
+
const result = buildFormDiff(def(), baseline, getData());
|
|
91
|
+
return {
|
|
92
|
+
isDirty: result.isDirty,
|
|
93
|
+
changes: result.changes
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
const isDirty = computed(() => diff.value.isDirty);
|
|
97
|
+
const changes = computed(() => diff.value.changes);
|
|
98
|
+
const dirtyPaths = computed(() => collectDirtyPaths(changes.value));
|
|
99
|
+
function getPatch(opts) {
|
|
100
|
+
if (baseline === void 0) return {};
|
|
101
|
+
const frozen = deepClone(getData(), toRaw);
|
|
102
|
+
return buildFormDiff(def(), baseline, frozen, opts).patch;
|
|
103
|
+
}
|
|
104
|
+
function getChanges() {
|
|
105
|
+
if (baseline === void 0) return [];
|
|
106
|
+
const frozen = deepClone(getData(), toRaw);
|
|
107
|
+
return buildFormDiff(def(), baseline, frozen).changes;
|
|
108
|
+
}
|
|
109
|
+
function isDirtyPath(path) {
|
|
110
|
+
return dirtyPaths.value.has(path);
|
|
111
|
+
}
|
|
112
|
+
function rebase() {
|
|
113
|
+
capture();
|
|
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
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
isDirty,
|
|
146
|
+
changes,
|
|
147
|
+
getPatch,
|
|
148
|
+
getChanges,
|
|
149
|
+
isDirtyPath,
|
|
150
|
+
rebase,
|
|
151
|
+
rebaseOnto
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const NOT_TRACKING_MSG = "useAsFormPatch(): change tracking is not enabled on this form. Pass `track-changes` to <AsForm> (or `trackChanges: true` to useAsForm).";
|
|
155
|
+
/**
|
|
156
|
+
* Reactive read-only access to the form's change-tracking handle from any
|
|
157
|
+
* descendant of an `<AsForm track-changes>`. Mirrors the `useAsData` /
|
|
158
|
+
* `useAsPath` injector pattern.
|
|
159
|
+
*
|
|
160
|
+
* THROWS when called outside a form, or inside a form that did not enable
|
|
161
|
+
* `track-changes` — fail loud rather than silently report "not dirty".
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```vue
|
|
165
|
+
* <script setup lang="ts">
|
|
166
|
+
* import { useAsFormPatch } from "@atscript/vue-form";
|
|
167
|
+
* const { isDirty, getPatch } = useAsFormPatch();
|
|
168
|
+
* <\/script>
|
|
169
|
+
* <template>
|
|
170
|
+
* <button :disabled="!isDirty" @click="save(getPatch())">Save</button>
|
|
171
|
+
* </template>
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
function useAsFormPatch() {
|
|
175
|
+
const handle = inject(FORM_PATCH_KEY, void 0);
|
|
176
|
+
if (!handle) throw new Error(NOT_TRACKING_MSG);
|
|
177
|
+
return handle;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
8
180
|
//#region src/composables/use-as-external-errors.ts
|
|
9
181
|
/**
|
|
10
182
|
* Local dismissal state for externally-supplied errors.
|
|
@@ -144,6 +316,10 @@ function useAsState(opts) {
|
|
|
144
316
|
}
|
|
145
317
|
//#endregion
|
|
146
318
|
//#region src/composables/use-as-form.ts
|
|
319
|
+
const EMPTY_CHANGES = Object.freeze([]);
|
|
320
|
+
const EMPTY_GET_PATCH = () => ({});
|
|
321
|
+
const EMPTY_GET_CHANGES = () => [];
|
|
322
|
+
const EMPTY_IS_DIRTY_PATH = () => false;
|
|
147
323
|
/**
|
|
148
324
|
* Composable backing `<AsForm>`. Owns the entire form state machine —
|
|
149
325
|
* data container, internal validator, external-error dismissal, action
|
|
@@ -179,6 +355,15 @@ function useAsForm(options) {
|
|
|
179
355
|
context: formContext.value ?? {}
|
|
180
356
|
})
|
|
181
357
|
});
|
|
358
|
+
const remountKey = ref(0);
|
|
359
|
+
const patch = options.trackChanges?.() ? createAsFormPatch(() => options.def(), () => data.value, () => {
|
|
360
|
+
remountKey.value++;
|
|
361
|
+
}) : void 0;
|
|
362
|
+
if (patch) provide(FORM_PATCH_KEY, patch);
|
|
363
|
+
const reset = patch ? async () => {
|
|
364
|
+
await resetState();
|
|
365
|
+
patch.rebase();
|
|
366
|
+
} : resetState;
|
|
182
367
|
provide(ROOT_DATA_KEY, data);
|
|
183
368
|
provide(PATH_PREFIX_KEY, computed(() => ""));
|
|
184
369
|
provide(TYPES_KEY, computed(() => options.types()));
|
|
@@ -220,12 +405,17 @@ function useAsForm(options) {
|
|
|
220
405
|
loading: options.loading?.() === true,
|
|
221
406
|
submitText: submitText.value,
|
|
222
407
|
submit: onSubmit,
|
|
223
|
-
reset
|
|
408
|
+
reset,
|
|
224
409
|
clearErrors,
|
|
225
410
|
setErrors,
|
|
226
411
|
dismissError: ext.dismissAt,
|
|
227
412
|
dismissFormError: ext.dismissForm,
|
|
228
|
-
formContext: formContext.value
|
|
413
|
+
formContext: formContext.value,
|
|
414
|
+
isDirty: patch?.isDirty.value ?? false,
|
|
415
|
+
changes: patch ? patch.changes.value : EMPTY_CHANGES,
|
|
416
|
+
getPatch: patch ? patch.getPatch : EMPTY_GET_PATCH,
|
|
417
|
+
getChanges: patch ? patch.getChanges : EMPTY_GET_CHANGES,
|
|
418
|
+
isDirtyPath: patch ? patch.isDirtyPath : EMPTY_IS_DIRTY_PATH
|
|
229
419
|
}));
|
|
230
420
|
function supportsAction(def, actionId) {
|
|
231
421
|
return getDeclaredFormActions(def).some((a) => a.id === actionId);
|
|
@@ -271,7 +461,7 @@ function useAsForm(options) {
|
|
|
271
461
|
errors: ext.effective,
|
|
272
462
|
formError: ext.formError,
|
|
273
463
|
internalErrors,
|
|
274
|
-
reset
|
|
464
|
+
reset,
|
|
275
465
|
clearErrors,
|
|
276
466
|
setErrors,
|
|
277
467
|
onSubmit,
|
|
@@ -284,7 +474,9 @@ function useAsForm(options) {
|
|
|
284
474
|
dismissError: ext.dismissAt,
|
|
285
475
|
dismissFormError: ext.dismissForm,
|
|
286
476
|
formContext,
|
|
287
|
-
handleChange
|
|
477
|
+
handleChange,
|
|
478
|
+
patch,
|
|
479
|
+
remountKey
|
|
288
480
|
};
|
|
289
481
|
}
|
|
290
482
|
//#endregion
|
|
@@ -352,6 +544,10 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
352
544
|
loading: {
|
|
353
545
|
type: Boolean,
|
|
354
546
|
required: false
|
|
547
|
+
},
|
|
548
|
+
trackChanges: {
|
|
549
|
+
type: Boolean,
|
|
550
|
+
required: false
|
|
355
551
|
}
|
|
356
552
|
},
|
|
357
553
|
emits: [
|
|
@@ -361,7 +557,7 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
361
557
|
"unsupported-action",
|
|
362
558
|
"change"
|
|
363
559
|
],
|
|
364
|
-
setup(__props, { emit: __emit }) {
|
|
560
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
365
561
|
const props = __props;
|
|
366
562
|
const emit = __emit;
|
|
367
563
|
const form = useAsForm({
|
|
@@ -375,6 +571,7 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
375
571
|
clientFactory: () => props.clientFactory,
|
|
376
572
|
hideRootTitle: () => props.hideRootTitle,
|
|
377
573
|
loading: () => props.loading,
|
|
574
|
+
trackChanges: () => props.trackChanges,
|
|
378
575
|
emits: {
|
|
379
576
|
submit: (data) => emit("submit", data),
|
|
380
577
|
error: (errors) => emit("error", errors),
|
|
@@ -383,6 +580,24 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
383
580
|
change: (type, path, value, formData) => emit("change", type, path, value, formData)
|
|
384
581
|
}
|
|
385
582
|
});
|
|
583
|
+
__expose({
|
|
584
|
+
submit: form.onSubmit,
|
|
585
|
+
reset: form.reset,
|
|
586
|
+
get isDirty() {
|
|
587
|
+
return form.patch?.isDirty.value ?? false;
|
|
588
|
+
},
|
|
589
|
+
get changes() {
|
|
590
|
+
return form.patch?.changes.value ?? [];
|
|
591
|
+
},
|
|
592
|
+
getPatch: form.patch ? form.patch.getPatch : () => ({}),
|
|
593
|
+
getChanges: form.patch ? form.patch.getChanges : () => [],
|
|
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
|
+
})
|
|
600
|
+
});
|
|
386
601
|
return (_ctx, _cache) => {
|
|
387
602
|
return openBlock(), createElementBlock("form", {
|
|
388
603
|
class: "as-form",
|
|
@@ -391,7 +606,10 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
391
606
|
}, [
|
|
392
607
|
renderSlot(_ctx.$slots, "form.header", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
|
|
393
608
|
renderSlot(_ctx.$slots, "form.before", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
|
|
394
|
-
|
|
609
|
+
(openBlock(), createBlock(as_field_default, {
|
|
610
|
+
key: unref(form).remountKey.value,
|
|
611
|
+
field: __props.def.rootField
|
|
612
|
+
}, null, 8, ["field"])),
|
|
395
613
|
renderSlot(_ctx.$slots, "form.after", normalizeProps(guardReactiveProps(unref(form).slotProps.value))),
|
|
396
614
|
unref(form).formError.value ? renderSlot(_ctx.$slots, "form.error", mergeProps({ key: 0 }, unref(form).slotProps.value, {
|
|
397
615
|
message: unref(form).formError.value,
|
|
@@ -420,4 +638,4 @@ var as_form_default = /* @__PURE__ */ defineComponent({
|
|
|
420
638
|
}
|
|
421
639
|
});
|
|
422
640
|
//#endregion
|
|
423
|
-
export { useAsExternalErrors as i, useAsForm as n, useAsState as r, as_form_default as t };
|
|
641
|
+
export { useAsFormPatch as a, useAsExternalErrors as i, useAsForm as n, useAsState as r, as_form_default as t };
|
package/dist/as-form.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const require_as_form = require("./as-form-
|
|
1
|
+
const require_as_form = require("./as-form-B4DKpAVk.cjs");
|
|
2
2
|
module.exports = require_as_form.as_form_default;
|
package/dist/as-form.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as _default, t as Props } from "./as-form.vue-
|
|
1
|
+
import { n as _default, t as Props } from "./as-form.vue-D5d1Y2ff.cjs";
|
|
2
2
|
export { Props, _default as default };
|
package/dist/as-form.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as _default, t as Props } from "./as-form.vue-
|
|
1
|
+
import { n as _default, t as Props } from "./as-form.vue-DH3phgWP.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-
|
|
1
|
+
import { t as as_form_default } from "./as-form-me0zA-52.mjs";
|
|
2
2
|
export { as_form_default as default };
|