@atscript/vue-form 0.1.58
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/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/as-action-71f9E_bL.cjs +208 -0
- package/dist/as-action-DU17rykn.mjs +203 -0
- package/dist/as-action.cjs +2 -0
- package/dist/as-action.d.cts +2 -0
- package/dist/as-action.d.mts +2 -0
- package/dist/as-action.mjs +2 -0
- package/dist/as-action.vue-BdbAOZCL.d.cts +13 -0
- package/dist/as-action.vue-CmY3eil1.d.mts +13 -0
- package/dist/as-adornment-shell-9UmdNIPR.cjs +97 -0
- package/dist/as-adornment-shell-knFiB7Ct.mjs +92 -0
- package/dist/as-array-Bn27x9cq.cjs +466 -0
- package/dist/as-array-DwarTaKP.mjs +455 -0
- package/dist/as-array-clear-btn-DAGervXL.cjs +48 -0
- package/dist/as-array-clear-btn-DIhzCKC9.mjs +43 -0
- package/dist/as-array.cjs +11 -0
- package/dist/as-array.d.cts +2 -0
- package/dist/as-array.d.mts +2 -0
- package/dist/as-array.mjs +11 -0
- package/dist/as-array.vue-C5r_ycIa.d.mts +9 -0
- package/dist/as-array.vue-CeBZRVm7.d.cts +9 -0
- package/dist/as-checkbox-B_9mwla6.mjs +285 -0
- package/dist/as-checkbox-DXGTVHPE.cjs +296 -0
- package/dist/as-checkbox.cjs +6 -0
- package/dist/as-checkbox.d.cts +2 -0
- package/dist/as-checkbox.d.mts +2 -0
- package/dist/as-checkbox.mjs +6 -0
- package/dist/as-checkbox.vue-BL53Xjmi.d.mts +9 -0
- package/dist/as-checkbox.vue-Da1KVG5J.d.cts +9 -0
- package/dist/as-collapsible-CqxeJut7.cjs +148 -0
- package/dist/as-collapsible-DtNCofNT.mjs +143 -0
- package/dist/as-date-C7tFQDkh.mjs +253 -0
- package/dist/as-date-k3MW3dmA.cjs +258 -0
- package/dist/as-date.cjs +8 -0
- package/dist/as-date.d.cts +2 -0
- package/dist/as-date.d.mts +2 -0
- package/dist/as-date.mjs +8 -0
- package/dist/as-date.vue-C5JaS3Sd.d.mts +9 -0
- package/dist/as-date.vue-e2ewS_V4.d.cts +9 -0
- package/dist/as-datetime-C030mzI5.cjs +258 -0
- package/dist/as-datetime-elcOoDT5.mjs +253 -0
- package/dist/as-datetime.cjs +8 -0
- package/dist/as-datetime.d.cts +2 -0
- package/dist/as-datetime.d.mts +2 -0
- package/dist/as-datetime.mjs +8 -0
- package/dist/as-datetime.vue-ClsSnJSV.d.mts +9 -0
- package/dist/as-datetime.vue-vfHFsby6.d.cts +9 -0
- package/dist/as-decimal-BJIGgPXU.cjs +711 -0
- package/dist/as-decimal-DtXjFPhe.mjs +694 -0
- package/dist/as-decimal.cjs +7 -0
- package/dist/as-decimal.d.cts +2 -0
- package/dist/as-decimal.d.mts +2 -0
- package/dist/as-decimal.mjs +7 -0
- package/dist/as-decimal.vue-B1pEKdjM.d.cts +25 -0
- package/dist/as-decimal.vue-CSCtYRRa.d.mts +25 -0
- package/dist/as-field-CXVjrEPQ.mjs +481 -0
- package/dist/as-field-shell-B2iTn-iM.cjs +346 -0
- package/dist/as-field-shell-Pdy3sAvr.mjs +341 -0
- package/dist/as-field-shell.cjs +5 -0
- package/dist/as-field-shell.d.cts +2 -0
- package/dist/as-field-shell.d.mts +2 -0
- package/dist/as-field-shell.mjs +5 -0
- package/dist/as-field-shell.vue-Ddnp8KxY.d.mts +37 -0
- package/dist/as-field-shell.vue-Dmt05vGD.d.cts +37 -0
- package/dist/as-field-wLYoaZnT.cjs +498 -0
- package/dist/as-field.cjs +4 -0
- package/dist/as-field.d.cts +25 -0
- package/dist/as-field.d.mts +26 -0
- package/dist/as-field.mjs +4 -0
- package/dist/as-form-CWwgyvfw.mjs +425 -0
- package/dist/as-form-DAIkyt7H.cjs +448 -0
- package/dist/as-form.cjs +7 -0
- package/dist/as-form.d.cts +2 -0
- package/dist/as-form.d.mts +2 -0
- package/dist/as-form.mjs +7 -0
- package/dist/as-form.vue-B4Bn0pbC.d.cts +158 -0
- package/dist/as-form.vue-DRrb_yoj.d.mts +158 -0
- package/dist/as-input-CpbV2k3s.cjs +231 -0
- package/dist/as-input-DIa8BzLv.mjs +226 -0
- package/dist/as-input-control-BzELjheN.mjs +266 -0
- package/dist/as-input-control-C5-DelZT.cjs +271 -0
- package/dist/as-input.cjs +8 -0
- package/dist/as-input.d.cts +2 -0
- package/dist/as-input.d.mts +2 -0
- package/dist/as-input.mjs +8 -0
- package/dist/as-input.vue-11ldp9uT.d.cts +17 -0
- package/dist/as-input.vue-fUhcvfv2.d.mts +17 -0
- package/dist/as-iterator-BYMNe6UJ.cjs +61 -0
- package/dist/as-iterator-CT5y1jyn.mjs +56 -0
- package/dist/as-iterator.cjs +5 -0
- package/dist/as-iterator.d.cts +14 -0
- package/dist/as-iterator.d.mts +15 -0
- package/dist/as-iterator.mjs +5 -0
- package/dist/as-number-BA55JIq1.cjs +387 -0
- package/dist/as-number-JPEwPK8Q.mjs +376 -0
- package/dist/as-number.cjs +8 -0
- package/dist/as-number.d.cts +2 -0
- package/dist/as-number.d.mts +2 -0
- package/dist/as-number.mjs +8 -0
- package/dist/as-number.vue-Bk-W7Vwv.d.mts +26 -0
- package/dist/as-number.vue-C2Aih98s.d.cts +26 -0
- package/dist/as-object-CT6lNEqt.mjs +300 -0
- package/dist/as-object-qUL7l8V1.cjs +305 -0
- package/dist/as-object.cjs +11 -0
- package/dist/as-object.d.cts +2 -0
- package/dist/as-object.d.mts +2 -0
- package/dist/as-object.mjs +11 -0
- package/dist/as-object.vue-CKwMyM_F.d.cts +13 -0
- package/dist/as-object.vue-Cg52b61-.d.mts +13 -0
- package/dist/as-paragraph-BGO-j4US.cjs +203 -0
- package/dist/as-paragraph-jIG_dg7_.mjs +198 -0
- package/dist/as-paragraph.cjs +2 -0
- package/dist/as-paragraph.d.cts +2 -0
- package/dist/as-paragraph.d.mts +2 -0
- package/dist/as-paragraph.mjs +2 -0
- package/dist/as-paragraph.vue-BDt0pBG-.d.cts +9 -0
- package/dist/as-paragraph.vue-C3FgTEt5.d.mts +9 -0
- package/dist/as-radio-B1N-gmoI.mjs +242 -0
- package/dist/as-radio-U3OK7bTg.cjs +247 -0
- package/dist/as-radio.cjs +6 -0
- package/dist/as-radio.d.cts +2 -0
- package/dist/as-radio.d.mts +2 -0
- package/dist/as-radio.mjs +6 -0
- package/dist/as-radio.vue-D_fweoN1.d.mts +9 -0
- package/dist/as-radio.vue-ZC4kLBnT.d.cts +9 -0
- package/dist/as-ref-CIifSSCQ.mjs +337 -0
- package/dist/as-ref-SImaIrwK.cjs +342 -0
- package/dist/as-ref.cjs +7 -0
- package/dist/as-ref.d.cts +2 -0
- package/dist/as-ref.d.mts +2 -0
- package/dist/as-ref.mjs +7 -0
- package/dist/as-ref.vue-BNeQeQpO.d.cts +9 -0
- package/dist/as-ref.vue-Cr5jeNDn.d.mts +9 -0
- package/dist/as-select-BB3uxACS.cjs +246 -0
- package/dist/as-select-UBGCVhku.mjs +241 -0
- package/dist/as-select.cjs +6 -0
- package/dist/as-select.d.cts +2 -0
- package/dist/as-select.d.mts +2 -0
- package/dist/as-select.mjs +6 -0
- package/dist/as-select.vue-Dd7huPq2.d.cts +9 -0
- package/dist/as-select.vue-RYpbZbKt.d.mts +9 -0
- package/dist/as-time-C24rvslH.cjs +258 -0
- package/dist/as-time-CQsxUs8P.mjs +253 -0
- package/dist/as-time.cjs +8 -0
- package/dist/as-time.d.cts +2 -0
- package/dist/as-time.d.mts +2 -0
- package/dist/as-time.mjs +8 -0
- package/dist/as-time.vue-huLx2B4l.d.mts +9 -0
- package/dist/as-time.vue-nMEHLXke.d.cts +9 -0
- package/dist/as-tuple-BU--cuuI.cjs +351 -0
- package/dist/as-tuple-DkI9swlW.mjs +340 -0
- package/dist/as-tuple.cjs +11 -0
- package/dist/as-tuple.d.cts +2 -0
- package/dist/as-tuple.d.mts +2 -0
- package/dist/as-tuple.mjs +11 -0
- package/dist/as-tuple.vue-CQhzOJsn.d.mts +9 -0
- package/dist/as-tuple.vue-DyskCkf-.d.cts +9 -0
- package/dist/as-union-BGvdxr3G.mjs +351 -0
- package/dist/as-union-C0btoJn3.cjs +368 -0
- package/dist/as-union.cjs +6 -0
- package/dist/as-union.d.cts +2 -0
- package/dist/as-union.d.mts +2 -0
- package/dist/as-union.mjs +6 -0
- package/dist/as-union.vue-BjlDPZn0.d.mts +9 -0
- package/dist/as-union.vue-CqjU3O10.d.cts +9 -0
- package/dist/as-variant-picker-BVs0AvjK.mjs +96 -0
- package/dist/as-variant-picker-DObQZHmm.cjs +107 -0
- package/dist/index.cjs +173 -0
- package/dist/index.d.cts +966 -0
- package/dist/index.d.mts +966 -0
- package/dist/index.mjs +105 -0
- package/dist/types-C4HRSxgV.d.cts +233 -0
- package/dist/types-Czm-Gtud.d.mts +233 -0
- package/dist/use-as-date-B7CtcRQd.cjs +329 -0
- package/dist/use-as-date-C39i9mzE.mjs +318 -0
- package/dist/use-as-dropdown-BMnEm6jF.mjs +82 -0
- package/dist/use-as-dropdown-C-Qy7Vt0.cjs +105 -0
- package/dist/use-as-locale-BrFdAgnU.mjs +23 -0
- package/dist/use-as-locale-C4z5stwD.cjs +34 -0
- package/dist/use-as-nested-sections-store-jdMRxjBE.cjs +80 -0
- package/dist/use-as-nested-sections-store-lhi0z5z1.mjs +63 -0
- package/dist/use-as-optional-add-flow-CuXEir_i.mjs +43 -0
- package/dist/use-as-optional-add-flow-STOaQWo9.cjs +48 -0
- package/dist/use-as-value-help-CBykDEjZ.mjs +89 -0
- package/dist/use-as-value-help-uANI3zWa.cjs +100 -0
- package/dist/use-form-context-Dwr8Ai1v.cjs +207 -0
- package/dist/use-form-context-bAj7UoSe.mjs +106 -0
- package/package.json +180 -0
- package/styles.d.ts +2 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
let vue = require("vue");
|
|
2
|
+
//#region src/composables/focus-after-toggle.ts
|
|
3
|
+
const FOCUSABLE_SELECTOR = [
|
|
4
|
+
"input:not([type=hidden]):not([type=checkbox]):not([type=radio]):not([disabled])",
|
|
5
|
+
"select:not([disabled])",
|
|
6
|
+
"textarea:not([disabled])"
|
|
7
|
+
].join(",");
|
|
8
|
+
/** Run an action, then focus the first focusable input descendant of `scope`. */
|
|
9
|
+
async function focusFirstAfter(action, scope, ticks = 1) {
|
|
10
|
+
action();
|
|
11
|
+
for (let i = 0; i < ticks; i++) await (0, vue.nextTick)();
|
|
12
|
+
const root = scope();
|
|
13
|
+
if (!root) return;
|
|
14
|
+
root.querySelector(FOCUSABLE_SELECTOR)?.focus();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run an action, then focus the first focusable input that wasn't present
|
|
18
|
+
* before the action. Used by array Add buttons so the user lands on the
|
|
19
|
+
* just-added row's first input regardless of how many items already exist.
|
|
20
|
+
* Falls back to first focusable when nothing existed before (the typical
|
|
21
|
+
* "enable optional + add first item" flow).
|
|
22
|
+
*/
|
|
23
|
+
async function focusNewFocusableAfter(action, scope, ticks = 1) {
|
|
24
|
+
const before = scope();
|
|
25
|
+
const known = before ? new Set(before.querySelectorAll(FOCUSABLE_SELECTOR)) : null;
|
|
26
|
+
action();
|
|
27
|
+
for (let i = 0; i < ticks; i++) await (0, vue.nextTick)();
|
|
28
|
+
const after = scope();
|
|
29
|
+
if (!after) return;
|
|
30
|
+
const all = after.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
31
|
+
for (const el of all) if (!known || !known.has(el)) {
|
|
32
|
+
el.focus();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Sugar over `focusFirstAfter` scoped to a template ref. */
|
|
37
|
+
function useAsFocusFirstAfter(onToggleOptional) {
|
|
38
|
+
const rootRef = (0, vue.ref)(null);
|
|
39
|
+
const runAndFocus = (action, ticks = 1) => {
|
|
40
|
+
focusFirstAfter(action, () => rootRef.value, ticks);
|
|
41
|
+
};
|
|
42
|
+
const runAndFocusNew = (action, ticks = 1) => {
|
|
43
|
+
focusNewFocusableAfter(action, () => rootRef.value, ticks);
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
rootRef,
|
|
47
|
+
runAndFocus,
|
|
48
|
+
runAndFocusNew,
|
|
49
|
+
enableOptional: () => runAndFocus(() => onToggleOptional?.(true))
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/composables/use-as-dropdown.ts
|
|
54
|
+
function useAsDropdown(containerRef) {
|
|
55
|
+
const isOpen = (0, vue.ref)(false);
|
|
56
|
+
function toggle() {
|
|
57
|
+
isOpen.value = !isOpen.value;
|
|
58
|
+
}
|
|
59
|
+
function close() {
|
|
60
|
+
isOpen.value = false;
|
|
61
|
+
}
|
|
62
|
+
function select(callback) {
|
|
63
|
+
callback();
|
|
64
|
+
close();
|
|
65
|
+
}
|
|
66
|
+
function onClickOutside(event) {
|
|
67
|
+
if (containerRef.value && !containerRef.value.contains(event.target)) close();
|
|
68
|
+
}
|
|
69
|
+
(0, vue.watch)(isOpen, (open) => {
|
|
70
|
+
if (open) document.addEventListener("click", onClickOutside, true);
|
|
71
|
+
else document.removeEventListener("click", onClickOutside, true);
|
|
72
|
+
});
|
|
73
|
+
(0, vue.onBeforeUnmount)(() => document.removeEventListener("click", onClickOutside, true));
|
|
74
|
+
return {
|
|
75
|
+
isOpen,
|
|
76
|
+
toggle,
|
|
77
|
+
close,
|
|
78
|
+
select
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
Object.defineProperty(exports, "focusFirstAfter", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
get: function() {
|
|
85
|
+
return focusFirstAfter;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(exports, "focusNewFocusableAfter", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
get: function() {
|
|
91
|
+
return focusNewFocusableAfter;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(exports, "useAsDropdown", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
get: function() {
|
|
97
|
+
return useAsDropdown;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
Object.defineProperty(exports, "useAsFocusFirstAfter", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
get: function() {
|
|
103
|
+
return useAsFocusFirstAfter;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { computed, inject, provide } from "vue";
|
|
2
|
+
//#region src/composables/use-as-locale.ts
|
|
3
|
+
/**
|
|
4
|
+
* BCP-47 locale for form-side decimal formatting (separator, thousands,
|
|
5
|
+
* currency symbol). Parallels `provideCellLocale` in `@atscript/vue-table`
|
|
6
|
+
* but lives in vue-form so the form package stays free of cross-package
|
|
7
|
+
* coupling — the host wires both providers from a single source (e.g.
|
|
8
|
+
* `useAppPrefs` in vue-table).
|
|
9
|
+
*
|
|
10
|
+
* The injected value is a getter so reactive sources (e.g.
|
|
11
|
+
* `prefs.value?.language`) can be passed without forcing the caller to
|
|
12
|
+
* wrap them in a computed.
|
|
13
|
+
*/
|
|
14
|
+
const AS_LOCALE_KEY = Symbol("as-locale");
|
|
15
|
+
function provideAsLocale(getter) {
|
|
16
|
+
provide(AS_LOCALE_KEY, getter);
|
|
17
|
+
}
|
|
18
|
+
function useAsLocale() {
|
|
19
|
+
const getter = inject(AS_LOCALE_KEY, () => void 0);
|
|
20
|
+
return { locale: computed(() => getter()) };
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { useAsLocale as n, provideAsLocale as t };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
let vue = require("vue");
|
|
2
|
+
//#region src/composables/use-as-locale.ts
|
|
3
|
+
/**
|
|
4
|
+
* BCP-47 locale for form-side decimal formatting (separator, thousands,
|
|
5
|
+
* currency symbol). Parallels `provideCellLocale` in `@atscript/vue-table`
|
|
6
|
+
* but lives in vue-form so the form package stays free of cross-package
|
|
7
|
+
* coupling — the host wires both providers from a single source (e.g.
|
|
8
|
+
* `useAppPrefs` in vue-table).
|
|
9
|
+
*
|
|
10
|
+
* The injected value is a getter so reactive sources (e.g.
|
|
11
|
+
* `prefs.value?.language`) can be passed without forcing the caller to
|
|
12
|
+
* wrap them in a computed.
|
|
13
|
+
*/
|
|
14
|
+
const AS_LOCALE_KEY = Symbol("as-locale");
|
|
15
|
+
function provideAsLocale(getter) {
|
|
16
|
+
(0, vue.provide)(AS_LOCALE_KEY, getter);
|
|
17
|
+
}
|
|
18
|
+
function useAsLocale() {
|
|
19
|
+
const getter = (0, vue.inject)(AS_LOCALE_KEY, () => void 0);
|
|
20
|
+
return { locale: (0, vue.computed)(() => getter()) };
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
Object.defineProperty(exports, "provideAsLocale", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function() {
|
|
26
|
+
return provideAsLocale;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(exports, "useAsLocale", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function() {
|
|
32
|
+
return useAsLocale;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
let vue = require("vue");
|
|
2
|
+
//#region src/composables/use-as-nested-sections-store.ts
|
|
3
|
+
const STORE_KEY = Symbol("atui.nested-sections");
|
|
4
|
+
/**
|
|
5
|
+
* AsForm provides a `Map<absolutePath, descendantErrorCount>` so each
|
|
6
|
+
* AsObject can render its error-count badge in O(1) instead of scanning
|
|
7
|
+
* all errors per-instance. Keyed by every dotted-path prefix that has at
|
|
8
|
+
* least one error at-or-below it.
|
|
9
|
+
*/
|
|
10
|
+
const DESCENDANT_ERROR_COUNTS_KEY = Symbol("atui.descendant-error-counts");
|
|
11
|
+
/**
|
|
12
|
+
* Create and provide a `AsNestedSectionsStore` to the current Vue subtree.
|
|
13
|
+
* Called automatically by `<AsForm>`, but exposed for cases where you
|
|
14
|
+
* want to scope a separate store (e.g. multiple independent forms in one
|
|
15
|
+
* page that should keep their open/closed state independent, or to drive
|
|
16
|
+
* page-level Expand-all / Collapse-all UI from above the form).
|
|
17
|
+
*/
|
|
18
|
+
function provideAsNestedSectionsStore() {
|
|
19
|
+
const open = (0, vue.ref)(/* @__PURE__ */ new Set());
|
|
20
|
+
const registered = (0, vue.ref)(/* @__PURE__ */ new Set());
|
|
21
|
+
const store = {
|
|
22
|
+
open,
|
|
23
|
+
register(id) {
|
|
24
|
+
registered.value.add(id);
|
|
25
|
+
},
|
|
26
|
+
unregister(id) {
|
|
27
|
+
registered.value.delete(id);
|
|
28
|
+
},
|
|
29
|
+
toggle(id) {
|
|
30
|
+
if (open.value.has(id)) open.value.delete(id);
|
|
31
|
+
else open.value.add(id);
|
|
32
|
+
},
|
|
33
|
+
setOpen(id, isOpen) {
|
|
34
|
+
if (isOpen) {
|
|
35
|
+
if (!open.value.has(id)) open.value.add(id);
|
|
36
|
+
} else if (open.value.has(id)) open.value.delete(id);
|
|
37
|
+
},
|
|
38
|
+
isOpen(id) {
|
|
39
|
+
return open.value.has(id);
|
|
40
|
+
},
|
|
41
|
+
expandAll() {
|
|
42
|
+
for (const id of registered.value) open.value.add(id);
|
|
43
|
+
},
|
|
44
|
+
collapseAll() {
|
|
45
|
+
open.value.clear();
|
|
46
|
+
},
|
|
47
|
+
allOpen() {
|
|
48
|
+
return open.value.size === registered.value.size;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
(0, vue.provide)(STORE_KEY, store);
|
|
52
|
+
return store;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Inject the nested-sections store provided by an ancestor `<AsForm>`
|
|
56
|
+
* (or by an explicit `provideAsNestedSectionsStore()` call). Returns
|
|
57
|
+
* `undefined` if no store is in scope.
|
|
58
|
+
*/
|
|
59
|
+
function useAsNestedSectionsStore() {
|
|
60
|
+
return (0, vue.inject)(STORE_KEY, void 0);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
Object.defineProperty(exports, "DESCENDANT_ERROR_COUNTS_KEY", {
|
|
64
|
+
enumerable: true,
|
|
65
|
+
get: function() {
|
|
66
|
+
return DESCENDANT_ERROR_COUNTS_KEY;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
Object.defineProperty(exports, "provideAsNestedSectionsStore", {
|
|
70
|
+
enumerable: true,
|
|
71
|
+
get: function() {
|
|
72
|
+
return provideAsNestedSectionsStore;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
Object.defineProperty(exports, "useAsNestedSectionsStore", {
|
|
76
|
+
enumerable: true,
|
|
77
|
+
get: function() {
|
|
78
|
+
return useAsNestedSectionsStore;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { inject, provide, ref } from "vue";
|
|
2
|
+
//#region src/composables/use-as-nested-sections-store.ts
|
|
3
|
+
const STORE_KEY = Symbol("atui.nested-sections");
|
|
4
|
+
/**
|
|
5
|
+
* AsForm provides a `Map<absolutePath, descendantErrorCount>` so each
|
|
6
|
+
* AsObject can render its error-count badge in O(1) instead of scanning
|
|
7
|
+
* all errors per-instance. Keyed by every dotted-path prefix that has at
|
|
8
|
+
* least one error at-or-below it.
|
|
9
|
+
*/
|
|
10
|
+
const DESCENDANT_ERROR_COUNTS_KEY = Symbol("atui.descendant-error-counts");
|
|
11
|
+
/**
|
|
12
|
+
* Create and provide a `AsNestedSectionsStore` to the current Vue subtree.
|
|
13
|
+
* Called automatically by `<AsForm>`, but exposed for cases where you
|
|
14
|
+
* want to scope a separate store (e.g. multiple independent forms in one
|
|
15
|
+
* page that should keep their open/closed state independent, or to drive
|
|
16
|
+
* page-level Expand-all / Collapse-all UI from above the form).
|
|
17
|
+
*/
|
|
18
|
+
function provideAsNestedSectionsStore() {
|
|
19
|
+
const open = ref(/* @__PURE__ */ new Set());
|
|
20
|
+
const registered = ref(/* @__PURE__ */ new Set());
|
|
21
|
+
const store = {
|
|
22
|
+
open,
|
|
23
|
+
register(id) {
|
|
24
|
+
registered.value.add(id);
|
|
25
|
+
},
|
|
26
|
+
unregister(id) {
|
|
27
|
+
registered.value.delete(id);
|
|
28
|
+
},
|
|
29
|
+
toggle(id) {
|
|
30
|
+
if (open.value.has(id)) open.value.delete(id);
|
|
31
|
+
else open.value.add(id);
|
|
32
|
+
},
|
|
33
|
+
setOpen(id, isOpen) {
|
|
34
|
+
if (isOpen) {
|
|
35
|
+
if (!open.value.has(id)) open.value.add(id);
|
|
36
|
+
} else if (open.value.has(id)) open.value.delete(id);
|
|
37
|
+
},
|
|
38
|
+
isOpen(id) {
|
|
39
|
+
return open.value.has(id);
|
|
40
|
+
},
|
|
41
|
+
expandAll() {
|
|
42
|
+
for (const id of registered.value) open.value.add(id);
|
|
43
|
+
},
|
|
44
|
+
collapseAll() {
|
|
45
|
+
open.value.clear();
|
|
46
|
+
},
|
|
47
|
+
allOpen() {
|
|
48
|
+
return open.value.size === registered.value.size;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
provide(STORE_KEY, store);
|
|
52
|
+
return store;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Inject the nested-sections store provided by an ancestor `<AsForm>`
|
|
56
|
+
* (or by an explicit `provideAsNestedSectionsStore()` call). Returns
|
|
57
|
+
* `undefined` if no store is in scope.
|
|
58
|
+
*/
|
|
59
|
+
function useAsNestedSectionsStore() {
|
|
60
|
+
return inject(STORE_KEY, void 0);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { provideAsNestedSectionsStore as n, useAsNestedSectionsStore as r, DESCENDANT_ERROR_COUNTS_KEY as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { r as useAsNestedSectionsStore } from "./use-as-nested-sections-store-lhi0z5z1.mjs";
|
|
2
|
+
import { r as focusNewFocusableAfter } from "./use-as-dropdown-BMnEm6jF.mjs";
|
|
3
|
+
//#region src/composables/use-as-optional-add-flow.ts
|
|
4
|
+
/**
|
|
5
|
+
* Choreography composable for "enable-optional + add + focus-first-new"
|
|
6
|
+
* flows. The four built-in structured-field defaults consume it; custom
|
|
7
|
+
* implementations of object/array/tuple/union components can use it too
|
|
8
|
+
* to get the same UX (smooth focus handoff after an empty-state click)
|
|
9
|
+
* without re-implementing the choreography.
|
|
10
|
+
*
|
|
11
|
+
* Pairs with:
|
|
12
|
+
* - {@link useAsFocusFirstAfter} — focus mechanics (used by AsCollapsible)
|
|
13
|
+
* - {@link useAsNestedSectionsStore} — open/closed state registry
|
|
14
|
+
*
|
|
15
|
+
* Typical use, inside an `as-object.vue` swap component:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const { composeAction } = useAsOptionalAddFlow({ path: () => props.path });
|
|
18
|
+
* function handleAddData() {
|
|
19
|
+
* collapsibleRef.value?.runAndFocusNew(
|
|
20
|
+
* composeAction(() => props.onToggleOptional?.(true)),
|
|
21
|
+
* 2,
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function useAsOptionalAddFlow(options) {
|
|
27
|
+
const store = useAsNestedSectionsStore();
|
|
28
|
+
const wrap = (action) => {
|
|
29
|
+
return () => {
|
|
30
|
+
action();
|
|
31
|
+
const path = options.path();
|
|
32
|
+
if (path) store?.setOpen(path, true);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
composeAction: wrap,
|
|
37
|
+
runAndFocusNew(scope, action, ticks = 2) {
|
|
38
|
+
return focusNewFocusableAfter(wrap(action), scope, ticks);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { useAsOptionalAddFlow as t };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const require_use_as_nested_sections_store = require("./use-as-nested-sections-store-jdMRxjBE.cjs");
|
|
2
|
+
const require_use_as_dropdown = require("./use-as-dropdown-C-Qy7Vt0.cjs");
|
|
3
|
+
//#region src/composables/use-as-optional-add-flow.ts
|
|
4
|
+
/**
|
|
5
|
+
* Choreography composable for "enable-optional + add + focus-first-new"
|
|
6
|
+
* flows. The four built-in structured-field defaults consume it; custom
|
|
7
|
+
* implementations of object/array/tuple/union components can use it too
|
|
8
|
+
* to get the same UX (smooth focus handoff after an empty-state click)
|
|
9
|
+
* without re-implementing the choreography.
|
|
10
|
+
*
|
|
11
|
+
* Pairs with:
|
|
12
|
+
* - {@link useAsFocusFirstAfter} — focus mechanics (used by AsCollapsible)
|
|
13
|
+
* - {@link useAsNestedSectionsStore} — open/closed state registry
|
|
14
|
+
*
|
|
15
|
+
* Typical use, inside an `as-object.vue` swap component:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const { composeAction } = useAsOptionalAddFlow({ path: () => props.path });
|
|
18
|
+
* function handleAddData() {
|
|
19
|
+
* collapsibleRef.value?.runAndFocusNew(
|
|
20
|
+
* composeAction(() => props.onToggleOptional?.(true)),
|
|
21
|
+
* 2,
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function useAsOptionalAddFlow(options) {
|
|
27
|
+
const store = require_use_as_nested_sections_store.useAsNestedSectionsStore();
|
|
28
|
+
const wrap = (action) => {
|
|
29
|
+
return () => {
|
|
30
|
+
action();
|
|
31
|
+
const path = options.path();
|
|
32
|
+
if (path) store?.setOpen(path, true);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
composeAction: wrap,
|
|
37
|
+
runAndFocusNew(scope, action, ticks = 2) {
|
|
38
|
+
return require_use_as_dropdown.focusNewFocusableAfter(wrap(action), scope, ticks);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
Object.defineProperty(exports, "useAsOptionalAddFlow", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function() {
|
|
46
|
+
return useAsOptionalAddFlow;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { computed, inject, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
|
2
|
+
import { ValueHelpClient, getMetaEntry, resolveValueHelp } from "@atscript/ui";
|
|
3
|
+
//#region src/composables/use-as-value-help.ts
|
|
4
|
+
/** Vue inject key used by `AsForm` to publish a per-form `ClientFactory` override. */
|
|
5
|
+
const CLIENT_FACTORY_KEY = Symbol("as-client-factory");
|
|
6
|
+
const DEBOUNCE_MS = 300;
|
|
7
|
+
function useAsValueHelp(options) {
|
|
8
|
+
const { info, model, onBlur } = options;
|
|
9
|
+
const injectedFactory = inject(CLIENT_FACTORY_KEY, null) ?? void 0;
|
|
10
|
+
const vhClient = new ValueHelpClient(getMetaEntry(info.url, injectedFactory).client);
|
|
11
|
+
const resolved = shallowRef(null);
|
|
12
|
+
const status = ref("loading");
|
|
13
|
+
const searchText = ref("");
|
|
14
|
+
const results = shallowRef([]);
|
|
15
|
+
const searching = ref(false);
|
|
16
|
+
const labelIsFkValue = computed(() => !!resolved.value && info.targetField === resolved.value.labelField);
|
|
17
|
+
let debounceTimer;
|
|
18
|
+
let kickoffPromise;
|
|
19
|
+
let lastSearchedText;
|
|
20
|
+
onUnmounted(() => {
|
|
21
|
+
if (debounceTimer !== void 0) clearTimeout(debounceTimer);
|
|
22
|
+
});
|
|
23
|
+
async function kickoff() {
|
|
24
|
+
if (kickoffPromise) return kickoffPromise;
|
|
25
|
+
kickoffPromise = resolveValueHelp(info.url).then((r) => {
|
|
26
|
+
resolved.value = r;
|
|
27
|
+
status.value = "ready";
|
|
28
|
+
}).catch(() => {
|
|
29
|
+
status.value = "error";
|
|
30
|
+
});
|
|
31
|
+
return kickoffPromise;
|
|
32
|
+
}
|
|
33
|
+
function scheduleSearch(text) {
|
|
34
|
+
if (debounceTimer !== void 0) clearTimeout(debounceTimer);
|
|
35
|
+
debounceTimer = setTimeout(() => {
|
|
36
|
+
doSearch(text);
|
|
37
|
+
}, DEBOUNCE_MS);
|
|
38
|
+
}
|
|
39
|
+
async function doSearch(text) {
|
|
40
|
+
const r = resolved.value;
|
|
41
|
+
if (!r) return;
|
|
42
|
+
if (text === lastSearchedText) return;
|
|
43
|
+
lastSearchedText = text;
|
|
44
|
+
searching.value = true;
|
|
45
|
+
try {
|
|
46
|
+
results.value = (await vhClient.search(r, {
|
|
47
|
+
text: text || void 0,
|
|
48
|
+
mode: "form",
|
|
49
|
+
limit: 20
|
|
50
|
+
})).items;
|
|
51
|
+
} catch {
|
|
52
|
+
results.value = [];
|
|
53
|
+
} finally {
|
|
54
|
+
searching.value = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
watch(searchText, (text) => {
|
|
58
|
+
if (resolved.value) scheduleSearch(text);
|
|
59
|
+
});
|
|
60
|
+
watch(resolved, (r) => {
|
|
61
|
+
if (r) doSearch(searchText.value);
|
|
62
|
+
});
|
|
63
|
+
onMounted(() => {
|
|
64
|
+
kickoff();
|
|
65
|
+
});
|
|
66
|
+
function selectItem(item) {
|
|
67
|
+
model.value = item[info.targetField];
|
|
68
|
+
onBlur();
|
|
69
|
+
}
|
|
70
|
+
function clear() {
|
|
71
|
+
model.value = void 0;
|
|
72
|
+
searchText.value = "";
|
|
73
|
+
results.value = [];
|
|
74
|
+
lastSearchedText = void 0;
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
resolved,
|
|
78
|
+
status,
|
|
79
|
+
searchText,
|
|
80
|
+
results,
|
|
81
|
+
searching,
|
|
82
|
+
labelIsFkValue,
|
|
83
|
+
kickoff,
|
|
84
|
+
selectItem,
|
|
85
|
+
clear
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { useAsValueHelp as n, CLIENT_FACTORY_KEY as t };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
let vue = require("vue");
|
|
2
|
+
let _atscript_ui = require("@atscript/ui");
|
|
3
|
+
//#region src/composables/use-as-value-help.ts
|
|
4
|
+
/** Vue inject key used by `AsForm` to publish a per-form `ClientFactory` override. */
|
|
5
|
+
const CLIENT_FACTORY_KEY = Symbol("as-client-factory");
|
|
6
|
+
const DEBOUNCE_MS = 300;
|
|
7
|
+
function useAsValueHelp(options) {
|
|
8
|
+
const { info, model, onBlur } = options;
|
|
9
|
+
const injectedFactory = (0, vue.inject)(CLIENT_FACTORY_KEY, null) ?? void 0;
|
|
10
|
+
const vhClient = new _atscript_ui.ValueHelpClient((0, _atscript_ui.getMetaEntry)(info.url, injectedFactory).client);
|
|
11
|
+
const resolved = (0, vue.shallowRef)(null);
|
|
12
|
+
const status = (0, vue.ref)("loading");
|
|
13
|
+
const searchText = (0, vue.ref)("");
|
|
14
|
+
const results = (0, vue.shallowRef)([]);
|
|
15
|
+
const searching = (0, vue.ref)(false);
|
|
16
|
+
const labelIsFkValue = (0, vue.computed)(() => !!resolved.value && info.targetField === resolved.value.labelField);
|
|
17
|
+
let debounceTimer;
|
|
18
|
+
let kickoffPromise;
|
|
19
|
+
let lastSearchedText;
|
|
20
|
+
(0, vue.onUnmounted)(() => {
|
|
21
|
+
if (debounceTimer !== void 0) clearTimeout(debounceTimer);
|
|
22
|
+
});
|
|
23
|
+
async function kickoff() {
|
|
24
|
+
if (kickoffPromise) return kickoffPromise;
|
|
25
|
+
kickoffPromise = (0, _atscript_ui.resolveValueHelp)(info.url).then((r) => {
|
|
26
|
+
resolved.value = r;
|
|
27
|
+
status.value = "ready";
|
|
28
|
+
}).catch(() => {
|
|
29
|
+
status.value = "error";
|
|
30
|
+
});
|
|
31
|
+
return kickoffPromise;
|
|
32
|
+
}
|
|
33
|
+
function scheduleSearch(text) {
|
|
34
|
+
if (debounceTimer !== void 0) clearTimeout(debounceTimer);
|
|
35
|
+
debounceTimer = setTimeout(() => {
|
|
36
|
+
doSearch(text);
|
|
37
|
+
}, DEBOUNCE_MS);
|
|
38
|
+
}
|
|
39
|
+
async function doSearch(text) {
|
|
40
|
+
const r = resolved.value;
|
|
41
|
+
if (!r) return;
|
|
42
|
+
if (text === lastSearchedText) return;
|
|
43
|
+
lastSearchedText = text;
|
|
44
|
+
searching.value = true;
|
|
45
|
+
try {
|
|
46
|
+
results.value = (await vhClient.search(r, {
|
|
47
|
+
text: text || void 0,
|
|
48
|
+
mode: "form",
|
|
49
|
+
limit: 20
|
|
50
|
+
})).items;
|
|
51
|
+
} catch {
|
|
52
|
+
results.value = [];
|
|
53
|
+
} finally {
|
|
54
|
+
searching.value = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
(0, vue.watch)(searchText, (text) => {
|
|
58
|
+
if (resolved.value) scheduleSearch(text);
|
|
59
|
+
});
|
|
60
|
+
(0, vue.watch)(resolved, (r) => {
|
|
61
|
+
if (r) doSearch(searchText.value);
|
|
62
|
+
});
|
|
63
|
+
(0, vue.onMounted)(() => {
|
|
64
|
+
kickoff();
|
|
65
|
+
});
|
|
66
|
+
function selectItem(item) {
|
|
67
|
+
model.value = item[info.targetField];
|
|
68
|
+
onBlur();
|
|
69
|
+
}
|
|
70
|
+
function clear() {
|
|
71
|
+
model.value = void 0;
|
|
72
|
+
searchText.value = "";
|
|
73
|
+
results.value = [];
|
|
74
|
+
lastSearchedText = void 0;
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
resolved,
|
|
78
|
+
status,
|
|
79
|
+
searchText,
|
|
80
|
+
results,
|
|
81
|
+
searching,
|
|
82
|
+
labelIsFkValue,
|
|
83
|
+
kickoff,
|
|
84
|
+
selectItem,
|
|
85
|
+
clear
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
Object.defineProperty(exports, "CLIENT_FACTORY_KEY", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
get: function() {
|
|
92
|
+
return CLIENT_FACTORY_KEY;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
Object.defineProperty(exports, "useAsValueHelp", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
get: function() {
|
|
98
|
+
return useAsValueHelp;
|
|
99
|
+
}
|
|
100
|
+
});
|