@mintplayer/ng-spark 22.25.0 → 22.27.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 (30) hide show
  1. package/README.md +6 -4
  2. package/fesm2022/mintplayer-ng-spark-grid.mjs +71 -44
  3. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  4. package/fesm2022/mintplayer-ng-spark-models.mjs +16 -7
  5. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-po-create.mjs +17 -12
  7. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  8. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +28 -13
  9. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  10. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +9 -2
  11. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  12. package/fesm2022/mintplayer-ng-spark-po-form.mjs +67 -13
  13. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  14. package/fesm2022/mintplayer-ng-spark-query-list.mjs +18 -2
  15. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +2 -0
  17. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  18. package/fesm2022/mintplayer-ng-spark-services.mjs +12 -9
  19. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  20. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +4 -3
  21. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -1
  22. package/package.json +3 -3
  23. package/types/mintplayer-ng-spark-grid.d.ts +47 -32
  24. package/types/mintplayer-ng-spark-models.d.ts +62 -27
  25. package/types/mintplayer-ng-spark-po-create.d.ts +10 -0
  26. package/types/mintplayer-ng-spark-po-detail.d.ts +8 -2
  27. package/types/mintplayer-ng-spark-po-form.d.ts +20 -0
  28. package/types/mintplayer-ng-spark-query-list.d.ts +11 -1
  29. package/types/mintplayer-ng-spark-services.d.ts +42 -8
  30. package/types/mintplayer-ng-spark-soft-delete.d.ts +1 -1
@@ -128,51 +128,71 @@ export interface PersistentObjectAttribute {
128
128
  */
129
129
  asDetailType?: string;
130
130
  }
131
+ /**
132
+ * One action of the composed catalogue (#467, D7), as `/spark/actions/list` reports it for a type:
133
+ * a built-in (New, Edit, Delete) or a custom action, already narrowed to what the caller may run.
134
+ * Text arrives resolved by the server (D26).
135
+ */
131
136
  export interface CustomActionDefinition {
132
137
  name: string;
133
- displayName: TranslatedString;
138
+ /** `actions.{name}.label`, or the humanized name when untranslated. */
139
+ label: TranslatedString;
134
140
  icon?: string;
135
- description?: string;
141
+ /** `actions.{name}.description`; absent when untranslated. */
142
+ description?: TranslatedString;
136
143
  showedOn: string;
137
144
  selectionRule?: string;
138
145
  refreshOnCompleted: boolean;
139
- confirmationMessageKey?: string;
146
+ /**
147
+ * The confirmation to ask before running, `actions.{name}.confirmation` or the explicit key the
148
+ * catalogue names. May contain `{count}`, the number of rows about to be acted on. Absent: no prompt.
149
+ */
150
+ confirmation?: TranslatedString;
140
151
  /**
141
152
  * How prominently, and how warily, to present the action: 'primary', 'secondary', 'danger',
142
153
  * 'warning'. Undefined renders the neutral default.
143
154
  *
144
155
  * Presentation only — the server decides who may see and run an action. A 'danger' variant just
145
- * asks the button to look like what it does; pair it with `confirmationMessageKey` for anything
156
+ * asks the button to look like what it does; pair it with a `confirmation` for anything
146
157
  * irreversible, because the colour warns and the prompt is what actually prevents the accident.
147
158
  */
148
159
  variant?: string;
149
160
  offset: number;
150
161
  /**
151
- * True for the framework's built-in `New` and `Delete` (#460, D18): catalogue entries with a
152
- * `showedOn` and a `selectionRule` like any custom action (defaults: New has no rule, Delete is
153
- * `>0`; an app overrides them in `customActions.json`), but run through `/po/new` and
154
- * `/po/delete-many`, never `/actions/execute`. Absent for a custom action.
162
+ * True for the framework's built-in `New`, `Edit` and `Delete` (#460 D18, #467 D7): catalogue
163
+ * entries with a `showedOn` and a `selectionRule` like any custom action (the core's
164
+ * `actions.json` ships them; an app overrides or removes them in its own), but run through
165
+ * `/po/new`, the edit page and `/po/delete-many`, never `/actions/execute`. Absent for a custom action.
155
166
  */
156
167
  isDefault?: boolean;
157
168
  }
