@mintplayer/ng-spark 22.23.0 → 22.25.0

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 (71) hide show
  1. package/README.md +196 -0
  2. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +100 -47
  3. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs.map +1 -1
  4. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +310 -329
  5. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-column-filter.mjs +175 -135
  7. package/fesm2022/mintplayer-ng-spark-column-filter.mjs.map +1 -1
  8. package/fesm2022/mintplayer-ng-spark-contributions.mjs +721 -0
  9. package/fesm2022/mintplayer-ng-spark-contributions.mjs.map +1 -0
  10. package/fesm2022/mintplayer-ng-spark-grid.mjs +2385 -1118
  11. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  12. package/fesm2022/mintplayer-ng-spark-history.mjs +598 -0
  13. package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -0
  14. package/fesm2022/mintplayer-ng-spark-icon.mjs +64 -27
  15. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-models.mjs +405 -762
  17. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  18. package/fesm2022/mintplayer-ng-spark-moderation.mjs +1212 -0
  19. package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -0
  20. package/fesm2022/mintplayer-ng-spark-panels.mjs +60 -0
  21. package/fesm2022/mintplayer-ng-spark-panels.mjs.map +1 -0
  22. package/fesm2022/mintplayer-ng-spark-pipes.mjs +1158 -765
  23. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  24. package/fesm2022/mintplayer-ng-spark-po-create.mjs +273 -169
  25. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  26. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +795 -410
  27. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  28. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +1196 -280
  29. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  30. package/fesm2022/mintplayer-ng-spark-po-form.mjs +2244 -1224
  31. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  32. package/fesm2022/mintplayer-ng-spark-query-list.mjs +571 -303
  33. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  34. package/fesm2022/mintplayer-ng-spark-renderers.mjs +44 -55
  35. package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
  36. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +216 -177
  37. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-routes.mjs +24 -28
  39. package/fesm2022/mintplayer-ng-spark-routes.mjs.map +1 -1
  40. package/fesm2022/mintplayer-ng-spark-services.mjs +625 -640
  41. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  42. package/fesm2022/mintplayer-ng-spark-shell.mjs +1031 -397
  43. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  44. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +294 -0
  45. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -0
  46. package/fesm2022/mintplayer-ng-spark.mjs +14 -48
  47. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  48. package/package.json +23 -2
  49. package/types/mintplayer-ng-spark-attribute-description.d.ts +25 -18
  50. package/types/mintplayer-ng-spark-client-operations.d.ts +139 -160
  51. package/types/mintplayer-ng-spark-column-filter.d.ts +73 -55
  52. package/types/mintplayer-ng-spark-contributions.d.ts +231 -0
  53. package/types/mintplayer-ng-spark-grid.d.ts +917 -448
  54. package/types/mintplayer-ng-spark-history.d.ts +216 -0
  55. package/types/mintplayer-ng-spark-icon.d.ts +15 -13
  56. package/types/mintplayer-ng-spark-models.d.ts +681 -573
  57. package/types/mintplayer-ng-spark-moderation.d.ts +402 -0
  58. package/types/mintplayer-ng-spark-panels.d.ts +143 -0
  59. package/types/mintplayer-ng-spark-pipes.d.ts +131 -149
  60. package/types/mintplayer-ng-spark-po-create.d.ts +46 -37
  61. package/types/mintplayer-ng-spark-po-detail.d.ts +180 -117
  62. package/types/mintplayer-ng-spark-po-edit.d.ts +367 -64
  63. package/types/mintplayer-ng-spark-po-form.d.ts +558 -373
  64. package/types/mintplayer-ng-spark-query-list.d.ts +143 -82
  65. package/types/mintplayer-ng-spark-renderers.d.ts +121 -85
  66. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +31 -34
  67. package/types/mintplayer-ng-spark-routes.d.ts +7 -11
  68. package/types/mintplayer-ng-spark-services.d.ts +377 -261
  69. package/types/mintplayer-ng-spark-shell.d.ts +228 -132
  70. package/types/mintplayer-ng-spark-soft-delete.d.ts +90 -0
  71. package/types/mintplayer-ng-spark.d.ts +19 -12
@@ -1,15 +1,15 @@
1
- import { DatatableSettings, BsDatatableFetch, DatatableDistincts, FilterChangeDetail } from '@mintplayer/ng-bootstrap/datatable';
2
- import * as _mintplayer_ng_spark_models from '@mintplayer/ng-spark/models';
3
- import { SparkQuery, EntityType, EntityAttributeDefinition, SparkCellColumn, QueryResultItem, QueryColumn, LookupReference, CustomActionDefinition, PersistentObject } from '@mintplayer/ng-spark/models';
4
- import * as _angular_core from '@angular/core';
5
- import { Type, TemplateRef } from '@angular/core';
6
- import { HttpErrorResponse } from '@angular/common/http';
7
- import { Color } from '@mintplayer/ng-bootstrap';
8
- import { SparkLanguageService } from '@mintplayer/ng-spark/services';
9
- import { ReferenceChip } from '@mintplayer/ng-spark/pipes';
10
-
1
+ import { BsDatatableFetch, BsDatatableRowEvent, DatatableDistincts, DatatableSettings, FilterChangeDetail } from "@mintplayer/ng-bootstrap/datatable";
2
+ import { CustomActionDefinition, EntityAttributeDefinition, EntityPermissions, EntityType, LookupReference, PersistentObject, QueryColumn, QueryColumnFilter, QueryResultItem, SparkCellColumn, SparkDeletedFilter, SparkQuery, SparkSelectionModeSetting } from "@mintplayer/ng-spark/models";
3
+ import * as i0 from "@angular/core";
4
+ import { TemplateRef, Type } from "@angular/core";
5
+ import { HttpErrorResponse } from "@angular/common/http";
6
+ import { Color } from "@mintplayer/ng-bootstrap";
7
+ import { ConnectedPosition } from "@angular/cdk/overlay";
8
+ import { SparkLanguageService } from "@mintplayer/ng-spark/services";
9
+ import { SparkQueryRowAction } from "@mintplayer/ng-spark/panels";
10
+ import { ReferenceChip } from "@mintplayer/ng-spark/pipes";
11
11
  /** Page sizes offered by every Spark grid. */
12
- declare const SPARK_GRID_PAGE_SIZES: number[];
12
+ export declare const SPARK_GRID_PAGE_SIZES: number[];
13
13
  /**
14
14
  * The attributes a grid shows, in display order.
15
15
  *
@@ -17,17 +17,16 @@ declare const SPARK_GRID_PAGE_SIZES: number[];
17
17
  * copy of this expression, which is the kind of thing that stays identical right up until it
18
18
  * doesn't.
19
19
  */
20
- declare function visibleGridAttributes(entityType: EntityType | null): EntityAttributeDefinition[];
20
+ export declare function visibleGridAttributes(entityType: EntityType | null): EntityAttributeDefinition[];
21
21
  /**
22
22
  * Initial datatable settings for a query, seeded with the query's declared sort.
23
23
  *
24
24
  * The datatable owns paging and sorting from here on and calls the fetch callback per page; this
25
25
  * only decides where it starts.
26
26
  */
27
- declare function initialGridSettings(query: SparkQuery | null): DatatableSettings;
27
+ export declare function initialGridSettings(query: SparkQuery | null): DatatableSettings;
28
28
  /** Whether a query renders as a virtual-scrolling grid rather than a paged one. */
29
- declare function isVirtualScrollingQuery(query: SparkQuery | null): boolean;
30
-
29
+ export declare function isVirtualScrollingQuery(query: SparkQuery | null): boolean;
31
30
  /**
32
31
  * Renderer lookup and lookup-reference loading for a Spark grid.
33
32
  *
@@ -41,38 +40,37 @@ declare function isVirtualScrollingQuery(query: SparkQuery | null): boolean;
41
40
  * separate because it is stateless service-shaped logic rather than view state, and because it is
42
41
  * what a custom grid would need in order to render Spark cells at all.
43
42
  */
