@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.
Files changed (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -0
  3. package/dist/as-action-71f9E_bL.cjs +208 -0
  4. package/dist/as-action-DU17rykn.mjs +203 -0
  5. package/dist/as-action.cjs +2 -0
  6. package/dist/as-action.d.cts +2 -0
  7. package/dist/as-action.d.mts +2 -0
  8. package/dist/as-action.mjs +2 -0
  9. package/dist/as-action.vue-BdbAOZCL.d.cts +13 -0
  10. package/dist/as-action.vue-CmY3eil1.d.mts +13 -0
  11. package/dist/as-adornment-shell-9UmdNIPR.cjs +97 -0
  12. package/dist/as-adornment-shell-knFiB7Ct.mjs +92 -0
  13. package/dist/as-array-Bn27x9cq.cjs +466 -0
  14. package/dist/as-array-DwarTaKP.mjs +455 -0
  15. package/dist/as-array-clear-btn-DAGervXL.cjs +48 -0
  16. package/dist/as-array-clear-btn-DIhzCKC9.mjs +43 -0
  17. package/dist/as-array.cjs +11 -0
  18. package/dist/as-array.d.cts +2 -0
  19. package/dist/as-array.d.mts +2 -0
  20. package/dist/as-array.mjs +11 -0
  21. package/dist/as-array.vue-C5r_ycIa.d.mts +9 -0
  22. package/dist/as-array.vue-CeBZRVm7.d.cts +9 -0
  23. package/dist/as-checkbox-B_9mwla6.mjs +285 -0
  24. package/dist/as-checkbox-DXGTVHPE.cjs +296 -0
  25. package/dist/as-checkbox.cjs +6 -0
  26. package/dist/as-checkbox.d.cts +2 -0
  27. package/dist/as-checkbox.d.mts +2 -0
  28. package/dist/as-checkbox.mjs +6 -0
  29. package/dist/as-checkbox.vue-BL53Xjmi.d.mts +9 -0
  30. package/dist/as-checkbox.vue-Da1KVG5J.d.cts +9 -0
  31. package/dist/as-collapsible-CqxeJut7.cjs +148 -0
  32. package/dist/as-collapsible-DtNCofNT.mjs +143 -0
  33. package/dist/as-date-C7tFQDkh.mjs +253 -0
  34. package/dist/as-date-k3MW3dmA.cjs +258 -0
  35. package/dist/as-date.cjs +8 -0
  36. package/dist/as-date.d.cts +2 -0
  37. package/dist/as-date.d.mts +2 -0
  38. package/dist/as-date.mjs +8 -0
  39. package/dist/as-date.vue-C5JaS3Sd.d.mts +9 -0
  40. package/dist/as-date.vue-e2ewS_V4.d.cts +9 -0
  41. package/dist/as-datetime-C030mzI5.cjs +258 -0
  42. package/dist/as-datetime-elcOoDT5.mjs +253 -0
  43. package/dist/as-datetime.cjs +8 -0
  44. package/dist/as-datetime.d.cts +2 -0
  45. package/dist/as-datetime.d.mts +2 -0
  46. package/dist/as-datetime.mjs +8 -0
  47. package/dist/as-datetime.vue-ClsSnJSV.d.mts +9 -0
  48. package/dist/as-datetime.vue-vfHFsby6.d.cts +9 -0
  49. package/dist/as-decimal-BJIGgPXU.cjs +711 -0
  50. package/dist/as-decimal-DtXjFPhe.mjs +694 -0
  51. package/dist/as-decimal.cjs +7 -0
  52. package/dist/as-decimal.d.cts +2 -0
  53. package/dist/as-decimal.d.mts +2 -0
  54. package/dist/as-decimal.mjs +7 -0
  55. package/dist/as-decimal.vue-B1pEKdjM.d.cts +25 -0
  56. package/dist/as-decimal.vue-CSCtYRRa.d.mts +25 -0
  57. package/dist/as-field-CXVjrEPQ.mjs +481 -0
  58. package/dist/as-field-shell-B2iTn-iM.cjs +346 -0
  59. package/dist/as-field-shell-Pdy3sAvr.mjs +341 -0
  60. package/dist/as-field-shell.cjs +5 -0
  61. package/dist/as-field-shell.d.cts +2 -0
  62. package/dist/as-field-shell.d.mts +2 -0
  63. package/dist/as-field-shell.mjs +5 -0
  64. package/dist/as-field-shell.vue-Ddnp8KxY.d.mts +37 -0
  65. package/dist/as-field-shell.vue-Dmt05vGD.d.cts +37 -0
  66. package/dist/as-field-wLYoaZnT.cjs +498 -0
  67. package/dist/as-field.cjs +4 -0
  68. package/dist/as-field.d.cts +25 -0
  69. package/dist/as-field.d.mts +26 -0
  70. package/dist/as-field.mjs +4 -0
  71. package/dist/as-form-CWwgyvfw.mjs +425 -0
  72. package/dist/as-form-DAIkyt7H.cjs +448 -0
  73. package/dist/as-form.cjs +7 -0
  74. package/dist/as-form.d.cts +2 -0
  75. package/dist/as-form.d.mts +2 -0
  76. package/dist/as-form.mjs +7 -0
  77. package/dist/as-form.vue-B4Bn0pbC.d.cts +158 -0
  78. package/dist/as-form.vue-DRrb_yoj.d.mts +158 -0
  79. package/dist/as-input-CpbV2k3s.cjs +231 -0
  80. package/dist/as-input-DIa8BzLv.mjs +226 -0
  81. package/dist/as-input-control-BzELjheN.mjs +266 -0
  82. package/dist/as-input-control-C5-DelZT.cjs +271 -0
  83. package/dist/as-input.cjs +8 -0
  84. package/dist/as-input.d.cts +2 -0
  85. package/dist/as-input.d.mts +2 -0
  86. package/dist/as-input.mjs +8 -0
  87. package/dist/as-input.vue-11ldp9uT.d.cts +17 -0
  88. package/dist/as-input.vue-fUhcvfv2.d.mts +17 -0
  89. package/dist/as-iterator-BYMNe6UJ.cjs +61 -0
  90. package/dist/as-iterator-CT5y1jyn.mjs +56 -0
  91. package/dist/as-iterator.cjs +5 -0
  92. package/dist/as-iterator.d.cts +14 -0
  93. package/dist/as-iterator.d.mts +15 -0
  94. package/dist/as-iterator.mjs +5 -0
  95. package/dist/as-number-BA55JIq1.cjs +387 -0
  96. package/dist/as-number-JPEwPK8Q.mjs +376 -0
  97. package/dist/as-number.cjs +8 -0
  98. package/dist/as-number.d.cts +2 -0
  99. package/dist/as-number.d.mts +2 -0
  100. package/dist/as-number.mjs +8 -0
  101. package/dist/as-number.vue-Bk-W7Vwv.d.mts +26 -0
  102. package/dist/as-number.vue-C2Aih98s.d.cts +26 -0
  103. package/dist/as-object-CT6lNEqt.mjs +300 -0
  104. package/dist/as-object-qUL7l8V1.cjs +305 -0
  105. package/dist/as-object.cjs +11 -0
  106. package/dist/as-object.d.cts +2 -0
  107. package/dist/as-object.d.mts +2 -0
  108. package/dist/as-object.mjs +11 -0
  109. package/dist/as-object.vue-CKwMyM_F.d.cts +13 -0
  110. package/dist/as-object.vue-Cg52b61-.d.mts +13 -0
  111. package/dist/as-paragraph-BGO-j4US.cjs +203 -0
  112. package/dist/as-paragraph-jIG_dg7_.mjs +198 -0
  113. package/dist/as-paragraph.cjs +2 -0
  114. package/dist/as-paragraph.d.cts +2 -0
  115. package/dist/as-paragraph.d.mts +2 -0
  116. package/dist/as-paragraph.mjs +2 -0
  117. package/dist/as-paragraph.vue-BDt0pBG-.d.cts +9 -0
  118. package/dist/as-paragraph.vue-C3FgTEt5.d.mts +9 -0
  119. package/dist/as-radio-B1N-gmoI.mjs +242 -0
  120. package/dist/as-radio-U3OK7bTg.cjs +247 -0
  121. package/dist/as-radio.cjs +6 -0
  122. package/dist/as-radio.d.cts +2 -0
  123. package/dist/as-radio.d.mts +2 -0
  124. package/dist/as-radio.mjs +6 -0
  125. package/dist/as-radio.vue-D_fweoN1.d.mts +9 -0
  126. package/dist/as-radio.vue-ZC4kLBnT.d.cts +9 -0
  127. package/dist/as-ref-CIifSSCQ.mjs +337 -0
  128. package/dist/as-ref-SImaIrwK.cjs +342 -0
  129. package/dist/as-ref.cjs +7 -0
  130. package/dist/as-ref.d.cts +2 -0
  131. package/dist/as-ref.d.mts +2 -0
  132. package/dist/as-ref.mjs +7 -0
  133. package/dist/as-ref.vue-BNeQeQpO.d.cts +9 -0
  134. package/dist/as-ref.vue-Cr5jeNDn.d.mts +9 -0
  135. package/dist/as-select-BB3uxACS.cjs +246 -0
  136. package/dist/as-select-UBGCVhku.mjs +241 -0
  137. package/dist/as-select.cjs +6 -0
  138. package/dist/as-select.d.cts +2 -0
  139. package/dist/as-select.d.mts +2 -0
  140. package/dist/as-select.mjs +6 -0
  141. package/dist/as-select.vue-Dd7huPq2.d.cts +9 -0
  142. package/dist/as-select.vue-RYpbZbKt.d.mts +9 -0
  143. package/dist/as-time-C24rvslH.cjs +258 -0
  144. package/dist/as-time-CQsxUs8P.mjs +253 -0
  145. package/dist/as-time.cjs +8 -0
  146. package/dist/as-time.d.cts +2 -0
  147. package/dist/as-time.d.mts +2 -0
  148. package/dist/as-time.mjs +8 -0
  149. package/dist/as-time.vue-huLx2B4l.d.mts +9 -0
  150. package/dist/as-time.vue-nMEHLXke.d.cts +9 -0
  151. package/dist/as-tuple-BU--cuuI.cjs +351 -0
  152. package/dist/as-tuple-DkI9swlW.mjs +340 -0
  153. package/dist/as-tuple.cjs +11 -0
  154. package/dist/as-tuple.d.cts +2 -0
  155. package/dist/as-tuple.d.mts +2 -0
  156. package/dist/as-tuple.mjs +11 -0
  157. package/dist/as-tuple.vue-CQhzOJsn.d.mts +9 -0
  158. package/dist/as-tuple.vue-DyskCkf-.d.cts +9 -0
  159. package/dist/as-union-BGvdxr3G.mjs +351 -0
  160. package/dist/as-union-C0btoJn3.cjs +368 -0
  161. package/dist/as-union.cjs +6 -0
  162. package/dist/as-union.d.cts +2 -0
  163. package/dist/as-union.d.mts +2 -0
  164. package/dist/as-union.mjs +6 -0
  165. package/dist/as-union.vue-BjlDPZn0.d.mts +9 -0
  166. package/dist/as-union.vue-CqjU3O10.d.cts +9 -0
  167. package/dist/as-variant-picker-BVs0AvjK.mjs +96 -0
  168. package/dist/as-variant-picker-DObQZHmm.cjs +107 -0
  169. package/dist/index.cjs +173 -0
  170. package/dist/index.d.cts +966 -0
  171. package/dist/index.d.mts +966 -0
  172. package/dist/index.mjs +105 -0
  173. package/dist/types-C4HRSxgV.d.cts +233 -0
  174. package/dist/types-Czm-Gtud.d.mts +233 -0
  175. package/dist/use-as-date-B7CtcRQd.cjs +329 -0
  176. package/dist/use-as-date-C39i9mzE.mjs +318 -0
  177. package/dist/use-as-dropdown-BMnEm6jF.mjs +82 -0
  178. package/dist/use-as-dropdown-C-Qy7Vt0.cjs +105 -0
  179. package/dist/use-as-locale-BrFdAgnU.mjs +23 -0
  180. package/dist/use-as-locale-C4z5stwD.cjs +34 -0
  181. package/dist/use-as-nested-sections-store-jdMRxjBE.cjs +80 -0
  182. package/dist/use-as-nested-sections-store-lhi0z5z1.mjs +63 -0
  183. package/dist/use-as-optional-add-flow-CuXEir_i.mjs +43 -0
  184. package/dist/use-as-optional-add-flow-STOaQWo9.cjs +48 -0
  185. package/dist/use-as-value-help-CBykDEjZ.mjs +89 -0
  186. package/dist/use-as-value-help-uANI3zWa.cjs +100 -0
  187. package/dist/use-form-context-Dwr8Ai1v.cjs +207 -0
  188. package/dist/use-form-context-bAj7UoSe.mjs +106 -0
  189. package/package.json +180 -0
  190. 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
+ });