@atscript/vue-table 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 (202) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/dist/as-action-form-dialog.cjs +221 -0
  4. package/dist/as-action-form-dialog.d.cts +6 -0
  5. package/dist/as-action-form-dialog.d.mts +7 -0
  6. package/dist/as-action-form-dialog.mjs +221 -0
  7. package/dist/as-action-menu-content-CXsdPn42.mjs +104 -0
  8. package/dist/as-action-menu-content-CyUfBrWH.cjs +109 -0
  9. package/dist/as-cell-array-CXeZzZqt.cjs +69 -0
  10. package/dist/as-cell-array-DOQKR6t5.mjs +64 -0
  11. package/dist/as-cell-array.cjs +3 -0
  12. package/dist/as-cell-array.d.cts +2 -0
  13. package/dist/as-cell-array.d.mts +2 -0
  14. package/dist/as-cell-array.mjs +3 -0
  15. package/dist/as-cell-array.vue-DZGM2VHh.d.mts +12 -0
  16. package/dist/as-cell-array.vue-pFs5GcCR.d.cts +12 -0
  17. package/dist/as-cell-date-CTrvxS1h.cjs +78 -0
  18. package/dist/as-cell-date-Cq49RHmL.mjs +73 -0
  19. package/dist/as-cell-date.cjs +3 -0
  20. package/dist/as-cell-date.d.cts +2 -0
  21. package/dist/as-cell-date.d.mts +2 -0
  22. package/dist/as-cell-date.mjs +3 -0
  23. package/dist/as-cell-date.vue-BBSps2B2.d.cts +12 -0
  24. package/dist/as-cell-date.vue-Zlt4mHWb.d.mts +12 -0
  25. package/dist/as-cell-json-BynWIs1d.mjs +37 -0
  26. package/dist/as-cell-json-DvHvQ6IL.cjs +42 -0
  27. package/dist/as-cell-json-popover-BWdNs1YU.cjs +70 -0
  28. package/dist/as-cell-json-popover-DUq25I0L.mjs +65 -0
  29. package/dist/as-cell-json.cjs +3 -0
  30. package/dist/as-cell-json.d.cts +2 -0
  31. package/dist/as-cell-json.d.mts +2 -0
  32. package/dist/as-cell-json.mjs +3 -0
  33. package/dist/as-cell-json.vue-C6wg4ARZ.d.cts +12 -0
  34. package/dist/as-cell-json.vue-CESWuCer.d.mts +12 -0
  35. package/dist/as-cell-number-0_WrSCzu.cjs +76 -0
  36. package/dist/as-cell-number-Bc1C97Vg.mjs +71 -0
  37. package/dist/as-cell-number.cjs +3 -0
  38. package/dist/as-cell-number.d.cts +2 -0
  39. package/dist/as-cell-number.d.mts +2 -0
  40. package/dist/as-cell-number.mjs +3 -0
  41. package/dist/as-cell-number.vue-1Oq7nVI3.d.mts +12 -0
  42. package/dist/as-cell-number.vue-CJ2K5zeM.d.cts +12 -0
  43. package/dist/as-cell-union-C1w3B38J.mjs +88 -0
  44. package/dist/as-cell-union-CFAI0utz.cjs +93 -0
  45. package/dist/as-cell-union.cjs +4 -0
  46. package/dist/as-cell-union.d.cts +2 -0
  47. package/dist/as-cell-union.d.mts +2 -0
  48. package/dist/as-cell-union.mjs +4 -0
  49. package/dist/as-cell-union.vue-CslPM_c2.d.cts +12 -0
  50. package/dist/as-cell-union.vue-NnDmQZOA.d.mts +12 -0
  51. package/dist/as-column-menu-CH9Htz0Q.cjs +220 -0
  52. package/dist/as-column-menu-DCfhorMP.mjs +215 -0
  53. package/dist/as-column-menu.cjs +2 -0
  54. package/dist/as-column-menu.d.cts +2 -0
  55. package/dist/as-column-menu.d.mts +2 -0
  56. package/dist/as-column-menu.mjs +2 -0
  57. package/dist/as-column-menu.vue-C9e6wJ3z.d.mts +47 -0
  58. package/dist/as-column-menu.vue-o0qFdzoL.d.cts +47 -0
  59. package/dist/as-config-dialog-d2k7_l0U.cjs +699 -0
  60. package/dist/as-config-dialog-vRklaKbi.mjs +688 -0
  61. package/dist/as-config-dialog.cjs +3 -0
  62. package/dist/as-config-dialog.d.cts +2 -0
  63. package/dist/as-config-dialog.d.mts +2 -0
  64. package/dist/as-config-dialog.mjs +3 -0
  65. package/dist/as-config-dialog.vue-C6Q62xF5.d.mts +7 -0
  66. package/dist/as-config-dialog.vue-DvvJi3xx.d.cts +7 -0
  67. package/dist/as-confirm-dialog-BLh3Ju4-.mjs +52 -0
  68. package/dist/as-confirm-dialog-BgpIEE2z.cjs +57 -0
  69. package/dist/as-confirm-dialog.cjs +3 -0
  70. package/dist/as-confirm-dialog.d.cts +2 -0
  71. package/dist/as-confirm-dialog.d.mts +2 -0
  72. package/dist/as-confirm-dialog.mjs +3 -0
  73. package/dist/as-confirm-dialog.vue-CXxLpzbu.d.cts +7 -0
  74. package/dist/as-confirm-dialog.vue-pas8jGhv.d.mts +7 -0
  75. package/dist/as-filter-dialog-C0HMpUPT.mjs +610 -0
  76. package/dist/as-filter-dialog-DcGvIV3h.cjs +621 -0
  77. package/dist/as-filter-dialog.cjs +15 -0
  78. package/dist/as-filter-dialog.d.cts +2 -0
  79. package/dist/as-filter-dialog.d.mts +2 -0
  80. package/dist/as-filter-dialog.mjs +15 -0
  81. package/dist/as-filter-dialog.vue-BV2J8PgZ.d.cts +7 -0
  82. package/dist/as-filter-dialog.vue-RDZjp4gJ.d.mts +7 -0
  83. package/dist/as-filter-field-B_tYzvvl.cjs +984 -0
  84. package/dist/as-filter-field-Bqvu2ASN.mjs +943 -0
  85. package/dist/as-filter-field.cjs +9 -0
  86. package/dist/as-filter-field.d.cts +2 -0
  87. package/dist/as-filter-field.d.mts +2 -0
  88. package/dist/as-filter-field.mjs +9 -0
  89. package/dist/as-filter-field.vue-ByQ8xIGq.d.cts +11 -0
  90. package/dist/as-filter-field.vue-QY8wi5S5.d.mts +11 -0
  91. package/dist/as-filter-input--nr72iwX.cjs +106 -0
  92. package/dist/as-filter-input-P1i0CW2-.mjs +101 -0
  93. package/dist/as-filter-input.cjs +2 -0
  94. package/dist/as-filter-input.d.cts +2 -0
  95. package/dist/as-filter-input.d.mts +2 -0
  96. package/dist/as-filter-input.mjs +2 -0
  97. package/dist/as-filter-input.vue-CBQ71eNg.d.mts +18 -0
  98. package/dist/as-filter-input.vue-CS4nOk_Q.d.cts +18 -0
  99. package/dist/as-filters-Bxa9ZEMm.mjs +44 -0
  100. package/dist/as-filters-xRT2qv56.cjs +49 -0
  101. package/dist/as-filters.cjs +10 -0
  102. package/dist/as-filters.d.cts +2 -0
  103. package/dist/as-filters.d.mts +2 -0
  104. package/dist/as-filters.mjs +10 -0
  105. package/dist/as-filters.vue-BsMgYUcX.d.mts +10 -0
  106. package/dist/as-filters.vue-fv-tRL2H.d.cts +10 -0
  107. package/dist/as-preset-dialog-BaTfwMnh.cjs +569 -0
  108. package/dist/as-preset-dialog-BdDRgwf_.mjs +564 -0
  109. package/dist/as-preset-dialog.cjs +4 -0
  110. package/dist/as-preset-dialog.d.cts +2 -0
  111. package/dist/as-preset-dialog.d.mts +2 -0
  112. package/dist/as-preset-dialog.mjs +4 -0
  113. package/dist/as-preset-dialog.vue-Bzv-ON9W.d.mts +7 -0
  114. package/dist/as-preset-dialog.vue-DP9fy00Y.d.cts +7 -0
  115. package/dist/as-preset-picker-BQbNEiy9.mjs +427 -0
  116. package/dist/as-preset-picker-Ce3crTQy.cjs +432 -0
  117. package/dist/as-preset-picker.cjs +4 -0
  118. package/dist/as-preset-picker.d.cts +2 -0
  119. package/dist/as-preset-picker.d.mts +2 -0
  120. package/dist/as-preset-picker.mjs +4 -0
  121. package/dist/as-preset-picker.vue-CTBk6leV.d.mts +7 -0
  122. package/dist/as-preset-picker.vue-DfXS3pGl.d.cts +7 -0
  123. package/dist/as-row-actions-B6Kob6gt.cjs +120 -0
  124. package/dist/as-row-actions-CeWBBGqh.mjs +115 -0
  125. package/dist/as-row-actions.cjs +4 -0
  126. package/dist/as-row-actions.d.cts +2 -0
  127. package/dist/as-row-actions.d.mts +2 -0
  128. package/dist/as-row-actions.mjs +4 -0
  129. package/dist/as-row-actions.vue-BPaQfGev.d.mts +11 -0
  130. package/dist/as-row-actions.vue-Bvcc2tUN.d.cts +11 -0
  131. package/dist/as-table-Cnw2fOqZ.mjs +204 -0
  132. package/dist/as-table-DlDFxdXI.cjs +209 -0
  133. package/dist/as-table-actions-BK1Thy2G.cjs +142 -0
  134. package/dist/as-table-actions-BpMiNFni.mjs +137 -0
  135. package/dist/as-table-actions.cjs +4 -0
  136. package/dist/as-table-actions.d.cts +2 -0
  137. package/dist/as-table-actions.d.mts +2 -0
  138. package/dist/as-table-actions.mjs +4 -0
  139. package/dist/as-table-actions.vue-B7Q-JA3z.d.cts +47 -0
  140. package/dist/as-table-actions.vue-Bs1Jl1ep.d.mts +47 -0
  141. package/dist/as-table-base-D0k4k7k_.mjs +646 -0
  142. package/dist/as-table-base-VIz-B_6_.cjs +651 -0
  143. package/dist/as-table-cell-value-B1CiJYFn.mjs +26 -0
  144. package/dist/as-table-cell-value-CuxRtFn9.cjs +31 -0
  145. package/dist/as-table-cell-value.cjs +3 -0
  146. package/dist/as-table-cell-value.d.cts +2 -0
  147. package/dist/as-table-cell-value.d.mts +2 -0
  148. package/dist/as-table-cell-value.mjs +3 -0
  149. package/dist/as-table-cell-value.vue-BgFDv2JQ.d.cts +12 -0
  150. package/dist/as-table-cell-value.vue-BuPCQ8YA.d.mts +12 -0
  151. package/dist/as-table-header-cell-C3zeZUZo.cjs +117 -0
  152. package/dist/as-table-header-cell-CBn_ioCe.mjs +112 -0
  153. package/dist/as-table-header-cell.cjs +3 -0
  154. package/dist/as-table-header-cell.d.cts +2 -0
  155. package/dist/as-table-header-cell.d.mts +2 -0
  156. package/dist/as-table-header-cell.mjs +3 -0
  157. package/dist/as-table-header-cell.vue-Bc_DSsGY.d.cts +31 -0
  158. package/dist/as-table-header-cell.vue-DNMOHfek.d.mts +31 -0
  159. package/dist/as-table-root-Br6WcGRo.cjs +263 -0
  160. package/dist/as-table-root-gG7pTIdD.mjs +258 -0
  161. package/dist/as-table-root.cjs +28 -0
  162. package/dist/as-table-root.d.cts +2 -0
  163. package/dist/as-table-root.d.mts +2 -0
  164. package/dist/as-table-root.mjs +28 -0
  165. package/dist/as-table-root.vue-5_OhVwse.d.mts +2258 -0
  166. package/dist/as-table-root.vue-CSqEtIll.d.cts +2258 -0
  167. package/dist/as-table-status-BjRGGuhC.mjs +683 -0
  168. package/dist/as-table-status-DWYoJIMC.cjs +724 -0
  169. package/dist/as-table.cjs +10 -0
  170. package/dist/as-table.d.cts +2 -0
  171. package/dist/as-table.d.mts +2 -0
  172. package/dist/as-table.mjs +10 -0
  173. package/dist/as-table.vue-BTYg-e3Z.d.mts +81 -0
  174. package/dist/as-table.vue-wdRARLIe.d.cts +81 -0
  175. package/dist/as-window-table-CKIfo3M_.mjs +709 -0
  176. package/dist/as-window-table-DE7_NyEP.cjs +714 -0
  177. package/dist/as-window-table.cjs +9 -0
  178. package/dist/as-window-table.d.cts +2 -0
  179. package/dist/as-window-table.d.mts +2 -0
  180. package/dist/as-window-table.mjs +9 -0
  181. package/dist/as-window-table.vue-Bf8xGC9M.d.mts +86 -0
  182. package/dist/as-window-table.vue-CA8qsrz4.d.cts +86 -0
  183. package/dist/format-cell-B2xMDYO9.mjs +27 -0
  184. package/dist/format-cell-D4mqaN0E.cjs +32 -0
  185. package/dist/get-cell-value-CZSVfDLg.cjs +19 -0
  186. package/dist/get-cell-value-DiH84HKL.mjs +14 -0
  187. package/dist/index.cjs +598 -0
  188. package/dist/index.d.cts +21 -0
  189. package/dist/index.d.mts +21 -0
  190. package/dist/index.mjs +505 -0
  191. package/dist/preset-aspect-display-BYeiSgcc.mjs +43 -0
  192. package/dist/preset-aspect-display-y8aal_EF.cjs +72 -0
  193. package/dist/types-BvvXN72P.d.mts +531 -0
  194. package/dist/types-CNMmF6W2.d.cts +531 -0
  195. package/dist/use-cell-locale-1uQaFTLQ.mjs +23 -0
  196. package/dist/use-cell-locale-B480_QYK.cjs +34 -0
  197. package/dist/use-table-column-handlers-CGYAY2xH.cjs +65 -0
  198. package/dist/use-table-column-handlers-t6xi1yCE.mjs +54 -0
  199. package/dist/use-table-state-C4JbonEZ.mjs +1822 -0
  200. package/dist/use-table-state-MU-vuzui.cjs +1917 -0
  201. package/package.json +195 -0
  202. package/styles.d.ts +2 -0