44
- declare class SparkGridRenderers {
45
- private readonly registry;
46
- private readonly sparkService;
47
- /** The registered column component for a column, or null to fall back to the default cell. */
48
- columnComponentFor(column: SparkCellColumn): Type<any> | null;
49
- /** The registered filter-label function for a column, or null to use the server-computed text. */
50
- filterLabelFor(column: SparkCellColumn): ((value: unknown) => string) | null;
51
- /**
52
- * Inputs for a query-grid cell renderer, filtered to what the component actually declares —
53
- * `NgComponentOutlet` throws on an input the target does not have, which is what lets every
54
- * member of the renderer contract be optional.
55
- */
56
- columnInputsFor(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any>;
57
- /**
58
- * The same contract for a row that is not a query result — an AsDetail sub-table row, which is
59
- * a plain dictionary of embedded values with no id and no column metadata of its own, so its
60
- * value cannot be read the way {@link columnInputsFor} reads one.
61
- *
62
- * Only the extraction differs; the renderer contract is identical, and stating it once is what
63
- * stops the two from drifting the way the cell markup already had.
64
- */
65
- cellInputsFor(component: Type<any>, value: unknown, column: SparkCellColumn, item: unknown): Record<string, any>;
66
- /**
67
- * Loads every lookup reference the visible columns need, in one pass.
68
- *
69
- * Returns an empty map rather than throwing when there are none, so callers never branch on it.
70
- */
71
- loadLookupOptions(columns: readonly (SparkCellColumn | EntityAttributeDefinition)[]): Promise<Record<string, LookupReference>>;
72
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkGridRenderers, never>;
73
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<SparkGridRenderers>;
43
+ export declare class SparkGridRenderers {
44
+ private readonly registry;
45
+ private readonly sparkService;
46
+ /** The registered column component for a column, or null to fall back to the default cell. */
47
+ columnComponentFor(column: SparkCellColumn): Type<any> | null;
48
+ /** The registered filter-label function for a column, or null to use the server-computed text. */
49
+ filterLabelFor(column: SparkCellColumn): ((value: unknown) => string) | null;
50
+ /**
51
+ * Inputs for a query-grid cell renderer, filtered to what the component actually declares —
52
+ * `NgComponentOutlet` throws on an input the target does not have, which is what lets every
53
+ * member of the renderer contract be optional.
54
+ */
55
+ columnInputsFor(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any>;
56
+ /**
57
+ * The same contract for a row that is not a query result — an AsDetail sub-table row, which is
58
+ * a plain dictionary of embedded values with no id and no column metadata of its own, so its
59
+ * value cannot be read the way {@link columnInputsFor} reads one.
60
+ *
61
+ * Only the extraction differs; the renderer contract is identical, and stating it once is what
62
+ * stops the two from drifting the way the cell markup already had.
63
+ */
64
+ cellInputsFor(component: Type<any>, value: unknown, column: SparkCellColumn, item: unknown): Record<string, any>;
65
+ /**
66
+ * Loads every lookup reference the visible columns need, in one pass.
67
+ *
68
+ * Returns an empty map rather than throwing when there are none, so callers never branch on it.
69
+ */
70
+ loadLookupOptions(columns: readonly (SparkCellColumn | EntityAttributeDefinition)[]): Promise<Record<string, LookupReference>>;
71
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkGridRenderers, never>;
72
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
74
73
  }
75
-
76
74
  /**
77
75
  * Header slots for `<spark-query-card>`.
78
76
  *
@@ -113,8 +111,8 @@ declare class SparkGridRenderers {
113
111
  * `contentChildren`.
114
112
  */
115
113
  /** Resolves a slot list to the one that applies to a query: targeted first, then untargeted. */
116
- declare function slotFor<T extends {
117
- forQuery: () => string;
114
+ export declare function slotFor<T extends {
115
+ forQuery: () => string;
118
116
  }>(slots: readonly T[], query: SparkQuery | null): T | null;
119
117
  /**
120
118
  * The icon at the header's leading edge. No default: nothing in the model describes one.
@@ -125,13 +123,19 @@ declare function slotFor<T extends {
125
123
  * </spark-query-card>
126
124
  * ```
127
125
  */
128
- declare class SparkQueryIconDirective {
129
- readonly templateRef: TemplateRef<SparkQuerySlotContext>;
130
- /** Query alias or id this slot applies to. Empty targets every query on the page. */
131
- readonly forQuery: _angular_core.InputSignal<string>;
132
- static ngTemplateContextGuard(_dir: SparkQueryIconDirective, ctx: unknown): ctx is SparkQuerySlotContext;
133
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryIconDirective, never>;
134
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkQueryIconDirective, "[sparkQueryIcon]", never, { "forQuery": { "alias": "sparkQueryIcon"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
126
+ export declare class SparkQueryIconDirective {
127
+ readonly templateRef: TemplateRef<SparkQuerySlotContext>;
128
+ /** Query alias or id this slot applies to. Empty targets every query on the page. */
129
+ readonly forQuery: import("@angular/core").InputSignal<string>;
130
+ static ngTemplateContextGuard(_dir: SparkQueryIconDirective, ctx: unknown): ctx is SparkQuerySlotContext;
131
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryIconDirective, never>;
132
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SparkQueryIconDirective, "[sparkQueryIcon]", never, {
133
+ "forQuery": {
134
+ "alias": "sparkQueryIcon";
135
+ "required": false;
136
+ "isSignal": true;
137
+ };
138
+ }, {}, never, never, true, never>;
135
139
  }
136
140
  /**
137
141
  * The header caption. Defaults to the query's translated description, falling back to its name.
@@ -139,12 +143,18 @@ declare class SparkQueryIconDirective {
139
143
  * The context carries that resolved default as `$implicit`, so a host can decorate it — add a
140
144
  * count, a badge — without re-resolving the translation itself.
141
145
  */
142
- declare class SparkQueryCaptionDirective {
143
- readonly templateRef: TemplateRef<SparkQueryCaptionContext>;
144
- readonly forQuery: _angular_core.InputSignal<string>;
145
- static ngTemplateContextGuard(_dir: SparkQueryCaptionDirective, ctx: unknown): ctx is SparkQueryCaptionContext;
146
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryCaptionDirective, never>;
147
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkQueryCaptionDirective, "[sparkQueryCaption]", never, { "forQuery": { "alias": "sparkQueryCaption"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
146
+ export declare class SparkQueryCaptionDirective {
147
+ readonly templateRef: TemplateRef<SparkQueryCaptionContext>;
148
+ readonly forQuery: import("@angular/core").InputSignal<string>;
149
+ static ngTemplateContextGuard(_dir: SparkQueryCaptionDirective, ctx: unknown): ctx is SparkQueryCaptionContext;
150
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryCaptionDirective, never>;
151
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SparkQueryCaptionDirective, "[sparkQueryCaption]", never, {
152
+ "forQuery": {
153
+ "alias": "sparkQueryCaption";
154
+ "required": false;
155
+ "isSignal": true;
156
+ };
157
+ }, {}, never, never, true, never>;
148
158
  }
149
159
  /**
150
160
  * The buttons at the header's trailing edge. Defaults to the server-declared custom actions.
@@ -160,30 +170,59 @@ declare class SparkQueryCaptionDirective {
160
170
  * </ng-container>
161
171
  * ```
162
172
  */
163
- declare class SparkQueryActionsDirective {
164
- readonly templateRef: TemplateRef<SparkQueryActionsContext>;
165
- readonly forQuery: _angular_core.InputSignal<string>;
166
- static ngTemplateContextGuard(_dir: SparkQueryActionsDirective, ctx: unknown): ctx is SparkQueryActionsContext;
167
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryActionsDirective, never>;
168
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkQueryActionsDirective, "[sparkQueryActions]", never, { "forQuery": { "alias": "sparkQueryActions"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
173
+ export declare class SparkQueryActionsDirective {
174
+ readonly templateRef: TemplateRef<SparkQueryActionsContext>;
175
+ readonly forQuery: import("@angular/core").InputSignal<string>;
176
+ static ngTemplateContextGuard(_dir: SparkQueryActionsDirective, ctx: unknown): ctx is SparkQueryActionsContext;
177
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryActionsDirective, never>;
178
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SparkQueryActionsDirective, "[sparkQueryActions]", never, {
179
+ "forQuery": {
180
+ "alias": "sparkQueryActions";
181
+ "required": false;
182
+ "isSignal": true;
183
+ };
184
+ }, {}, never, never, true, never>;
169
185
  }
170
186
  /** Context for the icon slot: the query whose card is being rendered. */
171
- declare class SparkQuerySlotContext {
172
- $implicit: SparkQuery | null;
187
+ export declare class SparkQuerySlotContext {
188
+ $implicit: SparkQuery | null;
173
189
  }
174
- declare class SparkQueryCaptionContext {
175
- /** The caption the card would have rendered: description, or the query name. */
176
- $implicit: string;
177
- query: SparkQuery | null;
190
+ export declare class SparkQueryCaptionContext {
191
+ /** The caption the card would have rendered: description, or the query name. */
192
+ $implicit: string;
193
+ query: SparkQuery | null;
178
194
  }
179
- declare class SparkQueryActionsContext {
180
- /** The custom actions the card would have rendered, already filtered for this query. */
181
- $implicit: CustomActionDefinition[];
182
- /** Rows currently ticked. Empty unless an action is selection-gated. */
183
- selection: PersistentObject[];
184
- query: SparkQuery | null;
195
+ export declare class SparkQueryActionsContext {
196
+ /** The custom actions the card would have rendered, already filtered for this query. */
197
+ $implicit: CustomActionDefinition[];
198
+ /** Rows currently ticked. Empty unless an action is selection-gated. */
199
+ selection: PersistentObject[];
200
+ query: SparkQuery | null;
185
201
  }
186
-
202
+ /**
203
+ * One button of a query's toolbar (#460, M15): the built-in New or Delete, or a custom action.
204
+ *
205
+ * Built by `SparkQueryGridComponent.toolbarActions()` and rendered by every host of the grid — the
206
+ * query card's header and the query-list page's action bar — so both surfaces offer the same
207
+ * actions, enabled by the same rules.
208
+ */
209
+ export interface SparkQueryToolbarAction {
210
+ kind: 'new' | 'delete' | 'custom' | 'addon';
211
+ /** For `addon` (row menu only): the add-on's entry (`SPARK_QUERY_ROW_ACTIONS`). */
212
+ addon?: SparkQueryRowAction;
213
+ /** The action's name, unique within the toolbar. */
214
+ name: string;
215
+ /** Its catalogue entry: label, icon, `selectionRule`, `variant`, confirmation. */
216
+ definition: CustomActionDefinition;
217
+ /** `*bsPriorityNavItem` priority: New first, then Delete, then custom actions by offset. */
218
+ priority: number;
219
+ }
220
+ /**
221
+ * The button classes for an action's `variant`: an allow-list, so a typo cannot inject a class, and
222
+ * the neutral outline default for anything else. Shared by the detail page, the query card and the
223
+ * query-list page.
224
+ */
225
+ export declare function sparkActionClass(definition: CustomActionDefinition, size?: 'sm'): string;
187
226
  /**
188
227
  * The one Spark grid: a `<bs-datatable>` rendering a query or a sub-query.
189
228
  *
@@ -213,273 +252,504 @@ declare class SparkQueryActionsContext {
213
252
  * reference variable — `<spark-query-grid #grid>` … `grid.query()` — not `viewChild`: hosts wrap
214
253
  * grids in `@if`, where a `viewChild` is intermittently undefined.
215
254
  */
216
- declare class SparkQueryGridComponent {
217
- private readonly sparkService;
218
- private readonly gridRenderers;
219
- private readonly queryRefresh;
220
- readonly lang: SparkLanguageService;
221
- /** Query alias or id. */
222
- queryId: _angular_core.InputSignal<string>;
223
- /**
224
- * The parent persistent object this query is scoped to, when it has one.
225
- *
226
- * Optional, because not every query is a detail of something: a page can host a grid that
227
- * stands on its own — "my accounts", a dashboard list — and the server already treats an absent
228
- * parent as "no parent" rather than as an error. Requiring these made that shape impossible to
229
- * express: the component simply never loaded, with no request, no error and no log.
230
- *
231
- * Pass both or neither. One without the other is ignored, matching `SparkService.executeQuery`,
232
- * which omits either param when it is falsy, and the execute endpoint, which resolves a parent
233
- * only when both are present.
234
- */
235
- parentId: _angular_core.InputSignal<string>;
236
- parentType: _angular_core.InputSignal<string>;
237
- /**
238
- * Rows supplied from outside. When bound, the grid renders these and runs no fetch.
239
- *
240
- * This is how streaming stays out of this component (see the class comment). `null` — the
241
- * default — means "fetch for yourself"; an empty array means "you have been given no rows",
242
- * which is a different statement and renders an empty grid rather than triggering a load.
243
- */
244
- data: _angular_core.InputSignal<QueryResultItem[] | null>;
245
- /**
246
- * Server-side search term, passed through to the query.
247
- *
248
- * An input rather than a box, so the host owns the control and the grid owns the request. A
249
- * host with client-side rows filters them itself before binding `data`.
250
- */
251
- search: _angular_core.InputSignal<string>;
252
- /**
253
- * Column metadata for externally supplied rows. Required whenever `data` is bound, because a
254
- * projection cannot describe itself — there is no per-row metadata left to infer it from.
255
- */
256
- columns: _angular_core.InputSignal<QueryColumn[] | null>;
257
- /**
258
- * Change this to re-run the query. Any value works; only its identity matters.
259
- *
260
- * A declarative token rather than only a `reload()` method, because calling a method means
261
- * holding a component handle, and hosts wrap this grid in `@if`, where a `viewChild` is
262
- * intermittently undefined.
263
- *
264
- * This drives the CHEAP refresh (see {@link reload}). It deliberately does not feed the main
265
- * effect: re-running `loadData` would re-resolve the query, the entity types, the permissions
266
- * and the lookups, and reset the user's page and sort on every button press.
267
- */
268
- reloadToken: _angular_core.InputSignal<unknown>;
269
- /**
270
- * Paging and sorting. Two-way, so a host driving `data` can sort those rows by whatever the
271
- * user actually clicked — the datatable writes the new sort back through here.
272
- */
273
- settings: _angular_core.ModelSignal<DatatableSettings>;
274
- /** Rows the user has ticked. Two-way so chrome outside the grid can read and clear them. */
275
- selection: _angular_core.ModelSignal<QueryResultItem[]>;
276
- /**
277
- * Replaces where the first column's link points, per row.
278
- *
279
- * Return `null` to suppress the link for that row — useful when a grid mixes rows that have a
280
- * detail page with rows that do not.
281
- *
282
- * ⚠️ **It changes the destination, not the permission.** `canRead()` still gates whether any
283
- * link is rendered at all, and this function is never consulted when that gate is closed. It
284
- * cannot be used to reach a row the rights model withheld; it exists for the case where the
285
- * row's natural destination is not `/po/{type}/{id}` — a composed row that maps to a page of
286
- * its own, or a grid embedded in a route that owns its own child paths.
287
- */
288
- rowRoute: _angular_core.InputSignal<((row: QueryResultItem) => unknown[] | null) | null>;
289
- /** Emitted whenever a load or a page fetch fails, for a host in bespoke chrome. */
290
- error: _angular_core.OutputEmitterRef<HttpErrorResponse>;
291
- rowClicked: _angular_core.OutputEmitterRef<QueryResultItem>;
292
- customActionExecuted: _angular_core.OutputEmitterRef<{
293
- action: CustomActionDefinition;
294
- }>;
295
- colors: typeof Color;
296
- /**
297
- * Where the first column's link points for this row: the host's `rowRoute` when it supplied one,
298
- * otherwise the row's own detail page.
299
- *
300
- * A method rather than a computed because the answer is per row, and the template already
301
- * iterates rows — a computed would have to be a map keyed by id, rebuilt on every page.
302
- */
303
- protected rowRouteFor(row: QueryResultItem): unknown[] | null;
304
- query: _angular_core.WritableSignal<SparkQuery | null>;
305
- entityType: _angular_core.WritableSignal<EntityType | null>;
306
- allEntityTypes: _angular_core.WritableSignal<EntityType[]>;
307
- lookupReferenceOptions: _angular_core.WritableSignal<Record<string, LookupReference>>;
308
- loading: _angular_core.WritableSignal<boolean>;
309
- canRead: _angular_core.WritableSignal<boolean>;
310
- canCreate: _angular_core.WritableSignal<boolean>;
311
- resultCount: _angular_core.WritableSignal<number | null>;
312
- customActions: _angular_core.WritableSignal<CustomActionDefinition[]>;
313
- /** Names the server withheld for this RESULT; see QueryResult.disabledActions. */
314
- private readonly disabledActions;
315
- /**
316
- * The actions actually offered for this result.
317
- *
318
- * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE and is never told what an
319
- * execution returned, so an action that applies to only some results cannot be filtered there.
320
- * A custom query withholds what does not apply via `CustomQueryArgs.DisableActions(...)`, and
321
- * the answer arrives on the result.
322
- *
323
- * An affordance, not a permission -- the action handler still refuses on its own terms.
324
- */
325
- visibleCustomActions: _angular_core.Signal<CustomActionDefinition[]>;
326
- fetchFn: _angular_core.WritableSignal<BsDatatableFetch<QueryResultItem> | null>;
327
- /**
328
- * Why the component renders its own failure instead of only reporting one.
329
- *
330
- * `SparkService` is a bare `firstValueFrom` passthrough with no interceptor, so every failure
331
- * surfaces here and nowhere else. A host embedding this grid cannot surface what it never sees,
332
- * and the default has to be visible with no host cooperation — hence a rendered alert, not just
333
- * an output.
334
- */
335
- errorMessage: _angular_core.WritableSignal<string | null>;
336
- /** 'none' unless an action is selection-gated, so unaffected grids gain no checkbox column. */
337
- selectionMode: _angular_core.Signal<_mintplayer_ng_spark_models.SparkSelectionMode>;
338
- isVirtualScrolling: _angular_core.Signal<boolean>;
339
- /**
340
- * Columns as sent with the most recent fetched page.
341
- *
342
- * A host binding `data` (streaming) supplies its own via the `columns` input instead — the
343
- * snapshot carries them once, exactly as a paged result does.
344
- */
345
- private readonly fetchedColumns;
346
- /** Every column on the wire, drawn or not — what renderers and lookups resolve against. */
347
- allColumns: _angular_core.Signal<QueryColumn[]>;
348
- /**
349
- * The columns the grid draws. `isVisible: false` ships a value without a column, so a renderer
350
- * can read a sibling the grid does not show; undefined means visible, so a server that predates
351
- * the field still draws everything.
352
- */
353
- visibleColumns: _angular_core.Signal<QueryColumn[]>;
354
- /**
355
- * True when rows do not come from this component's own fetch.
356
- *
357
- * Either because the host bound `data`, or because the query streams — a streaming query's rows
358
- * arrive over a socket and `/execute` is not a way to get them. The grid recognises the second
359
- * case itself rather than waiting to be told, because it resolves the query before the host can
360
- * see it: left to `data` alone, every streaming grid would fire one pointless fetch on mount,
361
- * before the host had anything to bind.
362
- */
363
- hasExternalData: _angular_core.Signal<boolean>;
364
- /** Whether an action's selection rule is satisfied right now. The server checks it again. */
365
- isActionEnabled(action: CustomActionDefinition): boolean;
366
- constructor();
367
- /**
368
- * Re-run the query, keeping the current page, sort and scroll position.
369
- *
370
- * Data-level on purpose: it re-seeds the fetch closure and nothing else. For a definition
371
- * change — new columns, a renamed query — the inputs themselves must change; that is the
372
- * expensive path. Inert when rows come from the host: there is nothing here to re-run.
373
- */
374
- reload(): void;
375
- private onSearchChanged;
376
- /**
377
- * Replaces the fetched columns only when they actually differ.
378
- *
379
- * Columns ship **once per result**, so every page fetch returns an identical set. Assigning it
380
- * unconditionally wrote a new array of new objects into the signal each time, re-rendering every
381
- * header and re-evaluating every `*bsDatatableColumn` input for no change in content.
382
- *
383
- * Compared by serialized value rather than by reference: the objects are freshly deserialized from
384
- * JSON on every response, so reference equality is always false and would defeat the check. A
385
- * dozen small objects per fetch is nothing beside the request that produced them.
386
- *
387
- * ⚠️ This was written believing it fixed a panel-teardown bug (#431 F7). Measured in a browser: it
388
- * does not. The columns were already identical on every fetch, so the guard hits every time and the
389
- * teardown had another cause entirely. Kept because avoiding a pointless re-render of every header
390
- * on every page is worth having on its own — but it is an optimization, not a fix, and the F7
391
- * question remains open.
392
- */
393
- private setFetchedColumns;
394
- /**
395
- * The per-column filters currently applied (#431).
396
- *
397
- * Read inside `makeFetch`'s closure, like `search`, so a change refetches by producing a new fetch
398
- * identity rather than by being captured.
399
- */
400
- private readonly filters;
401
- /**
402
- * The value source behind every column's panel.
403
- *
404
- * ⚠️ **Referentially stable, and that is the whole point.** It reads `filters()` when the datatable
405
- * *calls* it, not when it is built — so it always sees the current filters while never changing
406
- * identity.
407
- *
408
- * This was a `computed` first, on the reasoning that it had to close over the current filters
409
- * because `DistinctsRequest` carries only `{ column, search, signal }`. That was wrong, and the way
410
- * it was wrong is worth keeping: a new identity reassigns `[distincts]` on the element, and the
411
- * component aborts the in-flight request and bumps a generation counter — so a response already on
412
- * its way is discarded as stale and the list renders empty over a perfectly good answer.
413
- *
414
- * Reading the signal at call time removes the race entirely and is simpler besides.
415
- *
416
- * The asked-for column's own filter is excluded: a panel must offer the values you could still
417
- * pick, not only the ones you already picked.
418
- */
419
- /**
420
- * The last complete distinct list seen per column, for translating a `< none >` selection.
421
- *
422
- * ⚠️ Only stored when the server reported the list was NOT truncated (`hasMore === false`). The
423
- * translation below is the complement of what is listed, so a partial list would exclude the wrong
424
- * set — and silently, which is the failure mode this whole feature exists to remove.
425
- */
426
- private readonly completeDistincts;
427
- protected readonly distinctsFn: DatatableDistincts;
428
- /**
429
- * Translates one filter change into the wire shape and refetches.
430
- *
431
- * The event always arrives as the `'values'` shape because the panel only ever calls
432
- * `ctx.apply(...)`; comparison mode is never declared. `inverse` is a flag on the event and two
433
- * arrays on the wire — the translation is this method's whole job.
434
- */
435
- protected onFilterChange(detail: FilterChangeDetail): void;
436
- /**
437
- * Back to page 1 and a fresh fetch identity, exactly as a new search term does.
438
- *
439
- * Both halves matter: the old page number means nothing against a different result set, and the
440
- * datatable dedupes reloads by `(page, perPage, sort)` — none of which a filter changes — so
441
- * without a new fetch identity the request is silently never made.
442
- */
443
- private onFilterChanged;
444
- onCustomAction(action: CustomActionDefinition): Promise<void>;
445
- private reportError;
446
- /**
447
- * A 404 is deliberately generic.
448
- *
449
- * `Endpoints/Queries/Get.cs` answers 404 with byte-identical bodies for "no such query" and
450
- * "you may not see it", so existence is not disclosed (security audit M-3). The component
451
- * therefore genuinely cannot tell them apart, and both "Not found" and "Access denied" would be
452
- * a guess — one of them leaking, the other misleading.
453
- */
454
- private describe;
455
- private loadData;
456
- /**
457
- * The query's entity type.
458
- *
459
- * The source-name fallback is not optional garnish: a `Database.*` query need not declare
460
- * `entityType`, and the sub-query grid — which matched on the declared name alone — rendered
461
- * those as an empty card with no columns, no rows and no error, while the identical query
462
- * rendered correctly as a page. Both grids are this one now, so there is one answer.
463
- */
464
- private resolveEntityType;
465
- private makeFetch;
466
- private loadLookupReferenceOptions;
467
- /**
468
- * The underlying value a custom renderer receives, which is not the printable one.
469
- *
470
- * A projection is flat, so this is the cell's own value rather than a display string. It is
471
- * deliberately not the attribute-shaped `rendererValue`: a query row has no nested object to
472
- * fall back to, and reaching for one would hand every renderer `undefined`.
473
- */
474
- rendererValueFor(item: QueryResultItem, column: QueryColumn): unknown;
475
- getColumnRendererComponent(column: QueryColumn): Type<any> | null;
476
- /** The renderer's optional filter-label override for this column, or null for the server text. */
477
- filterLabelFor(column: QueryColumn): ((value: unknown) => string) | null;
478
- getColumnRendererInputs(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any>;
479
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryGridComponent, never>;
480
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkQueryGridComponent, "spark-query-grid", never, { "queryId": { "alias": "queryId"; "required": true; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "reloadToken": { "alias": "reloadToken"; "required": false; "isSignal": true; }; "settings": { "alias": "settings"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "rowRoute": { "alias": "rowRoute"; "required": false; "isSignal": true; }; }, { "settings": "settingsChange"; "selection": "selectionChange"; "error": "error"; "rowClicked": "rowClicked"; "customActionExecuted": "customActionExecuted"; }, never, never, true, never>;
255
+ export declare class SparkQueryGridComponent {
256
+ private readonly sparkService;
257
+ private readonly gridRenderers;
258
+ private readonly queryRefresh;
259
+ private readonly injector;
260
+ /** Add-on row-menu entries (`SPARK_QUERY_ROW_ACTIONS`), e.g. Contributions' revert. */
261
+ private readonly addonRowActions;
262
+ readonly lang: SparkLanguageService;
263
+ /** Query alias or id. */
264
+ queryId: import("@angular/core").InputSignal<string>;
265
+ /**
266
+ * The parent persistent object this query is scoped to, when it has one.
267
+ *
268
+ * Optional, because not every query is a detail of something: a page can host a grid that
269
+ * stands on its own — "my accounts", a dashboard list — and the server already treats an absent
270
+ * parent as "no parent" rather than as an error. Requiring these made that shape impossible to
271
+ * express: the component simply never loaded, with no request, no error and no log.
272
+ *
273
+ * Pass both or neither. One without the other is ignored, matching `SparkService.executeQuery`,
274
+ * which omits either param when it is falsy, and the execute endpoint, which resolves a parent
275
+ * only when both are present.
276
+ */
277
+ parentId: import("@angular/core").InputSignal<string>;
278
+ parentType: import("@angular/core").InputSignal<string>;
279
+ /**
280
+ * Column filters in force from the first fetch on — the query page's URL filters, e.g. the
281
+ * contributions History link's `?Language=en&Script=Latn`. Only those naming an attribute of the
282
+ * query's entity type are applied ({@link applicablePresetFilters}); see {@link appliedPresetFilters}.
283
+ * A change replaces the previous preset (filters the user set in a column panel on other columns
284
+ * stay) and refetches from page 1.
285
+ */
286
+ presetFilters: import("@angular/core").InputSignal<readonly QueryColumnFilter[]>;
287
+ /** The preset filters actually applied, for a host to show (and offer to clear). */
288
+ readonly appliedPresetFilters: import("@angular/core").WritableSignal<QueryColumnFilter[]>;
289
+ /**
290
+ * Rows supplied from outside. When bound, the grid renders these and runs no fetch.
291
+ *
292
+ * This is how streaming stays out of this component (see the class comment). `null` — the
293
+ * default — means "fetch for yourself"; an empty array means "you have been given no rows",
294
+ * which is a different statement and renders an empty grid rather than triggering a load.
295
+ */
296
+ data: import("@angular/core").InputSignal<QueryResultItem[] | null>;
297
+ /**
298
+ * Server-side search term, passed through to the query.
299
+ *
300
+ * An input rather than a box, so the host owns the control and the grid owns the request. A
301
+ * host with client-side rows filters them itself before binding `data`.
302
+ */
303
+ search: import("@angular/core").InputSignal<string>;
304
+ /**
305
+ * Column metadata for externally supplied rows. Required whenever `data` is bound, because a
306
+ * projection cannot describe itself — there is no per-row metadata left to infer it from.
307
+ */
308
+ columns: import("@angular/core").InputSignal<QueryColumn[] | null>;
309
+ /**
310
+ * Change this to re-run the query. Any value works; only its identity matters.
311
+ *
312
+ * A declarative token rather than only a `reload()` method, because calling a method means
313
+ * holding a component handle, and hosts wrap this grid in `@if`, where a `viewChild` is
314
+ * intermittently undefined.
315
+ *
316
+ * This drives the CHEAP refresh (see {@link reload}). It deliberately does not feed the main
317
+ * effect: re-running `loadData` would re-resolve the query, the entity types, the permissions
318
+ * and the lookups, and reset the user's page and sort on every button press.
319
+ */
320
+ reloadToken: import("@angular/core").InputSignal<unknown>;
321
+ /**
322
+ * Soft-deletion mode (#460, T2): `exclude` (the default, not sent), `include` or `only` (the
323
+ * recycle bin). Sent with every page and distinct-values request; the server honours a widening
324
+ * only for `ViewDeleted` holders. A change goes back to page 1 and refetches, like a filter.
325
+ * While set, row links carry `?deleted=` so the detail page loads the row the same way — a deleted
326
+ * row is a 404 by id otherwise.
327
+ */
328
+ deleted: import("@angular/core").InputSignal<SparkDeletedFilter | null | undefined>;
329
+ /** The mode actually sent: `exclude` and "unset" are the same request. */
330
+ protected readonly effectiveDeleted: import("@angular/core").Signal<"include" | "only" | undefined>;
331
+ /**
332
+ * The rows' parent is soft-deleted (#460): the detail page it sits on was opened from the recycle
333
+ * bin with `?deleted=only`. Nothing may be created, deleted or run under a deleted row, so the grid
334
+ * offers no actions — the same rule as {@link recycleBin}. The fetch is unchanged.
335
+ */
336
+ parentDeleted: import("@angular/core").InputSignal<boolean>;
337
+ /**
338
+ * The recycle-bin rule (#460), applied here once for every host: while the grid lists `?deleted=only`
339
+ * (or sits under a deleted parent), it offers no New, no default Delete and no custom action, in
340
+ * the toolbar or the row menu — as the detail page does for a row opened from the recycle bin. The
341
+ * server would refuse them anyway: every action judges live rows, and a deleted row is a 404 to it.
342
+ * Restore and Purge are the SoftDelete entry point's own actions, on the row's detail page.
343
+ */
344
+ readonly recycleBin: import("@angular/core").Signal<boolean>;
345
+ /** Query parameters for the row links; null keeps a plain link. */
346
+ protected readonly rowQueryParams: import("@angular/core").Signal<{
347
+ deleted: "include" | "only";
348
+ } | null>;
349
+ private readonly router;
350
+ private readonly currentUrl;
351
+ /**
352
+ * Navigation state for a row link: this page's URL, so the detail page can return to this list
353
+ * (with its `?deleted=` mode) once the row is deleted or purged. See SparkReturnNavigationService.
354
+ */
355
+ protected readonly rowLinkState: import("@angular/core").Signal<{
356
+ sparkReturnUrl: string;
357
+ }>;
358
+ /**
359
+ * A click anywhere on a row opens it, like its first-column link — the link used to be the only
360
+ * target, so a row whose first column was empty ("—") could not be opened at all. While rows are
361
+ * selectable a click selects instead, and a double-click opens. Clicks on the link itself or on an
362
+ * interactive element inside the row (a vote button, a checkbox) are theirs, not the row's; a
363
+ * modified click (Ctrl, ⌘, Shift, middle) is left to the browser.
364
+ */
365
+ protected onRowClick(event: BsDatatableRowEvent<unknown>): void;
366
+ protected onRowDblClick(event: BsDatatableRowEvent<unknown>): void;
367
+ private openRow;
368
+ /**
369
+ * Paging and sorting. Two-way, so a host driving `data` can sort those rows by whatever the
370
+ * user actually clicked — the datatable writes the new sort back through here.
371
+ */
372
+ settings: import("@angular/core").ModelSignal<DatatableSettings>;
373
+ /** Rows the user has ticked. Two-way so chrome outside the grid can read and clear them. */
374
+ selection: import("@angular/core").ModelSignal<QueryResultItem[]>;
375
+ /**
376
+ * Replaces where the first column's link points, per row.
377
+ *
378
+ * Return `null` to suppress the link for that row — useful when a grid mixes rows that have a
379
+ * detail page with rows that do not.
380
+ *
381
+ * ⚠️ **It changes the destination, not the permission.** `canRead()` still gates whether any
382
+ * link is rendered at all, and this function is never consulted when that gate is closed. It
383
+ * cannot be used to reach a row the rights model withheld; it exists for the case where the
384
+ * row's natural destination is not `/po/{type}/{id}` — a composed row that maps to a page of
385
+ * its own, or a grid embedded in a route that owns its own child paths.
386
+ */
387
+ rowRoute: import("@angular/core").InputSignal<((row: QueryResultItem) => unknown[] | null) | null>;
388
+ /**
389
+ * Overrides the query's own `selectionMode` (#460, D17) — the detail page passes the sub-query
390
+ * entry's. `null` (the default) defers to the query, whose absent setting is `'auto'`.
391
+ */
392
+ selectionModeSetting: import("@angular/core").InputSignal<SparkSelectionModeSetting | null>;
393
+ /** Emitted whenever a load or a page fetch fails, for a host in bespoke chrome. */
394
+ error: import("@angular/core").OutputEmitterRef<HttpErrorResponse>;
395
+ rowClicked: import("@angular/core").OutputEmitterRef<QueryResultItem>;
396
+ customActionExecuted: import("@angular/core").OutputEmitterRef<{
397
+ action: CustomActionDefinition;
398
+ }>;
399
+ /** Emitted when the toolbar's New is pressed, just before the grid navigates to the create page. */
400
+ createClicked: import("@angular/core").OutputEmitterRef<void>;
401
+ colors: typeof Color;
402
+ /**
403
+ * Where the first column's link points for this row: the host's `rowRoute` when it supplied one,
404
+ * otherwise the row's own detail page.
405
+ *
406
+ * A method rather than a computed because the answer is per row, and the template already
407
+ * iterates rows — a computed would have to be a map keyed by id, rebuilt on every page.
408
+ */
409
+ protected rowRouteFor(row: QueryResultItem): unknown[] | null;
410
+ query: import("@angular/core").WritableSignal<SparkQuery | null>;
411
+ entityType: import("@angular/core").WritableSignal<EntityType | null>;
412
+ allEntityTypes: import("@angular/core").WritableSignal<EntityType[]>;
413
+ lookupReferenceOptions: import("@angular/core").WritableSignal<Record<string, LookupReference>>;
414
+ loading: import("@angular/core").WritableSignal<boolean>;
415
+ canRead: import("@angular/core").WritableSignal<boolean>;
416
+ canCreate: import("@angular/core").WritableSignal<boolean>;
417
+ /** The entity type's full rights, for hosts (the query page hands them to its add-on actions). */
418
+ permissions: import("@angular/core").WritableSignal<EntityPermissions | null>;
419
+ resultCount: import("@angular/core").WritableSignal<number | null>;
420
+ customActions: import("@angular/core").WritableSignal<CustomActionDefinition[]>;
421
+ /** The built-in New and Delete the server listed for this caller (#460, D18); see `toolbarActions`. */
422
+ defaultActions: import("@angular/core").WritableSignal<CustomActionDefinition[]>;
423
+ /** Names the server withheld for this RESULT; see QueryResult.disabledActions. */
424
+ private readonly disabledActions;
425
+ /**
426
+ * The actions actually offered for this result.
427
+ *
428
+ * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE and is never told what an
429
+ * execution returned, so an action that applies to only some results cannot be filtered there.
430
+ * The entity's `OnDisableActionsAsync` hook withholds what does not apply to this query (#460,
431
+ * D13), and the answer arrives on the result. The server asks the same hook when an action is
432
+ * submitted from this query and refuses a disabled one with 403.
433
+ */
434
+ /**
435
+ * Whether to offer "New": the type-level right, unless the result withholds `New` (or `Save`) --
436
+ * the server's OnDisableActionsAsync said so and would refuse the create with 403 (#460, D13).
437
+ * `canCreate` stays the bare right, since hosts read it as exactly that.
438
+ */
439
+ offersCreate: import("@angular/core").Signal<boolean>;
440
+ visibleCustomActions: import("@angular/core").Signal<CustomActionDefinition[]>;
441
+ fetchFn: import("@angular/core").WritableSignal<BsDatatableFetch<QueryResultItem> | null>;
442
+ /**
443
+ * Why the component renders its own failure instead of only reporting one.
444
+ *
445
+ * `SparkService` is a bare `firstValueFrom` passthrough with no interceptor, so every failure
446
+ * surfaces here and nowhere else. A host embedding this grid cannot surface what it never sees,
447
+ * and the default has to be visible with no host cooperation — hence a rendered alert, not just
448
+ * an output.
449
+ */
450
+ errorMessage: import("@angular/core").WritableSignal<string | null>;
451
+ /**
452
+ * The rendered selection mode (#460, D17): the host's override (a sub-query entry's
453
+ * `selectionMode`), else the query's own, else `'auto'` — derived from the CUSTOM actions offered,
454
+ * exactly as before the setting existed, so no existing grid changes behaviour. The default Delete
455
+ * does not widen `'auto'`: selection is opt-in, and without it Delete stays reachable per row
456
+ * through the row menu.
457
+ */
458
+ selectionMode: import("@angular/core").Signal<import("@mintplayer/ng-spark/models").SparkSelectionMode>;
459
+ /**
460
+ * The actions a host renders in its toolbar, in one list (#460, M15): New, Delete and the custom
461
+ * actions, each with the rule that enables it. The query card and the query-list page render this
462
+ * same list, so the two surfaces cannot drift.
463
+ *
464
+ * - New: the default entry's `showedOn` includes the query, the caller holds `New/T`, and the
465
+ * result does not withhold `New`/`Save`.
466
+ * - Delete: likewise with `Delete/T` and `Delete`, and only while rows can be selected (otherwise
467
+ * it could never be enabled — the row menu offers it per row instead).
468
+ * - Custom: `visibleCustomActions()`.
469
+ * - In the recycle bin ({@link recycleBin}) none of the three: the list is empty.
470
+ */
471
+ toolbarActions: import("@angular/core").Signal<SparkQueryToolbarAction[]>;
472
+ /**
473
+ * The per-row `⋮` menu: every offered action whose rule accepts exactly one row — Delete included,
474
+ * whatever the selection mode. An action without a rule acts on the query, not on a row, and is
475
+ * left to the toolbar.
476
+ */
477
+ rowActions: import("@angular/core").Signal<SparkQueryToolbarAction[]>;
478
+ /** What an add-on row action is asked whether it is offered; null until the type resolved. */
479
+ private readonly rowActionScope;
480
+ /** The default Delete: not withheld by the result, and never in the recycle bin. */
481
+ private offersDelete;
482
+ /** Whether a toolbar action can run with the current selection. The server checks again. */
483
+ isToolbarActionEnabled(action: SparkQueryToolbarAction): boolean;
484
+ /** Runs a toolbar action on the current selection. */
485
+ runToolbarAction(action: SparkQueryToolbarAction): Promise<void>;
486
+ /**
487
+ * The chip's ⊗, offered only for single selection: there the datatable renders no checkbox
488
+ * column, so it has no header deselect-all, and a row click never deselects.
489
+ */
490
+ clearSelection(): void;
491
+ /** The id of the row whose `⋮` menu is open; at most one is. */
492
+ readonly openRowMenu: import("@angular/core").WritableSignal<string | null>;
493
+ /** Below the toggle, right-aligned; above it when there is no room below. */
494
+ readonly rowMenuPositions: ConnectedPosition[];
495
+ toggleRowMenu(row: QueryResultItem): void;
496
+ /**
497
+ * Closes the open row menu. With a `rowId`, only when that row's menu is the open one: a row's
498
+ * overlay reports its outside click and its detach AFTER another row's toggle has already opened
499
+ * that row's menu, and an unscoped close there shut the menu the user had just opened.
500
+ */
501
+ closeRowMenu(rowId?: string): void;
502
+ /**
503
+ * A click outside the open menu closes it — except on its own toggle, whose click handler toggles
504
+ * it; closing here as well would reopen it at once.
505
+ */
506
+ onRowMenuOutsideClick(event: MouseEvent, toggle: HTMLElement, rowId: string): void;
507
+ /** Runs the chosen row-menu item and closes the menu. */
508
+ chooseRowAction(action: SparkQueryToolbarAction, row: QueryResultItem): Promise<void>;
509
+ /**
510
+ * Runs a row-menu action on that ONE row. The checkbox selection is neither read nor changed:
511
+ * the menu is a shortcut for this row, not a second way to tick it.
512
+ */
513
+ runRowAction(action: SparkQueryToolbarAction, row: QueryResultItem): Promise<void>;
514
+ /** An add-on row action, in this grid's injection context; a failure lands in the grid's alert. */
515
+ private runAddonRowAction;
516
+ /**
517
+ * New from this grid: the create page, carrying the sub-query's parent (#460, D19) as query
518
+ * parameters so it survives the navigation and a reload of the create page. The server's
519
+ * `OnNewAsync` receives the parent; its base fills the parent reference.
520
+ */
521
+ startNew(): void;
522
+ /**
523
+ * The default Delete on `ids`: one request, all rows or none (#460, D18). Asks first — with the
524
+ * entry's `confirmationMessageKey` — then clears the selection and refreshes.
525
+ */
526
+ deleteRows(ids: string[], definition?: CustomActionDefinition): Promise<void>;
527
+ isVirtualScrolling: import("@angular/core").Signal<boolean>;
528
+ /**
529
+ * Columns as sent with the most recent fetched page.
530
+ *
531
+ * A host binding `data` (streaming) supplies its own via the `columns` input instead — the
532
+ * snapshot carries them once, exactly as a paged result does.
533
+ */
534
+ private readonly fetchedColumns;
535
+ /** Every column on the wire, drawn or not — what renderers and lookups resolve against. */
536
+ allColumns: import("@angular/core").Signal<QueryColumn[]>;
537
+ /**
538
+ * The columns the grid draws. `isVisible: false` ships a value without a column, so a renderer
539
+ * can read a sibling the grid does not show; undefined means visible, so a server that predates
540
+ * the field still draws everything.
541
+ */
542
+ visibleColumns: import("@angular/core").Signal<QueryColumn[]>;
543
+ /**
544
+ * True when rows do not come from this component's own fetch.
545
+ *
546
+ * Either because the host bound `data`, or because the query streams — a streaming query's rows
547
+ * arrive over a socket and `/execute` is not a way to get them. The grid recognises the second
548
+ * case itself rather than waiting to be told, because it resolves the query before the host can
549
+ * see it: left to `data` alone, every streaming grid would fire one pointless fetch on mount,
550
+ * before the host had anything to bind.
551
+ */
552
+ hasExternalData: import("@angular/core").Signal<boolean>;
553
+ /** Whether an action's selection rule is satisfied right now. The server checks it again. */
554
+ isActionEnabled(action: CustomActionDefinition): boolean;
555
+ constructor();
556
+ /**
557
+ * Re-run the query, keeping the current page, sort and scroll position.
558
+ *
559
+ * Data-level on purpose: it re-seeds the fetch closure and nothing else. For a definition
560
+ * change — new columns, a renamed query — the inputs themselves must change; that is the
561
+ * expensive path. Inert when rows come from the host: there is nothing here to re-run.
562
+ */
563
+ reload(): void;
564
+ private onSearchChanged;
565
+ /**
566
+ * Replaces the fetched columns only when they actually differ.
567
+ *
568
+ * Columns ship **once per result**, so every page fetch returns an identical set. Assigning it
569
+ * unconditionally wrote a new array of new objects into the signal each time, re-rendering every
570
+ * header and re-evaluating every `*bsDatatableColumn` input for no change in content.
571
+ *
572
+ * Compared by serialized value rather than by reference: the objects are freshly deserialized from
573
+ * JSON on every response, so reference equality is always false and would defeat the check. A
574
+ * dozen small objects per fetch is nothing beside the request that produced them.
575
+ *
576
+ * ⚠️ This was written believing it fixed a panel-teardown bug (#431 F7). Measured in a browser: it
577
+ * does not. The columns were already identical on every fetch, so the guard hits every time and the
578
+ * teardown had another cause entirely. Kept because avoiding a pointless re-render of every header
579
+ * on every page is worth having on its own — but it is an optimization, not a fix, and the F7
580
+ * question remains open.
581
+ */
582
+ private setFetchedColumns;
583
+ /**
584
+ * The per-column filters currently applied (#431).
585
+ *
586
+ * Read inside `makeFetch`'s closure, like `search`, so a change refetches by producing a new fetch
587
+ * identity rather than by being captured.
588
+ */
589
+ private readonly filters;
590
+ /**
591
+ * The value source behind every column's panel.
592
+ *
593
+ * ⚠️ **Referentially stable, and that is the whole point.** It reads `filters()` when the datatable
594
+ * *calls* it, not when it is built — so it always sees the current filters while never changing
595
+ * identity.
596
+ *
597
+ * This was a `computed` first, on the reasoning that it had to close over the current filters
598
+ * because `DistinctsRequest` carries only `{ column, search, signal }`. That was wrong, and the way
599
+ * it was wrong is worth keeping: a new identity reassigns `[distincts]` on the element, and the
600
+ * component aborts the in-flight request and bumps a generation counter — so a response already on
601
+ * its way is discarded as stale and the list renders empty over a perfectly good answer.
602
+ *
603
+ * Reading the signal at call time removes the race entirely and is simpler besides.
604
+ *
605
+ * The asked-for column's own filter is excluded: a panel must offer the values you could still
606
+ * pick, not only the ones you already picked.
607
+ */
608
+ /**
609
+ * The last complete distinct list seen per column, for translating a `< none >` selection.
610
+ *
611
+ * ⚠️ Only stored when the server reported the list was NOT truncated (`hasMore === false`). The
612
+ * translation below is the complement of what is listed, so a partial list would exclude the wrong
613
+ * set — and silently, which is the failure mode this whole feature exists to remove.
614
+ */
615
+ private readonly completeDistincts;
616
+ protected readonly distinctsFn: DatatableDistincts;
617
+ /**
618
+ * Translates one filter change into the wire shape and refetches.
619
+ *
620
+ * The event always arrives as the `'values'` shape because the panel only ever calls
621
+ * `ctx.apply(...)`; comparison mode is never declared. `inverse` is a flag on the event and two
622
+ * arrays on the wire — the translation is this method's whole job.
623
+ */
624
+ protected onFilterChange(detail: FilterChangeDetail): void;
625
+ /**
626
+ * Back to page 1 and a fresh fetch identity, exactly as a new search term does.
627
+ *
628
+ * Both halves matter: the old page number means nothing against a different result set, and the
629
+ * datatable dedupes reloads by `(page, perPage, sort)` — none of which a filter changes — so
630
+ * without a new fetch identity the request is silently never made.
631
+ */
632
+ private onFilterChanged;
633
+ /**
634
+ * Runs a custom action on the selection — or on `rowIds` when given (the row menu), which leaves
635
+ * the checkbox selection untouched.
636
+ */
637
+ onCustomAction(action: CustomActionDefinition, rowIds?: string[]): Promise<void>;
638
+ private reportError;
639
+ /**
640
+ * A 404 is deliberately generic.
641
+ *
642
+ * `Endpoints/Queries/Get.cs` answers 404 with byte-identical bodies for "no such query" and
643
+ * "you may not see it", so existence is not disclosed (security audit M-3). The component
644
+ * therefore genuinely cannot tell them apart, and both "Not found" and "Access denied" would be
645
+ * a guess — one of them leaking, the other misleading.
646
+ */
647
+ private describe;
648
+ private loadData;
649
+ /**
650
+ * The query's entity type.
651
+ *
652
+ * The source-name fallback is not optional garnish: a `Database.*` query need not declare
653
+ * `entityType`, and the sub-query grid — which matched on the declared name alone — rendered
654
+ * those as an empty card with no columns, no rows and no error, while the identical query
655
+ * rendered correctly as a page. Both grids are this one now, so there is one answer.
656
+ */
657
+ private resolveEntityType;
658
+ private makeFetch;
659
+ private loadLookupReferenceOptions;
660
+ /**
661
+ * The underlying value a custom renderer receives, which is not the printable one.
662
+ *
663
+ * A projection is flat, so this is the cell's own value rather than a display string. It is
664
+ * deliberately not the attribute-shaped `rendererValue`: a query row has no nested object to
665
+ * fall back to, and reaching for one would hand every renderer `undefined`.
666
+ */
667
+ rendererValueFor(item: QueryResultItem, column: QueryColumn): unknown;
668
+ getColumnRendererComponent(column: QueryColumn): Type<any> | null;
669
+ /** The renderer's optional filter-label override for this column, or null for the server text. */
670
+ filterLabelFor(column: QueryColumn): ((value: unknown) => string) | null;
671
+ getColumnRendererInputs(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any>;
672
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryGridComponent, never>;
673
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkQueryGridComponent, "spark-query-grid", never, {
674
+ "queryId": {
675
+ "alias": "queryId";
676
+ "required": true;
677
+ "isSignal": true;
678
+ };
679
+ "parentId": {
680
+ "alias": "parentId";
681
+ "required": false;
682
+ "isSignal": true;
683
+ };
684
+ "parentType": {
685
+ "alias": "parentType";
686
+ "required": false;
687
+ "isSignal": true;
688
+ };
689
+ "presetFilters": {
690
+ "alias": "presetFilters";
691
+ "required": false;
692
+ "isSignal": true;
693
+ };
694
+ "data": {
695
+ "alias": "data";
696
+ "required": false;
697
+ "isSignal": true;
698
+ };
699
+ "search": {
700
+ "alias": "search";
701
+ "required": false;
702
+ "isSignal": true;
703
+ };
704
+ "columns": {
705
+ "alias": "columns";
706
+ "required": false;
707
+ "isSignal": true;
708
+ };
709
+ "reloadToken": {
710
+ "alias": "reloadToken";
711
+ "required": false;
712
+ "isSignal": true;
713
+ };
714
+ "deleted": {
715
+ "alias": "deleted";
716
+ "required": false;
717
+ "isSignal": true;
718
+ };
719
+ "parentDeleted": {
720
+ "alias": "parentDeleted";
721
+ "required": false;
722
+ "isSignal": true;
723
+ };
724
+ "settings": {
725
+ "alias": "settings";
726
+ "required": false;
727
+ "isSignal": true;
728
+ };
729
+ "selection": {
730
+ "alias": "selection";
731
+ "required": false;
732
+ "isSignal": true;
733
+ };
734
+ "rowRoute": {
735
+ "alias": "rowRoute";
736
+ "required": false;
737
+ "isSignal": true;
738
+ };
739
+ "selectionModeSetting": {
740
+ "alias": "selectionModeSetting";
741
+ "required": false;
742
+ "isSignal": true;
743
+ };
744
+ }, {
745
+ "settings": "settingsChange";
746
+ "selection": "selectionChange";
747
+ "error": "error";
748
+ "rowClicked": "rowClicked";
749
+ "customActionExecuted": "customActionExecuted";
750
+ "createClicked": "createClicked";
751
+ }, never, never, true, never>;
481
752
  }
482
-
483
753
  /**
484
754
  * A `<bs-card>` around a {@link SparkQueryGridComponent}: icon, caption, actions, grid.
485
755
  *
@@ -502,61 +772,168 @@ declare class SparkQueryGridComponent {
502
772
  *
503
773
  * Content wins over a forwarded input: the closer declaration is the more specific one.
504
774
  */
505
- declare class SparkQueryCardComponent {
506
- readonly lang: SparkLanguageService;
507
- queryId: _angular_core.InputSignal<string>;
508
- parentId: _angular_core.InputSignal<string>;
509
- parentType: _angular_core.InputSignal<string>;
510
- reloadToken: _angular_core.InputSignal<unknown>;
511
- data: _angular_core.InputSignal<QueryResultItem[] | null>;
512
- search: _angular_core.InputSignal<string>;
513
- /**
514
- * Slots forwarded from a host that cannot project content — see the class comment. A card
515
- * reached through hand-written markup should use the structural directives instead.
516
- */
517
- iconTemplate: _angular_core.InputSignal<TemplateRef<any> | null>;
518
- captionTemplate: _angular_core.InputSignal<TemplateRef<any> | null>;
519
- actionsTemplate: _angular_core.InputSignal<TemplateRef<any> | null>;
520
- error: _angular_core.OutputEmitterRef<HttpErrorResponse>;
521
- rowClicked: _angular_core.OutputEmitterRef<QueryResultItem>;
522
- customActionExecuted: _angular_core.OutputEmitterRef<{
523
- action: CustomActionDefinition;
524
- }>;
525
- /**
526
- * Forwarded to the grid. Without this the escape hatch was unreachable from a card — i.e. from
527
- * every auto-rendered sub-query and program-unit query page, which is most grids.
528
- */
529
- rowRoute: _angular_core.InputSignal<((row: QueryResultItem) => unknown[] | null) | null>;
530
- colors: typeof Color;
531
- private readonly iconSlots;
532
- private readonly captionSlots;
533
- private readonly actionSlots;
534
- /**
535
- * The grid, read only to surface its state — the query, the actions, the selection — to this
536
- * component's own header.
537
- *
538
- * Optional rather than `required` on purpose. The header renders ABOVE the grid in this
539
- * template, so on the first change-detection pass the view query has not resolved yet and
540
- * `viewChild.required()` would throw where a plain one returns `undefined`. Everything below
541
- * therefore reads it defensively; the signal updates once the view initialises and the header
542
- * re-renders with the real query.
543
- *
544
- * A host reads grid state through a template reference variable instead, because a host may put
545
- * the grid behind an `@if`, where a view query genuinely is intermittently undefined.
546
- */
547
- protected readonly grid: _angular_core.Signal<SparkQueryGridComponent | undefined>;
548
- protected readonly query: _angular_core.Signal<_mintplayer_ng_spark_models.SparkQuery | null>;
549
- protected readonly iconTpl: _angular_core.Signal<TemplateRef<any> | null>;
550
- protected readonly captionTpl: _angular_core.Signal<TemplateRef<any> | null>;
551
- protected readonly actionsTpl: _angular_core.Signal<TemplateRef<any> | null>;
552
- /** The caption the card renders unless a slot replaces it. */
553
- protected readonly caption: _angular_core.Signal<string>;
554
- protected readonly customActions: _angular_core.Signal<CustomActionDefinition[]>;
555
- protected readonly selection: _angular_core.Signal<QueryResultItem[]>;
556
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryCardComponent, never>;
557
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkQueryCardComponent, "spark-query-card", never, { "queryId": { "alias": "queryId"; "required": true; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; "reloadToken": { "alias": "reloadToken"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "iconTemplate": { "alias": "iconTemplate"; "required": false; "isSignal": true; }; "captionTemplate": { "alias": "captionTemplate"; "required": false; "isSignal": true; }; "actionsTemplate": { "alias": "actionsTemplate"; "required": false; "isSignal": true; }; "rowRoute": { "alias": "rowRoute"; "required": false; "isSignal": true; }; }, { "error": "error"; "rowClicked": "rowClicked"; "customActionExecuted": "customActionExecuted"; }, ["iconSlots", "captionSlots", "actionSlots"], never, true, never>;
775
+ export declare class SparkQueryCardComponent {
776
+ readonly lang: SparkLanguageService;
777
+ queryId: import("@angular/core").InputSignal<string>;
778
+ parentId: import("@angular/core").InputSignal<string>;
779
+ parentType: import("@angular/core").InputSignal<string>;
780
+ reloadToken: import("@angular/core").InputSignal<unknown>;
781
+ data: import("@angular/core").InputSignal<QueryResultItem[] | null>;
782
+ /**
783
+ * The initial search term. The header's search box starts from it and the user takes over from
784
+ * there; a new value from the host replaces what was typed.
785
+ */
786
+ search: import("@angular/core").InputSignal<string>;
787
+ /**
788
+ * Whether the header offers the search box (#460 M15, D17 addendum). On by default, as every
789
+ * Vidyano sub-query tab has one; a card over bound `data` never shows it, because the grid does
790
+ * not refetch bound rows and the box would do nothing.
791
+ */
792
+ searchable: import("@angular/core").InputSignal<boolean>;
793
+ /** What the grid searches: the host's `search`, until the user types in the header box. */
794
+ readonly searchTerm: import("@angular/core").WritableSignal<string>;
795
+ protected readonly showSearch: import("@angular/core").Signal<boolean>;
796
+ /**
797
+ * Overrides the query's `selectionMode` for this card (#460, D17) — the detail page passes the
798
+ * sub-query entry's. `null` defers to the query (absent there = `'auto'`).
799
+ */
800
+ selectionMode: import("@angular/core").InputSignal<SparkSelectionModeSetting | null>;
801
+ /**
802
+ * Soft-deletion mode and deleted parent (#460), forwarded to the grid. In the recycle bin — the
803
+ * grid lists `deleted: 'only'`, or its parent row was opened with `?deleted=only` — the card offers
804
+ * no New, Delete or custom action, exactly as the query-list page does. See `recycleBin`.
805
+ */
806
+ deleted: import("@angular/core").InputSignal<SparkDeletedFilter | null | undefined>;
807
+ parentDeleted: import("@angular/core").InputSignal<boolean>;
808
+ /**
809
+ * Slots forwarded from a host that cannot project content — see the class comment. A card
810
+ * reached through hand-written markup should use the structural directives instead.
811
+ */
812
+ iconTemplate: import("@angular/core").InputSignal<TemplateRef<any> | null>;
813
+ captionTemplate: import("@angular/core").InputSignal<TemplateRef<any> | null>;
814
+ actionsTemplate: import("@angular/core").InputSignal<TemplateRef<any> | null>;
815
+ error: import("@angular/core").OutputEmitterRef<HttpErrorResponse>;
816
+ rowClicked: import("@angular/core").OutputEmitterRef<QueryResultItem>;
817
+ customActionExecuted: import("@angular/core").OutputEmitterRef<{
818
+ action: CustomActionDefinition;
819
+ }>;
820
+ /**
821
+ * Forwarded to the grid. Without this the escape hatch was unreachable from a card — i.e. from
822
+ * every auto-rendered sub-query and program-unit query page, which is most grids.
823
+ */
824
+ rowRoute: import("@angular/core").InputSignal<((row: QueryResultItem) => unknown[] | null) | null>;
825
+ colors: typeof Color;
826
+ private readonly iconSlots;
827
+ private readonly captionSlots;
828
+ private readonly actionSlots;
829
+ /**
830
+ * The grid, read only to surface its state — the query, the actions, the selection — to this
831
+ * component's own header.
832
+ *
833
+ * Optional rather than `required` on purpose. The header renders ABOVE the grid in this
834
+ * template, so on the first change-detection pass the view query has not resolved yet and
835
+ * `viewChild.required()` would throw where a plain one returns `undefined`. Everything below
836
+ * therefore reads it defensively; the signal updates once the view initialises and the header
837
+ * re-renders with the real query.
838
+ *
839
+ * A host reads grid state through a template reference variable instead, because a host may put
840
+ * the grid behind an `@if`, where a view query genuinely is intermittently undefined.
841
+ */
842
+ protected readonly grid: import("@angular/core").Signal<SparkQueryGridComponent | undefined>;
843
+ protected readonly query: import("@angular/core").Signal<import("@mintplayer/ng-spark/models").SparkQuery | null>;
844
+ protected readonly iconTpl: import("@angular/core").Signal<TemplateRef<any> | null>;
845
+ protected readonly captionTpl: import("@angular/core").Signal<TemplateRef<any> | null>;
846
+ protected readonly actionsTpl: import("@angular/core").Signal<TemplateRef<any> | null>;
847
+ /** The caption the card renders unless a slot replaces it. */
848
+ protected readonly caption: import("@angular/core").Signal<string>;
849
+ protected readonly customActions: import("@angular/core").Signal<CustomActionDefinition[]>;
850
+ /**
851
+ * The header's buttons: New, Delete and the custom actions, from the grid (#460, M15) — the same
852
+ * list the query-list page renders.
853
+ */
854
+ protected readonly toolbarActions: import("@angular/core").Signal<SparkQueryToolbarAction[]>;
855
+ protected actionClass(action: SparkQueryToolbarAction): string;
856
+ protected isEnabled(action: SparkQueryToolbarAction): boolean;
857
+ protected run(action: SparkQueryToolbarAction): void;
858
+ protected readonly selection: import("@angular/core").Signal<QueryResultItem[]>;
859
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryCardComponent, never>;
860
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkQueryCardComponent, "spark-query-card", never, {
861
+ "queryId": {
862
+ "alias": "queryId";
863
+ "required": true;
864
+ "isSignal": true;
865
+ };
866
+ "parentId": {
867
+ "alias": "parentId";
868
+ "required": false;
869
+ "isSignal": true;
870
+ };
871
+ "parentType": {
872
+ "alias": "parentType";
873
+ "required": false;
874
+ "isSignal": true;
875
+ };
876
+ "reloadToken": {
877
+ "alias": "reloadToken";
878
+ "required": false;
879
+ "isSignal": true;
880
+ };
881
+ "data": {
882
+ "alias": "data";
883
+ "required": false;
884
+ "isSignal": true;
885
+ };
886
+ "search": {
887
+ "alias": "search";
888
+ "required": false;
889
+ "isSignal": true;
890
+ };
891
+ "searchable": {
892
+ "alias": "searchable";
893
+ "required": false;
894
+ "isSignal": true;
895
+ };
896
+ "selectionMode": {
897
+ "alias": "selectionMode";
898
+ "required": false;
899
+ "isSignal": true;
900
+ };
901
+ "deleted": {
902
+ "alias": "deleted";
903
+ "required": false;
904
+ "isSignal": true;
905
+ };
906
+ "parentDeleted": {
907
+ "alias": "parentDeleted";
908
+ "required": false;
909
+ "isSignal": true;
910
+ };
911
+ "iconTemplate": {
912
+ "alias": "iconTemplate";
913
+ "required": false;
914
+ "isSignal": true;
915
+ };
916
+ "captionTemplate": {
917
+ "alias": "captionTemplate";
918
+ "required": false;
919
+ "isSignal": true;
920
+ };
921
+ "actionsTemplate": {
922
+ "alias": "actionsTemplate";
923
+ "required": false;
924
+ "isSignal": true;
925
+ };
926
+ "rowRoute": {
927
+ "alias": "rowRoute";
928
+ "required": false;
929
+ "isSignal": true;
930
+ };
931
+ }, {
932
+ "error": "error";
933
+ "rowClicked": "rowClicked";
934
+ "customActionExecuted": "customActionExecuted";
935
+ }, ["iconSlots", "captionSlots", "actionSlots"], never, true, never>;
558
936
  }
559
-
560
937
  /**
561
938
  * One cell of a Spark table, wherever that table is.
562
939
  *
@@ -585,52 +962,144 @@ declare class SparkQueryCardComponent {
585
962
  * gated on `Read`), and it stays in the grid, wrapping this component. Passing both would nest
586
963
  * anchors, which is invalid HTML; the grid therefore leaves `link` unset.
587
964
  */
588
- declare class SparkGridCellComponent {
589
- private readonly renderers;
590
- /** The column being rendered. Its `dataType` and `renderer` decide everything below. */
591
- column: _angular_core.InputSignal<SparkCellColumn>;
592
- /**
593
- * What the caller's value pipe produced: the display text for most columns, and the raw value
594
- * for `boolean` and `color`, which need the value rather than a rendering of it.
595
- */
596
- display: _angular_core.InputSignal<unknown>;
597
- /**
598
- * The value handed to a declared custom renderer.
599
- *
600
- * Separate from {@link display} because they are not the same thing: a renderer receives the
601
- * underlying value — for AsDetail, the nested object itself — while `display` has already been
602
- * flattened to something printable.
603
- */
604
- rendererValue: _angular_core.InputSignal<unknown>;
605
- /** The row, passed through to a custom renderer as `item`. */
606
- item: _angular_core.InputSignal<unknown>;
607
- /**
608
- * Pre-resolved chips for a reference-array column, or empty when the column is not one.
609
- *
610
- * Resolved by the caller because the label source differs: a query row carries a per-id
611
- * breadcrumb map, while an AsDetail row's `__sparkBreadcrumbs` is keyed by column name only and
612
- * so cannot label the members of an array. Rather than render a row of raw ids, an AsDetail
613
- * caller passes nothing and the cell falls through to text.
614
- */
615
- chips: _angular_core.InputSignal<ReferenceChip[]>;
616
- /** Route for a cell whose value references another entity. See the class comment. */
617
- link: _angular_core.InputSignal<unknown[] | null>;
618
- protected readonly rendererComponent: _angular_core.Signal<_angular_core.Type<any> | null>;
619
- protected readonly rendererInputs: _angular_core.Signal<Record<string, any>>;
620
- /** Null and undefined are distinct from `false` here — see the template. */
621
- protected readonly isUnsetBoolean: _angular_core.Signal<boolean>;
622
- /**
623
- * {@link display} when it really is a `Date`, and `null` otherwise.
624
- *
625
- * Angular's `DatePipe` does not degrade on a non-date: it **throws**
626
- * `NG02100: InvalidPipeArgument`, which would take down the whole cell. Both cell pipes
627
- * deliberately fall back to the raw text for an unparseable date rather than hiding the stored
628
- * value, so that text reaches this component and must not be piped. A template cannot express
629
- * `instanceof`, hence the computed.
630
- */
631
- protected readonly dateValue: _angular_core.Signal<Date | null>;
632
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkGridCellComponent, never>;
633
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkGridCellComponent, "spark-grid-cell", never, { "column": { "alias": "column"; "required": true; "isSignal": true; }; "display": { "alias": "display"; "required": false; "isSignal": true; }; "rendererValue": { "alias": "rendererValue"; "required": false; "isSignal": true; }; "item": { "alias": "item"; "required": false; "isSignal": true; }; "chips": { "alias": "chips"; "required": false; "isSignal": true; }; "link": { "alias": "link"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
965
+ export declare class SparkGridCellComponent {
966
+ private readonly renderers;
967
+ /** The column being rendered. Its `dataType` and `renderer` decide everything below. */
968
+ column: import("@angular/core").InputSignal<SparkCellColumn>;
969
+ /**
970
+ * What the caller's value pipe produced: the display text for most columns, and the raw value
971
+ * for `boolean` and `color`, which need the value rather than a rendering of it.
972
+ */
973
+ display: import("@angular/core").InputSignal<unknown>;
974
+ /**
975
+ * The value handed to a declared custom renderer.
976
+ *
977
+ * Separate from {@link display} because they are not the same thing: a renderer receives the
978
+ * underlying value — for AsDetail, the nested object itself — while `display` has already been
979
+ * flattened to something printable.
980
+ */
981
+ rendererValue: import("@angular/core").InputSignal<unknown>;
982
+ /** The row, passed through to a custom renderer as `item`. */
983
+ item: import("@angular/core").InputSignal<unknown>;
984
+ /**
985
+ * Pre-resolved chips for a reference-array column, or empty when the column is not one.
986
+ *
987
+ * Resolved by the caller because the label source differs: a query row carries a per-id
988
+ * breadcrumb map, while an AsDetail row's `__sparkBreadcrumbs` is keyed by column name only and
989
+ * so cannot label the members of an array. Rather than render a row of raw ids, an AsDetail
990
+ * caller passes nothing and the cell falls through to text.
991
+ */
992
+ chips: import("@angular/core").InputSignal<ReferenceChip[]>;
993
+ /** Route for a cell whose value references another entity. See the class comment. */
994
+ link: import("@angular/core").InputSignal<unknown[] | null>;
995
+ protected readonly rendererComponent: import("@angular/core").Signal<import("@angular/core").Type<any> | null>;
996
+ protected readonly rendererInputs: import("@angular/core").Signal<Record<string, any>>;
997
+ /** Null and undefined are distinct from `false` here — see the template. */
998
+ protected readonly isUnsetBoolean: import("@angular/core").Signal<boolean>;
999
+ /**
1000
+ * {@link display} when it really is a `Date`, and `null` otherwise.
1001
+ *
1002
+ * Angular's `DatePipe` does not degrade on a non-date: it **throws**
1003
+ * `NG02100: InvalidPipeArgument`, which would take down the whole cell. Both cell pipes
1004
+ * deliberately fall back to the raw text for an unparseable date rather than hiding the stored
1005
+ * value, so that text reaches this component and must not be piped. A template cannot express
1006
+ * `instanceof`, hence the computed.
1007
+ */
1008
+ protected readonly dateValue: import("@angular/core").Signal<Date | null>;
1009
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkGridCellComponent, never>;
1010
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkGridCellComponent, "spark-grid-cell", never, {
1011
+ "column": {
1012
+ "alias": "column";
1013
+ "required": true;
1014
+ "isSignal": true;
1015
+ };
1016
+ "display": {
1017
+ "alias": "display";
1018
+ "required": false;
1019
+ "isSignal": true;
1020
+ };
1021
+ "rendererValue": {
1022
+ "alias": "rendererValue";
1023
+ "required": false;
1024
+ "isSignal": true;
1025
+ };
1026
+ "item": {
1027
+ "alias": "item";
1028
+ "required": false;
1029
+ "isSignal": true;
1030
+ };
1031
+ "chips": {
1032
+ "alias": "chips";
1033
+ "required": false;
1034
+ "isSignal": true;
1035
+ };
1036
+ "link": {
1037
+ "alias": "link";
1038
+ "required": false;
1039
+ "isSignal": true;
1040
+ };
1041
+ }, {}, never, never, true, never>;
634
1042
  }
635
-
636
- export { SPARK_GRID_PAGE_SIZES, SparkGridCellComponent, SparkGridRenderers, SparkQueryActionsContext, SparkQueryActionsDirective, SparkQueryCaptionContext, SparkQueryCaptionDirective, SparkQueryCardComponent, SparkQueryGridComponent, SparkQueryIconDirective, SparkQuerySlotContext, initialGridSettings, isVirtualScrollingQuery, slotFor, visibleGridAttributes };
1043
+ /** How long typing must pause before the term is published, in milliseconds. */
1044
+ export declare const SPARK_SEARCH_DEBOUNCE_MS = 300;
1045
+ /**
1046
+ * The query search box: a magnifier, the input and a clear button (#460 M15, D17 addendum).
1047
+ *
1048
+ * One component for both surfaces that search a query — the query-list page above its grid and the
1049
+ * `<spark-query-card>` header next to its actions, as Vidyano's sub-query tabs have it — so they
1050
+ * behave identically: the same debounce, the same clear button, the same placeholder.
1051
+ *
1052
+ * Two-way bound through `term`, which is what a grid's `search` input takes:
1053
+ *
1054
+ * ```html
1055
+ * <spark-search-box [(term)]="searchTerm" />
1056
+ * <spark-query-grid [search]="searchTerm()" … />
1057
+ * ```
1058
+ *
1059
+ * Typing publishes the term once it has paused for {@link SPARK_SEARCH_DEBOUNCE_MS}, so a word is
1060
+ * one query rather than one per keystroke. Clearing (the ✕ button, or Escape) publishes at once. A
1061
+ * `term` set from outside — a host resetting it — replaces what the box shows.
1062
+ */
1063
+ export declare class SparkSearchBoxComponent {
1064
+ /** The published search term. Debounced while typing; immediate on clear. */
1065
+ readonly term: import("@angular/core").ModelSignal<string>;
1066
+ /** The input group's size: `sm` for a card header, the default for a page. */
1067
+ readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg">;
1068
+ /** Overrides {@link SPARK_SEARCH_DEBOUNCE_MS}; `0` publishes on every keystroke. */
1069
+ readonly debounce: import("@angular/core").InputSignal<number>;
1070
+ /** What the input shows, which runs ahead of `term` while the debounce is pending. */
1071
+ protected readonly text: import("@angular/core").WritableSignal<string>;
1072
+ private timer;
1073
+ constructor();
1074
+ protected onInput(value: string): void;
1075
+ /** Empties the box and publishes the empty term at once. */
1076
+ clear(): void;
1077
+ private cancelPending;
1078
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkSearchBoxComponent, never>;
1079
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkSearchBoxComponent, "spark-search-box", never, {
1080
+ "term": {
1081
+ "alias": "term";
1082
+ "required": false;
1083
+ "isSignal": true;
1084
+ };
1085
+ "size": {
1086
+ "alias": "size";
1087
+ "required": false;
1088
+ "isSignal": true;
1089
+ };
1090
+ "debounce": {
1091
+ "alias": "debounce";
1092
+ "required": false;
1093
+ "isSignal": true;
1094
+ };
1095
+ }, {
1096
+ "term": "termChange";
1097
+ }, never, never, true, never>;
1098
+ }
1099
+ /**
1100
+ * The preset column filters (a query page's URL filters) that name an attribute of the query's
1101
+ * entity type, with their values coerced to the attribute's type — a URL carries only strings, and
1102
+ * the server compares a number column against numbers. Filters naming anything else are dropped, so
1103
+ * an unrelated query-string parameter never becomes a request.
1104
+ */
1105
+ export declare function applicablePresetFilters(filters: readonly QueryColumnFilter[] | null | undefined, entityType: EntityType | null | undefined): QueryColumnFilter[];