158
- /** How many rows a grid lets the user select — what `<bs-datatable>` renders. */
159
- export type SparkSelectionMode = 'none' | 'single' | 'multiple';
160
169
  /**
161
- * What a query definition (or a sub-query entry) declares (#460, D17): a fixed mode, or `'auto'` —
170
+ * Whether a grid lets the user tick rows — what `<bs-datatable>` renders. There is no single
171
+ * selection (#467, D10): a selectable list is a checkbox list, and actions whose rule does not
172
+ * match the count are disabled rather than the selection trimmed.
173
+ */
174
+ export type SparkSelectionMode = 'none' | 'multiple';
175
+ /**
176
+ * What a query definition (or a sub-query entry) declares (#460 D17): a fixed mode, or `'auto'` —
162
177
  * the default, and what an absent field means — to derive it from the actions offered.
163
178
  */
164
179
  export type SparkSelectionModeSetting = 'auto' | SparkSelectionMode;
180
+ /** The largest selection the server accepts in one request (`SparkDefaultActions.MaxSelectedItems`). */
181
+ export declare const SPARK_MAX_SELECTED_ITEMS = 200;
165
182
  /**
166
- * The selection mode a grid renders.
167
- *
168
- * A declared `'none'`, `'single'` or `'multiple'` wins outright. `'auto'` (or nothing) derives it,
169
- * so a grid gains a checkbox column exactly when an action needs one and is otherwise
170
- * pixel-identical to a grid with no selection at all — as Vidyano's query grid does. The offered
171
- * actions include the default Delete (`>0`) when the caller may delete, so such a grid is
172
- * multi-select.
183
+ * Whether an action needs ticked rows (#467, R1(4)): it has a rule, and the rule accepts at least
184
+ * one row. `=0` accepts none, so an action that runs only without a selection never makes a list
185
+ * selectable; an action without a rule acts on the query, not on rows.
186
+ */
187
+ export declare function needsSelection(action: CustomActionDefinition): boolean;
188
+ /**
189
+ * The selection mode a grid renders (#467, R1).
173
190
  *
174
- * Derived: `'single'` when every gated action is satisfied by one row and refused by two; anything
175
- * else that cares about the count gets `'multiple'`.
191
+ * A declared `'none'` or `'multiple'` wins outright. `'auto'` (or nothing) derives it from
192
+ * `actions`, which must already be the **effective** list for this result — listed for the caller
193
+ * (the right), shown on queries, not withheld by `disabledActions` and not hidden by the recycle
194
+ * bin. Built-in Edit and Delete count like any other action. `'multiple'` when at least one of them
195
+ * {@link needsSelection}, else `'none'`, so a read-only user sees a list with no checkbox column.
176
196
  */
177
197
  export declare function selectionModeFor(actions: CustomActionDefinition[], declared?: SparkSelectionModeSetting | null): SparkSelectionMode;
178
198
  /**
@@ -293,7 +313,8 @@ export interface AttributeGroup {
293
313
  export interface EntityType {
294
314
  id: string;
295
315
  name: string;
296
- description?: TranslatedString;
316
+ /** The type's display name, resolved by the server (#467, D6; formerly `description`). */
317
+ label?: TranslatedString;
297
318
  /**
298
319
  * The backing CLR type, or absent for a JSON-only virtual type (#325).
299
320
  *
@@ -369,7 +390,8 @@ export interface SparkQuerySortColumn {
369
390
  export interface SparkQuery {
370
391
  id: string;
371
392
  name: string;
372
- description?: TranslatedString;
393
+ /** The query's display name, resolved by the server (#467; formerly `description`). */
394
+ label?: TranslatedString;
373
395
  source: string;
