@mintplayer/ng-spark 22.4.0 → 22.6.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.
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs +12 -12
- package/fesm2022/mintplayer-ng-spark-grid.mjs +21 -21
- package/fesm2022/mintplayer-ng-spark-icon.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-models.mjs +185 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +82 -70
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +356 -18
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +27 -15
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs +379 -0
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark.mjs +17 -1
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +5 -1
- package/types/mintplayer-ng-spark-models.d.ts +106 -2
- package/types/mintplayer-ng-spark-pipes.d.ts +9 -1
- package/types/mintplayer-ng-spark-po-form.d.ts +132 -4
- package/types/mintplayer-ng-spark-services.d.ts +10 -0
- package/types/mintplayer-ng-spark-shell.d.ts +242 -0
- package/types/mintplayer-ng-spark.d.ts +17 -2
|
@@ -61,10 +61,10 @@ class SparkClientOperationDispatcher {
|
|
|
61
61
|
// Unknown types: silently dropped (forward-compat).
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
65
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
64
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
65
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, providedIn: 'root' });
|
|
66
66
|
}
|
|
67
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
67
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkClientOperationDispatcher, decorators: [{
|
|
68
68
|
type: Injectable,
|
|
69
69
|
args: [{ providedIn: 'root' }]
|
|
70
70
|
}], ctorParameters: () => [] });
|
|
@@ -96,10 +96,10 @@ class SparkNotificationService {
|
|
|
96
96
|
clear() {
|
|
97
97
|
this._toasts.set([]);
|
|
98
98
|
}
|
|
99
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
100
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
99
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
100
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, providedIn: 'root' });
|
|
101
101
|
}
|
|
102
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkNotificationService, decorators: [{
|
|
103
103
|
type: Injectable,
|
|
104
104
|
args: [{ providedIn: 'root' }]
|
|
105
105
|
}] });
|
|
@@ -107,8 +107,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
|
|
|
107
107
|
class SparkToastContainerComponent {
|
|
108
108
|
notifications = inject(SparkNotificationService);
|
|
109
109
|
Kind = NotificationKind;
|
|
110
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
111
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
110
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
111
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkToastContainerComponent, isStandalone: true, selector: "spark-toast-container", ngImport: i0, template: `
|
|
112
112
|
<div class="spark-toast-container">
|
|
113
113
|
@for (toast of notifications.toasts(); track toast.id) {
|
|
114
114
|
<div
|
|
@@ -125,7 +125,7 @@ class SparkToastContainerComponent {
|
|
|
125
125
|
</div>
|
|
126
126
|
`, 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 });
|
|
127
127
|
}
|
|
128
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
128
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkToastContainerComponent, decorators: [{
|
|
129
129
|
type: Component,
|
|
130
130
|
args: [{ selector: 'spark-toast-container', standalone: true, template: `
|
|
131
131
|
<div class="spark-toast-container">
|
|
@@ -179,10 +179,10 @@ class SparkQueryRefreshService {
|
|
|
179
179
|
request(queryId) {
|
|
180
180
|
this.tokens.update(current => ({ ...current, [queryId]: (current[queryId] ?? 0) + 1 }));
|
|
181
181
|
}
|
|
182
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
183
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
182
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
183
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, providedIn: 'root' });
|
|
184
184
|
}
|
|
185
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryRefreshService, decorators: [{
|
|
186
186
|
type: Injectable,
|
|
187
187
|
args: [{ providedIn: 'root' }]
|
|
188
188
|
}] });
|
|
@@ -118,10 +118,10 @@ class SparkGridRenderers {
|
|
|
118
118
|
const entries = await Promise.all(names.map(async (name) => [name, await this.sparkService.getLookupReference(name)]));
|
|
119
119
|
return entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
|
|
120
120
|
}
|
|
121
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
122
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
121
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkGridRenderers, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
122
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkGridRenderers, providedIn: 'root' });
|
|
123
123
|
}
|
|
124
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
124
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkGridRenderers, decorators: [{
|
|
125
125
|
type: Injectable,
|
|
126
126
|
args: [{ providedIn: 'root' }]
|
|
127
127
|
}] });
|
|
@@ -191,10 +191,10 @@ class SparkQueryIconDirective {
|
|
|
191
191
|
static ngTemplateContextGuard(_dir, ctx) {
|
|
192
192
|
return true;
|
|
193
193
|
}
|
|
194
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
195
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
194
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
195
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: SparkQueryIconDirective, isStandalone: true, selector: "[sparkQueryIcon]", inputs: { forQuery: { classPropertyName: "forQuery", publicName: "sparkQueryIcon", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
196
196
|
}
|
|
197
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
197
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryIconDirective, decorators: [{
|
|
198
198
|
type: Directive,
|
|
199
199
|
args: [{ selector: '[sparkQueryIcon]' }]
|
|
200
200
|
}], propDecorators: { forQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkQueryIcon", required: false }] }] } });
|
|
@@ -210,10 +210,10 @@ class SparkQueryCaptionDirective {
|
|
|
210
210
|
static ngTemplateContextGuard(_dir, ctx) {
|
|
211
211
|
return true;
|
|
212
212
|
}
|
|
213
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
214
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
213
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryCaptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
214
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: SparkQueryCaptionDirective, isStandalone: true, selector: "[sparkQueryCaption]", inputs: { forQuery: { classPropertyName: "forQuery", publicName: "sparkQueryCaption", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
215
215
|
}
|
|
216
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryCaptionDirective, decorators: [{
|
|
217
217
|
type: Directive,
|
|
218
218
|
args: [{ selector: '[sparkQueryCaption]' }]
|
|
219
219
|
}], propDecorators: { forQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkQueryCaption", required: false }] }] } });
|
|
@@ -237,10 +237,10 @@ class SparkQueryActionsDirective {
|
|
|
237
237
|
static ngTemplateContextGuard(_dir, ctx) {
|
|
238
238
|
return true;
|
|
239
239
|
}
|
|
240
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
241
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
240
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryActionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
241
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: SparkQueryActionsDirective, isStandalone: true, selector: "[sparkQueryActions]", inputs: { forQuery: { classPropertyName: "forQuery", publicName: "sparkQueryActions", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
242
242
|
}
|
|
243
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
243
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryActionsDirective, decorators: [{
|
|
244
244
|
type: Directive,
|
|
245
245
|
args: [{ selector: '[sparkQueryActions]' }]
|
|
246
246
|
}], propDecorators: { forQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkQueryActions", required: false }] }] } });
|
|
@@ -337,10 +337,10 @@ class SparkGridCellComponent {
|
|
|
337
337
|
/** Null and undefined are distinct from `false` here — see the template. */
|
|
338
338
|
isUnsetBoolean = computed(() => this.display() == null, /* @ts-ignore */
|
|
339
339
|
...(ngDevMode ? [{ debugName: "isUnsetBoolean" }] : /* istanbul ignore next */ []));
|
|
340
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
341
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
340
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
341
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkGridCellComponent, isStandalone: true, selector: "spark-grid-cell", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, rendererValue: { classPropertyName: "rendererValue", publicName: "rendererValue", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, chips: { classPropertyName: "chips", publicName: "chips", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (rendererComponent(); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: rendererInputs()\"></ng-container>\n} @else if (column().dataType === 'boolean') {\n <!-- [indeterminate] matters: without it a null boolean renders as an unchecked box, which reads\n as an explicit `false`. One of the three copies of this cell bound it and the others had\n drifted, so the same column said different things on a detail page and in a grid. -->\n <input type=\"checkbox\"\n [checked]=\"display() === true\"\n [indeterminate]=\"isUnsetBoolean()\"\n disabled\n onclick=\"return false;\">\n} @else if (column().dataType === 'color') {\n @if (display(); as colorVal) {\n <!-- Inline on purpose, and it is not a style-guide slip. In a query grid this cell is\n rendered INSIDE <mp-datatable>'s shadow root, which does not see document\n stylesheets: neither this component's scoped rules nor Bootstrap utilities apply\n there. Inline styles do. Measured 2026-08-23 -- see the comment in the .scss. -->\n <span class=\"d-inline-block align-middle border rounded\"\n [style.background-color]=\"colorVal\"\n style=\"width: 1.5em; height: 1.5em;\"></span>\n }\n} @else if (chips().length) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of chips(); track chip.id) {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n </span>\n} @else if (link(); as route) {\n <a [routerLink]=\"route\">{{ display() }}</a>\n} @else {\n {{ display() }}\n}\n", styles: [".spark-chip{display:inline-block;padding:.35em .9em;font-size:.8125em;font-weight:600;line-height:1.4;white-space:nowrap;text-align:center;vertical-align:baseline;color:var(--bs-body-color);background-color:var(--bs-secondary-bg);border:1px solid var(--bs-border-color);border-radius:50rem;text-decoration:none}a.spark-chip:hover,a.spark-chip:focus-visible{background-color:var(--bs-tertiary-bg)}.spark-color-swatch{width:1.5em;height:1.5em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
342
342
|
}
|
|
343
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
343
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkGridCellComponent, decorators: [{
|
|
344
344
|
type: Component,
|
|
345
345
|
args: [{ selector: 'spark-grid-cell', imports: [CommonModule, NgComponentOutlet, RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (rendererComponent(); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: rendererInputs()\"></ng-container>\n} @else if (column().dataType === 'boolean') {\n <!-- [indeterminate] matters: without it a null boolean renders as an unchecked box, which reads\n as an explicit `false`. One of the three copies of this cell bound it and the others had\n drifted, so the same column said different things on a detail page and in a grid. -->\n <input type=\"checkbox\"\n [checked]=\"display() === true\"\n [indeterminate]=\"isUnsetBoolean()\"\n disabled\n onclick=\"return false;\">\n} @else if (column().dataType === 'color') {\n @if (display(); as colorVal) {\n <!-- Inline on purpose, and it is not a style-guide slip. In a query grid this cell is\n rendered INSIDE <mp-datatable>'s shadow root, which does not see document\n stylesheets: neither this component's scoped rules nor Bootstrap utilities apply\n there. Inline styles do. Measured 2026-08-23 -- see the comment in the .scss. -->\n <span class=\"d-inline-block align-middle border rounded\"\n [style.background-color]=\"colorVal\"\n style=\"width: 1.5em; height: 1.5em;\"></span>\n }\n} @else if (chips().length) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of chips(); track chip.id) {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n </span>\n} @else if (link(); as route) {\n <a [routerLink]=\"route\">{{ display() }}</a>\n} @else {\n {{ display() }}\n}\n", styles: [".spark-chip{display:inline-block;padding:.35em .9em;font-size:.8125em;font-weight:600;line-height:1.4;white-space:nowrap;text-align:center;vertical-align:baseline;color:var(--bs-body-color);background-color:var(--bs-secondary-bg);border:1px solid var(--bs-border-color);border-radius:50rem;text-decoration:none}a.spark-chip:hover,a.spark-chip:focus-visible{background-color:var(--bs-tertiary-bg)}.spark-color-swatch{width:1.5em;height:1.5em}\n"] }]
|
|
346
346
|
}], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], rendererValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "rendererValue", required: false }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: false }] }], chips: [{ type: i0.Input, args: [{ isSignal: true, alias: "chips", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
|
|
@@ -719,10 +719,10 @@ class SparkQueryGridComponent {
|
|
|
719
719
|
getColumnRendererInputs(component, item, attr) {
|
|
720
720
|
return this.gridRenderers.columnInputsFor(component, item, attr);
|
|
721
721
|
}
|
|
722
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
723
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
722
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
723
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkQueryGridComponent, isStandalone: true, selector: "spark-query-grid", inputs: { queryId: { classPropertyName: "queryId", publicName: "queryId", isSignal: true, isRequired: true, transformFunction: null }, parentId: { classPropertyName: "parentId", publicName: "parentId", isSignal: true, isRequired: false, transformFunction: null }, parentType: { classPropertyName: "parentType", publicName: "parentType", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, reloadToken: { classPropertyName: "reloadToken", publicName: "reloadToken", isSignal: true, isRequired: false, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { settings: "settingsChange", selection: "selectionChange", error: "error", rowClicked: "rowClicked", customActionExecuted: "customActionExecuted" }, ngImport: i0, template: "<!--\n Three explicit states, in this order, and the order is load-bearing.\n\n This template used to open with `@if (query(); as q)` wrapping EVERYTHING, and `query()` is only\n set after loadData's awaits resolve. Three separate bugs fell out of that one line: the spinner\n was unreachable on a first load (it rendered inside the gate, while `loading` starts true and\n `query` starts null), a first-load failure rendered ZERO DOM \u2014 no card, no message, nothing to\n see or report \u2014 and a failed RELOAD left the previous query's rows on screen.\n\n So: `loading` is checked first and outside any dependency on `query`, and the final `@else`\n always renders something. Do not fold these back into one gate, and do not move the spinner\n inside the `query()` branch.\n-->\n@if (loading()) {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n} @else if (query()) {\n <!-- A page-fetch failure while the grid is already resolved. Without this the catch returns an\n empty page and a failed query is indistinguishable from one that legitimately has no rows. -->\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n\n <!--\n One datatable, two row sources. `[data]` and `[fetch]` are mutually exclusive in\n ng-bootstrap \u2014 binding both makes `[fetch]` win silently \u2014 so exactly one is ever non-null\n here: `fetchFn` is left null whenever `data` is bound. See the class comment for why rows can\n arrive from outside at all (streaming must not put a websocket in every detail page's bundle).\n -->\n <bs-datatable\n [selectionMode]=\"selectionMode()\"\n [(selection)]=\"selection\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"data()\"\n [fetch]=\"fetchFn()\"\n [(settings)]=\"settings\">\n @for (attr of visibleAttributes(); track attr.id) {\n <div *bsDatatableColumn=\"attr.name; sortable: true\">\n {{ (attr.label | resolveTranslation) || attr.name }}\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @let row = $any(item);\n @for (attr of visibleAttributes(); track attr.id; let first = $first) {\n <td>\n @if (row) {\n <!-- The first column links to the row's detail page. Two things about this are\n routinely misread:\n\n 1. `cellContent` is projected INSIDE the anchor, so a custom `renderer` does NOT\n suppress this link \u2014 a renderer that emits its own <a> produces nested anchors\n (invalid HTML) rather than replacing this one.\n 2. `canRead()` is the whole gate, and it is the rights model: `Query` without\n `Read` lists the rows and withholds the link. Withhold `Read` for a query\n whose rows no detail page can load \u2014 a Custom.* query that fabricates rows in\n memory, say. -->\n @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\"\n (click)=\"rowClicked.emit(row)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, attr: attr }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, attr: attr }\"></ng-container>\n }\n } @else {\n \n }\n </td>\n }\n </ng-container>\n </bs-datatable>\n} @else {\n <!-- The query could not be resolved. Rendering something keeps the host's layout intact and,\n crucially, makes the failure visible: this branch used to render nothing at all. -->\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">\n {{ errorMessage() || ('spark.query.unavailable' | t) }}\n </bs-alert>\n}\n\n<ng-template #cellContent let-item let-attr=\"attr\">\n <!--\n Presentation lives in <spark-grid-cell>, shared with the AsDetail table on the PO detail page.\n Value resolution stays here: a query row is a PersistentObject with an attribute list, and the\n AsDetail table's rows are plain dictionaries, so only the rendering is common.\n\n `link` is deliberately not passed. The first-column link is the grid's own rule and wraps this\n template from outside; setting both would nest anchors.\n -->\n <spark-grid-cell\n [column]=\"attr\"\n [display]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes())\"\n [rendererValue]=\"rendererValueFor(item, attr)\"\n [item]=\"item\"\n [chips]=\"(attr.name | referenceChips:item)\" />\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsDatatableComponent, selector: "bs-datatable", inputs: ["columns", "data", "fetch", "settings", "selectionMode", "selectable", "selection", "rowKey", "resizableColumns", "pagination", "virtualScroll", "itemSize", "virtualBuffer", "isResponsive", "compareWith", "tree", "idKey", "childCountKey", "treeIndent", "expandedIds", "selectionStrategy"], outputs: ["settingsChange", "selectionChange", "rowClick", "rowDblClick", "rowContextMenu", "expandedIdsChange", "rowExpand", "rowCollapse"] }, { kind: "directive", type: BsDatatableColumnDirective, selector: "[bsDatatableColumn]", inputs: ["bsDatatableColumn", "bsDatatableColumnSortable"] }, { kind: "directive", type: BsRowTemplateDirective, selector: "[bsRowTemplate]" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "component", type: SparkGridCellComponent, selector: "spark-grid-cell", inputs: ["column", "display", "rendererValue", "item", "chips", "link"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: AttributeValuePipe, name: "attributeValue" }, { kind: "pipe", type: ReferenceChipsPipe, name: "referenceChips" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
724
724
|
}
|
|
725
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
725
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryGridComponent, decorators: [{
|
|
726
726
|
type: Component,
|
|
727
727
|
args: [{ selector: 'spark-query-grid', imports: [CommonModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsSpinnerComponent, SparkGridCellComponent, ResolveTranslationPipe, AttributeValuePipe, ReferenceChipsPipe, TranslateKeyPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Three explicit states, in this order, and the order is load-bearing.\n\n This template used to open with `@if (query(); as q)` wrapping EVERYTHING, and `query()` is only\n set after loadData's awaits resolve. Three separate bugs fell out of that one line: the spinner\n was unreachable on a first load (it rendered inside the gate, while `loading` starts true and\n `query` starts null), a first-load failure rendered ZERO DOM \u2014 no card, no message, nothing to\n see or report \u2014 and a failed RELOAD left the previous query's rows on screen.\n\n So: `loading` is checked first and outside any dependency on `query`, and the final `@else`\n always renders something. Do not fold these back into one gate, and do not move the spinner\n inside the `query()` branch.\n-->\n@if (loading()) {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n} @else if (query()) {\n <!-- A page-fetch failure while the grid is already resolved. Without this the catch returns an\n empty page and a failed query is indistinguishable from one that legitimately has no rows. -->\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n\n <!--\n One datatable, two row sources. `[data]` and `[fetch]` are mutually exclusive in\n ng-bootstrap \u2014 binding both makes `[fetch]` win silently \u2014 so exactly one is ever non-null\n here: `fetchFn` is left null whenever `data` is bound. See the class comment for why rows can\n arrive from outside at all (streaming must not put a websocket in every detail page's bundle).\n -->\n <bs-datatable\n [selectionMode]=\"selectionMode()\"\n [(selection)]=\"selection\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"data()\"\n [fetch]=\"fetchFn()\"\n [(settings)]=\"settings\">\n @for (attr of visibleAttributes(); track attr.id) {\n <div *bsDatatableColumn=\"attr.name; sortable: true\">\n {{ (attr.label | resolveTranslation) || attr.name }}\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @let row = $any(item);\n @for (attr of visibleAttributes(); track attr.id; let first = $first) {\n <td>\n @if (row) {\n <!-- The first column links to the row's detail page. Two things about this are\n routinely misread:\n\n 1. `cellContent` is projected INSIDE the anchor, so a custom `renderer` does NOT\n suppress this link \u2014 a renderer that emits its own <a> produces nested anchors\n (invalid HTML) rather than replacing this one.\n 2. `canRead()` is the whole gate, and it is the rights model: `Query` without\n `Read` lists the rows and withholds the link. Withhold `Read` for a query\n whose rows no detail page can load \u2014 a Custom.* query that fabricates rows in\n memory, say. -->\n @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\"\n (click)=\"rowClicked.emit(row)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, attr: attr }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, attr: attr }\"></ng-container>\n }\n } @else {\n \n }\n </td>\n }\n </ng-container>\n </bs-datatable>\n} @else {\n <!-- The query could not be resolved. Rendering something keeps the host's layout intact and,\n crucially, makes the failure visible: this branch used to render nothing at all. -->\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">\n {{ errorMessage() || ('spark.query.unavailable' | t) }}\n </bs-alert>\n}\n\n<ng-template #cellContent let-item let-attr=\"attr\">\n <!--\n Presentation lives in <spark-grid-cell>, shared with the AsDetail table on the PO detail page.\n Value resolution stays here: a query row is a PersistentObject with an attribute list, and the\n AsDetail table's rows are plain dictionaries, so only the rendering is common.\n\n `link` is deliberately not passed. The first-column link is the grid's own rule and wraps this\n template from outside; setting both would nest anchors.\n -->\n <spark-grid-cell\n [column]=\"attr\"\n [display]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes())\"\n [rendererValue]=\"rendererValueFor(item, attr)\"\n [item]=\"item\"\n [chips]=\"(attr.name | referenceChips:item)\" />\n</ng-template>\n" }]
|
|
728
728
|
}], ctorParameters: () => [], propDecorators: { queryId: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryId", required: true }] }], parentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentId", required: false }] }], parentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentType", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], reloadToken: [{ type: i0.Input, args: [{ isSignal: true, alias: "reloadToken", required: false }] }], settings: [{ type: i0.Input, args: [{ isSignal: true, alias: "settings", required: false }] }, { type: i0.Output, args: ["settingsChange"] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], error: [{ type: i0.Output, args: ["error"] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], customActionExecuted: [{ type: i0.Output, args: ["customActionExecuted"] }] } });
|
|
@@ -844,10 +844,10 @@ class SparkQueryCardComponent {
|
|
|
844
844
|
...(ngDevMode ? [{ debugName: "customActions" }] : /* istanbul ignore next */ []));
|
|
845
845
|
selection = computed(() => this.grid()?.selection() ?? [], /* @ts-ignore */
|
|
846
846
|
...(ngDevMode ? [{ debugName: "selection" }] : /* istanbul ignore next */ []));
|
|
847
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
848
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
847
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
848
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkQueryCardComponent, isStandalone: true, selector: "spark-query-card", inputs: { queryId: { classPropertyName: "queryId", publicName: "queryId", isSignal: true, isRequired: true, transformFunction: null }, parentId: { classPropertyName: "parentId", publicName: "parentId", isSignal: true, isRequired: false, transformFunction: null }, parentType: { classPropertyName: "parentType", publicName: "parentType", isSignal: true, isRequired: false, transformFunction: null }, reloadToken: { classPropertyName: "reloadToken", publicName: "reloadToken", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, iconTemplate: { classPropertyName: "iconTemplate", publicName: "iconTemplate", isSignal: true, isRequired: false, transformFunction: null }, captionTemplate: { classPropertyName: "captionTemplate", publicName: "captionTemplate", isSignal: true, isRequired: false, transformFunction: null }, actionsTemplate: { classPropertyName: "actionsTemplate", publicName: "actionsTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { error: "error", rowClicked: "rowClicked", customActionExecuted: "customActionExecuted" }, queries: [{ propertyName: "iconSlots", predicate: SparkQueryIconDirective, isSignal: true }, { propertyName: "captionSlots", predicate: SparkQueryCaptionDirective, isSignal: true }, { propertyName: "actionSlots", predicate: SparkQueryActionsDirective, isSignal: true }], viewQueries: [{ propertyName: "grid", first: true, predicate: SparkQueryGridComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n The card is always rendered. A host that wants no card uses <spark-query-grid> directly \u2014 which\n is what the old `showCard=\"false\"` meant, expressed as a component choice rather than a flag.\n-->\n<bs-card class=\"d-block my-3\">\n <bs-card-header>\n <div class=\"d-flex align-items-center gap-2\">\n @if (iconTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: query() }\"></ng-container>\n }\n\n <span class=\"me-auto\">\n @if (captionTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: caption(), query: query() }\"></ng-container>\n } @else {\n {{ caption() }}\n }\n </span>\n\n @if (actionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: customActions(), selection: selection(), query: query() }\"></ng-container>\n } @else if (customActions().length) {\n <!-- The default: the actions the server declares for this type. The nav renders only when\n there are any, so a query without actions is pixel-identical to a plain caption.\n\n Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"!grid()?.isActionEnabled(action)\" (click)=\"grid()?.onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n </bs-priority-nav>\n }\n </div>\n </bs-card-header>\n\n <div class=\"p-3\">\n <spark-query-grid\n [queryId]=\"queryId()\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [reloadToken]=\"reloadToken()\"\n [data]=\"data()\"\n [search]=\"search()\"\n (error)=\"error.emit($event)\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n </div>\n</bs-card>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsPriorityNavComponent, selector: "bs-priority-nav", inputs: ["moreLabel", "moreLabelTemplate", "collapseAt", "overflowFrom", "hideEmptyMore", "ariaLabel"], outputs: ["overflowChange"] }, { kind: "directive", type: BsPriorityNavItemDirective, selector: "[bsPriorityNavItem]", inputs: ["bsPriorityNavItem", "bsPriorityNavItemHideBelow"] }, { kind: "component", type: SparkQueryGridComponent, selector: "spark-query-grid", inputs: ["queryId", "parentId", "parentType", "data", "search", "reloadToken", "settings", "selection"], outputs: ["settingsChange", "selectionChange", "error", "rowClicked", "customActionExecuted"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
849
849
|
}
|
|
850
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
850
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryCardComponent, decorators: [{
|
|
851
851
|
type: Component,
|
|
852
852
|
args: [{ selector: 'spark-query-card', imports: [CommonModule, BsCardComponent, BsCardHeaderComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, SparkQueryGridComponent, ResolveTranslationPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n The card is always rendered. A host that wants no card uses <spark-query-grid> directly \u2014 which\n is what the old `showCard=\"false\"` meant, expressed as a component choice rather than a flag.\n-->\n<bs-card class=\"d-block my-3\">\n <bs-card-header>\n <div class=\"d-flex align-items-center gap-2\">\n @if (iconTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: query() }\"></ng-container>\n }\n\n <span class=\"me-auto\">\n @if (captionTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: caption(), query: query() }\"></ng-container>\n } @else {\n {{ caption() }}\n }\n </span>\n\n @if (actionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: customActions(), selection: selection(), query: query() }\"></ng-container>\n } @else if (customActions().length) {\n <!-- The default: the actions the server declares for this type. The nav renders only when\n there are any, so a query without actions is pixel-identical to a plain caption.\n\n Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"!grid()?.isActionEnabled(action)\" (click)=\"grid()?.onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n </bs-priority-nav>\n }\n </div>\n </bs-card-header>\n\n <div class=\"p-3\">\n <spark-query-grid\n [queryId]=\"queryId()\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [reloadToken]=\"reloadToken()\"\n [data]=\"data()\"\n [search]=\"search()\"\n (error)=\"error.emit($event)\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n </div>\n</bs-card>\n" }]
|
|
853
853
|
}], propDecorators: { queryId: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryId", required: true }] }], parentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentId", required: false }] }], parentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentType", required: false }] }], reloadToken: [{ type: i0.Input, args: [{ isSignal: true, alias: "reloadToken", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], iconTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTemplate", required: false }] }], captionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "captionTemplate", required: false }] }], actionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsTemplate", required: false }] }], error: [{ type: i0.Output, args: ["error"] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], customActionExecuted: [{ type: i0.Output, args: ["customActionExecuted"] }], iconSlots: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SparkQueryIconDirective), { isSignal: true }] }], captionSlots: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SparkQueryCaptionDirective), { isSignal: true }] }], actionSlots: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SparkQueryActionsDirective), { isSignal: true }] }], grid: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SparkQueryGridComponent), { isSignal: true }] }] } });
|
|
@@ -10,8 +10,8 @@ class SparkIconComponent {
|
|
|
10
10
|
...(ngDevMode ? [{ debugName: "iconHtml" }] : /* istanbul ignore next */ []));
|
|
11
11
|
cssFallbackClass = computed(() => `bi-${this.name()}`, /* @ts-ignore */
|
|
12
12
|
...(ngDevMode ? [{ debugName: "cssFallbackClass" }] : /* istanbul ignore next */ []));
|
|
13
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
14
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
13
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkIconComponent, isStandalone: true, selector: "spark-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
15
15
|
@if (iconHtml(); as html) {
|
|
16
16
|
<span [innerHTML]="html"></span>
|
|
17
17
|
} @else {
|
|
@@ -19,7 +19,7 @@ class SparkIconComponent {
|
|
|
19
19
|
}
|
|
20
20
|
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center}span{display:inline-flex;align-items:center}span ::ng-deep svg{width:1em;height:1em;fill:currentColor}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
21
21
|
}
|
|
22
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
22
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkIconComponent, decorators: [{
|
|
23
23
|
type: Component,
|
|
24
24
|
args: [{ selector: 'spark-icon', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
25
25
|
@if (iconHtml(); as html) {
|
|
@@ -333,9 +333,193 @@ function selectionModeFor(actions) {
|
|
|
333
333
|
return everyRuleWantsExactlyOne ? 'single' : 'multiple';
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
/** Applies an overlay to one attribute definition, returning a new object when anything changed. */
|
|
337
|
+
function applyOverlay(attr, overlay) {
|
|
338
|
+
if (!overlay)
|
|
339
|
+
return attr;
|
|
340
|
+
return {
|
|
341
|
+
...attr,
|
|
342
|
+
isRequired: overlay.isRequired ?? attr.isRequired,
|
|
343
|
+
isReadOnly: overlay.isReadOnly ?? attr.isReadOnly,
|
|
344
|
+
isVisible: overlay.isVisible ?? attr.isVisible,
|
|
345
|
+
rules: overlay.rules ?? attr.rules,
|
|
346
|
+
query: overlay.query ?? attr.query,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Reads a refresh response into an overlay.
|
|
351
|
+
*
|
|
352
|
+
* Everything here is presentation the server owns outright, so it is taken verbatim — there is no
|
|
353
|
+
* merging to do on this half, only on values.
|
|
354
|
+
*/
|
|
355
|
+
function overlayFromResponse(response) {
|
|
356
|
+
const overlay = {};
|
|
357
|
+
for (const attr of response.attributes ?? []) {
|
|
358
|
+
overlay[attr.name] = {
|
|
359
|
+
isRequired: attr.isRequired,
|
|
360
|
+
isReadOnly: attr.isReadOnly,
|
|
361
|
+
isVisible: attr.isVisible,
|
|
362
|
+
rules: attr.rules ?? [],
|
|
363
|
+
query: attr.query,
|
|
364
|
+
options: attr.options ?? undefined,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
return overlay;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Merges a refresh response's values into the live form.
|
|
371
|
+
*
|
|
372
|
+
* The rule, and the reason for it: a refresh is not instant, and the user keeps typing during it —
|
|
373
|
+
* the form is deliberately never frozen. So for each attribute we ask whether the *server* changed
|
|
374
|
+
* it, by comparing the response against the values that were **sent**, not against what is on
|
|
375
|
+
* screen now.
|
|
376
|
+
*
|
|
377
|
+
* - server value equals what we sent → the hook did not touch it, so whatever is in the form now
|
|
378
|
+
* wins, including anything typed while the request was in flight;
|
|
379
|
+
* - server value differs → the hook deliberately changed it, and it wins over a concurrent edit.
|
|
380
|
+
*
|
|
381
|
+
* Comparing against the displayed value instead is the classic "refresh eats my typing" bug;
|
|
382
|
+
* refusing to overwrite anything the user touched is the equally wrong opposite, where a dependent
|
|
383
|
+
* field the hook computed never appears.
|
|
384
|
+
*
|
|
385
|
+
* @param sent values as they were POSTed, captured before the request left
|
|
386
|
+
* @param current values as they are now, which may have moved on
|
|
387
|
+
* @param response the reshaped object
|
|
388
|
+
*/
|
|
389
|
+
function mergeRefreshValues(sent, current, response) {
|
|
390
|
+
const merged = { ...current };
|
|
391
|
+
for (const attr of response.attributes ?? []) {
|
|
392
|
+
const serverValue = attr.value ?? null;
|
|
393
|
+
const sentValue = sent[attr.name] ?? null;
|
|
394
|
+
if (!valuesEqual(serverValue, sentValue)) {
|
|
395
|
+
merged[attr.name] = attr.value;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return merged;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Structural for arrays, `===` otherwise.
|
|
402
|
+
*
|
|
403
|
+
* Multi-reference attributes hold `string[]`, and a fresh array of the same ids is a different
|
|
404
|
+
* object — so reference equality would report every one of them as "changed by the server" on
|
|
405
|
+
* every refresh, and clobber in-flight edits to them.
|
|
406
|
+
*/
|
|
407
|
+
function valuesEqual(a, b) {
|
|
408
|
+
if (a === b)
|
|
409
|
+
return true;
|
|
410
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
411
|
+
return a.length === b.length && a.every((item, i) => valuesEqual(item, b[i]));
|
|
412
|
+
}
|
|
413
|
+
// Values arrive off the wire as JSON scalars; anything else compares by JSON shape rather than
|
|
414
|
+
// by identity, which is what a caller means by "did this change".
|
|
415
|
+
if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') {
|
|
416
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const EMAIL = /^[^@\s]+@[^@\s]+\.[^@\s]+$/i;
|
|
422
|
+
const URL = /^https?:\/\/[^\s]+$/i;
|
|
423
|
+
function isEmpty(value) {
|
|
424
|
+
if (value === null || value === undefined)
|
|
425
|
+
return true;
|
|
426
|
+
if (typeof value === 'string')
|
|
427
|
+
return value.trim() === '';
|
|
428
|
+
if (Array.isArray(value))
|
|
429
|
+
return value.length === 0;
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Evaluates an attribute's rules against a value, mirroring the server's `ValidationService`.
|
|
434
|
+
*
|
|
435
|
+
* ⚠️ **Parity with the server is the point, and disagreement is worse than silence.** A client that
|
|
436
|
+
* rejects something the server would accept blocks legitimate work with no recourse; one that
|
|
437
|
+
* accepts something the server rejects merely defers the error to the round-trip, which is where it
|
|
438
|
+
* used to live anyway. So the rule set here is deliberately limited to the types the server
|
|
439
|
+
* implements, and an unrecognised rule type is ignored rather than guessed at.
|
|
440
|
+
*
|
|
441
|
+
* This exists because a refresh hook that imposes a rule needs it to bite before Save — previously
|
|
442
|
+
* `rules` was carried on the wire and never evaluated in the browser at all.
|
|
443
|
+
*/
|
|
444
|
+
function evaluateRules(attr, value) {
|
|
445
|
+
const failures = [];
|
|
446
|
+
const label = attr.label?.['en'] ?? attr.name;
|
|
447
|
+
if (attr.isRequired && isEmpty(value)) {
|
|
448
|
+
return [{ attributeName: attr.name, ruleType: 'required', message: `${label} is required` }];
|
|
449
|
+
}
|
|
450
|
+
// A rule other than "required" says nothing about an absent value — that is what required is for.
|
|
451
|
+
if (isEmpty(value))
|
|
452
|
+
return failures;
|
|
453
|
+
const text = value?.toString() ?? '';
|
|
454
|
+
for (const rule of attr.rules ?? []) {
|
|
455
|
+
const failure = evaluateRule(attr.name, label, rule, value, text);
|
|
456
|
+
if (failure)
|
|
457
|
+
failures.push(failure);
|
|
458
|
+
}
|
|
459
|
+
return failures;
|
|
460
|
+
}
|
|
461
|
+
function evaluateRule(name, label, rule, value, text) {
|
|
462
|
+
const message = rule.message?.['en'];
|
|
463
|
+
switch (rule.type?.toLowerCase()) {
|
|
464
|
+
case 'maxlength': {
|
|
465
|
+
const max = toInt(rule.value);
|
|
466
|
+
if (max === null || text.length <= max)
|
|
467
|
+
return null;
|
|
468
|
+
return { attributeName: name, ruleType: 'maxLength', message: message ?? `${label} must be at most ${max} characters` };
|
|
469
|
+
}
|
|
470
|
+
case 'minlength': {
|
|
471
|
+
const min = toInt(rule.value);
|
|
472
|
+
if (min === null || text.length >= min)
|
|
473
|
+
return null;
|
|
474
|
+
return { attributeName: name, ruleType: 'minLength', message: message ?? `${label} must be at least ${min} characters` };
|
|
475
|
+
}
|
|
476
|
+
case 'range': {
|
|
477
|
+
const numeric = toNumber(value);
|
|
478
|
+
if (numeric === null)
|
|
479
|
+
return null;
|
|
480
|
+
if (rule.min !== undefined && rule.min !== null && numeric < rule.min) {
|
|
481
|
+
return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at least ${rule.min}` };
|
|
482
|
+
}
|
|
483
|
+
if (rule.max !== undefined && rule.max !== null && numeric > rule.max) {
|
|
484
|
+
return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at most ${rule.max}` };
|
|
485
|
+
}
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
case 'regex': {
|
|
489
|
+
const pattern = rule.value?.toString();
|
|
490
|
+
if (!pattern)
|
|
491
|
+
return null;
|
|
492
|
+
let re;
|
|
493
|
+
try {
|
|
494
|
+
re = new RegExp(pattern);
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// An unparseable pattern is a server-side authoring bug. Staying silent leaves the server to
|
|
498
|
+
// report it, which is strictly better than blocking the user over a rule nobody can evaluate.
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
return re.test(text) ? null : { attributeName: name, ruleType: 'regex', message: message ?? `${label} is not in the expected format` };
|
|
502
|
+
}
|
|
503
|
+
case 'email':
|
|
504
|
+
return EMAIL.test(text) ? null : { attributeName: name, ruleType: 'email', message: message ?? `${label} must be a valid email address` };
|
|
505
|
+
case 'url':
|
|
506
|
+
return URL.test(text) ? null : { attributeName: name, ruleType: 'url', message: message ?? `${label} must be a valid URL` };
|
|
507
|
+
default:
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function toInt(value) {
|
|
512
|
+
const n = Number(value);
|
|
513
|
+
return Number.isFinite(n) ? Math.trunc(n) : null;
|
|
514
|
+
}
|
|
515
|
+
function toNumber(value) {
|
|
516
|
+
const n = Number(value);
|
|
517
|
+
return Number.isFinite(n) ? n : null;
|
|
518
|
+
}
|
|
519
|
+
|
|
336
520
|
/**
|
|
337
521
|
* Generated bundle index. Do not edit.
|
|
338
522
|
*/
|
|
339
523
|
|
|
340
|
-
export { AS_DETAIL_BREADCRUMBS_KEY, AS_DETAIL_SELF_BREADCRUMB_KEY, ELookupDisplayType, EReferenceDisplayType, ShowedOn, currentLanguage, dictToNestedPo, filterQueryActions, hasShowedOnFlag, nestedPoToDict, nestedPoToDisplayRow, parseSelectionRule, resolveTranslation, selectionModeFor, selfBreadcrumb };
|
|
524
|
+
export { AS_DETAIL_BREADCRUMBS_KEY, AS_DETAIL_SELF_BREADCRUMB_KEY, ELookupDisplayType, EReferenceDisplayType, ShowedOn, applyOverlay, currentLanguage, dictToNestedPo, evaluateRules, filterQueryActions, hasShowedOnFlag, mergeRefreshValues, nestedPoToDict, nestedPoToDisplayRow, overlayFromResponse, parseSelectionRule, resolveTranslation, selectionModeFor, selfBreadcrumb };
|
|
341
525
|
//# sourceMappingURL=mintplayer-ng-spark-models.mjs.map
|