@mintplayer/ng-spark 0.4.0 → 22.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +22 -10
  2. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  3. package/fesm2022/mintplayer-ng-spark-icon.mjs +9 -6
  4. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  5. package/fesm2022/mintplayer-ng-spark-pipes.mjs +66 -66
  6. package/fesm2022/mintplayer-ng-spark-po-create.mjs +17 -10
  7. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  8. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +96 -91
  9. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  10. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +17 -10
  11. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  12. package/fesm2022/mintplayer-ng-spark-po-form.mjs +71 -36
  13. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  14. package/fesm2022/mintplayer-ng-spark-query-list.mjs +111 -126
  15. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +10 -7
  17. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  18. package/fesm2022/mintplayer-ng-spark-services.mjs +48 -20
  19. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  20. package/package.json +8 -7
  21. package/types/mintplayer-ng-spark-client-operations.d.ts +11 -0
  22. package/types/mintplayer-ng-spark-icon.d.ts +1 -1
  23. package/types/mintplayer-ng-spark-po-create.d.ts +1 -1
  24. package/types/mintplayer-ng-spark-po-detail.d.ts +12 -15
  25. package/types/mintplayer-ng-spark-po-edit.d.ts +2 -2
  26. package/types/mintplayer-ng-spark-po-form.d.ts +10 -9
  27. package/types/mintplayer-ng-spark-query-list.d.ts +16 -14
  28. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +1 -1
  29. 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, ...(ngDevMode ? [{ debugName: "extraActionsTemplate" }] : []));
35
- showCustomActions = input(true, ...(ngDevMode ? [{ debugName: "showCustomActions" }] : []));
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, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
41
- query = signal(null, ...(ngDevMode ? [{ debugName: "query" }] : []));
42
- entityType = signal(null, ...(ngDevMode ? [{ debugName: "entityType" }] : []));
43
- allEntityTypes = signal([], ...(ngDevMode ? [{ debugName: "allEntityTypes" }] : []));
44
- lookupReferenceOptions = signal({}, ...(ngDevMode ? [{ debugName: "lookupReferenceOptions" }] : []));
45
- paginationData = signal(undefined, ...(ngDevMode ? [{ debugName: "paginationData" }] : []));
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, ...(ngDevMode ? [{ debugName: "canRead" }] : []));
48
- canCreate = signal(false, ...(ngDevMode ? [{ debugName: "canCreate" }] : []));
49
- customActions = signal([], ...(ngDevMode ? [{ debugName: "customActions" }] : []));
50
- isStreaming = signal(false, ...(ngDevMode ? [{ debugName: "isStreaming" }] : []));
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([], ...(ngDevMode ? [{ debugName: "allItems" }] : []));
53
- filteredItems = [];
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
- }), ...(ngDevMode ? [{ debugName: "settings" }] : []));
59
- virtualDataSource = signal(null, ...(ngDevMode ? [{ debugName: "virtualDataSource" }] : []));
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
- if (resolvedQuery?.renderMode === 'VirtualScrolling') {
110
- this.virtualSettings.set(new DatatableSettings({ sortColumns: initialSortColumns }));
111
- if (resolvedQuery?.isStreamingQuery) {
112
- // Streaming + VirtualScrolling: create a stable client-side data source, then connect WebSocket
113
- this.virtualDataSource.set(new VirtualDatatableDataSource((skip, take) => Promise.resolve({
114
- data: this.filteredItems.slice(skip, skip + take),
115
- totalRecords: this.filteredItems.length
116
- }), 50));
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
- this.settings.set(new DatatableSettings({
125
- perPage: { values: [10, 25, 50], selected: 10 },
126
- page: { values: [1], selected: 1 },
127
- sortColumns: initialSortColumns
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.onSettingsChange();
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
- initVirtualDataSource() {
199
- const currentQuery = this.query();
200
- if (!currentQuery)
201
- return;
202
- this.virtualDataSource.set(new VirtualDatatableDataSource((skip, take) => this.sparkService.executeQuery(currentQuery.id, {
203
- sortColumns: this.virtualSettings().sortColumns,
204
- skip, take,
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 => ({ data: r.data, totalRecords: r.totalRecords })), 50));
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
- const totalPages = Math.ceil(result.totalRecords / s.perPage.selected) || 1;
229
- this.paginationData.set({
230
- data: result.data,
231
- totalRecords: result.totalRecords,
232
- totalPages: totalPages,
233
- perPage: s.perPage.selected,
234
- page: s.page.selected
235
- });
236
- s.page.values = Array.from({ length: totalPages }, (_, i) => i + 1);
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.paginationData.set(undefined);
241
- }
233
+ this.resultCount.set(0);
234
+ return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };
235
+ });
242
236
  }
243
- onSettingsChange() {
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
- if (this.query()?.renderMode === 'VirtualScrolling') {
250
- this.virtualDataSource()?.reset();
251
- this.initVirtualDataSource();
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
- if (this.query()?.renderMode === 'VirtualScrolling') {
263
- this.virtualDataSource()?.reset();
264
- this.initVirtualDataSource();
265
- }
266
- else {
267
- this.settings().page.selected = 1;
268
- this.loadItems();
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', ...(ngDevMode ? [{ debugName: "isVirtualScrolling" }] : []));
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
- }, ...(ngDevMode ? [{ debugName: "visibleAttributes" }] : []));
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
- const isVirtual = this.query()?.renderMode === 'VirtualScrolling';
374
- const sortCols = isVirtual ? this.virtualSettings().sortColumns : this.settings().sortColumns;
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
- if (isVirtual) {
388
- // Update the mutable filtered items array.
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: "21.1.6", ngImport: i0, type: SparkQueryListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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 &nbsp;\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 &nbsp;\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 &nbsp;\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: "21.1.6", ngImport: i0, type: SparkQueryListComponent, decorators: [{
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, BsContainerComponent, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsVirtualDatatableComponent, BsVirtualRowTemplateDirective, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
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 && 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 &nbsp;\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"] }]
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 &nbsp;\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 &nbsp;\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
  /**