@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,115 +1,124 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, signal, ChangeDetectionStrategy, Component, makeEnvironmentProviders } from '@angular/core';
3
- import { Router } from '@angular/router';
4
-
5
- // Wire types matching MintPlayer.Spark.Abstractions.ClientOperations on the server.
6
- // Discriminator is the `type` field. Unknown operation types are silently dropped
7
- // by the dispatcher (forward-compat: new types can land server-side without updating
8
- // older clients).
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, Injectable, InjectionToken, inject, makeEnvironmentProviders, signal } from "@angular/core";
3
+ import { Router } from "@angular/router";
4
+ import { resolveTranslation } from "@mintplayer/ng-spark/models";
9
5
  var NotificationKind;
10
- (function (NotificationKind) {
11
- NotificationKind[NotificationKind["Info"] = 0] = "Info";
12
- NotificationKind[NotificationKind["Success"] = 1] = "Success";
13
- NotificationKind[NotificationKind["Warning"] = 2] = "Warning";
14
- NotificationKind[NotificationKind["Error"] = 3] = "Error";
6
+ (function(NotificationKind) {
7
+ NotificationKind[NotificationKind["Info"] = 0] = "Info";
8
+ NotificationKind[NotificationKind["Success"] = 1] = "Success";
9
+ NotificationKind[NotificationKind["Warning"] = 2] = "Warning";
10
+ NotificationKind[NotificationKind["Error"] = 3] = "Error";
15
11
  })(NotificationKind || (NotificationKind = {}));
16
-
17
- /**
18
- * Multi-provider token. `provideSparkClientOperations()` registers the
19
- * built-in handlers; apps can add their own with additional `multi: true`
20
- * providers using this token.
21
- */
22
- const SPARK_CLIENT_OPERATION_HANDLERS = new InjectionToken('SPARK_CLIENT_OPERATION_HANDLERS');
23
-
24
- /**
25
- * Routes received operations to registered handlers. Unknown operation types
26
- * (no matching registration) are silently dropped — this is the forward-compat
27
- * contract that lets new operation types ship server-side without coordinated
28
- * client updates.
29
- *
30
- * Last-registered-wins on duplicate `type` values, matching standard
31
- * Angular multi-provider override semantics.
32
- *
33
- * R2-H19 — security contract for handler authors:
34
- * The dispatcher treats handler resolution as allow-list-by-type (unknown
35
- * types drop). It does NOT validate the *content* of each operation. Handlers
36
- * that act on URL-shaped fields (navigate, redirect, openWindow) MUST run
37
- * the value through `sanitizeReturnUrl` from `@mintplayer/ng-spark-auth/models`
38
- * (or an equivalent same-origin check) before acting on it. Otherwise a
39
- * single attribute-echo XSS or a single mid-channel byte flip on a non-TLS
40
- * path lets the server drive client navigation to an attacker host. The
41
- * built-in `notify` handler renders via Angular interpolation (escaped) so
42
- * it's safe to pass through, but anything more powerful must validate.
43
- */
44
- class SparkClientOperationDispatcher {
45
- handlerMap;
46
- constructor() {
47
- const registrations = inject(SPARK_CLIENT_OPERATION_HANDLERS, { optional: true }) ?? [];
48
- const map = new Map();
49
- for (const { type, handler } of registrations) {
50
- map.set(type, handler);
51
- }
52
- this.handlerMap = map;
53
- }
54
- dispatch(operations) {
55
- if (!operations || operations.length === 0)
56
- return;
57
- for (const operation of operations) {
58
- const handler = this.handlerMap.get(operation.type);
59
- if (handler) {
60
- handler(operation);
61
- }
62
- // Unknown types: silently dropped (forward-compat).
63
- }
64
- }
65
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
66
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, providedIn: 'root' });
67
- }
68
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, decorators: [{
69
- type: Injectable,
70
- args: [{ providedIn: 'root' }]
71
- }], ctorParameters: () => [] });
72
-
73
- const DEFAULT_DURATION_MS = 4000;
74
- /**
75
- * Holds the active toasts as a signal. The `<spark-toast-container>` component
76
- * renders them; the built-in `notify` operation handler pushes new toasts here.
77
- *
78
- * Auto-dismissal: each toast schedules its own removal after `durationMs`. Pass
79
- * `0` to make a toast sticky (manual dismissal only).
80
- */
81
- class SparkNotificationService {
82
- _toasts = signal([], /* @ts-ignore */
83
- ...(ngDevMode ? [{ debugName: "_toasts" }] : /* istanbul ignore next */ []));
84
- toasts = this._toasts.asReadonly();
85
- show(message, kind = NotificationKind.Info, durationMs) {
86
- const id = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
87
- const effectiveDuration = durationMs ?? DEFAULT_DURATION_MS;
88
- const toast = { id, message, kind, durationMs: effectiveDuration };
89
- this._toasts.update(toasts => [...toasts, toast]);
90
- if (effectiveDuration > 0) {
91
- setTimeout(() => this.dismiss(id), effectiveDuration);
92
- }
93
- }
94
- dismiss(id) {
95
- this._toasts.update(toasts => toasts.filter(t => t.id !== id));
96
- }
97
- clear() {
98
- this._toasts.set([]);
99
- }
100
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
101
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, providedIn: 'root' });
102
- }
103
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, decorators: [{
104
- type: Injectable,
105
- args: [{ providedIn: 'root' }]
106
- }] });
107
-
108
- class SparkToastContainerComponent {
109
- notifications = inject(SparkNotificationService);
110
- Kind = NotificationKind;
111
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
112
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkToastContainerComponent, isStandalone: true, selector: "spark-toast-container", ngImport: i0, template: `
12
+ const SPARK_CLIENT_OPERATION_HANDLERS = new InjectionToken("SPARK_CLIENT_OPERATION_HANDLERS");
13
+ var SparkClientOperationDispatcher = class SparkClientOperationDispatcher {
14
+ handlerMap;
15
+ constructor() {
16
+ const registrations = inject(SPARK_CLIENT_OPERATION_HANDLERS, { optional: true }) ?? [];
17
+ const map = /* @__PURE__ */ new Map();
18
+ for (const { type, handler } of registrations) map.set(type, handler);
19
+ this.handlerMap = map;
20
+ }
21
+ dispatch(operations) {
22
+ if (!operations || operations.length === 0) return;
23
+ for (const operation of operations) {
24
+ const handler = this.handlerMap.get(operation.type);
25
+ if (handler) handler(operation);
26
+ }
27
+ }
28
+ static ɵfac = i0.ɵɵngDeclareFactory({
29
+ minVersion: "12.0.0",
30
+ version: "22.2.0",
31
+ ngImport: i0,
32
+ type: SparkClientOperationDispatcher,
33
+ deps: [],
34
+ target: i0.ɵɵFactoryTarget.Injectable
35
+ });
36
+ static ɵprov = i0.ɵɵngDeclareInjectable({
37
+ minVersion: "12.0.0",
38
+ version: "22.2.0",
39
+ ngImport: i0,
40
+ type: SparkClientOperationDispatcher,
41
+ providedIn: "root"
42
+ });
43
+ };
44
+ i0.ɵɵngDeclareClassMetadata({
45
+ minVersion: "12.0.0",
46
+ version: "22.2.0",
47
+ ngImport: i0,
48
+ type: SparkClientOperationDispatcher,
49
+ decorators: [{
50
+ type: Injectable,
51
+ args: [{ providedIn: "root" }]
52
+ }],
53
+ ctorParameters: () => []
54
+ });
55
+ const DEFAULT_DURATION_MS = 4e3;
56
+ var SparkNotificationService = class SparkNotificationService {
57
+ _toasts = signal([], ...ngDevMode ? [{ debugName: "_toasts" }] : /* istanbul ignore next */ []);
58
+ toasts = this._toasts.asReadonly();
59
+ show(message, kind = NotificationKind.Info, durationMs) {
60
+ const id = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
61
+ const effectiveDuration = durationMs ?? DEFAULT_DURATION_MS;
62
+ const toast = {
63
+ id,
64
+ message,
65
+ kind,
66
+ durationMs: effectiveDuration
67
+ };
68
+ this._toasts.update((toasts) => [...toasts, toast]);
69
+ if (effectiveDuration > 0) setTimeout(() => this.dismiss(id), effectiveDuration);
70
+ }
71
+ dismiss(id) {
72
+ this._toasts.update((toasts) => toasts.filter((t) => t.id !== id));
73
+ }
74
+ clear() {
75
+ this._toasts.set([]);
76
+ }
77
+ static ɵfac = i0.ɵɵngDeclareFactory({
78
+ minVersion: "12.0.0",
79
+ version: "22.2.0",
80
+ ngImport: i0,
81
+ type: SparkNotificationService,
82
+ deps: [],
83
+ target: i0.ɵɵFactoryTarget.Injectable
84
+ });
85
+ static ɵprov = i0.ɵɵngDeclareInjectable({
86
+ minVersion: "12.0.0",
87
+ version: "22.2.0",
88
+ ngImport: i0,
89
+ type: SparkNotificationService,
90
+ providedIn: "root"
91
+ });
92
+ };
93
+ i0.ɵɵngDeclareClassMetadata({
94
+ minVersion: "12.0.0",
95
+ version: "22.2.0",
96
+ ngImport: i0,
97
+ type: SparkNotificationService,
98
+ decorators: [{
99
+ type: Injectable,
100
+ args: [{ providedIn: "root" }]
101
+ }]
102
+ });
103
+ var SparkToastContainerComponent = class SparkToastContainerComponent {
104
+ notifications = inject(SparkNotificationService);
105
+ Kind = NotificationKind;
106
+ static ɵfac = i0.ɵɵngDeclareFactory({
107
+ minVersion: "12.0.0",
108
+ version: "22.2.0",
109
+ ngImport: i0,
110
+ type: SparkToastContainerComponent,
111
+ deps: [],
112
+ target: i0.ɵɵFactoryTarget.Component
113
+ });
114
+ static ɵcmp = i0.ɵɵngDeclareComponent({
115
+ minVersion: "17.0.0",
116
+ version: "22.2.0",
117
+ type: SparkToastContainerComponent,
118
+ isStandalone: true,
119
+ selector: "spark-toast-container",
120
+ ngImport: i0,
121
+ template: `
113
122
  <div class="spark-toast-container">
114
123
  @for (toast of notifications.toasts(); track toast.id) {
115
124
  <div
@@ -124,11 +133,23 @@ class SparkToastContainerComponent {
124
133
  </div>
125
134
  }
126
135
  </div>
127
- `, isInline: true, styles: [".spark-toast-container{position:fixed;top:1rem;right:1rem;z-index:9999;display:flex;flex-direction:column;gap:.5rem;pointer-events:none}.spark-toast{padding:.75rem 1rem;border-radius:4px;box-shadow:0 2px 8px #00000026;cursor:pointer;min-width:220px;max-width:400px;color:#fff;font-size:.95rem;pointer-events:auto;animation:spark-toast-in .18s ease-out}.spark-toast--info{background:#0d6efd}.spark-toast--success{background:#198754}.spark-toast--warning{background:#ffc107;color:#000}.spark-toast--error{background:#dc3545}@keyframes spark-toast-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
128
- }
129
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkToastContainerComponent, decorators: [{
130
- type: Component,
131
- args: [{ selector: 'spark-toast-container', standalone: true, template: `
136
+ `,
137
+ isInline: true,
138
+ styles: [".spark-toast-container{position:fixed;top:1rem;right:1rem;z-index:9999;display:flex;flex-direction:column;gap:.5rem;pointer-events:none}.spark-toast{padding:.75rem 1rem;border-radius:4px;box-shadow:0 2px 8px #00000026;cursor:pointer;min-width:220px;max-width:400px;color:var(--bs-white);font-size:.95rem;pointer-events:auto;animation:spark-toast-in .18s ease-out}.spark-toast--info{background:var(--bs-primary)}.spark-toast--success{background:var(--bs-success)}.spark-toast--warning{background:var(--bs-warning);color:var(--bs-dark)}.spark-toast--error{background:var(--bs-danger)}@keyframes spark-toast-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}\n"],
139
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
140
+ });
141
+ };
142
+ i0.ɵɵngDeclareClassMetadata({
143
+ minVersion: "12.0.0",
144
+ version: "22.2.0",
145
+ ngImport: i0,
146
+ type: SparkToastContainerComponent,
147
+ decorators: [{
148
+ type: Component,
149
+ args: [{
150
+ selector: "spark-toast-container",
151
+ standalone: true,
152
+ template: `
132
153
  <div class="spark-toast-container">
133
154
  @for (toast of notifications.toasts(); track toast.id) {
134
155
  <div
@@ -143,220 +164,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
143
164
  </div>
144
165
  }
145
166
  </div>
146
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".spark-toast-container{position:fixed;top:1rem;right:1rem;z-index:9999;display:flex;flex-direction:column;gap:.5rem;pointer-events:none}.spark-toast{padding:.75rem 1rem;border-radius:4px;box-shadow:0 2px 8px #00000026;cursor:pointer;min-width:220px;max-width:400px;color:#fff;font-size:.95rem;pointer-events:auto;animation:spark-toast-in .18s ease-out}.spark-toast--info{background:#0d6efd}.spark-toast--success{background:#198754}.spark-toast--warning{background:#ffc107;color:#000}.spark-toast--error{background:#dc3545}@keyframes spark-toast-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}\n"] }]
147
- }] });
148
-
149
- class SparkAttributeRefreshService {
150
- tokens = signal({}, /* @ts-ignore */
151
- ...(ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []));
152
- patches = signal({}, /* @ts-ignore */
153
- ...(ngDevMode ? [{ debugName: "patches" }] : /* istanbul ignore next */ []));
154
- /**
155
- * Objects are keyed by type AND id: the same id can exist under two types, and a detail page
156
- * only ever wants the attributes meant for the object it is showing.
157
- */
158
- static keyFor(objectTypeId, id) {
159
- return `${objectTypeId}${id}`;
160
- }
161
- /** Bumped every time the server patches an attribute of this object. */
162
- tokenFor(objectTypeId, id) {
163
- if (!objectTypeId || !id)
164
- return 0;
165
- return this.tokens()[SparkAttributeRefreshService.keyFor(objectTypeId, id)] ?? 0;
166
- }
167
- /**
168
- * The newest value the server sent for each patched attribute of this object.
169
- *
170
- * Cumulative rather than drained on read: a detail page that mounts after the operation arrived
171
- * still sees the value, and two pages showing the same object both get it. Values are overwritten
172
- * by later patches, so this holds one entry per attribute, not a history.
173
- */
174
- patchesFor(objectTypeId, id) {
175
- if (!objectTypeId || !id)
176
- return {};
177
- return this.patches()[SparkAttributeRefreshService.keyFor(objectTypeId, id)] ?? {};
178
- }
179
- /**
180
- * Record a new value for one attribute and wake whoever is showing the object.
181
- *
182
- * Callers patching several attributes for one user action should do so in one synchronous run:
183
- * the signals coalesce within a tick into a single effect run, and patches split across an await
184
- * become one run each.
185
- */
186
- request(objectTypeId, id, attributeName, patch) {
187
- const key = SparkAttributeRefreshService.keyFor(objectTypeId, id);
188
- this.patches.update(current => ({
189
- ...current,
190
- [key]: { ...(current[key] ?? {}), [attributeName]: patch },
191
- }));
192
- this.tokens.update(current => ({ ...current, [key]: (current[key] ?? 0) + 1 }));
193
- }
194
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkAttributeRefreshService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
195
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkAttributeRefreshService, providedIn: 'root' });
196
- }
197
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkAttributeRefreshService, decorators: [{
198
- type: Injectable,
199
- args: [{ providedIn: 'root' }]
200
- }] });
201
-
202
- /**
203
- * Carries a server-issued `refreshQuery` to whichever grids are showing that query.
204
- *
205
- * A broadcast signal rather than a registry of component handles: grids come and go behind
206
- * `@if` and lazy routes, and nothing else in ng-spark holds a component reference. A grid
207
- * reads {@link tokenFor} in an effect and re-fetches when it changes, which is the same
208
- * declarative shape as the `reloadToken` input a host would use.
209
- *
210
- * Until this existed the server could emit the operation and the dispatcher dropped it: only
211
- * `notify` was registered, and unknown types are ignored silently — so `refreshOnCompleted`
212
- * on the server had no effect on any grid the action did not happen to be hosted in.
213
- */
214
- class SparkQueryRefreshService {
215
- tokens = signal({}, /* @ts-ignore */
216
- ...(ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []));
217
- /** Bumped every time the server asks for this query to refresh. */
218
- tokenFor(queryId) {
219
- if (!queryId)
220
- return 0;
221
- return this.tokens()[queryId] ?? 0;
222
- }
223
- /**
224
- * Ask every grid showing `queryId` to re-fetch.
225
- *
226
- * Matched on the EXACT string a grid passes to {@link tokenFor} -- whichever of id or alias
227
- * its `queryId` input holds. A caller that knows only the other form will not reach it.
228
- *
229
- * Callers bumping several keys for one user action should do so in one synchronous run: the
230
- * signal coalesces bumps within a tick into a single effect run, and bumps split across an
231
- * await become one re-fetch each.
232
- */
233
- request(queryId) {
234
- this.tokens.update(current => ({ ...current, [queryId]: (current[queryId] ?? 0) + 1 }));
235
- }
236
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
237
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, providedIn: 'root' });
238
- }
239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, decorators: [{
240
- type: Injectable,
241
- args: [{ providedIn: 'root' }]
242
- }] });
243
-
244
- /**
245
- * Registers the built-in client-operation handlers: `notify`, `refreshQuery`,
246
- * `refreshAttribute` and `navigate`. Apps add this once in their bootstrap providers.
247
- *
248
- * Unregistered operation types are dropped SILENTLY by the dispatcher, which is why
249
- * `refreshQuery` did nothing at all for as long as it went unhandled — the server emitted
250
- * it, nothing listened, and no error said so.
251
- *
252
- * That happened twice more before anyone noticed. `refreshAttribute` and `navigate` were both
253
- * declared wire types with no handler anywhere in the repository: every
254
- * `IClientAccessor.RefreshAttribute` and `Navigate` call in every Spark application was
255
- * computed, serialised, sent and discarded. Both are registered below. The remaining gap is
256
- * `disableAction`, registered purely to log so that it stays visible rather than invisible.
257
- *
258
- * The lesson worth keeping: a wire type in `operations.ts` is not a feature. Adding one without
259
- * a handler here produces a server API that appears to work and does nothing.
260
- *
261
- * To register custom operation types alongside the built-ins, add additional
262
- * `multi: true` providers using <see cref="SPARK_CLIENT_OPERATION_HANDLERS" />.
263
- */
167
+ `,
168
+ changeDetection: ChangeDetectionStrategy.OnPush,
169
+ styles: [".spark-toast-container{position:fixed;top:1rem;right:1rem;z-index:9999;display:flex;flex-direction:column;gap:.5rem;pointer-events:none}.spark-toast{padding:.75rem 1rem;border-radius:4px;box-shadow:0 2px 8px #00000026;cursor:pointer;min-width:220px;max-width:400px;color:var(--bs-white);font-size:.95rem;pointer-events:auto;animation:spark-toast-in .18s ease-out}.spark-toast--info{background:var(--bs-primary)}.spark-toast--success{background:var(--bs-success)}.spark-toast--warning{background:var(--bs-warning);color:var(--bs-dark)}.spark-toast--error{background:var(--bs-danger)}@keyframes spark-toast-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}\n"]
170
+ }]
171
+ }]
172
+ });
173
+ var SparkAttributeRefreshService = class SparkAttributeRefreshService {
174
+ tokens = signal({}, ...ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []);
175
+ patches = signal({}, ...ngDevMode ? [{ debugName: "patches" }] : /* istanbul ignore next */ []);
176
+ static keyFor(objectTypeId, id) {
177
+ return `${objectTypeId}${id}`;
178
+ }
179
+ tokenFor(objectTypeId, id) {
180
+ if (!objectTypeId || !id) return 0;
181
+ return this.tokens()[SparkAttributeRefreshService.keyFor(objectTypeId, id)] ?? 0;
182
+ }
183
+ patchesFor(objectTypeId, id) {
184
+ if (!objectTypeId || !id) return {};
185
+ return this.patches()[SparkAttributeRefreshService.keyFor(objectTypeId, id)] ?? {};
186
+ }
187
+ request(objectTypeId, id, attributeName, patch) {
188
+ const key = SparkAttributeRefreshService.keyFor(objectTypeId, id);
189
+ this.patches.update((current) => ({
190
+ ...current,
191
+ [key]: {
192
+ ...current[key] ?? {},
193
+ [attributeName]: patch
194
+ }
195
+ }));
196
+ this.tokens.update((current) => ({
197
+ ...current,
198
+ [key]: (current[key] ?? 0) + 1
199
+ }));
200
+ }
201
+ static ɵfac = i0.ɵɵngDeclareFactory({
202
+ minVersion: "12.0.0",
203
+ version: "22.2.0",
204
+ ngImport: i0,
205
+ type: SparkAttributeRefreshService,
206
+ deps: [],
207
+ target: i0.ɵɵFactoryTarget.Injectable
208
+ });
209
+ static ɵprov = i0.ɵɵngDeclareInjectable({
210
+ minVersion: "12.0.0",
211
+ version: "22.2.0",
212
+ ngImport: i0,
213
+ type: SparkAttributeRefreshService,
214
+ providedIn: "root"
215
+ });
216
+ };
217
+ i0.ɵɵngDeclareClassMetadata({
218
+ minVersion: "12.0.0",
219
+ version: "22.2.0",
220
+ ngImport: i0,
221
+ type: SparkAttributeRefreshService,
222
+ decorators: [{
223
+ type: Injectable,
224
+ args: [{ providedIn: "root" }]
225
+ }]
226
+ });
227
+ var SparkQueryRefreshService = class SparkQueryRefreshService {
228
+ tokens = signal({}, ...ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []);
229
+ tokenFor(queryId) {
230
+ if (!queryId) return 0;
231
+ return this.tokens()[queryId] ?? 0;
232
+ }
233
+ request(queryId) {
234
+ this.tokens.update((current) => ({
235
+ ...current,
236
+ [queryId]: (current[queryId] ?? 0) + 1
237
+ }));
238
+ }
239
+ static ɵfac = i0.ɵɵngDeclareFactory({
240
+ minVersion: "12.0.0",
241
+ version: "22.2.0",
242
+ ngImport: i0,
243
+ type: SparkQueryRefreshService,
244
+ deps: [],
245
+ target: i0.ɵɵFactoryTarget.Injectable
246
+ });
247
+ static ɵprov = i0.ɵɵngDeclareInjectable({
248
+ minVersion: "12.0.0",
249
+ version: "22.2.0",
250
+ ngImport: i0,
251
+ type: SparkQueryRefreshService,
252
+ providedIn: "root"
253
+ });
254
+ };
255
+ i0.ɵɵngDeclareClassMetadata({
256
+ minVersion: "12.0.0",
257
+ version: "22.2.0",
258
+ ngImport: i0,
259
+ type: SparkQueryRefreshService,
260
+ decorators: [{
261
+ type: Injectable,
262
+ args: [{ providedIn: "root" }]
263
+ }]
264
+ });
264
265
  function provideSparkClientOperations() {
265
- return makeEnvironmentProviders([
266
- {
267
- provide: SPARK_CLIENT_OPERATION_HANDLERS,
268
- useFactory: () => {
269
- const notifications = inject(SparkNotificationService);
270
- return {
271
- type: 'notify',
272
- handler: (operation) => {
273
- const notify = operation;
274
- notifications.show(notify.message, notify.kind, notify.durationMs);
275
- },
276
- };
277
- },
278
- multi: true,
279
- },
280
- {
281
- provide: SPARK_CLIENT_OPERATION_HANDLERS,
282
- useFactory: () => {
283
- const refresh = inject(SparkQueryRefreshService);
284
- return {
285
- type: 'refreshQuery',
286
- handler: (operation) => {
287
- refresh.request(operation.queryId);
288
- },
289
- };
290
- },
291
- multi: true,
292
- },
293
- {
294
- provide: SPARK_CLIENT_OPERATION_HANDLERS,
295
- useFactory: () => {
296
- const attributes = inject(SparkAttributeRefreshService);
297
- return {
298
- type: 'refreshAttribute',
299
- handler: (operation) => {
300
- const patch = operation;
301
- // Forwarded field by field, not as one value: an AsDetail attribute's rows
302
- // travel in object/objects and its value is null, so collapsing them here
303
- // is what made a detail-grid refresh impossible to express.
304
- attributes.request(patch.objectTypeId, patch.id, patch.attributeName, {
305
- value: patch.value,
306
- object: patch.object,
307
- objects: patch.objects,
308
- });
309
- },
310
- };
311
- },
312
- multi: true,
313
- },
314
- {
315
- provide: SPARK_CLIENT_OPERATION_HANDLERS,
316
- useFactory: () => {
317
- const router = inject(Router);
318
- return {
319
- type: 'navigate',
320
- handler: (operation) => {
321
- const navigate = operation;
322
- if (navigate.routeName) {
323
- void router.navigateByUrl(navigate.routeName.startsWith('/') ? navigate.routeName : `/${navigate.routeName}`);
324
- return;
325
- }
326
- // `/po/{type}/{id}` is the convention the grid's row links already assume,
327
- // and the detail route resolves a type by id as well as by alias, so the
328
- // operation's objectTypeId can be used verbatim.
329
- if (navigate.objectTypeId && navigate.id) {
330
- void router.navigate(['/po', navigate.objectTypeId, navigate.id]);
331
- return;
332
- }
333
- console.warn('[spark] navigate operation carried neither routeName nor objectTypeId+id; ignored.');
334
- },
335
- };
336
- },
337
- multi: true,
338
- },
339
- {
340
- provide: SPARK_CLIENT_OPERATION_HANDLERS,
341
- useFactory: () => ({
342
- type: 'disableAction',
343
- handler: (operation) => {
344
- // Deliberately a no-op with a warning, not silence. The server's
345
- // IClientAccessor.DisableQueryActions presumes a client that honours it;
346
- // nothing renders the disabled state yet, and a silently dropped operation
347
- // reads as "the server did not send it" when debugging.
348
- const disable = operation;
349
- console.warn(`[spark] disableAction('${disable.actionName}') is not implemented by this client; the action stays enabled.`);
350
- },
351
- }),
352
- multi: true,
353
- },
354
- ]);
266
+ return makeEnvironmentProviders([
267
+ {
268
+ provide: SPARK_CLIENT_OPERATION_HANDLERS,
269
+ useFactory: () => {
270
+ const notifications = inject(SparkNotificationService);
271
+ return {
272
+ type: "notify",
273
+ handler: (operation) => {
274
+ const notify = operation;
275
+ const message = notify.translatedMessage && resolveTranslation(notify.translatedMessage) || notify.message;
276
+ notifications.show(message, notify.kind, notify.durationMs);
277
+ }
278
+ };
279
+ },
280
+ multi: true
281
+ },
282
+ {
283
+ provide: SPARK_CLIENT_OPERATION_HANDLERS,
284
+ useFactory: () => {
285
+ const refresh = inject(SparkQueryRefreshService);
286
+ return {
287
+ type: "refreshQuery",
288
+ handler: (operation) => {
289
+ refresh.request(operation.queryId);
290
+ }
291
+ };
292
+ },
293
+ multi: true
294
+ },
295
+ {
296
+ provide: SPARK_CLIENT_OPERATION_HANDLERS,
297
+ useFactory: () => {
298
+ const attributes = inject(SparkAttributeRefreshService);
299
+ return {
300
+ type: "refreshAttribute",
301
+ handler: (operation) => {
302
+ const patch = operation;
303
+ attributes.request(patch.objectTypeId, patch.id, patch.attributeName, {
304
+ value: patch.value,
305
+ object: patch.object,
306
+ objects: patch.objects
307
+ });
308
+ }
309
+ };
310
+ },
311
+ multi: true
312
+ },
313
+ {
314
+ provide: SPARK_CLIENT_OPERATION_HANDLERS,
315
+ useFactory: () => {
316
+ const router = inject(Router);
317
+ return {
318
+ type: "navigate",
319
+ handler: (operation) => {
320
+ const navigate = operation;
321
+ if (navigate.routeName) {
322
+ router.navigateByUrl(navigate.routeName.startsWith("/") ? navigate.routeName : `/${navigate.routeName}`);
323
+ return;
324
+ }
325
+ if (navigate.objectTypeId && navigate.id) {
326
+ router.navigate([
327
+ "/po",
328
+ navigate.objectTypeId,
329
+ navigate.id
330
+ ]);
331
+ return;
332
+ }
333
+ console.warn("[spark] navigate operation carried neither routeName nor objectTypeId+id; ignored.");
334
+ }
335
+ };
336
+ },
337
+ multi: true
338
+ }
339
+ ]);
355
340
  }
356
-
357
- /**
358
- * Generated bundle index. Do not edit.
359
- */
360
-
361
341
  export { NotificationKind, SPARK_CLIENT_OPERATION_HANDLERS, SparkAttributeRefreshService, SparkClientOperationDispatcher, SparkNotificationService, SparkQueryRefreshService, SparkToastContainerComponent, provideSparkClientOperations };
362
- //# sourceMappingURL=mintplayer-ng-spark-client-operations.mjs.map
342
+
343
+ //# sourceMappingURL=mintplayer-ng-spark-client-operations.mjs.map