@atscript/vue-table 0.1.146 → 0.1.147

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 (167) hide show
  1. package/dist/as-action-form-dialog.cjs +3 -2
  2. package/dist/as-action-form-dialog.mjs +3 -2
  3. package/dist/{as-action-menu-content-DoZnQP0I.mjs → as-action-menu-content-35yQWbLF.mjs} +1 -1
  4. package/dist/{as-action-menu-content-FQ2miGid.cjs → as-action-menu-content-BJ0tn6jq.cjs} +1 -1
  5. package/dist/as-cell-array.d.cts +1 -1
  6. package/dist/as-cell-array.d.mts +1 -1
  7. package/dist/as-cell-date.d.cts +1 -1
  8. package/dist/as-cell-date.d.mts +1 -1
  9. package/dist/as-cell-json.d.cts +1 -1
  10. package/dist/as-cell-json.d.mts +1 -1
  11. package/dist/as-cell-number.d.cts +1 -1
  12. package/dist/as-cell-number.d.mts +1 -1
  13. package/dist/as-cell-union.d.cts +1 -1
  14. package/dist/as-cell-union.d.mts +1 -1
  15. package/dist/as-column-menu.d.cts +1 -1
  16. package/dist/as-column-menu.d.mts +1 -1
  17. package/dist/{as-column-menu.vue-ByTECTbX.d.cts → as-column-menu.vue-B6iuKmIj.d.cts} +1 -1
  18. package/dist/{as-column-menu.vue-TChT482i.d.mts → as-column-menu.vue-wxb2f92k.d.mts} +1 -1
  19. package/dist/{as-config-dialog-CpW1Qemf.cjs → as-config-dialog-BJkBzaku.cjs} +2 -2
  20. package/dist/{as-config-dialog-ObHtungs.mjs → as-config-dialog-D503Ek_m.mjs} +2 -2
  21. package/dist/as-config-dialog.cjs +1 -1
  22. package/dist/as-config-dialog.d.cts +1 -1
  23. package/dist/as-config-dialog.d.mts +1 -1
  24. package/dist/as-config-dialog.mjs +1 -1
  25. package/dist/{as-confirm-dialog-D3Vu1Rb_.cjs → as-confirm-dialog-C5xPbkox.cjs} +1 -1
  26. package/dist/{as-confirm-dialog-zg7jmS3u.mjs → as-confirm-dialog-CJfDnaiU.mjs} +1 -1
  27. package/dist/as-confirm-dialog.cjs +1 -1
  28. package/dist/as-confirm-dialog.d.cts +1 -1
  29. package/dist/as-confirm-dialog.d.mts +1 -1
  30. package/dist/as-confirm-dialog.mjs +1 -1
  31. package/dist/{as-filter-dialog-C4EyRr2_.cjs → as-filter-dialog-Co0WfWOe.cjs} +7 -7
  32. package/dist/{as-filter-dialog-CWy0mjZl.mjs → as-filter-dialog-CxjO-Ge5.mjs} +7 -7
  33. package/dist/as-filter-dialog.cjs +1 -1
  34. package/dist/as-filter-dialog.d.cts +1 -1
  35. package/dist/as-filter-dialog.d.mts +1 -1
  36. package/dist/as-filter-dialog.mjs +1 -1
  37. package/dist/{as-filter-field-CW1_GHZT.mjs → as-filter-field-ByPlGyWz.mjs} +3 -3
  38. package/dist/{as-filter-field-BQ9UlV0y.cjs → as-filter-field-FTrxFkTw.cjs} +3 -3
  39. package/dist/as-filter-field.cjs +1 -1
  40. package/dist/as-filter-field.d.cts +1 -1
  41. package/dist/as-filter-field.d.mts +1 -1
  42. package/dist/as-filter-field.mjs +1 -1
  43. package/dist/as-filter-input.d.cts +1 -1
  44. package/dist/as-filter-input.d.mts +1 -1
  45. package/dist/{as-filters-BLFSS_VP.mjs → as-filters-CJZJiOXb.mjs} +4 -4
  46. package/dist/{as-filters-DUfFkXAo.cjs → as-filters-W3iMELuL.cjs} +4 -4
  47. package/dist/as-filters.cjs +1 -1
  48. package/dist/as-filters.mjs +1 -1
  49. package/dist/{as-preset-dialog-CI2Hw8vi.mjs → as-preset-dialog-CLOVEpWZ.mjs} +1 -1
  50. package/dist/{as-preset-dialog-BlXqTZiT.cjs → as-preset-dialog-Dvvx0jmU.cjs} +1 -1
  51. package/dist/as-preset-dialog.cjs +1 -1
  52. package/dist/as-preset-dialog.d.cts +1 -1
  53. package/dist/as-preset-dialog.d.mts +1 -1
  54. package/dist/as-preset-dialog.mjs +1 -1
  55. package/dist/{as-preset-picker-CbZDGPaf.mjs → as-preset-picker-CwFObhME.mjs} +1 -1
  56. package/dist/{as-preset-picker-CVgyZBG7.cjs → as-preset-picker-DFLhbm59.cjs} +1 -1
  57. package/dist/as-preset-picker.cjs +1 -1
  58. package/dist/as-preset-picker.mjs +1 -1
  59. package/dist/{as-residual-filter-CUTouIn5.mjs → as-residual-filter-BRsEs8oe.mjs} +1 -1
  60. package/dist/{as-residual-filter-C8n7kdGW.cjs → as-residual-filter-sZlsFPDS.cjs} +1 -1
  61. package/dist/as-residual-filter.cjs +1 -1
  62. package/dist/as-residual-filter.d.cts +1 -1
  63. package/dist/as-residual-filter.d.mts +1 -1
  64. package/dist/as-residual-filter.mjs +1 -1
  65. package/dist/{as-row-actions-DfUL9sYB.cjs → as-row-actions-DFv2cSrP.cjs} +7 -5
  66. package/dist/{as-row-actions-ed1P6GEJ.mjs → as-row-actions-DyjIXePd.mjs} +7 -5
  67. package/dist/as-row-actions.cjs +1 -1
  68. package/dist/as-row-actions.d.cts +1 -1
  69. package/dist/as-row-actions.d.mts +1 -1
  70. package/dist/as-row-actions.mjs +1 -1
  71. package/dist/{as-row-actions.vue-Ba3VO9W-.d.cts → as-row-actions.vue-BHrZBXVU.d.cts} +1 -1
  72. package/dist/{as-row-actions.vue-CKOnE0eb.d.mts → as-row-actions.vue-D4MylwCW.d.mts} +1 -1
  73. package/dist/{as-select-cell-DwMfqBMV.cjs → as-select-cell-CoQJjhnq.cjs} +10 -5
  74. package/dist/{as-select-cell-BMptU5sQ.mjs → as-select-cell-Dl8fohIj.mjs} +10 -5
  75. package/dist/as-selection-banner-BI5gKHqW.cjs +206 -0
  76. package/dist/as-selection-banner-DBtLjulh.mjs +189 -0
  77. package/dist/as-table-B1FowaPF.cjs +215 -0
  78. package/dist/as-table-Cpvfv9zA.mjs +210 -0
  79. package/dist/{as-table-actions-DplblFlK.cjs → as-table-actions-B0xaicfv.cjs} +32 -6
  80. package/dist/{as-table-actions-DQco4pGu.mjs → as-table-actions-BlbAps1h.mjs} +32 -6
  81. package/dist/as-table-actions.cjs +1 -1
  82. package/dist/as-table-actions.d.cts +1 -1
  83. package/dist/as-table-actions.d.mts +1 -1
  84. package/dist/as-table-actions.mjs +1 -1
  85. package/dist/{as-table-actions.vue-BYphfAe2.d.cts → as-table-actions.vue-7ml3JiOO.d.mts} +9 -2
  86. package/dist/{as-table-actions.vue-C6hzRtS7.d.mts → as-table-actions.vue-BKRqHprR.d.cts} +9 -2
  87. package/dist/{as-table-base-lfyjPgsO.mjs → as-table-base-EzKaChOp.mjs} +3 -3
  88. package/dist/{as-table-base-CNAn-vpa.cjs → as-table-base-LZ26YlWp.cjs} +3 -3
  89. package/dist/as-table-cell-value.d.cts +1 -1
  90. package/dist/as-table-cell-value.d.mts +1 -1
  91. package/dist/{as-table-header-cell-Csv1KQDa.mjs → as-table-header-cell-DrVzYaof.mjs} +1 -1
  92. package/dist/{as-table-header-cell-BwaOPYwI.cjs → as-table-header-cell-gHhCdxJX.cjs} +1 -1
  93. package/dist/as-table-header-cell.cjs +1 -1
  94. package/dist/as-table-header-cell.d.cts +1 -1
  95. package/dist/as-table-header-cell.d.mts +1 -1
  96. package/dist/as-table-header-cell.mjs +1 -1
  97. package/dist/{as-table-header-cell.vue-DU5h-2C_.d.cts → as-table-header-cell.vue-BCk4NPYT.d.cts} +1 -1
  98. package/dist/{as-table-header-cell.vue-KkANwimG.d.mts → as-table-header-cell.vue-BZU8s9CT.d.mts} +1 -1
  99. package/dist/{as-table-root-BreMYhnv.mjs → as-table-root-BYHXuNPI.mjs} +33 -6
  100. package/dist/{as-table-root-DXewWV9h.cjs → as-table-root-K6fWKHtF.cjs} +33 -6
  101. package/dist/as-table-root.cjs +1 -1
  102. package/dist/as-table-root.d.cts +1 -1
  103. package/dist/as-table-root.d.mts +1 -1
  104. package/dist/as-table-root.mjs +1 -1
  105. package/dist/{as-table-root.vue-XtDgUjO2.d.mts → as-table-root.vue-Bm2DxAqU.d.mts} +319 -9
  106. package/dist/{as-table-root.vue-Bjzp8TJl.d.cts → as-table-root.vue-CF03c4vu.d.cts} +320 -10
  107. package/dist/as-table.cjs +1 -1
  108. package/dist/as-table.d.cts +1 -1
  109. package/dist/as-table.d.mts +1 -1
  110. package/dist/as-table.mjs +1 -1
  111. package/dist/{as-window-table-JOTOi_Nm.mjs → as-window-table-CygDqZMU.mjs} +99 -53
  112. package/dist/{as-window-table-Bj1dXOS6.cjs → as-window-table-_flc8o64.cjs} +101 -55
  113. package/dist/as-window-table.cjs +1 -1
  114. package/dist/as-window-table.d.cts +1 -1
  115. package/dist/as-window-table.d.mts +1 -1
  116. package/dist/as-window-table.mjs +1 -1
  117. package/dist/index.cjs +23 -19
  118. package/dist/index.d.cts +19 -21
  119. package/dist/index.d.mts +19 -21
  120. package/dist/index.mjs +20 -20
  121. package/dist/{types-D1NxhrpW.d.cts → intent-scope-BXPEN5kJ.d.cts} +202 -35
  122. package/dist/{types-BEZ4_erB.d.mts → intent-scope-Ck7RZ0dK.d.mts} +202 -35
  123. package/dist/{use-table-W0KmaJ6L.cjs → use-table-CydIJZbx.cjs} +5 -3
  124. package/dist/{use-table-BTsNGBVx.mjs → use-table-DP6Y0kbl.mjs} +5 -3
  125. package/dist/{use-table-component-DRMXTk9M.mjs → use-table-component-DNGUvhf7.mjs} +1 -1
  126. package/dist/{use-table-component-BvAC82_c.cjs → use-table-component-aqfGzECx.cjs} +1 -1
  127. package/dist/{use-table-nav-bridge-CPI1tql6.cjs → use-table-nav-bridge-B7A30yja.cjs} +1 -1
  128. package/dist/{use-table-nav-bridge-Bw0NFDSf.mjs → use-table-nav-bridge-BhOPIT2R.mjs} +1 -1
  129. package/dist/{use-table-selection-ULbKT2xW.mjs → use-table-selection-CdaOPo1d.mjs} +8 -3
  130. package/dist/{use-table-selection-DtCV3rhu.cjs → use-table-selection-Cs7-gjHg.cjs} +8 -3
  131. package/dist/{use-table-state-DuY-Mf8e.mjs → use-table-state-1OZfdhQi.mjs} +414 -41
  132. package/dist/{use-table-state-Ddr2UZzB.cjs → use-table-state-sDnqOi3b.cjs} +447 -38
  133. package/package.json +11 -10
  134. package/dist/as-table-BjKzKB0a.cjs +0 -207
  135. package/dist/as-table-DGWfzV4w.mjs +0 -202
  136. package/dist/as-table.vue-BULJe49x.d.cts +0 -115
  137. package/dist/as-table.vue-DgCtlV4u.d.mts +0 -115
  138. package/dist/as-window-table.vue-CYFgK10g.d.mts +0 -132
  139. package/dist/as-window-table.vue-ijutqrJ0.d.cts +0 -132
  140. package/dist/use-table-column-handlers-BitvioH4.mjs +0 -85
  141. package/dist/use-table-column-handlers-DksXpbON.cjs +0 -96
  142. /package/dist/{as-cell-array.vue-pFs5GcCR.d.cts → as-cell-array.vue-C4BTM8YW.d.cts} +0 -0
  143. /package/dist/{as-cell-array.vue-ow34clSF.d.mts → as-cell-array.vue-Dj3nZop3.d.mts} +0 -0
  144. /package/dist/{as-cell-date.vue-B4JCr1qP.d.mts → as-cell-date.vue-BPuyH7q3.d.mts} +0 -0
  145. /package/dist/{as-cell-date.vue-BBSps2B2.d.cts → as-cell-date.vue-BcMgxBfm.d.cts} +0 -0
  146. /package/dist/{as-cell-json.vue-B-T3TuFj.d.mts → as-cell-json.vue-D8J7YwVV.d.mts} +0 -0
  147. /package/dist/{as-cell-json.vue-C6wg4ARZ.d.cts → as-cell-json.vue-pwE9uJdv.d.cts} +0 -0
  148. /package/dist/{as-cell-number.vue-CJ2K5zeM.d.cts → as-cell-number.vue-pbgVCIWT.d.cts} +0 -0
  149. /package/dist/{as-cell-number.vue-CNH2fbCB.d.mts → as-cell-number.vue-sUoZvuat.d.mts} +0 -0
  150. /package/dist/{as-cell-union.vue-CslPM_c2.d.cts → as-cell-union.vue-BK_M7opL.d.cts} +0 -0
  151. /package/dist/{as-cell-union.vue-CZwN81H4.d.mts → as-cell-union.vue-DZBejMxR.d.mts} +0 -0
  152. /package/dist/{as-config-dialog.vue-BKo2GpzN.d.mts → as-config-dialog.vue-C-Jw_rb7.d.cts} +0 -0
  153. /package/dist/{as-config-dialog.vue-DvvJi3xx.d.cts → as-config-dialog.vue-D2AVR1a5.d.mts} +0 -0
  154. /package/dist/{as-confirm-dialog.vue-BN_yzu3h.d.mts → as-confirm-dialog.vue-BxAaCuYf.d.mts} +0 -0
  155. /package/dist/{as-confirm-dialog.vue-CXxLpzbu.d.cts → as-confirm-dialog.vue-D8hJ5qPY.d.cts} +0 -0
  156. /package/dist/{as-filter-dialog.vue-BV2J8PgZ.d.cts → as-filter-dialog.vue-CyD6qtuX.d.cts} +0 -0
  157. /package/dist/{as-filter-dialog.vue-Bl8OIKOf.d.mts → as-filter-dialog.vue-WYXGtymZ.d.mts} +0 -0
  158. /package/dist/{as-filter-field.vue-DD0IZXS1.d.mts → as-filter-field.vue-DTpuDkHh.d.mts} +0 -0
  159. /package/dist/{as-filter-field.vue-ByQ8xIGq.d.cts → as-filter-field.vue-ajAFhraz.d.cts} +0 -0
  160. /package/dist/{as-filter-input.vue-CS4nOk_Q.d.cts → as-filter-input.vue-CksJOtqw.d.cts} +0 -0
  161. /package/dist/{as-filter-input.vue-CFZ33B--.d.mts → as-filter-input.vue-DMwr0ArL.d.mts} +0 -0
  162. /package/dist/{as-preset-dialog.vue-ChM7H7B4.d.mts → as-preset-dialog.vue-BEM-L4BY.d.cts} +0 -0
  163. /package/dist/{as-preset-dialog.vue-DP9fy00Y.d.cts → as-preset-dialog.vue-BrnvBWxJ.d.mts} +0 -0
  164. /package/dist/{as-residual-filter.vue-Dfw1l2wS.d.cts → as-residual-filter.vue-CGXCipEn.d.cts} +0 -0
  165. /package/dist/{as-residual-filter.vue-N89YqlCz.d.mts → as-residual-filter.vue-Cev_8Wy7.d.mts} +0 -0
  166. /package/dist/{as-table-cell-value.vue-C0VR9GRK.d.cts → as-table-cell-value.vue-UkAuJanv.d.cts} +0 -0
  167. /package/dist/{as-table-cell-value.vue-DphckGq-.d.mts → as-table-cell-value.vue-kfNgVhzn.d.mts} +0 -0