@@ -0,0 +1,1917 @@
1
+ let _atscript_ui = require("@atscript/ui");
2
+ let vue = require("vue");
3
+ let _atscript_ui_table = require("@atscript/ui-table");
4
+ let _atscript_db_client = require("@atscript/db-client");
5
+ //#region src/types.ts
6
+ /** UI-side sentinel for the synthesised row-delete processor. */
7
+ const REMOVE_PROCESSOR = "__remove";
8
+ /** Column `path` and cell-type for the synthesised row-actions pseudo-column. */
9
+ const ROW_ACTIONS_PATH = "__actions";
10
+ const ROW_ACTIONS_TYPE = "__actions";
11
+ //#endregion
12
+ //#region src/composables/state/intent-scope.ts
13
+ /**
14
+ * Processors exempt from the per-row `$actions` gate: `navigate`/`custom`
15
+ * have no backend to ask, and `__remove` is synthesised client-side.
16
+ */
17
+ const EXEMPT_PROCESSORS = new Set([
18
+ "navigate",
19
+ "custom",
20
+ REMOVE_PROCESSOR
21
+ ]);
22
+ /**
23
+ * Build a per-row availability predicate from `row.$actions: string[]`
24
+ * (server-evaluated names of NOT-disabled row/rows-level actions for that
25
+ * row). Returns `null` when the row carries no `$actions` (legacy server,
26
+ * `?$actions` opt-out, or table-level surfaces) so callers can skip the
27
+ * filter pass and keep the source-array reference stable.
28
+ */
29
+ function rowActionGate(row) {
30
+ const raw = row?.$actions;
31
+ if (!Array.isArray(raw)) return null;
32
+ const allowed = new Set(raw);
33
+ return (a) => EXEMPT_PROCESSORS.has(a.processor) || allowed.has(a.name);
34
+ }
35
+ /**
36
+ * Apply the per-row `$actions` gate to a `{default, others, rows}` triple.
37
+ * When the row carries no `$actions`, returns the input unchanged so
38
+ * source-array references stay stable downstream (no spurious recomputes
39
+ * in consumers that compare array identity).
40
+ */
41
+ function applyRowGate(buckets, row) {
42
+ const gate = rowActionGate(row);
43
+ if (!gate) return buckets;
44
+ return {
45
+ default: buckets.default && gate(buckets.default) ? buckets.default : void 0,
46
+ others: buckets.others.filter(gate),
47
+ rows: buckets.rows.filter(gate)
48
+ };
49
+ }
50
+ /**
51
+ * Map `TDbActionInfo['intent']` (positive/negative/warning/primary/secondary)
52
+ * onto a vunor scope name accepted by `state.prompt()`. Used by row-actions
53
+ * and table-actions cells when surfacing an action's confirmation dialog —
54
+ * `negative → error`, `positive → good`, `warning → warn`, the rest pass
55
+ * through verbatim. Undefined intent → undefined scope (button stays default
56
+ * primary via the dialog's `c8-filled` chrome).
57
+ */
58
+ function intentToScope(intent) {
59
+ switch (intent) {
60
+ case "positive": return "good";
61
+ case "negative": return "error";
62
+ case "warning": return "warn";
63
+ case "primary": return "primary";
64
+ case "secondary": return "secondary";
65
+ default: return;
66
+ }
67
+ }
68
+ /**
69
+ * Build the identifier object to forward to `client.action` / `client.remove`.
70
+ *
71
+ * Per `@atscript/db-client` invariant #11, identifier bodies are object-only
72
+ * — never bare scalars, even for single-field PK tables. This helper accepts:
73
+ * - a row-shaped object (default `rowValueFn`) → picks `preferredId` fields;
74
+ * - a scalar value when `preferredId` has exactly one field (consumers that
75
+ * override `rowValueFn` to return the PK scalar) → wraps it.
76
+ *
77
+ * Returns `undefined` when no `preferredId` is declared, the source is
78
+ * `null`/`undefined`, or a scalar can't be paired with a single-field
79
+ * identifier.
80
+ */
81
+ function extractIdentifier(source, preferredId) {
82
+ if (source === void 0 || source === null) return void 0;
83
+ if (preferredId.length === 0) return void 0;
84
+ if (typeof source === "object" && !Array.isArray(source)) {
85
+ const row = source;
86
+ const out = {};
87
+ for (const k of preferredId) out[k] = row[k];
88
+ return out;
89
+ }
90
+ if (preferredId.length === 1) return { [preferredId[0]]: source };
91
+ }
92
+ /**
93
+ * Map a list of sources (full row objects or scalar `rowValueFn` values)
94
+ * through `extractIdentifier`. Scalar sources rehydrate from
95
+ * `state.windowCache` via `state.rowValueFn` so consumers that override
96
+ * `rowValueFn` to return a scalar can still reconstruct multi-field
97
+ * identifiers; the lookup `Map` is built lazily so all-object source
98
+ * lists pay nothing.
99
+ */
100
+ function collectIdentifiers(state, sources, preferredId) {
101
+ if (preferredId.length === 0 || sources.length === 0) return [];
102
+ const out = [];
103
+ let lookup = null;
104
+ for (const s of sources) {
105
+ if (s === void 0 || s === null) continue;
106
+ let row;
107
+ if (typeof s === "object") row = s;
108
+ else {
109
+ if (lookup === null) {
110
+ const fn = state.rowValueFn;
111
+ lookup = /* @__PURE__ */ new Map();
112
+ for (const r of state.windowCache.value.values()) lookup.set(fn(r), r);
113
+ }
114
+ row = lookup.get(s);
115
+ }
116
+ const id = extractIdentifier(row ?? s, preferredId);
117
+ if (id) out.push(id);
118
+ }
119
+ return out;
120
+ }
121
+ function ariaLabelFor(action) {
122
+ return action.label || action.name;
123
+ }
124
+ /**
125
+ * Compose the runtime intent class — `as-{prefix}-intent-{intent}` — applied
126
+ * by both `<AsRowActions>` and `<AsTableActions>` on buttons + menu items.
127
+ * Returns `undefined` when the action declares no intent so callers can
128
+ * spread the result into a class array without conditional plumbing.
129
+ */
130
+ function intentClass(prefix, action) {
131
+ return action.intent ? `${prefix}-intent-${action.intent}` : void 0;
132
+ }
133
+ /**
134
+ * Run `state.prompt()` if the action declares a `promptText`. Resolves
135
+ * `true` on accept, or `true` immediately when no prompt is needed.
136
+ *
137
+ * `promptText` may be a string or `[singular, plural]` tuple. Tuple form
138
+ * picks `singular` when there is at most one identifier, `plural` otherwise.
139
+ * Substitutions:
140
+ * - `$1` → `formatIdentifier(ctx.identifiers[0], ctx.preferredId)`
141
+ * - `$N` → `String(ctx.identifiers.length)`
142
+ */
143
+ async function confirmAction(state, action, ctx) {
144
+ const raw = action.promptText;
145
+ if (!raw) return true;
146
+ const count = ctx.identifiers.length;
147
+ const message = substitute(Array.isArray(raw) ? count <= 1 ? raw[0] : raw[1] : raw, ctx);
148
+ return state.prompt(message, { scope: intentToScope(action.intent) });
149
+ }
150
+ /** Substitute `$1` and `$N` into a prompt-text template. */
151
+ function substitute(template, ctx) {
152
+ return template.replace(/\$1/g, () => (0, _atscript_db_client.formatIdentifier)(ctx.identifiers[0], ctx.preferredId)).replace(/\$N/g, () => String(ctx.identifiers.length));
153
+ }
154
+ /**
155
+ * Dispatch user-initiated invocation: actions with `inputForm` open the form
156
+ * dialog (the form IS the confirm surface, so `promptText` is ignored);
157
+ * others run `confirmAction()`. Cancelling either dialog short-circuits.
158
+ */
159
+ async function triggerAction(state, action, ctx, event) {
160
+ const pk = pkForLevel(action.level, ctx.identifiers);
161
+ if (action.inputForm) {
162
+ const input = await state.requestActionInput(action, ctx);
163
+ if (input === null) return;
164
+ state.actions.invoke(action, pk, {
165
+ event,
166
+ input
167
+ });
168
+ return;
169
+ }
170
+ if (!await confirmAction(state, action, ctx)) return;
171
+ state.actions.invoke(action, pk, { event });
172
+ }
173
+ /**
174
+ * Pick the `pk` argument to forward to `state.actions.invoke` based on the
175
+ * action's level. `'table'` → `undefined`; `'row'` → first identifier;
176
+ * `'rows'` → full array.
177
+ */
178
+ function pkForLevel(level, ids) {
179
+ if (level === "table") return void 0;
180
+ if (level === "row") return ids[0];
181
+ return ids;
182
+ }
183
+ /**
184
+ * Inverse of `pkForLevel`: shape the `ids[]` surfaced by the `@action` emit
185
+ * from the `pk` value passed to `invoke`.
186
+ */
187
+ function idsForAction(level, pk) {
188
+ if (level === "table") return [];
189
+ if (level === "rows") return Array.isArray(pk) ? pk : pk === void 0 ? [] : [pk];
190
+ return pk === void 0 ? [] : [pk];
191
+ }
192
+ //#endregion
193
+ //#region src/composables/state/create-actions.ts
194
+ const REMOVE_NAME = REMOVE_PROCESSOR;
195
+ const REMOVE_DEFAULTS = {
196
+ label: "Delete",
197
+ icon: "i-as-trash",
198
+ intent: "negative",
199
+ promptText: ["Delete item $1?", "Delete $N items?"]
200
+ };
201
+ function createActions(opts) {
202
+ const invoking = (0, vue.shallowRef)(/* @__PURE__ */ new Set());
203
+ const lastResult = (0, vue.shallowRef)(/* @__PURE__ */ new Map());
204
+ const groups = (0, vue.computed)(() => buildGroups(opts.tableDef.value, opts.rowDelete()));
205
+ (0, vue.watch)(opts.tableDef, () => {
206
+ if (lastResult.value.size > 0) lastResult.value = /* @__PURE__ */ new Map();
207
+ });
208
+ function setInvoking(name, on) {
209
+ const next = new Set(invoking.value);
210
+ if (on) next.add(name);
211
+ else next.delete(name);
212
+ invoking.value = next;
213
+ }
214
+ function setLastResult(name, result) {
215
+ const next = new Map(lastResult.value);
216
+ next.set(name, result);
217
+ lastResult.value = next;
218
+ }
219
+ async function invoke(action, pk, callOpts) {
220
+ const name = action.name;
221
+ setInvoking(name, true);
222
+ let result;
223
+ try {
224
+ switch (action.processor) {
225
+ case "custom":
226
+ result = {
227
+ ok: true,
228
+ kind: "custom",
229
+ dispatched: true
230
+ };
231
+ break;
232
+ case "navigate":
233
+ await opts.client.action(action.name, pk);
234
+ result = {
235
+ ok: true,
236
+ kind: "navigate"
237
+ };
238
+ break;
239
+ case REMOVE_PROCESSOR:
240
+ result = {
241
+ ok: true,
242
+ kind: "remove",
243
+ data: await opts.client.remove(pk)
244
+ };
245
+ break;
246
+ default: {
247
+ const data = await opts.client.action(action.name, pk, callOpts?.input);
248
+ result = {
249
+ ok: true,
250
+ kind: "backend",
251
+ data,
252
+ message: typeof data === "object" && data !== null && "message" in data ? data.message : void 0
253
+ };
254
+ break;
255
+ }
256
+ }
257
+ } catch (err) {
258
+ result = {
259
+ ok: false,
260
+ kind: "error",
261
+ error: err instanceof Error ? err : new Error(String(err))
262
+ };
263
+ }
264
+ setLastResult(name, result);
265
+ setInvoking(name, false);
266
+ if (callOpts?.suppressRefresh !== true && opts.refreshOnAction() !== false && result.ok && (result.kind === "backend" || result.kind === "remove")) opts.scheduleQuery("query");
267
+ if (opts.onResolved) opts.onResolved(action, idsForAction(action.level, pk), result, callOpts?.event);
268
+ return result;
269
+ }
270
+ return {
271
+ actions: {
272
+ get table() {
273
+ return groups.value.table;
274
+ },
275
+ get row() {
276
+ return groups.value.row;
277
+ },
278
+ get rows() {
279
+ return groups.value.rows;
280
+ },
281
+ get default() {
282
+ return groups.value.default;
283
+ },
284
+ get others() {
285
+ return groups.value.others;
286
+ },
287
+ get cellRow() {
288
+ return groups.value.cellRow;
289
+ },
290
+ invoke,
291
+ invoking,
292
+ lastResult
293
+ },
294
+ groups
295
+ };
296
+ }
297
+ const EMPTY_GROUPS = Object.freeze({
298
+ table: [],
299
+ row: [],
300
+ rows: [],
301
+ default: {},
302
+ others: {
303
+ table: [],
304
+ row: [],
305
+ rows: []
306
+ },
307
+ cellRow: []
308
+ });
309
+ function buildGroups(def, rowDelete) {
310
+ if (def === null) return EMPTY_GROUPS;
311
+ const srcActions = def.actions ?? {
312
+ table: [],
313
+ row: [],
314
+ rows: [],
315
+ default: {}
316
+ };
317
+ const table = srcActions.table;
318
+ const row = srcActions.row.slice();
319
+ const rows = srcActions.rows;
320
+ if (rowDelete && def.canRemove) row.push(buildRemoveAction(rowDelete === true ? {} : rowDelete));
321
+ const defTable = srcActions.default.table;
322
+ const defRow = srcActions.default.row;
323
+ const defRows = srcActions.default.rows;
324
+ const othersTable = defTable ? table.filter((a) => a !== defTable) : table;
325
+ const othersRow = defRow ? row.filter((a) => a !== defRow) : row;
326
+ const othersRows = defRows ? rows.filter((a) => a !== defRows) : rows;
327
+ const cellRow = defRow ? [
328
+ defRow,
329
+ ...othersRow,
330
+ ...rows
331
+ ] : othersRow.length > 0 || rows.length > 0 ? [...othersRow, ...rows] : [];
332
+ return {
333
+ table,
334
+ row,
335
+ rows,
336
+ default: {
337
+ table: defTable,
338
+ row: defRow,
339
+ rows: defRows
340
+ },
341
+ others: {
342
+ table: othersTable,
343
+ row: othersRow,
344
+ rows: othersRows
345
+ },
346
+ cellRow
347
+ };
348
+ }
349
+ function buildRemoveAction(opts) {
350
+ return {
351
+ name: REMOVE_NAME,
352
+ label: opts.label ?? REMOVE_DEFAULTS.label,
353
+ level: "row",
354
+ processor: REMOVE_PROCESSOR,
355
+ value: "",
356
+ icon: opts.icon ?? REMOVE_DEFAULTS.icon,
357
+ intent: opts.intent ?? REMOVE_DEFAULTS.intent,
358
+ promptText: opts.confirm ?? REMOVE_DEFAULTS.promptText
359
+ };
360
+ }
361
+ //#endregion
362
+ //#region src/composables/state/create-selection.ts
363
+ function createSelectionApi(opts, getActiveRow) {
364
+ const selectedRows = opts?.selectedRows ?? (0, vue.shallowRef)([]);
365
+ const selectedCount = (0, vue.computed)(() => selectedRows.value.length);
366
+ const rowValueFn = opts?.rowValueFn ?? ((row) => row);
367
+ const selectedSet = (0, vue.computed)(() => new Set(selectedRows.value));
368
+ function isPkSelected(pk) {
369
+ return selectedSet.value.has(pk);
370
+ }
371
+ function toggleActiveSelection(mode) {
372
+ if (mode === "none") return;
373
+ const row = getActiveRow();
374
+ if (row === void 0) return;
375
+ selectedRows.value = (0, _atscript_ui_table.togglePk)(selectedRows.value, rowValueFn(row), mode);
376
+ }
377
+ return {
378
+ selectedRows,
379
+ selectedCount,
380
+ selectedSet,
381
+ rowValueFn,
382
+ isPkSelected,
383
+ toggleActiveSelection
384
+ };
385
+ }
386
+ //#endregion
387
+ //#region src/composables/state/create-main-action-registry.ts
388
+ /**
389
+ * Listener registry for the `main-action` event. When listeners are present,
390
+ * `requestMainAction` builds a `MainActionRequest` and dispatches it. When no
391
+ * listener is registered, falls back to invoking `actions.default.row` against
392
+ * the active row's PK (if both are defined). The fallback path SHALL NOT
393
+ * construct a `MainActionRequest` payload — there is nothing to receive it.
394
+ */
395
+ function createMainActionRegistry(opts) {
396
+ const listeners = /* @__PURE__ */ new Set();
397
+ const hasMainActionListener = (0, vue.ref)(false);
398
+ const hasMainActionAvailable = (0, vue.computed)(() => hasMainActionListener.value || opts.getDefaultRowAction?.() !== void 0);
399
+ function registerMainActionListener(cb) {
400
+ listeners.add(cb);
401
+ hasMainActionListener.value = listeners.size > 0;
402
+ let disposed = false;
403
+ return () => {
404
+ if (disposed) return;
405
+ disposed = true;
406
+ listeners.delete(cb);
407
+ hasMainActionListener.value = listeners.size > 0;
408
+ };
409
+ }
410
+ function requestMainAction(event) {
411
+ const abs = opts.getActiveIndex();
412
+ if (abs < 0) return;
413
+ const row = opts.getActiveRow();
414
+ if (row === void 0) return;
415
+ if (listeners.size > 0) {
416
+ const req = {
417
+ row,
418
+ absIndex: abs,
419
+ event
420
+ };
421
+ for (const cb of listeners) cb(req);
422
+ return;
423
+ }
424
+ const fallback = opts.getDefaultRowAction?.();
425
+ if (fallback && opts.invokeFallback) opts.invokeFallback(fallback, row, event);
426
+ }
427
+ return {
428
+ hasMainActionListener,
429
+ hasMainActionAvailable,
430
+ registerMainActionListener,
431
+ requestMainAction
432
+ };
433
+ }
434
+ //#endregion
435
+ //#region src/composables/state/create-nav-controller.ts
436
+ function createNavController(inputs) {
437
+ const { activeIndex, totalCount, results, viewportRowCount, topIndex, hasMainActionAvailable, requestMainAction, toggleActiveSelection } = inputs;
438
+ const navMode = (0, vue.ref)("pagination");
439
+ const navViewportRowCount = (0, vue.ref)(0);
440
+ function clampActive(idx) {
441
+ let upper;
442
+ if (navMode.value === "window") upper = totalCount.value;
443
+ else {
444
+ const r = results.value.length;
445
+ const t = totalCount.value;
446
+ if (r === 0) upper = t;
447
+ else if (t === 0) upper = 0;
448
+ else upper = Math.min(r, t);
449
+ }
450
+ if (upper === 0) return -1;
451
+ if (idx === -1) return -1;
452
+ if (idx < 0) return 0;
453
+ if (idx > upper - 1) return upper - 1;
454
+ return idx;
455
+ }
456
+ function setActive(absIndex) {
457
+ const next = clampActive(absIndex);
458
+ if (next !== activeIndex.value) activeIndex.value = next;
459
+ }
460
+ function clearActive() {
461
+ if (activeIndex.value !== -1) activeIndex.value = -1;
462
+ }
463
+ (0, vue.watch)([
464
+ () => totalCount.value,
465
+ () => results.value.length,
466
+ () => navMode.value
467
+ ], () => setActive(activeIndex.value));
468
+ function pageStep() {
469
+ return Math.max(viewportRowCount.value, navViewportRowCount.value, 10) - 1;
470
+ }
471
+ function activeBase() {
472
+ return activeIndex.value < 0 ? topIndex.value : activeIndex.value;
473
+ }
474
+ function navStep(delta) {
475
+ if (activeIndex.value < 0) setActive(topIndex.value);
476
+ else setActive(activeIndex.value + delta);
477
+ }
478
+ function navPage(delta) {
479
+ setActive(activeBase() + delta);
480
+ }
481
+ function handleNavKey(event, opts) {
482
+ if (totalCount.value === 0) return;
483
+ const enterAction = opts?.enterAction ?? "main-action";
484
+ const mode = opts?.mode ?? "none";
485
+ const key = event.key;
486
+ const meta = event.metaKey;
487
+ const ctrl = event.ctrlKey;
488
+ const alt = event.altKey;
489
+ if (key === "ArrowDown" && (meta || ctrl)) {
490
+ event.preventDefault();
491
+ setActive(totalCount.value - 1);
492
+ return;
493
+ }
494
+ if (key === "ArrowUp" && (meta || ctrl)) {
495
+ event.preventDefault();
496
+ setActive(0);
497
+ return;
498
+ }
499
+ if (key === "ArrowDown" && alt) {
500
+ event.preventDefault();
501
+ navPage(pageStep());
502
+ return;
503
+ }
504
+ if (key === "ArrowUp" && alt) {
505
+ event.preventDefault();
506
+ navPage(-pageStep());
507
+ return;
508
+ }
509
+ switch (key) {
510
+ case "ArrowDown":
511
+ event.preventDefault();
512
+ navStep(1);
513
+ return;
514
+ case "ArrowUp":
515
+ event.preventDefault();
516
+ navStep(-1);
517
+ return;
518
+ case "PageDown":
519
+ event.preventDefault();
520
+ navPage(pageStep());
521
+ return;
522
+ case "PageUp":
523
+ event.preventDefault();
524
+ navPage(-pageStep());
525
+ return;
526
+ case "Home":
527
+ event.preventDefault();
528
+ setActive(0);
529
+ return;
530
+ case "End":
531
+ event.preventDefault();
532
+ setActive(totalCount.value - 1);
533
+ return;
534
+ case " ":
535
+ if (mode === "none") return;
536
+ event.preventDefault();
537
+ toggleActiveSelection(mode);
538
+ return;
539
+ case "Enter":
540
+ if (enterAction === "passthrough") return;
541
+ event.preventDefault();
542
+ if (enterAction === "toggle-select") {
543
+ toggleActiveSelection(mode);
544
+ return;
545
+ }
546
+ if (mode !== "none") {
547
+ toggleActiveSelection(mode);
548
+ return;
549
+ }
550
+ if (hasMainActionAvailable.value) requestMainAction(event);
551
+ return;
552
+ case "Escape":
553
+ case "Esc":
554
+ clearActive();
555
+ return;
556
+ }
557
+ }
558
+ return {
559
+ navMode,
560
+ navViewportRowCount,
561
+ setActive,
562
+ clearActive,
563
+ handleNavKey
564
+ };
565
+ }
566
+ //#endregion
567
+ //#region src/composables/state/create-preset-state.ts
568
+ const DEFAULT_AVAILABLE_ASPECTS = [
569
+ "columns",
570
+ "filters",
571
+ "filterOps",
572
+ "sorters"
573
+ ];
574
+ /**
575
+ * Default `columnNames` for a system preset that doesn't specify them — every
576
+ * column whose schema declares it visible (i.e. lacking `@ui.table.hidden`).
577
+ * Used by both `apply` (no-explicit-columnNames fallback) and `expandDefault`,
578
+ * so the two paths can never drift apart.
579
+ */
580
+ function defaultVisibleColumnPaths(o) {
581
+ const out = [];
582
+ for (const col of o.allColumns.value) if (col.visible) out.push(col.path);
583
+ return out;
584
+ }
585
+ /**
586
+ * Slice that turns `usePresets` + `useLocalDraft` into the table-state
587
+ * surface declared on `ReactiveTableState`. Pure mutation: writes to the
588
+ * model arrays but never calls `query()` — the root watcher reacts.
589
+ */
590
+ function createPresetState(opts) {
591
+ const availableAspects = opts.availableAspects ?? DEFAULT_AVAILABLE_ASPECTS;
592
+ const availableSet = new Set(availableAspects);
593
+ const dialogOpen = (0, vue.ref)(false);
594
+ const gate = (0, vue.ref)(false);
595
+ const ready = (0, vue.computed)(() => opts.presetsHandle ? gate.value : true);
596
+ const handle = opts.presetsHandle ?? createInertHandle(opts.fallbackSystemPresets);
597
+ const { presets, presetsById, userConf, capabilities, systemPresets, systemPresetsById, available, currentUser, activePresetId: activeId } = handle;
598
+ const canSaveActive = (0, vue.computed)(() => {
599
+ const id = activeId.value;
600
+ if (!id || (0, _atscript_ui_table.isSystemPresetId)(id)) return false;
601
+ return opts.presetsHandle ? opts.presetsHandle.isOwned(id) : false;
602
+ });
603
+ const ASPECT_HANDLERS = {
604
+ columns: {
605
+ capture(o) {
606
+ const overrides = {};
607
+ for (const col of o.allColumns.value) {
608
+ const entry = o.columnWidths.value[col.path];
609
+ if (entry && entry.w !== entry.d) overrides[col.path] = entry.w;
610
+ }
611
+ const columns = { columnNames: [...o.columnNames.value] };
612
+ if (Object.keys(overrides).length > 0) columns.columnWidths = overrides;
613
+ return columns;
614
+ },
615
+ apply(o, value, isSystem) {
616
+ if (!value && !isSystem) return;
617
+ const cols = value;
618
+ const columnNames = cols?.columnNames ?? defaultVisibleColumnPaths(o);
619
+ const columnWidths = cols?.columnWidths;
620
+ o.columnNames.value = [...columnNames];
621
+ const next = {};
622
+ for (const [path, entry] of Object.entries(o.columnWidths.value)) next[path] = {
623
+ w: entry.d,
624
+ d: entry.d
625
+ };
626
+ if (columnWidths) for (const [path, w] of Object.entries(columnWidths)) {
627
+ const existing = next[path];
628
+ if (existing) next[path] = {
629
+ w,
630
+ d: existing.d
631
+ };
632
+ }
633
+ o.columnWidths.value = next;
634
+ },
635
+ expandDefault(o) {
636
+ return { columnNames: defaultVisibleColumnPaths(o) };
637
+ }
638
+ },
639
+ filters: {
640
+ capture: (o) => [...o.filterFields.value],
641
+ apply(o, value, isSystem) {
642
+ if (!value && !isSystem) return;
643
+ o.filterFields.value = value ? [...value] : [];
644
+ },
645
+ expandDefault: () => []
646
+ },
647
+ filterOps: {
648
+ capture(o) {
649
+ const dict = {};
650
+ for (const [field, conditions] of Object.entries(o.filters.value)) dict[field] = conditions;
651
+ return dict;
652
+ },
653
+ apply(o, value, isSystem) {
654
+ if (!value && !isSystem) return;
655
+ o.filters.value = value ? { ...value } : {};
656
+ },
657
+ expandDefault: () => ({})
658
+ },
659
+ sorters: {
660
+ capture: (o) => [...o.sorters.value],
661
+ apply(o, value, isSystem) {
662
+ if (!value && !isSystem) return;
663
+ o.sorters.value = value ? [...value] : [];
664
+ },
665
+ expandDefault: () => []
666
+ },
667
+ itemsPerPage: {
668
+ capture: (o) => o.pagination.value.itemsPerPage,
669
+ apply(o, value) {
670
+ if (value === void 0) return;
671
+ o.pagination.value = {
672
+ page: 1,
673
+ itemsPerPage: value
674
+ };
675
+ }
676
+ }
677
+ };
678
+ function maskAllows(aspect, mask) {
679
+ if (!availableSet.has(aspect)) return false;
680
+ if (!mask) return true;
681
+ return mask[aspect] === true;
682
+ }
683
+ function captureSnapshot(mask) {
684
+ const out = {};
685
+ for (const aspect of availableAspects) {
686
+ if (!maskAllows(aspect, mask)) continue;
687
+ out[aspect] = ASPECT_HANDLERS[aspect].capture(opts);
688
+ }
689
+ return out;
690
+ }
691
+ function resolveSnapshot(idOrSnapshot) {
692
+ if (typeof idOrSnapshot !== "string") return {
693
+ snapshot: idOrSnapshot,
694
+ nextActiveId: activeId.value
695
+ };
696
+ const id = idOrSnapshot;
697
+ if ((0, _atscript_ui_table.isSystemPresetId)(id)) {
698
+ const sp = systemPresetsById.value.get(id);
699
+ if (sp) return {
700
+ snapshot: sp.content,
701
+ nextActiveId: id
702
+ };
703
+ return {
704
+ snapshot: systemPresetsById.value.get(_atscript_ui_table.STANDARD_PRESET_ID)?.content ?? {},
705
+ nextActiveId: _atscript_ui_table.STANDARD_PRESET_ID
706
+ };
707
+ }
708
+ const row = presetsById.value.get(id);
709
+ if (!row) return {
710
+ snapshot: {},
711
+ nextActiveId: null
712
+ };
713
+ const wire = row.data?.content;
714
+ return {
715
+ snapshot: wire ? (0, _atscript_ui_table.fromWireSnapshot)(wire) : {},
716
+ nextActiveId: id
717
+ };
718
+ }
719
+ function apply(idOrSnapshot) {
720
+ const { snapshot, nextActiveId } = resolveSnapshot(idOrSnapshot);
721
+ const isSystem = typeof idOrSnapshot === "string" && nextActiveId !== null && (0, _atscript_ui_table.isSystemPresetId)(nextActiveId);
722
+ for (const aspect of availableAspects) {
723
+ const value = snapshot[aspect];
724
+ ASPECT_HANDLERS[aspect].apply(opts, value, isSystem);
725
+ }
726
+ if (snapshot.itemsPerPage === void 0) {
727
+ const current = opts.pagination.value;
728
+ if (current.page !== 1) opts.pagination.value = {
729
+ ...current,
730
+ page: 1
731
+ };
732
+ }
733
+ if (typeof idOrSnapshot === "string") activeId.value = nextActiveId;
734
+ }
735
+ function resetActive() {
736
+ const id = activeId.value;
737
+ if (!id) return;
738
+ apply(id);
739
+ }
740
+ function clearLocalDraft() {
741
+ opts.draftHandle?.clear();
742
+ }
743
+ /**
744
+ * Fill missing aspects with factory defaults so a system preset's snapshot
745
+ * "claims all aspects in `availableAspects`" per spec §3.7. Stored presets
746
+ * keep dict-form opt-in (untouched).
747
+ */
748
+ function expandSystemSnapshot(snapshot) {
749
+ const out = { ...snapshot };
750
+ for (const aspect of availableAspects) {
751
+ const handler = ASPECT_HANDLERS[aspect];
752
+ if (!handler.expandDefault) continue;
753
+ if (out[aspect] !== void 0) continue;
754
+ out[aspect] = handler.expandDefault(opts);
755
+ }
756
+ return out;
757
+ }
758
+ const activeSnapshot = (0, vue.computed)(() => {
759
+ const id = activeId.value;
760
+ if (!id) return {};
761
+ if ((0, _atscript_ui_table.isSystemPresetId)(id)) return expandSystemSnapshot(systemPresetsById.value.get(id)?.content ?? {});
762
+ const row = presetsById.value.get(id);
763
+ if (!row) return {};
764
+ const wire = row.data?.content;
765
+ return wire ? (0, _atscript_ui_table.fromWireSnapshot)(wire) : {};
766
+ });
767
+ const isDirty = (0, vue.computed)(() => (0, _atscript_ui_table.isDirtyAgainst)(activeSnapshot.value, captureSnapshot()));
768
+ function requirePresets() {
769
+ if (!opts.presetsHandle) throw new Error("[vue-table] presets feature not configured (preset.url missing)");
770
+ return opts.presetsHandle;
771
+ }
772
+ async function saveActive() {
773
+ const handle = requirePresets();
774
+ const id = activeId.value;
775
+ if (!id) throw new Error("[vue-table] saveActive: no active preset");
776
+ if ((0, _atscript_ui_table.isSystemPresetId)(id)) throw new Error("[vue-table] saveActive: system presets cannot be overwritten");
777
+ const existing = activeSnapshot.value;
778
+ const mask = {};
779
+ for (const aspect of availableAspects) if (existing[aspect] !== void 0) mask[aspect] = true;
780
+ await handle.savePreset(captureSnapshot(mask));
781
+ clearLocalDraft();
782
+ }
783
+ async function saveAs(label, saveOpts = {}) {
784
+ const id = await requirePresets().savePresetAs(label, captureSnapshot(saveOpts.aspects), { public: saveOpts.public });
785
+ clearLocalDraft();
786
+ return id;
787
+ }
788
+ async function rename(id, label) {
789
+ await requirePresets().renamePreset(id, label);
790
+ }
791
+ async function remove(id) {
792
+ const handle = requirePresets();
793
+ const wasActive = activeId.value === id;
794
+ await handle.deletePreset(id);
795
+ if (wasActive) {
796
+ apply(_atscript_ui_table.STANDARD_PRESET_ID);
797
+ clearLocalDraft();
798
+ }
799
+ }
800
+ async function togglePublic(id) {
801
+ await requirePresets().togglePublic(id);
802
+ }
803
+ async function setDefault(id) {
804
+ await requirePresets().setDefault(id);
805
+ }
806
+ async function toggleFav(id) {
807
+ await requirePresets().toggleFav(id);
808
+ }
809
+ async function setFavorites(ids) {
810
+ await requirePresets().setFavorites(ids);
811
+ }
812
+ function batch(fn) {
813
+ return opts.presetsHandle ? opts.presetsHandle.batch(fn) : fn();
814
+ }
815
+ function resolveDefaultId() {
816
+ const pinned = (userConf.value?.data)?.defaultPresetId;
817
+ if (!pinned) return _atscript_ui_table.STANDARD_PRESET_ID;
818
+ if ((0, _atscript_ui_table.isSystemPresetId)(pinned)) return systemPresetsById.value.has(pinned) ? pinned : _atscript_ui_table.STANDARD_PRESET_ID;
819
+ return presetsById.value.has(pinned) ? pinned : _atscript_ui_table.STANDARD_PRESET_ID;
820
+ }
821
+ function bootstrap() {
822
+ if (!opts.presetsHandle) return;
823
+ const handle = opts.presetsHandle;
824
+ const stop = (0, vue.watch)(() => handle.loading.value, (loading) => {
825
+ if (loading) return;
826
+ stop();
827
+ const id = resolveDefaultId();
828
+ const { snapshot, nextActiveId } = resolveSnapshot(id);
829
+ const base = (0, _atscript_ui_table.isSystemPresetId)(id) ? expandSystemSnapshot(snapshot) : snapshot;
830
+ const draftEnabled = !!(opts.draftHandle && opts.persistDrafts);
831
+ apply(draftEnabled ? opts.draftHandle.hydrate(base) : base);
832
+ activeId.value = nextActiveId;
833
+ if (draftEnabled) opts.draftHandle.watchAndPersist(() => captureSnapshot(), () => activeSnapshot.value);
834
+ (0, vue.nextTick)(() => {
835
+ gate.value = true;
836
+ });
837
+ }, { immediate: true });
838
+ }
839
+ return {
840
+ slice: {
841
+ presets,
842
+ presetsById,
843
+ userConf,
844
+ capabilities,
845
+ systemPresets,
846
+ systemPresetsById,
847
+ availableAspects,
848
+ available,
849
+ activeId,
850
+ activeSnapshot,
851
+ isDirty,
852
+ canSaveActive,
853
+ currentUser,
854
+ isOwned: (id) => handle.isOwned(id),
855
+ dialogOpen,
856
+ ready,
857
+ captureSnapshot,
858
+ apply,
859
+ resetActive,
860
+ clearLocalDraft,
861
+ resolveDefaultId,
862
+ saveActive,
863
+ saveAs,
864
+ rename,
865
+ remove,
866
+ togglePublic,
867
+ setDefault,
868
+ toggleFav,
869
+ setFavorites,
870
+ batch
871
+ },
872
+ internals: {
873
+ gate,
874
+ bootstrap
875
+ }
876
+ };
877
+ }
878
+ function inertMutator() {
879
+ throw new Error("[vue-table] presets feature not configured (preset.url missing)");
880
+ }
881
+ function createInertHandle(fallbackSystemPresets) {
882
+ const systemList = fallbackSystemPresets ?? (0, _atscript_ui_table.resolveSystemPresets)();
883
+ const systemPresets = (0, vue.computed)(() => systemList);
884
+ const systemPresetsById = (0, vue.computed)(() => {
885
+ const map = /* @__PURE__ */ new Map();
886
+ for (const sp of systemList) map.set(sp.id, sp);
887
+ return map;
888
+ });
889
+ return {
890
+ presets: (0, vue.shallowRef)([]),
891
+ presetsById: (0, vue.computed)(() => /* @__PURE__ */ new Map()),
892
+ userConf: (0, vue.shallowRef)(null),
893
+ capabilities: (0, vue.ref)(null),
894
+ systemPresets,
895
+ systemPresetsById,
896
+ available: (0, vue.computed)(() => false),
897
+ loading: (0, vue.ref)(false),
898
+ error: (0, vue.ref)(null),
899
+ currentUser: (0, vue.computed)(() => null),
900
+ activePresetId: (0, vue.ref)(null),
901
+ activePreset: (0, vue.computed)(() => null),
902
+ isOwned: () => false,
903
+ reload: async () => {},
904
+ batch: (fn) => fn(),
905
+ savePreset: inertMutator,
906
+ savePresetAs: inertMutator,
907
+ renamePreset: inertMutator,
908
+ deletePreset: inertMutator,
909
+ togglePublic: inertMutator,
910
+ setDefault: inertMutator,
911
+ toggleFav: inertMutator,
912
+ setFavorites: inertMutator
913
+ };
914
+ }
915
+ //#endregion
916
+ //#region src/composables/state/create-window-fetcher.ts
917
+ function createWindowFetcher(inputs) {
918
+ const { blockSize, dragReleaseDebounceMs, tableDef, totalCount, results, resultsStart, queryingNext, getGeneration, isQueryBlocked, buildCurrentQuery, dispatchPages, reportError } = inputs;
919
+ const windowCache = (0, vue.shallowRef)(/* @__PURE__ */ new Map());
920
+ /** Per-block firstIndex values currently in flight. `loadingAt(absIdx)`
921
+ * derives membership via `blockStartFor(absIdx, blockSize)`. */
922
+ const windowLoading = (0, vue.shallowRef)(/* @__PURE__ */ new Set());
923
+ const errors = (0, vue.shallowRef)(/* @__PURE__ */ new Map());
924
+ const topIndex = (0, vue.ref)(0);
925
+ const viewportRowCount = (0, vue.ref)(0);
926
+ const settlements = [];
927
+ let flushScheduled = false;
928
+ function flushSettlements() {
929
+ flushScheduled = false;
930
+ if (settlements.length === 0) return;
931
+ const currentGen = getGeneration();
932
+ let applied = false;
933
+ let nextCache = windowCache.value;
934
+ let nextLoading = windowLoading.value;
935
+ let nextErrors = errors.value;
936
+ let totalCountChanged = false;
937
+ let nextTotal = totalCount.value;
938
+ for (const s of settlements) {
939
+ if (s.gen !== currentGen) continue;
940
+ if (!applied) {
941
+ applied = true;
942
+ nextCache = new Map(windowCache.value);
943
+ nextLoading = new Set(windowLoading.value);
944
+ nextErrors = new Map(errors.value);
945
+ }
946
+ nextLoading.delete(s.firstIndex);
947
+ if (s.kind === "ok") {
948
+ for (let i = 0; i < s.rows.length; i++) nextCache.set(s.firstIndex + i, s.rows[i]);
949
+ nextErrors.delete(s.firstIndex);
950
+ nextTotal = s.count;
951
+ totalCountChanged = true;
952
+ } else {
953
+ nextErrors.set(s.firstIndex, s.error);
954
+ reportError(s.error, s.sourceKind);
955
+ }
956
+ }
957
+ settlements.length = 0;
958
+ if (!applied) return;
959
+ windowCache.value = nextCache;
960
+ windowLoading.value = nextLoading;
961
+ errors.value = nextErrors;
962
+ if (totalCountChanged && nextTotal !== totalCount.value) totalCount.value = nextTotal;
963
+ const fwd = (0, _atscript_ui_table.walkForwardAbsorb)(results.value, resultsStart.value, nextCache);
964
+ const bwd = (0, _atscript_ui_table.walkBackwardAbsorb)(fwd.newResults, fwd.newResultsStart, nextCache);
965
+ if (bwd.newResults !== results.value) {
966
+ results.value = bwd.newResults;
967
+ resultsStart.value = bwd.newResultsStart;
968
+ }
969
+ }
970
+ function scheduleFlush() {
971
+ if (flushScheduled) return;
972
+ flushScheduled = true;
973
+ queueMicrotask(flushSettlements);
974
+ }
975
+ function loadRangeInternal(skip, limit, kind) {
976
+ if (isQueryBlocked()) return Promise.resolve();
977
+ const blocks = (0, _atscript_ui_table.pageAlignedBlocksFor)(skip, limit, blockSize);
978
+ if (blocks.length === 0) return Promise.resolve();
979
+ const cache = windowCache.value;
980
+ const loading = windowLoading.value;
981
+ const missing = [];
982
+ for (const b of blocks) {
983
+ if (loading.has(b.firstIndex)) continue;
984
+ const end = b.firstIndex + blockSize;
985
+ for (let idx = b.firstIndex; idx < end; idx++) if (!cache.has(idx)) {
986
+ missing.push(b);
987
+ break;
988
+ }
989
+ }
990
+ if (missing.length === 0) return Promise.resolve();
991
+ const thisGen = getGeneration();
992
+ const baseQuery = buildCurrentQuery();
993
+ const nextLoading = new Set(windowLoading.value);
994
+ const prevErrors = errors.value;
995
+ let nextErrors = prevErrors;
996
+ let errorsChanged = false;
997
+ for (const b of missing) {
998
+ nextLoading.add(b.firstIndex);
999
+ if (prevErrors.has(b.firstIndex)) {
1000
+ if (!errorsChanged) {
1001
+ nextErrors = new Map(prevErrors);
1002
+ errorsChanged = true;
1003
+ }
1004
+ nextErrors.delete(b.firstIndex);
1005
+ }
1006
+ }
1007
+ windowLoading.value = nextLoading;
1008
+ if (errorsChanged) errors.value = nextErrors;
1009
+ const promises = missing.map(async (b) => {
1010
+ try {
1011
+ const { data, count } = await dispatchPages(baseQuery, b.page, blockSize);
1012
+ if (thisGen !== getGeneration()) return;
1013
+ settlements.push({
1014
+ kind: "ok",
1015
+ gen: thisGen,
1016
+ firstIndex: b.firstIndex,
1017
+ rows: data,
1018
+ count
1019
+ });
1020
+ scheduleFlush();
1021
+ } catch (err) {
1022
+ if (thisGen !== getGeneration()) return;
1023
+ const error = err instanceof Error ? err : new Error(String(err));
1024
+ settlements.push({
1025
+ kind: "err",
1026
+ gen: thisGen,
1027
+ firstIndex: b.firstIndex,
1028
+ error,
1029
+ sourceKind: kind
1030
+ });
1031
+ scheduleFlush();
1032
+ }
1033
+ });
1034
+ return Promise.allSettled(promises).then(() => void 0);
1035
+ }
1036
+ function loadRange(skip, limit) {
1037
+ return loadRangeInternal(skip, limit, "loadRange");
1038
+ }
1039
+ function queryNext() {
1040
+ if (queryingNext.value) return;
1041
+ if (isQueryBlocked()) return;
1042
+ queryingNext.value = true;
1043
+ loadRangeInternal(resultsStart.value + results.value.length, blockSize, "queryNext").finally(() => {
1044
+ queryingNext.value = false;
1045
+ });
1046
+ }
1047
+ function dataAt(absIdx) {
1048
+ return windowCache.value.get(absIdx);
1049
+ }
1050
+ function loadingAt(absIdx) {
1051
+ if (blockSize <= 0) return false;
1052
+ return windowLoading.value.has((0, _atscript_ui_table.blockStartFor)(absIdx, blockSize));
1053
+ }
1054
+ function errorAt(absIdx) {
1055
+ if (blockSize <= 0) return null;
1056
+ return errors.value.get((0, _atscript_ui_table.blockStartFor)(absIdx, blockSize)) ?? null;
1057
+ }
1058
+ function clearSettlements() {
1059
+ settlements.length = 0;
1060
+ }
1061
+ function resetWindow() {
1062
+ settlements.length = 0;
1063
+ windowCache.value = /* @__PURE__ */ new Map();
1064
+ windowLoading.value = /* @__PURE__ */ new Set();
1065
+ errors.value = /* @__PURE__ */ new Map();
1066
+ }
1067
+ let pendingJumpPlan = null;
1068
+ const debouncedJumpDispatch = (0, _atscript_ui_table.debounce)(() => {
1069
+ const plan = pendingJumpPlan;
1070
+ pendingJumpPlan = null;
1071
+ if (plan) loadRange(plan.skip, plan.limit);
1072
+ }, dragReleaseDebounceMs);
1073
+ const prefetchBuffer = Math.max(1, Math.floor(blockSize / 4));
1074
+ (0, vue.watch)([() => topIndex.value, () => viewportRowCount.value], () => {
1075
+ if (tableDef.value === null) return;
1076
+ if (viewportRowCount.value <= 0) return;
1077
+ const plan = (0, _atscript_ui_table.planFetch)({
1078
+ top: topIndex.value,
1079
+ viewport: viewportRowCount.value,
1080
+ totalCount: totalCount.value,
1081
+ cache: windowCache.value,
1082
+ blockSize,
1083
+ buffer: prefetchBuffer
1084
+ });
1085
+ if (plan === null) return;
1086
+ if (plan.mode === "jump") {
1087
+ pendingJumpPlan = plan;
1088
+ debouncedJumpDispatch();
1089
+ } else {
1090
+ if (windowLoading.value.has((0, _atscript_ui_table.blockStartFor)(plan.skip, blockSize))) return;
1091
+ loadRange(plan.skip, plan.limit);
1092
+ }
1093
+ });
1094
+ function disposeDebounces() {
1095
+ debouncedJumpDispatch.cancel();
1096
+ }
1097
+ return {
1098
+ windowCache,
1099
+ windowLoading,
1100
+ errors,
1101
+ topIndex,
1102
+ viewportRowCount,
1103
+ dataAt,
1104
+ loadingAt,
1105
+ errorAt,
1106
+ loadRange,
1107
+ queryNext,
1108
+ clearSettlements,
1109
+ resetWindow,
1110
+ disposeDebounces
1111
+ };
1112
+ }
1113
+ //#endregion
1114
+ //#region src/composables/use-table-state.ts
1115
+ const TABLE_KEY = "__as_table";
1116
+ const FILTER_DEBOUNCE_MS = 500;
1117
+ const DEFAULT_BLOCK_SIZE = 100;
1118
+ const DEFAULT_DRAG_RELEASE_DEBOUNCE_MS = 300;
1119
+ const DEFAULT_ITEMS_PER_PAGE = 25;
1120
+ let _tblUid = 0;
1121
+ /**
1122
+ * Coerce a primitive cell value to a string for the static-mode query
1123
+ * function's substring search and locale-aware sort. Objects fall back to
1124
+ * `""` so `'[object Object]'` never leaks into the search index. Module
1125
+ * scope so it's not recreated on every fetch.
1126
+ */
1127
+ function cellAsString(v) {
1128
+ if (v == null) return "";
1129
+ if (typeof v === "string") return v;
1130
+ if (typeof v === "number" || typeof v === "boolean") return v.toString();
1131
+ return "";
1132
+ }
1133
+ /**
1134
+ * Promise-based dialog slot. A second `request()` while one is open
1135
+ * auto-resolves the prior one with `cancelValue` (the user couldn't have
1136
+ * answered both). `dismiss()` is the same path; `accept(v)` resolves with `v`.
1137
+ */
1138
+ function createRequestSlot(cancelValue) {
1139
+ const r = (0, vue.shallowRef)(null);
1140
+ function request(body) {
1141
+ if (r.value) {
1142
+ r.value.resolve(cancelValue);
1143
+ r.value = null;
1144
+ }
1145
+ return new Promise((resolve) => {
1146
+ r.value = {
1147
+ ...body,
1148
+ resolve
1149
+ };
1150
+ });
1151
+ }
1152
+ function accept(value) {
1153
+ const req = r.value;
1154
+ if (!req) return;
1155
+ r.value = null;
1156
+ req.resolve(value);
1157
+ }
1158
+ function dismiss() {
1159
+ const req = r.value;
1160
+ if (!req) return;
1161
+ r.value = null;
1162
+ req.resolve(cancelValue);
1163
+ }
1164
+ return {
1165
+ ref: r,
1166
+ request,
1167
+ accept,
1168
+ dismiss
1169
+ };
1170
+ }
1171
+ function createTableState(opts) {
1172
+ const client = opts.client;
1173
+ const modelOpts = opts.model;
1174
+ const selectionOpts = opts.selection;
1175
+ const windowOpts = opts.window;
1176
+ const queryOpts = opts.query;
1177
+ const blockSize = windowOpts?.blockSize ?? DEFAULT_BLOCK_SIZE;
1178
+ const dragReleaseDebounceMs = windowOpts?.dragReleaseDebounceMs ?? DEFAULT_DRAG_RELEASE_DEBOUNCE_MS;
1179
+ const tableDef = (0, vue.shallowRef)(null);
1180
+ const loadingMetadata = (0, vue.ref)(true);
1181
+ const allColumns = (0, vue.shallowRef)([]);
1182
+ const filterFields = modelOpts?.filterFields ?? (0, vue.shallowRef)([]);
1183
+ const columnNames = modelOpts?.columnNames ?? (0, vue.shallowRef)([]);
1184
+ const columnWidths = modelOpts?.columnWidths ?? (0, vue.ref)({});
1185
+ const sorters = modelOpts?.sorters ?? (0, vue.shallowRef)([]);
1186
+ const columns = (0, vue.computed)(() => {
1187
+ const all = allColumns.value;
1188
+ if (all.length === 0 || columnNames.value.length === 0) return [];
1189
+ const map = new Map(all.map((c) => [c.path, c]));
1190
+ const result = [];
1191
+ for (const name of columnNames.value) {
1192
+ const col = map.get(name);
1193
+ if (col) result.push(col);
1194
+ }
1195
+ return result;
1196
+ });
1197
+ const rowDelete = (0, vue.ref)(false);
1198
+ const includeActions = (0, vue.ref)(false);
1199
+ const filters = (0, vue.shallowRef)({});
1200
+ const results = (0, vue.shallowRef)([]);
1201
+ const resultsStart = (0, vue.ref)(0);
1202
+ const querying = (0, vue.ref)(false);
1203
+ const queryingNext = (0, vue.ref)(false);
1204
+ const totalCount = (0, vue.ref)(0);
1205
+ const loadedCount = (0, vue.computed)(() => results.value.length);
1206
+ const pagination = (0, vue.ref)({
1207
+ page: 1,
1208
+ itemsPerPage: opts.limit ?? DEFAULT_ITEMS_PER_PAGE
1209
+ });
1210
+ const queryError = (0, vue.ref)(null);
1211
+ const metadataError = (0, vue.ref)(null);
1212
+ const lastError = (0, vue.ref)(null);
1213
+ function reportError(error, kind) {
1214
+ lastError.value = {
1215
+ error,
1216
+ kind
1217
+ };
1218
+ }
1219
+ const mustRefresh = (0, vue.ref)(false);
1220
+ const searchTerm = (0, vue.ref)("");
1221
+ const configDialogOpen = (0, vue.ref)(false);
1222
+ const configTab = (0, vue.ref)("columns");
1223
+ const filterDialogColumn = (0, vue.ref)(null);
1224
+ const { slice: presetSlice, internals: presetInternals } = createPresetState({
1225
+ columnNames,
1226
+ columnWidths,
1227
+ filterFields,
1228
+ filters,
1229
+ sorters,
1230
+ pagination,
1231
+ allColumns,
1232
+ presetsHandle: opts.preset?.presetsHandle,
1233
+ draftHandle: opts.preset?.draftHandle,
1234
+ availableAspects: opts.preset?.availableAspects,
1235
+ persistDrafts: opts.preset?.persistDrafts,
1236
+ fallbackSystemPresets: opts.preset?.fallbackSystemPresets
1237
+ });
1238
+ presetInternals.bootstrap();
1239
+ const promptSlot = createRequestSlot(false);
1240
+ const confirmRequest = promptSlot.ref;
1241
+ function promptFn(message, opts = {}) {
1242
+ return promptSlot.request({
1243
+ ...opts,
1244
+ message
1245
+ });
1246
+ }
1247
+ const acceptPrompt = () => promptSlot.accept(true);
1248
+ const dismissPrompt = promptSlot.dismiss;
1249
+ const formSlot = createRequestSlot(null);
1250
+ const actionFormRequest = formSlot.ref;
1251
+ function requestActionInput(action, ctx) {
1252
+ return formSlot.request({
1253
+ action,
1254
+ identifiers: ctx.identifiers,
1255
+ preferredId: ctx.preferredId
1256
+ });
1257
+ }
1258
+ const acceptActionForm = formSlot.accept;
1259
+ const dismissActionForm = formSlot.dismiss;
1260
+ const stateUid = `tbl-${++_tblUid}`;
1261
+ function rowId(absIndex) {
1262
+ return `${stateUid}-row-${absIndex}`;
1263
+ }
1264
+ let generation = 0;
1265
+ let queryDetected = false;
1266
+ let skipPaginationWatch = 0;
1267
+ function buildCurrentQuery() {
1268
+ return (0, _atscript_ui_table.buildTableQuery)({
1269
+ visibleColumnPaths: columnNames.value,
1270
+ sorters: sorters.value,
1271
+ forceSorters: queryOpts?.forceSorters,
1272
+ filters: filters.value,
1273
+ forceFilters: queryOpts?.forceFilters,
1274
+ search: searchTerm.value || void 0,
1275
+ includeActions: includeActions.value
1276
+ });
1277
+ }
1278
+ function dispatchPages(query, page, size) {
1279
+ return (queryOpts?.fn ?? ((q, p, s) => client.pages(q, p, s)))(query, page, size);
1280
+ }
1281
+ function resetPagination() {
1282
+ if (pagination.value.page !== 1) {
1283
+ skipPaginationWatch++;
1284
+ pagination.value = {
1285
+ ...pagination.value,
1286
+ page: 1
1287
+ };
1288
+ }
1289
+ }
1290
+ /** -1 == nothing active. */
1291
+ const activeIndex = (0, vue.ref)(-1);
1292
+ const { windowCache, windowLoading, errors, topIndex, viewportRowCount, dataAt, loadingAt, errorAt, loadRange, queryNext, clearSettlements, resetWindow, disposeDebounces } = createWindowFetcher({
1293
+ blockSize,
1294
+ dragReleaseDebounceMs,
1295
+ tableDef,
1296
+ totalCount,
1297
+ results,
1298
+ resultsStart,
1299
+ queryingNext,
1300
+ getGeneration: () => generation,
1301
+ isQueryBlocked: () => !!queryOpts?.blockQuery,
1302
+ buildCurrentQuery,
1303
+ dispatchPages,
1304
+ reportError
1305
+ });
1306
+ function getActiveRow() {
1307
+ const abs = activeIndex.value;
1308
+ if (abs < 0) return void 0;
1309
+ return dataAt(abs);
1310
+ }
1311
+ const { selectedRows, selectedCount, rowValueFn, isPkSelected, toggleActiveSelection } = createSelectionApi(selectionOpts, getActiveRow);
1312
+ const { actions } = createActions({
1313
+ tableDef,
1314
+ client,
1315
+ rowDelete: () => rowDelete.value,
1316
+ scheduleQuery,
1317
+ refreshOnAction: () => opts.actions?.refreshOnAction?.(),
1318
+ onResolved: opts.actions?.onResolved
1319
+ });
1320
+ let stateRef = null;
1321
+ const { hasMainActionListener, hasMainActionAvailable, registerMainActionListener, requestMainAction } = createMainActionRegistry({
1322
+ getActiveIndex: () => activeIndex.value,
1323
+ getActiveRow,
1324
+ getDefaultRowAction: () => actions.default.row,
1325
+ invokeFallback: (action, row, event) => {
1326
+ if (!stateRef) return;
1327
+ const preferredId = tableDef.value?.preferredId ?? [];
1328
+ const identifiers = collectIdentifiers(stateRef, [row], preferredId);
1329
+ triggerAction(stateRef, action, {
1330
+ identifiers,
1331
+ preferredId
1332
+ }, event);
1333
+ }
1334
+ });
1335
+ const { navMode, navViewportRowCount, setActive, clearActive, handleNavKey } = createNavController({
1336
+ activeIndex,
1337
+ totalCount,
1338
+ results,
1339
+ viewportRowCount,
1340
+ topIndex,
1341
+ hasMainActionAvailable,
1342
+ requestMainAction,
1343
+ toggleActiveSelection
1344
+ });
1345
+ async function runQuery(kind) {
1346
+ if (queryOpts?.blockQuery) return;
1347
+ mustRefresh.value = false;
1348
+ if (kind !== "initial" && topIndex.value !== 0) topIndex.value = 0;
1349
+ const thisGen = ++generation;
1350
+ clearSettlements();
1351
+ querying.value = true;
1352
+ queryDetected = true;
1353
+ try {
1354
+ const query = buildCurrentQuery();
1355
+ const { page, itemsPerPage } = pagination.value;
1356
+ const newResultsStart = (page - 1) * itemsPerPage;
1357
+ const fetchSize = viewportRowCount.value > 0 ? blockSize : itemsPerPage;
1358
+ const { data, count } = await dispatchPages(query, fetchSize === itemsPerPage ? page : Math.floor(newResultsStart / fetchSize) + 1, fetchSize);
1359
+ if (thisGen !== generation) return;
1360
+ const fresh = /* @__PURE__ */ new Map();
1361
+ for (let i = 0; i < data.length; i++) fresh.set(newResultsStart + i, data[i]);
1362
+ windowCache.value = fresh;
1363
+ windowLoading.value = /* @__PURE__ */ new Set();
1364
+ errors.value = /* @__PURE__ */ new Map();
1365
+ results.value = data;
1366
+ resultsStart.value = newResultsStart;
1367
+ totalCount.value = count;
1368
+ queryError.value = null;
1369
+ } catch (err) {
1370
+ if (thisGen !== generation) return;
1371
+ const error = err instanceof Error ? err : new Error(String(err));
1372
+ queryError.value = error;
1373
+ results.value = [];
1374
+ windowCache.value = /* @__PURE__ */ new Map();
1375
+ windowLoading.value = /* @__PURE__ */ new Set();
1376
+ totalCount.value = 0;
1377
+ reportError(error, kind);
1378
+ } finally {
1379
+ if (thisGen === generation) querying.value = false;
1380
+ }
1381
+ }
1382
+ let pendingScheduledKind = null;
1383
+ let queryFlushScheduled = false;
1384
+ function scheduleQuery(kind = "query") {
1385
+ if (queryOpts?.blockQuery) return;
1386
+ if (tableDef.value === null) return;
1387
+ pendingScheduledKind = pendingScheduledKind ?? kind;
1388
+ querying.value = true;
1389
+ if (queryFlushScheduled) return;
1390
+ queryFlushScheduled = true;
1391
+ queueMicrotask(() => {
1392
+ queryFlushScheduled = false;
1393
+ const k = pendingScheduledKind;
1394
+ pendingScheduledKind = null;
1395
+ if (k === null) return;
1396
+ if (tableDef.value === null) return;
1397
+ runQuery(k);
1398
+ });
1399
+ }
1400
+ function query() {
1401
+ scheduleQuery("query");
1402
+ }
1403
+ function requestRefresh() {
1404
+ mustRefresh.value = true;
1405
+ scheduleQuery();
1406
+ }
1407
+ async function queryImmediate() {
1408
+ pendingScheduledKind = null;
1409
+ if (queryOpts?.blockQuery) return;
1410
+ if (tableDef.value === null) return;
1411
+ await runQuery("query");
1412
+ }
1413
+ function invalidate() {
1414
+ generation++;
1415
+ results.value = [];
1416
+ resetWindow();
1417
+ resultsStart.value = (pagination.value.page - 1) * pagination.value.itemsPerPage;
1418
+ totalCount.value = 0;
1419
+ }
1420
+ function writeColumnWidth(path, width) {
1421
+ const entry = columnWidths.value[path];
1422
+ if (!entry || entry.w === width) return;
1423
+ columnWidths.value = {
1424
+ ...columnWidths.value,
1425
+ [path]: {
1426
+ ...entry,
1427
+ w: width
1428
+ }
1429
+ };
1430
+ }
1431
+ const state = {
1432
+ tableDef,
1433
+ loadingMetadata,
1434
+ columnNames,
1435
+ columns,
1436
+ allColumns,
1437
+ columnWidths,
1438
+ filterFields,
1439
+ filters,
1440
+ sorters,
1441
+ results,
1442
+ resultsStart,
1443
+ windowCache,
1444
+ windowLoading,
1445
+ topIndex,
1446
+ viewportRowCount,
1447
+ navViewportRowCount,
1448
+ querying,
1449
+ queryingNext,
1450
+ totalCount,
1451
+ loadedCount,
1452
+ pagination,
1453
+ queryError,
1454
+ metadataError,
1455
+ lastError,
1456
+ mustRefresh,
1457
+ searchTerm,
1458
+ configDialogOpen,
1459
+ configTab,
1460
+ filterDialogColumn,
1461
+ selectedRows,
1462
+ selectedCount,
1463
+ rowValueFn,
1464
+ isPkSelected,
1465
+ rowDelete,
1466
+ includeActions,
1467
+ activeIndex,
1468
+ navMode,
1469
+ hasMainActionListener,
1470
+ rowId,
1471
+ setActive,
1472
+ clearActive,
1473
+ toggleActiveSelection,
1474
+ requestMainAction,
1475
+ handleNavKey,
1476
+ registerMainActionListener,
1477
+ actions,
1478
+ confirmRequest,
1479
+ prompt: promptFn,
1480
+ acceptPrompt,
1481
+ dismissPrompt,
1482
+ actionFormRequest,
1483
+ requestActionInput,
1484
+ acceptActionForm,
1485
+ dismissActionForm,
1486
+ query,
1487
+ queryImmediate,
1488
+ queryNext,
1489
+ loadRange,
1490
+ invalidate,
1491
+ dataAt,
1492
+ loadingAt,
1493
+ errorAt,
1494
+ resetFilters() {
1495
+ if (Object.keys(filters.value).length === 0) return;
1496
+ filters.value = {};
1497
+ },
1498
+ showConfigDialog(tab) {
1499
+ configTab.value = tab ?? "columns";
1500
+ configDialogOpen.value = true;
1501
+ },
1502
+ addFilterField(path) {
1503
+ if (!filterFields.value.includes(path)) filterFields.value = [...filterFields.value, path];
1504
+ },
1505
+ removeFilterField(path) {
1506
+ if (!filterFields.value.includes(path)) return;
1507
+ filterFields.value = filterFields.value.filter((f) => f !== path);
1508
+ },
1509
+ setFieldFilter(path, conditions) {
1510
+ if (!conditions.some(_atscript_ui_table.isFilled)) {
1511
+ if (!(path in filters.value)) return;
1512
+ const { [path]: _, ...rest } = filters.value;
1513
+ filters.value = rest;
1514
+ } else filters.value = {
1515
+ ...filters.value,
1516
+ [path]: conditions
1517
+ };
1518
+ },
1519
+ setColumnWidth(path, width) {
1520
+ writeColumnWidth(path, width);
1521
+ },
1522
+ resetColumnWidth(path) {
1523
+ const entry = columnWidths.value[path];
1524
+ if (entry) writeColumnWidth(path, entry.d);
1525
+ },
1526
+ removeFieldFilter(path) {
1527
+ const { [path]: _, ...rest } = filters.value;
1528
+ filters.value = rest;
1529
+ },
1530
+ openFilterDialog(column) {
1531
+ filterDialogColumn.value = column;
1532
+ },
1533
+ closeFilterDialog() {
1534
+ filterDialogColumn.value = null;
1535
+ },
1536
+ applyUrlQuery,
1537
+ preset: presetSlice
1538
+ };
1539
+ stateRef = state;
1540
+ let lastEmittedUrl = "";
1541
+ let hydratingFromUrl = false;
1542
+ const urlDefaultItemsPerPage = opts.limit ?? DEFAULT_ITEMS_PER_PAGE;
1543
+ const urlQuerySync = queryOpts?.urlQuerySync;
1544
+ function serializeStateForUrl() {
1545
+ return (0, _atscript_ui_table.stateToUrlQueryString)({
1546
+ filters: filters.value,
1547
+ sorters: sorters.value,
1548
+ page: pagination.value.page,
1549
+ itemsPerPage: pagination.value.itemsPerPage,
1550
+ searchTerm: searchTerm.value
1551
+ }, {
1552
+ defaultItemsPerPage: urlDefaultItemsPerPage,
1553
+ sync: urlQuerySync
1554
+ });
1555
+ }
1556
+ function urlsEquivalent(a, b) {
1557
+ if (a === b) return true;
1558
+ try {
1559
+ return decodeURIComponent(a) === decodeURIComponent(b);
1560
+ } catch {
1561
+ return false;
1562
+ }
1563
+ }
1564
+ function emitUrlIfChanged() {
1565
+ if (!queryOpts?.onUrlQueryChange) return;
1566
+ if (hydratingFromUrl) return;
1567
+ if (queryOpts.urlQueryReady && !queryOpts.urlQueryReady.value) return;
1568
+ const next = serializeStateForUrl();
1569
+ if (next === lastEmittedUrl) return;
1570
+ lastEmittedUrl = next;
1571
+ queryOpts.onUrlQueryChange(next);
1572
+ }
1573
+ function applyUrlQuery(urlString) {
1574
+ if (urlsEquivalent(urlString, lastEmittedUrl)) return;
1575
+ const cols = allColumns.value;
1576
+ const parsed = (0, _atscript_ui_table.urlQueryStringToState)(urlString, {
1577
+ knownFields: cols.length > 0 ? cols.map((c) => c.path) : void 0,
1578
+ sync: urlQuerySync
1579
+ });
1580
+ const currentItemsPerPage = pagination.value.itemsPerPage;
1581
+ const skip = parsed.skip ?? 0;
1582
+ const nextPage = skip > 0 && currentItemsPerPage > 0 ? Math.floor(skip / currentItemsPerPage) + 1 : 1;
1583
+ const wasQueryDetected = queryDetected;
1584
+ hydratingFromUrl = true;
1585
+ const filtersGate = (0, _atscript_ui_table.resolveAspectGate)(urlQuerySync?.filters);
1586
+ if (filtersGate !== "none") {
1587
+ const next = { ...filters.value };
1588
+ for (const path in parsed.filters) next[path] = parsed.filters[path];
1589
+ filters.value = next;
1590
+ }
1591
+ if ((0, _atscript_ui_table.resolveAspectGate)(urlQuerySync?.sorters) !== "none") {
1592
+ const urlFields = /* @__PURE__ */ new Set();
1593
+ for (const s of parsed.sorters) urlFields.add(s.field);
1594
+ const merged = [...sorters.value.filter((s) => !urlFields.has(s.field)), ...parsed.sorters];
1595
+ if (!(0, _atscript_ui_table.sortersEqual)(sorters.value, merged)) sorters.value = merged;
1596
+ }
1597
+ if (urlQuerySync?.search !== false) {
1598
+ if (searchTerm.value !== parsed.searchTerm) searchTerm.value = parsed.searchTerm;
1599
+ }
1600
+ if (urlQuerySync?.pagination !== false) {
1601
+ if (pagination.value.page !== nextPage) pagination.value = {
1602
+ ...pagination.value,
1603
+ page: nextPage
1604
+ };
1605
+ }
1606
+ if (filtersGate !== "none") {
1607
+ const present = new Set(filterFields.value);
1608
+ let merged = null;
1609
+ for (const f in parsed.filters) {
1610
+ if (present.has(f)) continue;
1611
+ (merged ??= filterFields.value.slice()).push(f);
1612
+ present.add(f);
1613
+ }
1614
+ if (merged) filterFields.value = merged;
1615
+ }
1616
+ lastEmittedUrl = serializeStateForUrl();
1617
+ (0, vue.nextTick)(() => {
1618
+ hydratingFromUrl = false;
1619
+ if (wasQueryDetected && !queryOpts?.blockQuery && tableDef.value !== null) scheduleQuery();
1620
+ });
1621
+ }
1622
+ const debouncedFilterQuery = (0, _atscript_ui_table.debounce)(() => {
1623
+ if (queryDetected) scheduleQuery();
1624
+ }, FILTER_DEBOUNCE_MS);
1625
+ (0, vue.watch)([() => filters.value, () => searchTerm.value], () => {
1626
+ if (hydratingFromUrl) return;
1627
+ if (!queryDetected) return;
1628
+ mustRefresh.value = true;
1629
+ resetPagination();
1630
+ debouncedFilterQuery();
1631
+ });
1632
+ (0, vue.watch)(() => sorters.value, (next, prev) => {
1633
+ if (hydratingFromUrl) return;
1634
+ if ((0, _atscript_ui_table.sortersEqual)(prev, next)) return;
1635
+ if (!queryDetected) return;
1636
+ requestRefresh();
1637
+ }, { immediate: false });
1638
+ (0, vue.watch)(() => columnNames.value, (next, prev) => {
1639
+ if (!queryDetected) return;
1640
+ if ((0, _atscript_ui_table.sameColumnSet)(prev, next)) return;
1641
+ requestRefresh();
1642
+ }, { immediate: false });
1643
+ (0, vue.watch)(() => pagination.value, (next, prev) => {
1644
+ if (skipPaginationWatch > 0) {
1645
+ skipPaginationWatch--;
1646
+ return;
1647
+ }
1648
+ if (hydratingFromUrl) return;
1649
+ if (next.page === prev.page && next.itemsPerPage === prev.itemsPerPage) return;
1650
+ if (!queryDetected) return;
1651
+ scheduleQuery();
1652
+ });
1653
+ (0, vue.watch)([
1654
+ () => filters.value,
1655
+ () => sorters.value,
1656
+ () => searchTerm.value,
1657
+ () => pagination.value
1658
+ ], () => emitUrlIfChanged());
1659
+ (0, vue.watch)(() => includeActions.value, () => {
1660
+ if (!queryDetected) return;
1661
+ requestRefresh();
1662
+ });
1663
+ const presetGateOpen = () => opts.preset?.presetsHandle ? presetInternals.gate.value : true;
1664
+ (0, vue.watch)([
1665
+ () => tableDef.value,
1666
+ () => queryOpts?.urlQueryReady?.value ?? true,
1667
+ presetGateOpen
1668
+ ], ([def, urlReady, presetReady]) => {
1669
+ if (queryDetected) return;
1670
+ if (def === null || !urlReady || !presetReady) return;
1671
+ if (queryOpts?.queryOnMount === false) return;
1672
+ if (allColumns.value.length === 0) return;
1673
+ if (results.value.length !== 0) return;
1674
+ queryDetected = true;
1675
+ scheduleQuery("initial");
1676
+ });
1677
+ (0, vue.onScopeDispose)(() => {
1678
+ debouncedFilterQuery.cancel();
1679
+ disposeDebounces();
1680
+ promptSlot.dismiss();
1681
+ formSlot.dismiss();
1682
+ });
1683
+ return {
1684
+ state,
1685
+ internals: {
1686
+ init(def) {
1687
+ allColumns.value = def.columns;
1688
+ const reconciled = (0, _atscript_ui_table.reconcileColumnWidthDefaults)(def.columns, columnWidths.value);
1689
+ if (reconciled !== columnWidths.value) columnWidths.value = reconciled;
1690
+ if (columnNames.value.length === 0) columnNames.value = (0, _atscript_ui.getVisibleColumns)(def).map((c) => c.path);
1691
+ tableDef.value = def;
1692
+ },
1693
+ resetPagination
1694
+ }
1695
+ };
1696
+ }
1697
+ /**
1698
+ * Build a `ReactiveTableState` backed by an in-memory row list. Used by the
1699
+ * enum value-help branch (`column.options`) where there's no client and no
1700
+ * metadata fetch — sorting/searching/pagination run locally against `rows`.
1701
+ */
1702
+ function createStaticTableState(opts) {
1703
+ let _state = null;
1704
+ const queryFn = (q, page, size) => {
1705
+ if (!_state) return Promise.resolve({
1706
+ data: [],
1707
+ count: 0,
1708
+ page,
1709
+ itemsPerPage: size,
1710
+ pages: 1
1711
+ });
1712
+ return buildStaticQueryFn(opts, _state)(q, page, size);
1713
+ };
1714
+ const result = createTableState({
1715
+ client: {},
1716
+ selection: opts.selection,
1717
+ limit: opts.limit,
1718
+ query: { fn: queryFn }
1719
+ });
1720
+ _state = result.state;
1721
+ result.state.loadingMetadata.value = false;
1722
+ result.internals.init({
1723
+ type: void 0,
1724
+ columns: opts.columns,
1725
+ flatMap: /* @__PURE__ */ new Map(),
1726
+ primaryKeys: [],
1727
+ preferredId: [],
1728
+ crud: {
1729
+ query: [],
1730
+ pages: [],
1731
+ one: []
1732
+ },
1733
+ canRemove: false,
1734
+ actions: {
1735
+ table: [],
1736
+ row: [],
1737
+ rows: [],
1738
+ default: {}
1739
+ },
1740
+ searchable: (opts.searchPaths?.length ?? 0) > 0,
1741
+ vectorSearchable: false,
1742
+ searchIndexes: [],
1743
+ relations: []
1744
+ });
1745
+ return result;
1746
+ }
1747
+ function buildStaticQueryFn(opts, state) {
1748
+ const searchPaths = opts.searchPaths ?? [];
1749
+ return (_query, page, size) => {
1750
+ let filtered = opts.rows;
1751
+ const term = state.searchTerm.value.trim().toLowerCase();
1752
+ if (term && searchPaths.length > 0) filtered = filtered.filter((row) => searchPaths.some((p) => cellAsString(row[p]).toLowerCase().includes(term)));
1753
+ const active = state.sorters.value;
1754
+ if (active.length > 0) filtered = filtered.toSorted((a, b) => {
1755
+ for (const s of active) {
1756
+ const dir = s.direction === "desc" ? -1 : 1;
1757
+ const av = a[s.field];
1758
+ const bv = b[s.field];
1759
+ if (typeof av === "number" && typeof bv === "number") {
1760
+ if (av < bv) return -dir;
1761
+ if (av > bv) return dir;
1762
+ } else {
1763
+ const cmp = cellAsString(av).localeCompare(cellAsString(bv));
1764
+ if (cmp !== 0) return cmp * dir;
1765
+ }
1766
+ }
1767
+ return 0;
1768
+ });
1769
+ const start = (page - 1) * size;
1770
+ return Promise.resolve({
1771
+ data: filtered.slice(start, start + size),
1772
+ count: filtered.length,
1773
+ page,
1774
+ itemsPerPage: size,
1775
+ pages: Math.max(1, Math.ceil(filtered.length / size))
1776
+ });
1777
+ };
1778
+ }
1779
+ /** Provide the full table context to the component subtree. */
1780
+ function provideTableContext(ctx) {
1781
+ (0, vue.provide)(TABLE_KEY, ctx);
1782
+ }
1783
+ /** Inject the full table context (throws if used outside as-table-root). */
1784
+ function useTableContext() {
1785
+ const ctx = (0, vue.inject)(TABLE_KEY);
1786
+ if (!ctx) throw new Error("[vue-table] useTableContext() called outside of <as-table-root>.");
1787
+ return ctx;
1788
+ }
1789
+ /**
1790
+ * Inject the table context if present; return undefined when no
1791
+ * `<as-table-root>` ancestor provided one. Use from components that may mount
1792
+ * inside or outside a table subtree (`<AsTableBase>` in combobox/listbox modes,
1793
+ * external composables that probe for context).
1794
+ */
1795
+ function useTableContextOptional() {
1796
+ return (0, vue.inject)(TABLE_KEY);
1797
+ }
1798
+ /**
1799
+ * Register `listener` as a main-action handler whenever `enabled` is truthy.
1800
+ * Reactive — toggling `enabled` registers / disposes live. Skipping
1801
+ * registration when `enabled` is false is what lets `handleNavKey` fall
1802
+ * back to `toggle-select` semantics; see `requestMainAction` early-return
1803
+ * gate. Callers detect "did the parent bind `@main-action`?" via
1804
+ * `useHasEmitListener("onMainAction")`.
1805
+ */
1806
+ function useRegisterMainActionListener(state, listener, enabled) {
1807
+ let dispose = null;
1808
+ const stop = (0, vue.watch)(() => (0, vue.toValue)(enabled), (on) => {
1809
+ if (on && !dispose) dispose = state.registerMainActionListener(listener);
1810
+ else if (!on && dispose) {
1811
+ dispose();
1812
+ dispose = null;
1813
+ }
1814
+ }, { immediate: true });
1815
+ (0, vue.onBeforeUnmount)(() => {
1816
+ stop();
1817
+ dispose?.();
1818
+ dispose = null;
1819
+ });
1820
+ }
1821
+ //#endregion
1822
+ Object.defineProperty(exports, "DEFAULT_AVAILABLE_ASPECTS", {
1823
+ enumerable: true,
1824
+ get: function() {
1825
+ return DEFAULT_AVAILABLE_ASPECTS;
1826
+ }
1827
+ });
1828
+ Object.defineProperty(exports, "ROW_ACTIONS_PATH", {
1829
+ enumerable: true,
1830
+ get: function() {
1831
+ return ROW_ACTIONS_PATH;
1832
+ }
1833
+ });
1834
+ Object.defineProperty(exports, "ROW_ACTIONS_TYPE", {
1835
+ enumerable: true,
1836
+ get: function() {
1837
+ return ROW_ACTIONS_TYPE;
1838
+ }
1839
+ });
1840
+ Object.defineProperty(exports, "applyRowGate", {
1841
+ enumerable: true,
1842
+ get: function() {
1843
+ return applyRowGate;
1844
+ }
1845
+ });
1846
+ Object.defineProperty(exports, "ariaLabelFor", {
1847
+ enumerable: true,
1848
+ get: function() {
1849
+ return ariaLabelFor;
1850
+ }
1851
+ });
1852
+ Object.defineProperty(exports, "collectIdentifiers", {
1853
+ enumerable: true,
1854
+ get: function() {
1855
+ return collectIdentifiers;
1856
+ }
1857
+ });
1858
+ Object.defineProperty(exports, "createStaticTableState", {
1859
+ enumerable: true,
1860
+ get: function() {
1861
+ return createStaticTableState;
1862
+ }
1863
+ });
1864
+ Object.defineProperty(exports, "createTableState", {
1865
+ enumerable: true,
1866
+ get: function() {
1867
+ return createTableState;
1868
+ }
1869
+ });
1870
+ Object.defineProperty(exports, "extractIdentifier", {
1871
+ enumerable: true,
1872
+ get: function() {
1873
+ return extractIdentifier;
1874
+ }
1875
+ });
1876
+ Object.defineProperty(exports, "intentClass", {
1877
+ enumerable: true,
1878
+ get: function() {
1879
+ return intentClass;
1880
+ }
1881
+ });
1882
+ Object.defineProperty(exports, "intentToScope", {
1883
+ enumerable: true,
1884
+ get: function() {
1885
+ return intentToScope;
1886
+ }
1887
+ });
1888
+ Object.defineProperty(exports, "provideTableContext", {
1889
+ enumerable: true,
1890
+ get: function() {
1891
+ return provideTableContext;
1892
+ }
1893
+ });
1894
+ Object.defineProperty(exports, "triggerAction", {
1895
+ enumerable: true,
1896
+ get: function() {
1897
+ return triggerAction;
1898
+ }
1899
+ });
1900
+ Object.defineProperty(exports, "useRegisterMainActionListener", {
1901
+ enumerable: true,
1902
+ get: function() {
1903
+ return useRegisterMainActionListener;
1904
+ }
1905
+ });
1906
+ Object.defineProperty(exports, "useTableContext", {
1907
+ enumerable: true,
1908
+ get: function() {
1909
+ return useTableContext;
1910
+ }
1911
+ });
1912
+ Object.defineProperty(exports, "useTableContextOptional", {
1913
+ enumerable: true,
1914
+ get: function() {
1915
+ return useTableContextOptional;
1916
+ }
1917
+ });