@mintplayer/ng-spark 0.4.0 → 22.0.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 +22 -10
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-icon.mjs +9 -6
- package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +66 -66
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +17 -10
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +95 -90
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +17 -10
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +71 -36
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +111 -126
- package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +10 -7
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +48 -20
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/package.json +8 -7
- package/types/mintplayer-ng-spark-client-operations.d.ts +11 -0
- package/types/mintplayer-ng-spark-icon.d.ts +1 -1
- package/types/mintplayer-ng-spark-po-create.d.ts +1 -1
- package/types/mintplayer-ng-spark-po-detail.d.ts +12 -15
- package/types/mintplayer-ng-spark-po-edit.d.ts +2 -2
- package/types/mintplayer-ng-spark-po-form.d.ts +10 -9
- package/types/mintplayer-ng-spark-query-list.d.ts +16 -14
- package/types/mintplayer-ng-spark-retry-action-modal.d.ts +1 -1
- package/types/mintplayer-ng-spark-services.d.ts +26 -2
|
@@ -10,9 +10,7 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
|
|
10
10
|
import { Color } from '@mintplayer/ng-bootstrap';
|
|
11
11
|
import { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';
|
|
12
12
|
import { DatatableSettings, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective } from '@mintplayer/ng-bootstrap/datatable';
|
|
13
|
-
import { VirtualDatatableDataSource, BsVirtualDatatableComponent, BsVirtualRowTemplateDirective } from '@mintplayer/ng-bootstrap/virtual-datatable';
|
|
14
13
|
import { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';
|
|
15
|
-
import { BsContainerComponent } from '@mintplayer/ng-bootstrap/container';
|
|
16
14
|
import { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';
|
|
17
15
|
import { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';
|
|
18
16
|
import { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';
|
|
@@ -31,40 +29,61 @@ class SparkQueryListComponent {
|
|
|
31
29
|
lang = inject(SparkLanguageService);
|
|
32
30
|
rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);
|
|
33
31
|
destroyRef = inject(DestroyRef);
|
|
34
|
-
extraActionsTemplate = input(null,
|
|
35
|
-
|
|
32
|
+
extraActionsTemplate = input(null, /* @ts-ignore */
|
|
33
|
+
...(ngDevMode ? [{ debugName: "extraActionsTemplate" }] : /* istanbul ignore next */ []));
|
|
34
|
+
showCustomActions = input(true, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "showCustomActions" }] : /* istanbul ignore next */ []));
|
|
36
36
|
rowClicked = output();
|
|
37
37
|
createClicked = output();
|
|
38
38
|
customActionExecuted = output();
|
|
39
39
|
colors = Color;
|
|
40
|
-
errorMessage = signal(null,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
errorMessage = signal(null, /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
42
|
+
query = signal(null, /* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
44
|
+
entityType = signal(null, /* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [{ debugName: "entityType" }] : /* istanbul ignore next */ []));
|
|
46
|
+
allEntityTypes = signal([], /* @ts-ignore */
|
|
47
|
+
...(ngDevMode ? [{ debugName: "allEntityTypes" }] : /* istanbul ignore next */ []));
|
|
48
|
+
lookupReferenceOptions = signal({}, /* @ts-ignore */
|
|
49
|
+
...(ngDevMode ? [{ debugName: "lookupReferenceOptions" }] : /* istanbul ignore next */ []));
|
|
50
|
+
resultCount = signal(null, /* @ts-ignore */
|
|
51
|
+
...(ngDevMode ? [{ debugName: "resultCount" }] : /* istanbul ignore next */ []));
|
|
46
52
|
searchTerm = '';
|
|
47
|
-
canRead = signal(false,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
canRead = signal(false, /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "canRead" }] : /* istanbul ignore next */ []));
|
|
55
|
+
canCreate = signal(false, /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "canCreate" }] : /* istanbul ignore next */ []));
|
|
57
|
+
customActions = signal([], /* @ts-ignore */
|
|
58
|
+
...(ngDevMode ? [{ debugName: "customActions" }] : /* istanbul ignore next */ []));
|
|
59
|
+
isStreaming = signal(false, /* @ts-ignore */
|
|
60
|
+
...(ngDevMode ? [{ debugName: "isStreaming" }] : /* istanbul ignore next */ []));
|
|
51
61
|
streamingSub = null;
|
|
52
|
-
allItems = signal([],
|
|
53
|
-
|
|
62
|
+
allItems = signal([], /* @ts-ignore */
|
|
63
|
+
...(ngDevMode ? [{ debugName: "allItems" }] : /* istanbul ignore next */ []));
|
|
64
|
+
streamItems = signal([], /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "streamItems" }] : /* istanbul ignore next */ []));
|
|
66
|
+
fetchFn = signal(null, /* @ts-ignore */
|
|
67
|
+
...(ngDevMode ? [{ debugName: "fetchFn" }] : /* istanbul ignore next */ []));
|
|
54
68
|
settings = signal(new DatatableSettings({
|
|
55
69
|
perPage: { values: [10, 25, 50], selected: 10 },
|
|
56
70
|
page: { values: [1], selected: 1 },
|
|
57
71
|
sortColumns: []
|
|
58
|
-
}),
|
|
59
|
-
|
|
60
|
-
virtualSettings = signal(new DatatableSettings({
|
|
61
|
-
sortColumns: []
|
|
62
|
-
}), ...(ngDevMode ? [{ debugName: "virtualSettings" }] : []));
|
|
72
|
+
}), /* @ts-ignore */
|
|
73
|
+
...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
|
|
63
74
|
constructor() {
|
|
64
75
|
this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => this.onParamsChange(params));
|
|
65
76
|
this.destroyRef.onDestroy(() => this.disconnectStreaming());
|
|
66
77
|
}
|
|
67
78
|
async onParamsChange(params) {
|
|
79
|
+
// Reset prior-route state so we render the spinner (not stale rows from
|
|
80
|
+
// the previous query) while the new query/entityType resolve.
|
|
81
|
+
this.entityType.set(null);
|
|
82
|
+
this.fetchFn.set(null);
|
|
83
|
+
this.resultCount.set(null);
|
|
84
|
+
this.allItems.set([]);
|
|
85
|
+
this.streamItems.set([]);
|
|
86
|
+
this.disconnectStreaming();
|
|
68
87
|
const queryId = params.get('queryId');
|
|
69
88
|
const typeParam = params.get('type');
|
|
70
89
|
let resolvedQuery = null;
|
|
@@ -106,27 +125,20 @@ class SparkQueryListComponent {
|
|
|
106
125
|
property: sc.property,
|
|
107
126
|
direction: sc.direction === 'desc' ? 'descending' : 'ascending'
|
|
108
127
|
}));
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.connectStreaming(resolvedQuery.id);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
this.initVirtualDataSource();
|
|
121
|
-
}
|
|
128
|
+
this.settings.set(new DatatableSettings({
|
|
129
|
+
perPage: { values: [10, 25, 50], selected: 10 },
|
|
130
|
+
page: { values: [1], selected: 1 },
|
|
131
|
+
sortColumns: initialSortColumns
|
|
132
|
+
}));
|
|
133
|
+
if (resolvedQuery?.isStreamingQuery) {
|
|
134
|
+
// Streaming: WebSocket feeds allItems; the datatable binds [data]="streamItems()".
|
|
135
|
+
this.connectStreaming(resolvedQuery.id);
|
|
122
136
|
}
|
|
123
|
-
else {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}));
|
|
129
|
-
await this.loadItems();
|
|
137
|
+
else if (resolvedQuery) {
|
|
138
|
+
// Non-streaming: the datatable drives paging/sorting via [(settings)] and calls
|
|
139
|
+
// fetchFn per page. Virtual scrolling is just the [virtualScroll] template flag —
|
|
140
|
+
// the datatable front-loads all pages from fetchFn when virtual.
|
|
141
|
+
this.fetchFn.set(this.makeFetch(resolvedQuery));
|
|
130
142
|
}
|
|
131
143
|
this.loadLookupReferenceOptions();
|
|
132
144
|
const [permissions, actions] = await Promise.all([
|
|
@@ -148,7 +160,7 @@ class SparkQueryListComponent {
|
|
|
148
160
|
await this.sparkService.executeCustomAction(this.entityType().id, action.name);
|
|
149
161
|
this.customActionExecuted.emit({ action });
|
|
150
162
|
if (action.refreshOnCompleted) {
|
|
151
|
-
this.
|
|
163
|
+
this.refresh();
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
catch (e) {
|
|
@@ -195,89 +207,76 @@ class SparkQueryListComponent {
|
|
|
195
207
|
}
|
|
196
208
|
return plural;
|
|
197
209
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Builds the server-side fetch callback the datatable invokes per page/sort.
|
|
212
|
+
* Reads `searchTerm` live, so a settings change (or a new fetchFn identity)
|
|
213
|
+
* refetches with the current search term.
|
|
214
|
+
*/
|
|
215
|
+
makeFetch(query) {
|
|
216
|
+
return (req) => this.sparkService.executeQuery(query.id, {
|
|
217
|
+
sortColumns: req.sortColumns,
|
|
218
|
+
skip: (req.page - 1) * req.perPage,
|
|
219
|
+
take: req.perPage,
|
|
205
220
|
search: this.searchTerm || undefined,
|
|
206
|
-
}).then(r =>
|
|
207
|
-
}
|
|
208
|
-
async loadItems() {
|
|
209
|
-
const currentQuery = this.query();
|
|
210
|
-
if (!currentQuery)
|
|
211
|
-
return;
|
|
212
|
-
// Streaming queries use WebSocket instead of HTTP
|
|
213
|
-
if (currentQuery.isStreamingQuery) {
|
|
214
|
-
this.connectStreaming(currentQuery.id);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
// Non-streaming: disconnect any previous streaming connection
|
|
218
|
-
this.disconnectStreaming();
|
|
219
|
-
try {
|
|
220
|
-
const s = this.settings();
|
|
221
|
-
const result = await this.sparkService.executeQuery(currentQuery.id, {
|
|
222
|
-
sortColumns: s.sortColumns,
|
|
223
|
-
skip: (s.page.selected - 1) * s.perPage.selected,
|
|
224
|
-
take: s.perPage.selected,
|
|
225
|
-
search: this.searchTerm || undefined,
|
|
226
|
-
});
|
|
221
|
+
}).then(r => {
|
|
227
222
|
this.errorMessage.set(null);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
data:
|
|
231
|
-
totalRecords:
|
|
232
|
-
totalPages:
|
|
233
|
-
perPage:
|
|
234
|
-
page:
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
catch (e) {
|
|
223
|
+
this.resultCount.set(r.totalRecords);
|
|
224
|
+
return {
|
|
225
|
+
data: r.data,
|
|
226
|
+
totalRecords: r.totalRecords,
|
|
227
|
+
totalPages: Math.ceil(r.totalRecords / req.perPage) || 1,
|
|
228
|
+
perPage: req.perPage,
|
|
229
|
+
page: req.page,
|
|
230
|
+
};
|
|
231
|
+
}).catch((e) => {
|
|
239
232
|
this.errorMessage.set(e.error?.error || e.message || 'An unexpected error occurred');
|
|
240
|
-
this.
|
|
241
|
-
|
|
233
|
+
this.resultCount.set(0);
|
|
234
|
+
return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };
|
|
235
|
+
});
|
|
242
236
|
}
|
|
243
|
-
|
|
237
|
+
/** Force a refetch (e.g. after a custom action) without changing page/sort. */
|
|
238
|
+
refresh() {
|
|
244
239
|
if (this.isStreaming()) {
|
|
245
|
-
// Streaming: sort/filter is client-side only
|
|
246
240
|
this.applyFilter();
|
|
247
241
|
return;
|
|
248
242
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
this.loadItems();
|
|
255
|
-
}
|
|
243
|
+
const q = this.query();
|
|
244
|
+
if (q)
|
|
245
|
+
this.fetchFn.set(this.makeFetch(q));
|
|
256
246
|
}
|
|
257
247
|
onSearchChange() {
|
|
258
248
|
if (this.isStreaming()) {
|
|
259
249
|
this.applyFilter();
|
|
260
250
|
return;
|
|
261
251
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
252
|
+
// Reset to page 1 for the new search.
|
|
253
|
+
const s = this.settings();
|
|
254
|
+
this.settings.set(new DatatableSettings({
|
|
255
|
+
perPage: { values: s.perPage.values, selected: s.perPage.selected },
|
|
256
|
+
page: { values: [1], selected: 1 },
|
|
257
|
+
sortColumns: s.sortColumns,
|
|
258
|
+
}));
|
|
259
|
+
// Re-assign the fetch callback so the datatable refetches even when
|
|
260
|
+
// page/perPage/sort are unchanged. ng-bootstrap 22.4's web component dedupes
|
|
261
|
+
// reloads by {sortColumns, perPage, page}; setting a new fetch identity resets
|
|
262
|
+
// that key (set fetch → _lastReloadKey = null) and forces the reload. makeFetch
|
|
263
|
+
// reads searchTerm live (mirrors refresh()).
|
|
264
|
+
const q = this.query();
|
|
265
|
+
if (q)
|
|
266
|
+
this.fetchFn.set(this.makeFetch(q));
|
|
270
267
|
}
|
|
271
268
|
clearSearch() {
|
|
272
269
|
this.searchTerm = '';
|
|
273
270
|
this.onSearchChange();
|
|
274
271
|
}
|
|
275
|
-
isVirtualScrolling = computed(() => this.query()?.renderMode === 'VirtualScrolling',
|
|
272
|
+
isVirtualScrolling = computed(() => this.query()?.renderMode === 'VirtualScrolling', /* @ts-ignore */
|
|
273
|
+
...(ngDevMode ? [{ debugName: "isVirtualScrolling" }] : /* istanbul ignore next */ []));
|
|
276
274
|
visibleAttributes = computed(() => {
|
|
277
275
|
return this.entityType()?.attributes
|
|
278
276
|
.filter(a => a.isVisible && hasShowedOnFlag(a.showedOn, ShowedOn.Query))
|
|
279
277
|
.sort((a, b) => a.order - b.order) || [];
|
|
280
|
-
},
|
|
278
|
+
}, /* @ts-ignore */
|
|
279
|
+
...(ngDevMode ? [{ debugName: "visibleAttributes" }] : /* istanbul ignore next */ []));
|
|
281
280
|
getColumnRendererComponent(attr) {
|
|
282
281
|
if (!attr.renderer)
|
|
283
282
|
return null;
|
|
@@ -369,9 +368,9 @@ class SparkQueryListComponent {
|
|
|
369
368
|
const term = this.searchTerm.toLowerCase();
|
|
370
369
|
items = items.filter(item => item.attributes.some(a => String(a.value ?? '').toLowerCase().includes(term)));
|
|
371
370
|
}
|
|
372
|
-
// Apply sorting
|
|
373
|
-
|
|
374
|
-
const sortCols =
|
|
371
|
+
// Apply sorting (client-side for the streaming snapshot; the datatable in
|
|
372
|
+
// [data] mode also auto-sorts on header clicks).
|
|
373
|
+
const sortCols = this.settings().sortColumns;
|
|
375
374
|
if (sortCols.length > 0) {
|
|
376
375
|
items = [...items].sort((a, b) => {
|
|
377
376
|
for (const col of sortCols) {
|
|
@@ -384,31 +383,17 @@ class SparkQueryListComponent {
|
|
|
384
383
|
return 0;
|
|
385
384
|
});
|
|
386
385
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
// The stable data source's fetchFn closure reads from this.filteredItems,
|
|
390
|
-
// so clearing its cache and emitting empty triggers the CDK viewport to re-fetch.
|
|
391
|
-
this.filteredItems = items;
|
|
392
|
-
this.virtualDataSource()?.reset();
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
this.paginationData.set({
|
|
396
|
-
data: items,
|
|
397
|
-
totalRecords: items.length,
|
|
398
|
-
totalPages: 1,
|
|
399
|
-
perPage: items.length,
|
|
400
|
-
page: 1
|
|
401
|
-
});
|
|
402
|
-
}
|
|
386
|
+
this.streamItems.set(items);
|
|
387
|
+
this.resultCount.set(items.length);
|
|
403
388
|
}
|
|
404
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
405
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: SparkQueryListComponent, isStandalone: true, selector: "spark-query-list", inputs: { extraActionsTemplate: { classPropertyName: "extraActionsTemplate", publicName: "extraActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, showCustomActions: { classPropertyName: "showCustomActions", publicName: "showCustomActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClicked: "rowClicked", createClicked: "createClicked", customActionExecuted: "customActionExecuted" }, host: { properties: { "class.virtual-scrolling": "isVirtualScrolling()" } }, ngImport: i0, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\" [class.px-3]=\"!isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @if (canCreate()) {\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-primary\" (click)=\"onCreate()\">\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\" (click)=\"onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n </bs-priority-nav>\n </div>\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <span class=\"badge bg-success ms-2\" style=\"font-size: 0.5em; vertical-align: middle;\">LIVE</span>\n }\n </h2>\n\n @if (entityType()) {\n <!-- Search box -->\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-form>\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"4\">\n <bs-input-group>\n <span class=\"input-group-text\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n <div [md]=\"8\" class=\"text-end\">\n @if (searchTerm && paginationData()) {\n <span class=\"text-muted\">\n {{ paginationData()!.totalRecords }} {{ paginationData()!.totalRecords === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n }\n </div>\n </div>\n </bs-grid>\n </bs-form>\n\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n </div>\n\n @if (query()?.renderMode === 'VirtualScrolling') {\n @if (virtualDataSource(); as vds) {\n <bs-virtual-datatable class=\"flex-grow-1 overflow-hidden\"\n [(settings)]=\"virtualSettings\"\n [dataSource]=\"vds\"\n [isResponsive]=\"true\"\n (settingsChange)=\"onSettingsChange()\">\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-template bsVirtualRowTemplate let-item>\n @for (attr of visibleAttributes(); track attr.id; let first = $first) {\n <td>\n @if (item) {\n @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, item.id]\" (click)=\"rowClicked.emit(item)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: item, attr: attr }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: item, attr: attr }\"></ng-container>\n }\n } @else {\n \n }\n </td>\n }\n </ng-template>\n </bs-virtual-datatable>\n }\n } @else {\n <bs-datatable class=\"flex-grow-1\" [(settings)]=\"settings\" (settingsChange)=\"onSettingsChange()\">\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 <tr *bsRowTemplate=\"let item of paginationData()\">\n @for (attr of visibleAttributes(); track attr.id; let first = $first) {\n <td>\n @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, item.id]\" (click)=\"rowClicked.emit(item)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: item, attr: attr }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: item, attr: attr }\"></ng-container>\n }\n </td>\n }\n </tr>\n </bs-datatable>\n }\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n\n<ng-template #cellContent let-item let-attr=\"attr\">\n @if (getColumnRendererComponent(attr); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: getColumnRendererInputs(item, attr)\"></ng-container>\n } @else if (attr.dataType === 'boolean') {\n <input type=\"checkbox\"\n [checked]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) === true\"\n [indeterminate]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) == null\"\n disabled\n onclick=\"return false;\">\n } @else if (attr.dataType === 'color') {\n @let colorVal = (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes());\n @if (colorVal) {\n <span class=\"d-inline-block align-middle border rounded\" [style.background-color]=\"colorVal\" style=\"width: 1.5em; height: 1.5em;\"></span>\n }\n } @else {\n {{ (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) }}\n }\n</ng-template>\n", styles: [".spark-actionbar{position:sticky;top:0;z-index:400;background-color:var(--bs-tertiary-bg);border-bottom:1px solid var(--bs-border-color);margin-bottom:1rem}.spark-actionbar .btn{border-radius:0}:host{display:flex;flex-direction:column;flex:1;min-height:0}:host.virtual-scrolling{margin:0 -1.5rem -1.5rem}tr:hover{background-color:#0000000d}td input[type=checkbox]:disabled{opacity:1;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsDatatableComponent, selector: "bs-datatable", inputs: ["data"], outputs: ["dataChange"] }, { kind: "directive", type: BsDatatableColumnDirective, selector: "[bsDatatableColumn]", inputs: ["bsDatatableColumn", "bsDatatableColumnSortable"] }, { kind: "directive", type: BsRowTemplateDirective, selector: "[bsRowTemplate]", inputs: ["bsRowTemplateOf"] }, { kind: "component", type: BsVirtualDatatableComponent, selector: "bs-virtual-datatable", inputs: ["dataSource", "isResponsive", "itemSize"] }, { kind: "directive", type: BsVirtualRowTemplateDirective, selector: "[bsVirtualRowTemplate]" }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsGridComponent, selector: "bs-grid", inputs: ["stopFullWidthAt"] }, { kind: "directive", type: BsGridRowDirective, selector: "[bsRow]" }, { kind: "directive", type: BsGridColumnDirective, selector: "[xxs],[xs],[sm],[md],[lg],[xl],[xxl]", inputs: ["xxs", "xs", "sm", "md", "lg", "xl", "xxl"] }, { kind: "component", type: BsInputGroupComponent, selector: "bs-input-group" }, { kind: "component", type: BsPriorityNavComponent, selector: "bs-priority-nav", inputs: ["moreLabel", "moreLabelTemplate", "collapseAt", "overflowFrom", "hideEmptyMore"], outputs: ["overflowChange"] }, { kind: "directive", type: BsPriorityNavItemDirective, selector: "[bsPriorityNavItem]", inputs: ["bsPriorityNavItem", "bsPriorityNavItemHideBelow"] }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "component", type: SparkIconComponent, selector: "spark-icon", inputs: ["name"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }, { kind: "pipe", type: AttributeValuePipe, name: "attributeValue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
389
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkQueryListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
390
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkQueryListComponent, isStandalone: true, selector: "spark-query-list", inputs: { extraActionsTemplate: { classPropertyName: "extraActionsTemplate", publicName: "extraActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, showCustomActions: { classPropertyName: "showCustomActions", publicName: "showCustomActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClicked: "rowClicked", createClicked: "createClicked", customActionExecuted: "customActionExecuted" }, host: { properties: { "class.virtual-scrolling": "isVirtualScrolling()" } }, ngImport: i0, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\" [class.px-3]=\"!isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @if (canCreate()) {\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-primary\" (click)=\"onCreate()\">\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\" (click)=\"onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n </bs-priority-nav>\n </div>\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <span class=\"badge bg-success ms-2\" style=\"font-size: 0.5em; vertical-align: middle;\">LIVE</span>\n }\n </h2>\n\n @if (entityType()) {\n <!-- Search box -->\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-form>\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"4\">\n <bs-input-group>\n <span class=\"input-group-text\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n <div [md]=\"8\" class=\"text-end\">\n @if (searchTerm && resultCount() !== null) {\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n }\n </div>\n </div>\n </bs-grid>\n </bs-form>\n\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n </div>\n\n @if (isStreaming()) {\n <bs-datatable class=\"flex-grow-1\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"streamItems()\"\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 @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\" (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 <bs-datatable class=\"flex-grow-1\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\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 @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\" (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 }\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n\n<ng-template #cellContent let-item let-attr=\"attr\">\n @if (getColumnRendererComponent(attr); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: getColumnRendererInputs(item, attr)\"></ng-container>\n } @else if (attr.dataType === 'boolean') {\n <input type=\"checkbox\"\n [checked]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) === true\"\n [indeterminate]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) == null\"\n disabled\n onclick=\"return false;\">\n } @else if (attr.dataType === 'color') {\n @let colorVal = (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes());\n @if (colorVal) {\n <span class=\"d-inline-block align-middle border rounded\" [style.background-color]=\"colorVal\" style=\"width: 1.5em; height: 1.5em;\"></span>\n }\n } @else {\n {{ (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) }}\n }\n</ng-template>\n", styles: [".spark-actionbar{position:sticky;top:0;z-index:400;background-color:var(--bs-tertiary-bg);border-bottom:1px solid var(--bs-border-color);margin-bottom:1rem}.spark-actionbar .btn{border-radius:0}:host{display:flex;flex-direction:column;flex:1;min-height:0}:host.virtual-scrolling{margin:0 -1.5rem -1.5rem}:host.virtual-scrolling ::ng-deep bs-datatable{display:flex;flex-direction:column;min-height:0;--mp-datatable-virtual-max-height: 100%}:host.virtual-scrolling ::ng-deep bs-datatable mp-datatable{flex:1 1 auto;min-height:0}tr:hover{background-color:#0000000d}td input[type=checkbox]:disabled{opacity:1;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3.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", "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: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsGridComponent, selector: "bs-grid", inputs: ["stopFullWidthAt"] }, { kind: "directive", type: BsGridRowDirective, selector: "[bsRow]" }, { kind: "directive", type: BsGridColumnDirective, selector: "[xxs],[xs],[sm],[md],[lg],[xl],[xxl]", inputs: ["xxs", "xs", "sm", "md", "lg", "xl", "xxl"] }, { kind: "component", type: BsInputGroupComponent, selector: "bs-input-group" }, { 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: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "component", type: SparkIconComponent, selector: "spark-icon", inputs: ["name"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }, { kind: "pipe", type: AttributeValuePipe, name: "attributeValue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
406
391
|
}
|
|
407
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
392
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkQueryListComponent, decorators: [{
|
|
408
393
|
type: Component,
|
|
409
|
-
args: [{ selector: 'spark-query-list', imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, RouterModule, BsAlertComponent,
|
|
394
|
+
args: [{ selector: 'spark-query-list', imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
410
395
|
'[class.virtual-scrolling]': 'isVirtualScrolling()'
|
|
411
|
-
}, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\" [class.px-3]=\"!isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @if (canCreate()) {\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-primary\" (click)=\"onCreate()\">\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\" (click)=\"onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n </bs-priority-nav>\n </div>\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <span class=\"badge bg-success ms-2\" style=\"font-size: 0.5em; vertical-align: middle;\">LIVE</span>\n }\n </h2>\n\n @if (entityType()) {\n <!-- Search box -->\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-form>\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"4\">\n <bs-input-group>\n <span class=\"input-group-text\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n <div [md]=\"8\" class=\"text-end\">\n @if (searchTerm &&
|
|
396
|
+
}, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\" [class.px-3]=\"!isVirtualScrolling()\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @if (canCreate()) {\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-primary\" (click)=\"onCreate()\">\n <spark-icon name=\"plus-lg\" /> {{ 'common.new' | t }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\" (click)=\"onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n </bs-priority-nav>\n </div>\n <h2 class=\"mb-4 flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n {{ (query()?.description | resolveTranslation) || query()?.name || ('common.loading' | t) }}\n @if (isStreaming()) {\n <span class=\"badge bg-success ms-2\" style=\"font-size: 0.5em; vertical-align: middle;\">LIVE</span>\n }\n </h2>\n\n @if (entityType()) {\n <!-- Search box -->\n <div class=\"flex-shrink-0\" [class.px-4]=\"isVirtualScrolling()\">\n <bs-form>\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"4\">\n <bs-input-group>\n <span class=\"input-group-text\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n <div [md]=\"8\" class=\"text-end\">\n @if (searchTerm && resultCount() !== null) {\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n }\n </div>\n </div>\n </bs-grid>\n </bs-form>\n\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n </div>\n\n @if (isStreaming()) {\n <bs-datatable class=\"flex-grow-1\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"streamItems()\"\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 @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\" (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 <bs-datatable class=\"flex-grow-1\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\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 @if (first && canRead()) {\n <a [routerLink]=\"['/po', entityType()!.alias || entityType()!.id, row.id]\" (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 }\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n\n<ng-template #cellContent let-item let-attr=\"attr\">\n @if (getColumnRendererComponent(attr); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: getColumnRendererInputs(item, attr)\"></ng-container>\n } @else if (attr.dataType === 'boolean') {\n <input type=\"checkbox\"\n [checked]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) === true\"\n [indeterminate]=\"(attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) == null\"\n disabled\n onclick=\"return false;\">\n } @else if (attr.dataType === 'color') {\n @let colorVal = (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes());\n @if (colorVal) {\n <span class=\"d-inline-block align-middle border rounded\" [style.background-color]=\"colorVal\" style=\"width: 1.5em; height: 1.5em;\"></span>\n }\n } @else {\n {{ (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) }}\n }\n</ng-template>\n", styles: [".spark-actionbar{position:sticky;top:0;z-index:400;background-color:var(--bs-tertiary-bg);border-bottom:1px solid var(--bs-border-color);margin-bottom:1rem}.spark-actionbar .btn{border-radius:0}:host{display:flex;flex-direction:column;flex:1;min-height:0}:host.virtual-scrolling{margin:0 -1.5rem -1.5rem}:host.virtual-scrolling ::ng-deep bs-datatable{display:flex;flex-direction:column;min-height:0;--mp-datatable-virtual-max-height: 100%}:host.virtual-scrolling ::ng-deep bs-datatable mp-datatable{flex:1 1 auto;min-height:0}tr:hover{background-color:#0000000d}td input[type=checkbox]:disabled{opacity:1;pointer-events:none}\n"] }]
|
|
412
397
|
}], ctorParameters: () => [], propDecorators: { extraActionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraActionsTemplate", required: false }] }], showCustomActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomActions", required: false }] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], createClicked: [{ type: i0.Output, args: ["createClicked"] }], customActionExecuted: [{ type: i0.Output, args: ["customActionExecuted"] }] } });
|
|
413
398
|
|
|
414
399
|
/**
|