374
396
  alias?: string;
375
397
  sortColumns: SparkQuerySortColumn[];
@@ -479,14 +501,20 @@ export interface QueryColumn extends SparkCellColumn {
479
501
  /**
480
502
  * One row: an id, a display string, and a value per column.
481
503
  *
482
- * Deliberately too weak to act on — no attribute metadata, no `can` block, no etag, because none
483
- * of those can be trusted from a projection.
504
+ * Deliberately too weak to act on — no attribute metadata, no `can` block, because neither can be
505
+ * trusted from a projection. It does carry its document's `etag` (#467, D14), a version rather than
506
+ * a judgement, so a delete from the list says which version it removes.
484
507
  */
485
508
  export interface QueryResultItem {
486
509
  /** Never null and unique within a result; the server refuses anything else. */
487
510
  id: string;
488
511
  /** What to show when the row is named rather than tabulated (a reference picker's value). */
489
512
  breadcrumb?: string;
513
+ /**
514
+ * The document's change vector when the list loaded (#467, D14). A delete sends it back; a row
515
+ * changed since is a 409. Absent for a row with no document behind it.
516
+ */
517
+ etag?: string;
490
518
  values: QueryResultItemValue[];
491
519
  /** Presentation hints for the whole row. Keys arrive lower-cased. */
492
520
  typeHints?: Record<string, string>;
@@ -926,6 +954,13 @@ export declare function comparableValue(attribute: PersistentObjectAttribute | u
926
954
  export declare function comparableFormValue(value: unknown): string;
927
955
  /** Whether two form-dict values hold the same content (see {@link comparableFormValue}). */
928
956
  export declare function formValuesEqual(a: unknown, b: unknown): boolean;
957
+ /**
958
+ * The prompt to show before running `action` on `count` rows, with `{count}` substituted, or null
959
+ * when the action asks for no confirmation (#467, D7).
960
+ */
961
+ export declare function confirmationText(action: CustomActionDefinition | undefined | null, count: number, lang?: string): string | null;
962
+ /** The built-in action named `name` (`New`, `Edit`, `Delete`) in `actions`, when the server listed it. */
963
+ export declare function builtInAction(actions: CustomActionDefinition[], name: 'New' | 'Edit' | 'Delete'): CustomActionDefinition | undefined;
929
964
  /** Whether `showedOn` offers the action on a query: `"query"` or `"both"`, in any casing. */
930
965
  export declare function isShowedOnQuery(showedOn: string | undefined | null): boolean;
931
966
  /** Whether `showedOn` offers the action on a detail page: `"detail"` or `"both"`, in any casing. */
@@ -937,7 +972,7 @@ export declare function isShowedOnDetail(showedOn: string | undefined | null): b
937
972
  * and `"both"` — as the server model and the custom-actions guide have always
938
973
  * documented — compared case-insensitively (#460 M15: `"Both"` used to render nowhere).
939
974
  *
940
- * The built-in New and Delete (`isDefault`, #460 D18) are left out: they are not executed as
975
+ * The built-in New, Edit and Delete (`isDefault`, #460 D18, #467 D7) are left out: they are not executed as
941
976
  * custom actions, and the grid offers them itself (see {@link defaultQueryActions}).
942
977
  *
943
978
  * ⚠️ This narrows what is DISPLAYED. It is NOT an authorization boundary: the grant
@@ -948,8 +983,8 @@ export declare function filterQueryActions(actions: CustomActionDefinition[]): C
948
983
  /** The custom actions a detail page should offer — the detail-side counterpart of {@link filterQueryActions}. */
949
984
  export declare function filterDetailActions(actions: CustomActionDefinition[]): CustomActionDefinition[];
950
985
  /**
951
- * The built-in New and Delete the server listed for this caller (#460, D18), when their `showedOn`
952
- * includes the query side. Present only when the caller holds `New/T` or `Delete/T`.
986
+ * The built-in New, Edit and Delete the server listed for this caller (#460 D18, #467 D7), when their `showedOn`
987
+ * includes the query side. Present only when the caller holds `New/T`, `Edit/T` or `Delete/T`.
953
988
  */
954
989
  export declare function defaultQueryActions(actions: CustomActionDefinition[]): CustomActionDefinition[];
955
990
  /**
@@ -17,6 +17,16 @@ export declare class SparkPoCreateComponent {
17
17
  isSaving: import("@angular/core").WritableSignal<boolean>;
18
18
  private allEntityTypes;
19
19
  generalErrors: import("@angular/core").Signal<ValidationError[]>;
20
+ /**
21
+ * The parent a sub-query card's New passed in the URL (`parentId`, `parentType`, `queryId`), or
22
+ * null. Forwarded to the form so a Reference attribute's option query runs under the object the
23
+ * New was started from — the same parent `/po/new` receives.
24
+ */
25
+ subQueryParent: import("@angular/core").WritableSignal<{
26
+ parentId: string;
27
+ parentType: string;
28
+ queryId: string;
29
+ } | null>;
20
30
  constructor();
21
31
  private onParamsChange;
22
32
  /**
@@ -60,8 +60,12 @@ export declare class SparkPoDetailComponent {
60
60
  asDetailReferenceOptions: import("@angular/core").WritableSignal<Record<string, Record<string, QueryResultItem[]>>>;
61
61
  type: string;
62
62
  id: string;
63
- canEdit: import("@angular/core").WritableSignal<boolean>;
64
- canDelete: import("@angular/core").WritableSignal<boolean>;
63
+ /** The catalogue's Edit (#467, D8) when this page offers it: listed for the caller, on the detail side, and allowed on this row. */
64
+ editAction: import("@angular/core").WritableSignal<CustomActionDefinition | null>;
65
+ /** The catalogue's Delete, on the same terms as {@link editAction}. */
66
+ deleteAction: import("@angular/core").WritableSignal<CustomActionDefinition | null>;
67
+ canEdit: import("@angular/core").Signal<boolean>;
68
+ canDelete: import("@angular/core").Signal<boolean>;
65
69
  customActions: import("@angular/core").WritableSignal<CustomActionDefinition[]>;
66
70
  /** Type-level rights, kept for the add-on context; null until loaded. */
67
71
  permissions: import("@angular/core").WritableSignal<EntityPermissions | null>;
@@ -125,6 +129,8 @@ export declare class SparkPoDetailComponent {
125
129
  * a louder style. Restricted to a known set rather than interpolated, because the value arrives
126
130
  * from a JSON file and must not be able to put arbitrary classes on the button.
127
131
  */
132
+ /** Edit's and Delete's button classes: the catalogue's variant, else the look they always had. */
133
+ protected builtInActionClass(action: CustomActionDefinition, fallback: 'primary' | 'danger'): string;
128
134
  protected customActionClass(action: CustomActionDefinition): string;
129
135
  /**
130
136
  * The action currently running, or null. Drives the disabled state on every custom-action
@@ -139,6 +139,16 @@ export declare class SparkPoFormComponent {
139
139
  isSaving: import("@angular/core").InputSignal<boolean>;
140
140
  parentId: import("@angular/core").InputSignal<string | undefined>;
141
141
  parentType: import("@angular/core").InputSignal<string | undefined>;
142
+ /**
143
+ * The parent a Reference attribute's option query runs under, when it differs from
144
+ * `parentId`/`parentType`. Those two also name the object that owns an AsDetail row for the row
145
+ * round-trips (`parentTypeForWire`), so they cannot carry a create page's sub-query parent: a New
146
+ * opened from an Account's card must offer that account's repositories, while its detail rows still
147
+ * belong to the type being created. Unset, the option queries use `parentId`/`parentType` as before.
148
+ */
149
+ referenceParentId: import("@angular/core").InputSignal<string | undefined>;
150
+ referenceParentType: import("@angular/core").InputSignal<string | undefined>;
151
+ private readonly optionQueryParent;
142
152
  save: import("@angular/core").OutputEmitterRef<void>;
143
153
  cancel: import("@angular/core").OutputEmitterRef<void>;
144
154
  /**
@@ -433,6 +443,16 @@ export declare class SparkPoFormComponent {
433
443
  "required": false;
434
444
  "isSignal": true;
435
445
  };
446
+ "referenceParentId": {
447
+ "alias": "referenceParentId";
448
+ "required": false;
449
+ "isSignal": true;
450
+ };
451
+ "referenceParentType": {
452
+ "alias": "referenceParentType";
453
+ "required": false;
454
+ "isSignal": true;
455
+ };
436
456
  "objectTypeId": {
437
457
  "alias": "objectTypeId";
438
458
  "required": false;
@@ -3,7 +3,7 @@ import { TemplateRef } from "@angular/core";
3
3
  import { Color } from "@mintplayer/ng-bootstrap";
4
4
  import { SparkLanguageService } from "@mintplayer/ng-spark/services";
5
5
  import { SparkQueryToolbarAction } from "@mintplayer/ng-spark/grid";
6
- import { CustomActionDefinition, QueryColumn, QueryColumnFilter, QueryResultItem, SparkDeletedFilter } from "@mintplayer/ng-spark/models";
6
+ import { CustomActionDefinition, QueryColumn, QueryColumnFilter, QueryResultItem, SparkDeletedFilter, SparkSelectionModeSetting } from "@mintplayer/ng-spark/models";
7
7
  import { SparkQueryListContext } from "@mintplayer/ng-spark/panels";
8
8
  /**
9
9
  * The routed query page: chrome around one {@link SparkQueryGridComponent}.
@@ -31,6 +31,11 @@ export declare class SparkQueryListComponent {
31
31
  protected readonly lang: SparkLanguageService;
32
32
  extraActionsTemplate: import("@angular/core").InputSignal<TemplateRef<void> | null>;
33
33
  showCustomActions: import("@angular/core").InputSignal<boolean>;
34
+ /**
35
+ * Overrides the query's own `selectionMode` (#467, R3), as the sub-query card's input does: for a
36
+ * host that embeds this page and wants, say, `'none'` on a list the actions would make selectable.
37
+ */
38
+ selectionMode: import("@angular/core").InputSignal<SparkSelectionModeSetting | null>;
34
39
  /**
35
40
  * Forwarded to the grid, so a query PAGE can replace its row links — the same reason the card
36
41
  * forwards it. Without this the escape hatch existed only for a directly-embedded grid.
@@ -147,6 +152,11 @@ export declare class SparkQueryListComponent {
147
152
  "required": false;
148
153
  "isSignal": true;
149
154
  };
155
+ "selectionMode": {
156
+ "alias": "selectionMode";
157
+ "required": false;
158
+ "isSignal": true;
159
+ };
150
160
  "rowRoute": {
151
161
  "alias": "rowRoute";
152
162
  "required": false;
@@ -62,11 +62,22 @@ export interface NewObjectOptions {
62
62
  }
63
63
  /** Where a bulk delete was started from; see {@link SparkService.deleteMany}. */
64
64
  export interface DeleteManyOptions {
65
- /** The query the rows were selected in, for the server's `OnDisableActionsAsync`. */
66
- queryId?: string;
65
+ /**
66
+ * The query the rows were selected in. Required (#467, D12): the server fetches the rows through it
67
+ * and applies its `OnDisableActionsAsync` decision; without it the call is a 400.
68
+ */
69
+ queryId: string;
67
70
  /** The sub-query's container, when deleting from a sub-query. */
68
71
  parentId?: string;
69
72
  parentType?: string;
73
+ /** One reason for the whole batch, recorded on every soft-deleted row (#467, D20). */
74
+ reason?: string;
75
+ }
76
+ /** A row and the version of it the caller saw (#467, D14): what a bulk delete names. */
77
+ export interface SparkRowVersion {
78
+ id: string;
79
+ /** The row's `etag` from the query result. */
80
+ etag: string;
70
81
  }
71
82
  /** Context for {@link SparkService.deleteRow}. Every field is required — see `DeleteRow.cs`. */
72
83
  export interface DeleteRowOptions {
@@ -151,7 +162,24 @@ export declare class SparkService {
151
162
  get(type: string, id: string, options?: {
152
163
  deleted?: SparkDeletedFilter;
153
164
  }): Promise<PersistentObject>;
154
- create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
165
+ /**
166
+ * `parent` is the sub-query the New was started from — the same `parentId`/`parentType`/`queryId`
167
+ * `newObject` was given. The server resolves and authorizes it again and hands it to the save hooks
168
+ * as `PersistentObject.Parent`.
169
+ *
170
+ * Resolves to `null` when a server interceptor cancelled the create (#482: the user answered a prompt with
171
+ * Cancel) — the server answers 204 and nothing was created.
172
+ */
173
+ create(type: string, data: Partial<PersistentObject>, parent?: {
174
+ parentId: string;
175
+ parentType: string;
176
+ queryId: string;
177
+ }): Promise<PersistentObject | null>;
178
+ /**
179
+ * Saves an edit. `data.etag` — the etag the object was loaded with — is required (#467, D16): it is
180
+ * the version being edited. A 409 means the object changed since (`reason: 'changed'`) or was
181
+ * deleted since (`reason: 'deleted'`).
182
+ */
155
183
  update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
156
184
  /**
157
185
  * Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.
@@ -190,14 +218,20 @@ export declare class SparkService {
190
218
  * of the route variables.
191
219
  */
192
220
  deleteRow(type: string, options: DeleteRowOptions): Promise<void>;
193
- delete(type: string, id: string): Promise<void>;
221
+ /**
222
+ * Deletes one object. `etag` is the version being deleted — the loaded object's `etag` — and is
223
+ * required (#467, D14): an object changed since is a 409.
224
+ */
225
+ delete(type: string, id: string, etag: string): Promise<void>;
194
226
  /**
195
227
  * Deletes several rows of one type in one request — the default Delete action on a selection
196
- * (`POST /spark/po/delete-many`, #460 D18). All or nothing: a 404 means some row is missing or not
197
- * yours to delete, a 403 that the server's `OnDisableActionsAsync` withholds Delete on the query or
198
- * on one of the rows, a 400 that the selection breaks the rule or the 200-row cap.
228
+ * (`POST /spark/po/delete-many`, #460 D18). Each row carries the `etag` its query result row had
229
+ * (#467, D14). All or nothing: a 404 means some row is missing or not yours to delete, a 403 that
230
+ * the server's `OnDisableActionsAsync` withholds Delete on the query or on one of the rows, a 409
231
+ * that rows changed since the list loaded (the body's `message` names them), a 400 that the
232
+ * selection breaks the rule or the 200-row cap.
199
233
  */
200
- deleteMany(type: string, ids: string[], options?: DeleteManyOptions): Promise<void>;
234
+ deleteMany(type: string, items: SparkRowVersion[], options: DeleteManyOptions): Promise<void>;
201
235
  getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]>;
202
236
  /**
203
237
  * @param parent The object of THIS type the action is operating on — the detail-page invocation.
@@ -17,7 +17,7 @@ export declare class SparkSoftDeleteService {
17
17
  * `POST /spark/po/purge` → 204. Requires `Purge/T` and a deleted row. Removes the document and
18
18
  * every revision of it; it cannot be undone.
19
19
  */
20
- purge(type: string, id: string): Promise<void>;
20
+ purge(type: string, id: string, etag: string): Promise<void>;
21
21
  static ɵfac: i0.ɵɵFactoryDeclaration<SparkSoftDeleteService, never>;
22
22
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
23
23
  }