@@ -186,6 +186,24 @@ function rowsActionGate(rows) {
186
186
  return text;
187
187
  });
188
188
  }
189
+ /** Disabled reason of a non-query-target action in a query selection. Since 0.1.147. */
190
+ const QUERY_TARGET_UNSUPPORTED_REASON = "Not available for all matching rows — select rows individually";
191
+ /**
192
+ * The gate a query selection ("every row matching the query") puts on the
193
+ * `'rows'` actions: enabled iff the action takes a query target
194
+ * (`queryTarget`) of at least `count` rows; otherwise kept DISABLED with the
195
+ * reason ({@link QUERY_TARGET_UNSUPPORTED_REASON}, or "At most {max} rows").
196
+ * The loaded rows' `$actions` are deliberately not consulted — a sample of
197
+ * the matching rows cannot speak for the rest; the server gates every row
198
+ * when the action runs. Since 0.1.147.
199
+ */
200
+ function queryTargetGate(count) {
201
+ return (a) => {
202
+ if (!a.queryTarget) return QUERY_TARGET_UNSUPPORTED_REASON;
203
+ const max = a.queryTarget.maxRows;
204
+ return count > max ? `At most ${rowsLabel(max)}` : true;
205
+ };
206
+ }
189
207
  /**
190
208
  * Apply the BULK union gate to a `{default, others, rows}` triple. See
191
209
  * {@link rowsActionGate} for the union semantics; identity-stable (returns
@@ -212,47 +230,111 @@ function intentToScope(intent) {
212
230
  default: return;
213
231
  }
214
232
  }
233
+ /** The value at a dot `path` of `row` (a flat dotted key wins). */
234
+ function valueAt(row, path) {
235
+ return Object.hasOwn(row, path) ? row[path] : require_get_cell_value.getCellValue(row, path);
236
+ }
237
+ /** Write `value` at a dot `path` of `out`, creating the nested objects. */
238
+ function setAt(out, path, value) {
239
+ (0, _atscript_ui.setByPath)({ value: out }, path, value);
240
+ }
241
+ /**
242
+ * The fields an action identifier is built from — `tableDef.identifierFields`
243
+ * (`preferredId` plus every delegated action's `idMap` path), falling back to
244
+ * `preferredId` for a hand-built `TableDef` without it. Since 0.1.147.
245
+ */
246
+ function identifierFieldsOf(def) {
247
+ return def?.identifierFields ?? def?.preferredId ?? [];
248
+ }
215
249
  /**
216
250
  * Build the identifier object to forward to `client.action` / `client.remove`.
217
251
  *
218
252
  * Per `@atscript/db-client` invariant #11, identifier bodies are object-only
219
253
  * — never bare scalars, even for single-field PK tables. This helper accepts:
220
- * - a row-shaped object (default `rowValueFn`) → picks `preferredId` fields;
221
- * - a scalar value when `preferredId` has exactly one field (consumers that
254
+ * - a row-shaped object (default `rowValueFn`) → picks the `fields` (a dot
255
+ * path keeps the row's nesting: `ref.key` → `{ ref: { key } }`);
256
+ * - a scalar value when `fields` has exactly one entry (consumers that
222
257
  * override `rowValueFn` to return the PK scalar) → wraps it.
223
258
  *
224
- * Returns `undefined` when no `preferredId` is declared, the source is
225
- * `null`/`undefined`, or a scalar can't be paired with a single-field
226
- * identifier.
259
+ * `fields` is usually `identifierFields` (see {@link identifierFieldsOf}), so
260
+ * the identifier carries what a delegated action maps from; each action then
261
+ * takes the part it needs ({@link actionIdentifiers}).
262
+ *
263
+ * Returns `undefined` when `fields` is empty, the source is
264
+ * `null`/`undefined`, or a scalar can't be paired with a single field.
227
265
  */
228
- function extractIdentifier(source, preferredId) {
266
+ function extractIdentifier(source, fields) {
229
267
  if (source === void 0 || source === null) return void 0;
230
- if (preferredId.length === 0) return void 0;
268
+ if (fields.length === 0) return void 0;
231
269
  if (typeof source === "object" && !Array.isArray(source)) {
232
270
  const row = source;
233
271
  const out = {};
234
- for (const k of preferredId) out[k] = row[k];
272
+ for (const k of fields) setAt(out, k, valueAt(row, k));
235
273
  return out;
236
274
  }
237
- if (preferredId.length === 1) return { [preferredId[0]]: source };
275
+ if (fields.length === 1) return { [fields[0]]: source };
238
276
  }
239
277
  /**
240
278
  * Map a list of sources (full row objects or scalar `rowValueFn` values)
241
279
  * through `extractIdentifier`. Scalar sources resolve to their loaded row via
242
280
  * `state.rowOf` so consumers that override `rowValueFn` to return a scalar
243
281
  * can still reconstruct multi-field identifiers; a scalar whose row is not
244
- * loaded falls back to itself (a single-field `preferredId` wraps it).
282
+ * loaded falls back to itself (a single field wraps it — or, when `fields`
283
+ * has several, the table's single-field `preferredId` does).
245
284
  */
246
- function collectIdentifiers(state, sources, preferredId) {
247
- if (preferredId.length === 0 || sources.length === 0) return [];
285
+ function collectIdentifiers(state, sources, fields) {
286
+ if (fields.length === 0 || sources.length === 0) return [];
248
287
  const out = [];
249
288
  for (const s of sources) {
250
289
  if (s === void 0 || s === null) continue;
251
- const id = extractIdentifier(state.rowOf(s) ?? s, preferredId);
290
+ const row = state.rowOf(s);
291
+ const id = extractIdentifier(row ?? s, fields) ?? (row ? void 0 : extractIdentifier(s, preferredIdOf(state)));
252
292
  if (id) out.push(id);
253
293
  }
254
294
  return out;
255
295
  }
296
+ /** The fields `action` is addressed by: its `idMap` paths, else `preferredId`. */
297
+ function actionFields(action, preferredId) {
298
+ return action.idMap ? Object.values(action.idMap) : preferredId;
299
+ }
300
+ /**
301
+ * The identifiers `action` is sent, from the table's identifiers (built
302
+ * from `identifierFields`): a delegated action (`idMap`) keeps exactly its
303
+ * map's paths — the client maps them to the owner's identification — any
304
+ * other action exactly the `preferredId` fields, the identification the
305
+ * server validates. No fields → no identifiers. Identifiers already in that
306
+ * shape come back as the same array. Since 0.1.147.
307
+ */
308
+ function actionIdentifiers(action, ids, preferredId) {
309
+ const fields = actionFields(action, preferredId);
310
+ if (fields.length === 0) return [];
311
+ const flat = !fields.some((f) => f.includes("."));
312
+ const exact = (id) => {
313
+ return Object.keys(id).length === fields.length && fields.every((f) => f in id);
314
+ };
315
+ if (flat && ids.every(exact)) return ids;
316
+ return project(ids, fields, setAt);
317
+ }
318
+ /** `ids` cut down to `fields`, each written with `write`. */
319
+ function project(ids, fields, write) {
320
+ return ids.map((id) => {
321
+ const out = {};
322
+ for (const f of fields) write(out, f, valueAt(id, f));
323
+ return out;
324
+ });
325
+ }
326
+ /**
327
+ * A query target's `exclude` for `action`: like {@link actionIdentifiers},
328
+ * but keyed by the flat (dotted) field names — the shape the server matches
329
+ * exclusions by.
330
+ */
331
+ function excludeIdentifiers(action, ids, preferredId) {
332
+ const fields = actionFields(action, preferredId);
333
+ if (fields.length === 0) return [];
334
+ return project(ids, fields, (out, f, v) => {
335
+ out[f] = v;
336
+ });
337
+ }
256
338
  /**
257
339
  * Accessible name of an action trigger. A disabled action appends its reason
258
340
  * (`"Ship, Order already shipped"` — the same `label, reason` shape as the
@@ -330,7 +412,7 @@ function isModClick(e) {
330
412
  */
331
413
  function actionHref(state, action, id) {
332
414
  if (action.processor !== "navigate" || action.inputForm) return void 0;
333
- const href = (0, _atscript_ui.navigateHrefFor)(action, id, state.tableDef.value?.preferredId ?? []);
415
+ const href = (0, _atscript_ui.navigateHrefFor)(action, id, preferredIdOf(state));
334
416
  return href === void 0 ? void 0 : state.resolveHref(href);
335
417
  }
336
418
  /**
@@ -378,6 +460,14 @@ function createNavigateGestures(state, promptCtx, hrefFor) {
378
460
  }
379
461
  };
380
462
  }
463
+ /** The table's `preferredId` (empty before its definition loads). */
464
+ function preferredIdOf(state) {
465
+ return state.tableDef.value?.preferredId ?? [];
466
+ }
467
+ /** Row count a prompt speaks about — `count` when set, else the identifiers. */
468
+ function countOf(ctx) {
469
+ return ctx.count ?? ctx.identifiers.length;
470
+ }
381
471
  /**
382
472
  * Run `state.prompt()` if the action declares a `promptText`. Resolves
383
473
  * `true` on accept, or `true` immediately when no prompt is needed.
@@ -386,18 +476,32 @@ function createNavigateGestures(state, promptCtx, hrefFor) {
386
476
  * picks `singular` when there is at most one identifier, `plural` otherwise.
387
477
  * Substitutions:
388
478
  * - `$1` → `formatIdentifier(ctx.identifiers[0], ctx.preferredId)`
389
- * - `$N` → `String(ctx.identifiers.length)`
479
+ * - `$N` → the row count (`ctx.count`, else `ctx.identifiers.length`)
480
+ *
481
+ * A query-targeted run (`ctx.target`) is always confirmed: an action without
482
+ * a `promptText` asks "Run “{label}” on {N} rows?".
390
483
  */
391
484
  async function confirmAction(state, action, ctx) {
392
485
  const raw = action.promptText;
393
- if (!raw) return true;
394
- const count = ctx.identifiers.length;
486
+ const count = countOf(ctx);
487
+ if (!raw) {
488
+ if (!ctx.target) return true;
489
+ return state.prompt(targetPrompt(action, count), { scope: intentToScope(action.intent) });
490
+ }
395
491
  const message = substitute(Array.isArray(raw) ? count <= 1 ? raw[0] : raw[1] : raw, ctx);
396
492
  return state.prompt(message, { scope: intentToScope(action.intent) });
397
493
  }
398
494
  /** Substitute `$1` and `$N` into a prompt-text template. */
399
495
  function substitute(template, ctx) {
400
- return template.replace(/\$1/g, () => (0, _atscript_db_client.formatIdentifier)(ctx.identifiers[0], ctx.preferredId)).replace(/\$N/g, () => String(ctx.identifiers.length));
496
+ return template.replace(/\$1/g, () => (0, _atscript_db_client.formatIdentifier)(ctx.identifiers[0], ctx.preferredId)).replace(/\$N/g, () => String(countOf(ctx)));
497
+ }
498
+ /** `N row(s)`. */
499
+ function rowsLabel(count) {
500
+ return `${count} ${count === 1 ? "row" : "rows"}`;
501
+ }
502
+ /** Default confirmation of a query-targeted run without a `promptText`. */
503
+ function targetPrompt(action, count) {
504
+ return `Run “${action.label || action.name}” on ${rowsLabel(count)}?`;
401
505
  }
402
506
  /**
403
507
  * Dispatch user-initiated invocation: actions with `inputForm` open the form
@@ -409,7 +513,11 @@ function substitute(template, ctx) {
409
513
  */
410
514
  async function triggerAction(state, action, ctx, event) {
411
515
  if (isActionDisabled(action)) return;
412
- const pk = pkForLevel(action.level, ctx.identifiers);
516
+ if (ctx.target) {
517
+ await triggerTargetAction(state, action, ctx, ctx.target, event);
518
+ return;
519
+ }
520
+ const pk = pkForLevel(action.level, actionIdentifiers(action, ctx.identifiers, ctx.preferredId));
413
521
  if (action.inputForm) {
414
522
  const input = await state.requestActionInput(action, ctx);
415
523
  if (input === null) return;
@@ -427,6 +535,42 @@ async function triggerAction(state, action, ctx, event) {
427
535
  });
428
536
  }
429
537
  /**
538
+ * The query-target path of {@link triggerAction}: count the matching rows
539
+ * (`state.actions.countTarget` — a failure settles as the action's error
540
+ * result), confirm with that count (the input form, which shows it, or the
541
+ * prompt), then run with `expectCount` so a set that changed in between is
542
+ * re-confirmed once with its new count instead of silently run.
543
+ */
544
+ async function triggerTargetAction(state, action, ctx, selection, event) {
545
+ if (action.level !== "rows" || !action.queryTarget) return;
546
+ const exclude = excludeIdentifiers(action, selection.exclude, ctx.preferredId);
547
+ const target = {
548
+ filter: selection.filter,
549
+ search: selection.search,
550
+ index: selection.index,
551
+ exclude: exclude.length > 0 ? exclude : void 0
552
+ };
553
+ const matched = await state.actions.countTarget(action, target, event);
554
+ if (matched === void 0) return;
555
+ const counted = {
556
+ ...ctx,
557
+ identifiers: [],
558
+ count: matched
559
+ };
560
+ let input;
561
+ if (action.inputForm) {
562
+ input = await state.requestActionInput(action, counted);
563
+ if (input === null) return;
564
+ } else if (!await confirmAction(state, action, counted)) return;
565
+ state.actions.invoke(action, void 0, {
566
+ event,
567
+ input,
568
+ target,
569
+ expectCount: matched,
570
+ confirmTargetChange: (now) => state.prompt(`The rows matching the query changed. ${targetPrompt(action, now)}`, { scope: intentToScope(action.intent) })
571
+ });
572
+ }
573
+ /**
430
574
  * Pick the `pk` argument to forward to `state.actions.invoke` based on the
431
575
  * action's level. `'table'` → `undefined`; `'row'` → first identifier;
432
576
  * `'rows'` → full array.
@@ -557,6 +701,24 @@ const REMOVE_DEFAULTS = {
557
701
  intent: "negative",
558
702
  promptText: ["Delete item $1?", "Delete $N items?"]
559
703
  };
704
+ /** `true` for a {@link TDbActionTargetSummary}-shaped handler return. */
705
+ function isTargetSummary(data) {
706
+ if (typeof data !== "object" || data === null) return false;
707
+ const d = data;
708
+ return typeof d.matched === "number" && typeof d.processed === "number" && Array.isArray(d.skipped) && Array.isArray(d.failed);
709
+ }
710
+ /** The new match count of a 409 `TARGET_CHANGED` refusal, else `undefined`. */
711
+ function targetChangedCount(err) {
712
+ const e = err;
713
+ return e?.name === "ActionTargetError" && e.code === "TARGET_CHANGED" && typeof e.matched === "number" ? e.matched : void 0;
714
+ }
715
+ /** `data.message` when the handler returned one. */
716
+ function messageOf(data) {
717
+ return typeof data === "object" && data !== null && "message" in data ? data.message : void 0;
718
+ }
719
+ function toError(err) {
720
+ return err instanceof Error ? err : new Error(String(err));
721
+ }
560
722
  function createActions(opts) {
561
723
  const invoking = (0, vue.shallowRef)(/* @__PURE__ */ new Set());
562
724
  const lastResult = (0, vue.shallowRef)(/* @__PURE__ */ new Map());
@@ -606,12 +768,16 @@ function createActions(opts) {
606
768
  };
607
769
  break;
608
770
  default: {
771
+ if (callOpts?.target) {
772
+ result = await invokeOnTarget(action, callOpts.target, callOpts);
773
+ break;
774
+ }
609
775
  const data = await opts.client.action(action.name, pk, callOpts?.input);
610
776
  result = {
611
777
  ok: true,
612
778
  kind: "backend",
613
779
  data,
614
- message: typeof data === "object" && data !== null && "message" in data ? data.message : void 0
780
+ message: messageOf(data)
615
781
  };
616
782
  break;
617
783
  }
@@ -620,15 +786,62 @@ function createActions(opts) {
620
786
  result = {
621
787
  ok: false,
622
788
  kind: "error",
623
- error: err instanceof Error ? err : new Error(String(err))
789
+ error: toError(err)
624
790
  };
625
791
  }
626
792
  setLastResult(name, result);
627
793
  setInvoking(name, false);
628
794
  if (callOpts?.suppressRefresh !== true && opts.refreshOnAction() !== false && result.ok && (result.kind === "backend" || result.kind === "remove")) opts.scheduleQuery("query");
795
+ if (result.ok && result.kind === "backend" && result.target) opts.onTargetSettled?.();
629
796
  if (opts.onResolved) opts.onResolved(action, idsForAction(action.level, pk), result, callOpts?.event);
630
797
  return result;
631
798
  }
799
+ /**
800
+ * `client.actionOnQuery` with the confirmed `expectCount`; a 409
801
+ * `TARGET_CHANGED` asks `confirmTargetChange` once with the new count and,
802
+ * on yes, runs again expecting it. Throws what the client throws.
803
+ */
804
+ async function invokeOnTarget(action, target, callOpts) {
805
+ let expectCount = callOpts.expectCount;
806
+ const run = () => opts.client.actionOnQuery(action.name, {
807
+ ...target,
808
+ expectCount
809
+ }, callOpts.input);
810
+ let data;
811
+ try {
812
+ data = await run();
813
+ } catch (err) {
814
+ const now = targetChangedCount(err);
815
+ if (now === void 0 || !await callOpts.confirmTargetChange?.(now)) throw err;
816
+ expectCount = now;
817
+ data = await run();
818
+ }
819
+ const summary = isTargetSummary(data) ? data : void 0;
820
+ return {
821
+ ok: true,
822
+ kind: "backend",
823
+ data,
824
+ message: messageOf(data),
825
+ target: {
826
+ matched: summary?.matched ?? expectCount ?? 0,
827
+ summary
828
+ }
829
+ };
830
+ }
831
+ async function countTarget(action, target, event) {
832
+ try {
833
+ return (await opts.client.countActionTarget(action.name, target)).matched;
834
+ } catch (err) {
835
+ const result = {
836
+ ok: false,
837
+ kind: "error",
838
+ error: toError(err)
839
+ };
840
+ setLastResult(action.name, result);
841
+ opts.onResolved?.(action, [], result, event);
842
+ return;
843
+ }
844
+ }
632
845
  return {
633
846
  actions: {
634
847
  get table() {
@@ -650,6 +863,7 @@ function createActions(opts) {
650
863
  return groups.value.cellRow;
651
864
  },
652
865
  invoke,
866
+ countTarget,
653
867
  invoking,
654
868
  lastResult
655
869
  },
@@ -727,10 +941,12 @@ const SELECTABLE = Object.freeze({ ok: true });
727
941
  const NOT_SELECTABLE = Object.freeze({ ok: false });
728
942
  /**
729
943
  * The header tri-state from how many of the rows in scope are eligible and
730
- * how many of those are selected.
944
+ * how many of those are selected. In a query selection (`queryMode`) the
945
+ * header is never `"none"`: rows outside the loaded ones stay selected, so a
946
+ * page whose rows are all excluded still reads `"some"`.
731
947
  */
732
- function toSelectAllState(selected, selectable) {
733
- if (selected === 0) return "none";
948
+ function toSelectAllState(selected, selectable, queryMode = false) {
949
+ if (selected === 0 && !queryMode) return "none";
734
950
  return selected >= selectable ? "all" : "some";
735
951
  }
736
952
  /** Normalise a {@link RowSelectableHook} return value. `""` carries no reason. */
@@ -743,8 +959,27 @@ function verdictOf(value) {
743
959
  }
744
960
  function createSelectionApi(opts, getActiveRow, activeIndex, windowCache) {
745
961
  const selectedRows = opts?.selectedRows ?? (0, vue.shallowRef)([]);
746
- const selectedCount = (0, vue.computed)(() => selectedRows.value.length);
962
+ const querySelection = opts?.querySelection ?? (0, vue.shallowRef)(null);
963
+ const selectedCount = (0, vue.computed)(() => {
964
+ const q = querySelection.value;
965
+ return q ? Math.max(0, q.total - q.excluded.length) : selectedRows.value.length;
966
+ });
967
+ const selection = (0, vue.computed)(() => {
968
+ const q = querySelection.value;
969
+ return q ? {
970
+ mode: "query",
971
+ query: q.query,
972
+ excluded: q.excluded,
973
+ total: q.total,
974
+ count: selectedCount.value
975
+ } : {
976
+ mode: "ids",
977
+ ids: selectedRows.value,
978
+ count: selectedCount.value
979
+ };
980
+ });
747
981
  const rowValueFn = opts?.rowValueFn ?? ((row) => row);
982
+ const identityOf = opts?.identityOf ?? ((value) => value);
748
983
  const rowByValue = (0, vue.computed)(() => {
749
984
  const map = /* @__PURE__ */ new Map();
750
985
  for (const row of windowCache.value.values()) map.set(rowValueFn(row), row);
@@ -757,6 +992,10 @@ function createSelectionApi(opts, getActiveRow, activeIndex, windowCache) {
757
992
  }
758
993
  const selectedRowObjects = (0, vue.computed)(() => {
759
994
  const out = [];
995
+ if (querySelection.value) {
996
+ for (const row of windowCache.value.values()) if (!excludedSet.value.has(identityOf(rowValueFn(row)))) out.push(row);
997
+ return out;
998
+ }
760
999
  for (const value of selectedRows.value) {
761
1000
  const row = rowOf(value);
762
1001
  if (row) out.push(row);
@@ -764,8 +1003,27 @@ function createSelectionApi(opts, getActiveRow, activeIndex, windowCache) {
764
1003
  return out;
765
1004
  });
766
1005
  const selectedSet = (0, vue.computed)(() => new Set(selectedRows.value));
1006
+ /** Identity keys of the excluded values (see `identityOf`). */
1007
+ const excludedSet = (0, vue.computed)(() => new Set((querySelection.value?.excluded ?? []).map(identityOf)));
767
1008
  function isPkSelected(pk) {
768
- return selectedSet.value.has(pk);
1009
+ return querySelection.value ? !excludedSet.value.has(identityOf(pk)) : selectedSet.value.has(pk);
1010
+ }
1011
+ /** Replace the query selection's `excluded` list (a no-op when unchanged). */
1012
+ function setExcluded(next) {
1013
+ const q = querySelection.value;
1014
+ if (q && next !== q.excluded) querySelection.value = {
1015
+ ...q,
1016
+ excluded: next
1017
+ };
1018
+ }
1019
+ function enterQuerySelection(query, total) {
1020
+ if (selectedRows.value.length > 0) selectedRows.value = [];
1021
+ querySelection.value = {
1022
+ query,
1023
+ signature: (0, _atscript_ui_table.selectionSignature)(query),
1024
+ excluded: [],
1025
+ total
1026
+ };
769
1027
  }
770
1028
  const rowSelectable = (0, vue.ref)();
771
1029
  function isRowSelectable(row, index) {
@@ -788,32 +1046,57 @@ function createSelectionApi(opts, getActiveRow, activeIndex, windowCache) {
788
1046
  for (let i = 0; i < rows.length; i++) if (isRowSelectable(rows[i], i).ok) n++;
789
1047
  return n;
790
1048
  }
791
- function eligiblePks(rows) {
792
- return (0, _atscript_ui_table.rowsToPks)(selectableRows(rows), rowValueFn);
1049
+ function eligiblePks(rows, indexOf) {
1050
+ return (0, _atscript_ui_table.rowsToPks)(indexOf ? rows.filter((row, i) => isRowSelectable(row, indexOf(i)).ok) : selectableRows(rows), rowValueFn);
793
1051
  }
794
1052
  function addPks(pks) {
1053
+ if (querySelection.value) {
1054
+ const drop = new Set(pks.map(identityOf));
1055
+ const excluded = querySelection.value.excluded;
1056
+ const next = excluded.filter((pk) => !drop.has(identityOf(pk)));
1057
+ setExcluded(next.length === excluded.length ? excluded : next);
1058
+ return;
1059
+ }
795
1060
  const current = selectedSet.value;
796
1061
  const added = [...new Set(pks)].filter((pk) => !current.has(pk));
797
1062
  if (added.length > 0) selectedRows.value = [...selectedRows.value, ...added];
798
1063
  }
799
1064
  function removePks(pks) {
1065
+ if (querySelection.value) {
1066
+ const seen = new Set(excludedSet.value);
1067
+ const added = [];
1068
+ for (const pk of pks) {
1069
+ const key = identityOf(pk);
1070
+ if (seen.has(key)) continue;
1071
+ seen.add(key);
1072
+ added.push(pk);
1073
+ }
1074
+ if (added.length > 0) setExcluded([...querySelection.value.excluded, ...added]);
1075
+ return;
1076
+ }
800
1077
  const drop = new Set(pks);
801
1078
  const next = selectedRows.value.filter((pk) => !drop.has(pk));
802
1079
  if (next.length !== selectedRows.value.length) selectedRows.value = next;
803
1080
  }
804
1081
  function selectAll(rows, indexOf) {
805
- addPks((0, _atscript_ui_table.rowsToPks)(indexOf ? rows.filter((row, i) => isRowSelectable(row, indexOf(i)).ok) : selectableRows(rows), rowValueFn));
1082
+ addPks(eligiblePks(rows, indexOf));
806
1083
  }
807
1084
  function deselectAll(rows) {
808
1085
  removePks(eligiblePks(rows));
809
1086
  }
810
- function toggleAll(rows) {
811
- const pks = eligiblePks(rows);
812
- const selected = pks.filter((pk) => selectedSet.value.has(pk)).length;
813
- if (toSelectAllState(selected, pks.length) === "all") removePks(pks);
1087
+ function toggleAll(rows, indexOf) {
1088
+ const pks = eligiblePks(rows, indexOf);
1089
+ const all = pks.every(isPkSelected);
1090
+ if (querySelection.value) {
1091
+ if (all) clearSelection();
1092
+ else addPks(pks);
1093
+ return;
1094
+ }
1095
+ if (pks.length > 0 && all) removePks(pks);
814
1096
  else addPks(pks);
815
1097
  }
816
1098
  function clearSelection() {
1099
+ if (querySelection.value) querySelection.value = null;
817
1100
  if (selectedRows.value.length > 0) selectedRows.value = [];
818
1101
  }
819
1102
  function toggleActiveSelection(mode) {
@@ -821,10 +1104,18 @@ function createSelectionApi(opts, getActiveRow, activeIndex, windowCache) {
821
1104
  const row = getActiveRow();
822
1105
  if (row === void 0) return;
823
1106
  if (!isRowSelectable(row, activeIndex.value).ok) return;
1107
+ const q = querySelection.value;
1108
+ if (q) {
1109
+ setExcluded((0, _atscript_ui_table.togglePk)(q.excluded, rowValueFn(row), "multi", identityOf));
1110
+ return;
1111
+ }
824
1112
  selectedRows.value = (0, _atscript_ui_table.togglePk)(selectedRows.value, rowValueFn(row), mode);
825
1113
  }
826
1114
  return {
827
1115
  selectedRows,
1116
+ querySelection,
1117
+ selection,
1118
+ enterQuerySelection,
828
1119
  selectedCount,
829
1120
  selectedSet,
830
1121
  rowValueFn,
@@ -1868,7 +2159,8 @@ function sameResidualFilters(a, b) {
1868
2159
  */
1869
2160
  function warnUnsupportedFilter(issue) {
1870
2161
  if (!_atscript_ui_table.DEV) return;
1871
- console.warn(`[vue-table] URL filter left out (${issue.reason}): ${JSON.stringify(issue.expr)}. Field filters cannot express it, so the table shows more rows than the URL described.`);
2162
+ const why = issue.reason === "syntax" ? "It does not parse" : "Field filters cannot express it";
2163
+ console.warn(`[vue-table] URL filter left out (${issue.reason}): ${issue.raw ?? JSON.stringify(issue.expr)}. ${why}, so the table shows more rows than the URL described.`);
1872
2164
  }
1873
2165
  /**
1874
2166
  * The report for fields the table left out when nobody handles it
@@ -1907,6 +2199,8 @@ function createTableState(opts) {
1907
2199
  });
1908
2200
  const rowDelete = (0, vue.ref)(false);
1909
2201
  const includeActions = (0, vue.ref)(false);
2202
+ const selectMode = (0, vue.ref)("none");
2203
+ const allowSelectAllMatching = (0, vue.ref)(false);
1910
2204
  const rowActions = (0, vue.ref)(void 0);
1911
2205
  /**
1912
2206
  * The per-screen row-action policy, compiled once per config change instead
@@ -2012,7 +2306,8 @@ function createTableState(opts) {
2012
2306
  return formSlot.request({
2013
2307
  action,
2014
2308
  identifiers: ctx.identifiers,
2015
- preferredId: ctx.preferredId
2309
+ preferredId: ctx.preferredId,
2310
+ count: ctx.count
2016
2311
  });
2017
2312
  }
2018
2313
  const actionFormDisplay = formSlot.display;
@@ -2113,6 +2408,10 @@ function createTableState(opts) {
2113
2408
  if (queryOpts?.alwaysSelected) {
2114
2409
  for (const p of queryOpts.alwaysSelected) if (available.has(p)) extra.add(p);
2115
2410
  }
2411
+ const def = tableDef.value;
2412
+ if (def?.identifierFields && def.identifierFields !== def.preferredId) {
2413
+ for (const p of def.identifierFields) if (available.has(p) && !def.preferredId.includes(p)) extra.add(p);
2414
+ }
2116
2415
  const model = queryModel.value;
2117
2416
  if (_atscript_ui_table.DEV && model.stripped.length > 0) {
2118
2417
  const key = model.stripped.join(",");
@@ -2187,14 +2486,79 @@ function createTableState(opts) {
2187
2486
  if (idx < 0) return void 0;
2188
2487
  return navMode.value === "window" ? dataAt(idx) : results.value[idx];
2189
2488
  }
2190
- const { selectedRows, selectedCount, rowValueFn, rowByValue, rowOf, selectedRowObjects, isPkSelected, rowSelectable, isRowSelectable, selectableRows, selectableCount, selectAll, deselectAll, toggleAll, clearSelection, toggleActiveSelection } = createSelectionApi(selectionOpts, getActiveRow, activeIndex, windowCache);
2489
+ /**
2490
+ * A query selection's exclusions compare row objects (the default
2491
+ * `rowValueFn`) by their identifier — a refetch replaces every row object.
2492
+ */
2493
+ let identityDef = null;
2494
+ let identityCache = /* @__PURE__ */ new WeakMap();
2495
+ function selectionIdentity(value) {
2496
+ const def = tableDef.value;
2497
+ if (value === null || typeof value !== "object") return value;
2498
+ if (def !== identityDef) {
2499
+ identityDef = def;
2500
+ identityCache = /* @__PURE__ */ new WeakMap();
2501
+ }
2502
+ if (identityCache.has(value)) return identityCache.get(value);
2503
+ const id = extractIdentifier(value, identifierFieldsOf(def));
2504
+ const key = id ? JSON.stringify(Object.values(id)) : value;
2505
+ identityCache.set(value, key);
2506
+ return key;
2507
+ }
2508
+ const selection = createSelectionApi({
2509
+ ...selectionOpts,
2510
+ identityOf: selectionIdentity
2511
+ }, getActiveRow, activeIndex, windowCache);
2512
+ const { selectedRows, querySelection, selection: tableSelection, selectedCount, rowValueFn, rowByValue, rowOf, selectedRowObjects, isPkSelected, rowSelectable, isRowSelectable, selectableRows, selectableCount, selectAll, deselectAll, toggleAll, clearSelection, toggleActiveSelection } = selection;
2513
+ const loadedEligiblePks = (0, vue.computed)(() => {
2514
+ const out = [];
2515
+ const offset = navMode.value === "window" ? 0 : resultsStart.value;
2516
+ const gated = !!rowSelectable.value;
2517
+ for (const [abs, row] of windowCache.value) if (!gated || isRowSelectable(row, abs - offset).ok) out.push(rowValueFn(row));
2518
+ return out;
2519
+ });
2520
+ const loadedSelectedCount = (0, vue.computed)(() => {
2521
+ let n = 0;
2522
+ for (const pk of loadedEligiblePks.value) if (isPkSelected(pk)) n++;
2523
+ return n;
2524
+ });
2525
+ const hasQueryTargetAction = (0, vue.computed)(() => tableDef.value?.actions.rows.some((a) => !!a.queryTarget) ?? false);
2526
+ const canSelectAllMatching = (0, vue.computed)(() => allowSelectAllMatching.value && selectMode.value === "multi" && !queryOpts?.fn && totalCount.value > windowCache.value.size && hasQueryTargetAction.value);
2527
+ /** The filter / search / index of the query the table would send now. */
2528
+ function currentSelectionQuery() {
2529
+ return (0, _atscript_ui_table.selectionQueryOf)(buildCurrentQuery({ includeActions: false }));
2530
+ }
2531
+ function selectAllMatching() {
2532
+ if (!canSelectAllMatching.value) return;
2533
+ selection.enterQuerySelection(currentSelectionQuery(), totalCount.value);
2534
+ }
2535
+ const holdingQuerySelection = (0, vue.computed)(() => querySelection.value !== null);
2536
+ (0, vue.watch)(() => holdingQuerySelection.value ? (0, _atscript_ui_table.selectionSignature)(currentSelectionQuery()) : null, (live) => {
2537
+ const q = querySelection.value;
2538
+ if (live === null || !q || live === q.signature) return;
2539
+ querySelection.value = null;
2540
+ selectionOpts?.onSelectionReset?.({ reason: "scope" });
2541
+ });
2542
+ (0, vue.watch)([totalCount, querying], ([count, busy]) => {
2543
+ const q = querySelection.value;
2544
+ if (!q || busy || queryError.value || count === q.total) return;
2545
+ if (count === 0 && windowCache.value.size === 0) return;
2546
+ querySelection.value = {
2547
+ ...q,
2548
+ total: count
2549
+ };
2550
+ });
2551
+ (0, vue.watch)(selectedRows, (rows) => {
2552
+ if (rows.length > 0 && querySelection.value) querySelection.value = null;
2553
+ });
2191
2554
  const { actions } = createActions({
2192
2555
  tableDef,
2193
2556
  client,
2194
2557
  rowDelete: () => rowDelete.value,
2195
2558
  scheduleQuery,
2196
2559
  refreshOnAction: () => opts.actions?.refreshOnAction?.(),
2197
- onResolved: opts.actions?.onResolved
2560
+ onResolved: opts.actions?.onResolved,
2561
+ onTargetSettled: clearSelection
2198
2562
  });
2199
2563
  let stateRef = null;
2200
2564
  const { hasMainActionListener, hasMainActionAvailable, registerMainActionListener, requestMainAction } = createMainActionRegistry({
@@ -2206,7 +2570,7 @@ function createTableState(opts) {
2206
2570
  const action = resolveRowActions(stateRef, row, { withRows: false }).default;
2207
2571
  if (!action) return;
2208
2572
  const preferredId = tableDef.value?.preferredId ?? [];
2209
- const identifiers = collectIdentifiers(stateRef, [row], preferredId);
2573
+ const identifiers = collectIdentifiers(stateRef, [row], identifierFieldsOf(tableDef.value));
2210
2574
  triggerAction(stateRef, action, {
2211
2575
  identifiers,
2212
2576
  preferredId,
@@ -2357,6 +2721,14 @@ function createTableState(opts) {
2357
2721
  filterDialogColumn,
2358
2722
  selectedRows,
2359
2723
  selectedCount,
2724
+ querySelection,
2725
+ selection: tableSelection,
2726
+ allowSelectAllMatching,
2727
+ selectMode,
2728
+ loadedEligiblePks,
2729
+ loadedSelectedCount,
2730
+ canSelectAllMatching,
2731
+ selectAllMatching,
2360
2732
  rowValueFn,
2361
2733
  rowByValue,
2362
2734
  rowOf,
@@ -2766,6 +3138,7 @@ function createStaticTableState(opts) {
2766
3138
  flatMap: /* @__PURE__ */ new Map(),
2767
3139
  fetchableFields: new Set(opts.columns.map((c) => c.path)),
2768
3140
  primaryKeys: [],
3141
+ identifierFields: [],
2769
3142
  preferredId: [],
2770
3143
  crud: {
2771
3144
  query: [],
@@ -2883,6 +3256,12 @@ Object.defineProperty(exports, "INTERACTIVE_SELECTOR", {
2883
3256
  return INTERACTIVE_SELECTOR;
2884
3257
  }
2885
3258
  });
3259
+ Object.defineProperty(exports, "QUERY_TARGET_UNSUPPORTED_REASON", {
3260
+ enumerable: true,
3261
+ get: function() {
3262
+ return QUERY_TARGET_UNSUPPORTED_REASON;
3263
+ }
3264
+ });
2886
3265
  Object.defineProperty(exports, "ROW_ACTIONS_PATH", {
2887
3266
  enumerable: true,
2888
3267
  get: function() {
@@ -2895,6 +3274,18 @@ Object.defineProperty(exports, "ROW_ACTIONS_TYPE", {
2895
3274
  return ROW_ACTIONS_TYPE;
2896
3275
  }
2897
3276
  });
3277
+ Object.defineProperty(exports, "actionIdentifiers", {
3278
+ enumerable: true,
3279
+ get: function() {
3280
+ return actionIdentifiers;
3281
+ }
3282
+ });
3283
+ Object.defineProperty(exports, "applyGate", {
3284
+ enumerable: true,
3285
+ get: function() {
3286
+ return applyGate;
3287
+ }
3288
+ });
2898
3289
  Object.defineProperty(exports, "applyRowsGate", {
2899
3290
  enumerable: true,
2900
3291
  get: function() {
@@ -2943,6 +3334,12 @@ Object.defineProperty(exports, "extractIdentifier", {
2943
3334
  return extractIdentifier;
2944
3335
  }
2945
3336
  });
3337
+ Object.defineProperty(exports, "identifierFieldsOf", {
3338
+ enumerable: true,
3339
+ get: function() {
3340
+ return identifierFieldsOf;
3341
+ }
3342
+ });
2946
3343
  Object.defineProperty(exports, "intentClass", {
2947
3344
  enumerable: true,
2948
3345
  get: function() {
@@ -2997,6 +3394,12 @@ Object.defineProperty(exports, "provideTableContext", {
2997
3394
  return provideTableContext;
2998
3395
  }
2999
3396
  });
3397
+ Object.defineProperty(exports, "queryTargetGate", {
3398
+ enumerable: true,
3399
+ get: function() {
3400
+ return queryTargetGate;
3401
+ }
3402
+ });
3000
3403
  Object.defineProperty(exports, "readPresetLabel", {
3001
3404
  enumerable: true,
3002
3405
  get: function() {
@@ -3015,6 +3418,12 @@ Object.defineProperty(exports, "rowActionHref", {
3015
3418
  return rowActionHref;
3016
3419
  }
3017
3420
  });
3421
+ Object.defineProperty(exports, "rowsLabel", {
3422
+ enumerable: true,
3423
+ get: function() {
3424
+ return rowsLabel;
3425
+ }
3426
+ });
3018
3427
  Object.defineProperty(exports, "toSelectAllState", {
3019
3428
  enumerable: true,
3020
3429
  get: function() {