@mintplayer/ng-spark 22.0.10 → 22.0.11

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.
@@ -18,7 +18,7 @@ import { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';
18
18
  import { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';
19
19
  import { ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe, ReferenceChipsPipe } from '@mintplayer/ng-spark/pipes';
20
20
  import { SparkIconComponent } from '@mintplayer/ng-spark/icon';
21
- import { SPARK_ATTRIBUTE_RENDERERS } from '@mintplayer/ng-spark/renderers';
21
+ import { SPARK_ATTRIBUTE_RENDERERS, withDeclaredInputs, rendererValue } from '@mintplayer/ng-spark/renderers';
22
22
  import { hasShowedOnFlag, ShowedOn } from '@mintplayer/ng-spark/models';
23
23
 
24
24
  class SparkQueryListComponent {
@@ -282,13 +282,14 @@ class SparkQueryListComponent {
282
282
  return null;
283
283
  return this.rendererRegistry.find(r => r.name === attr.renderer)?.columnComponent ?? null;
284
284
  }
285
- getColumnRendererInputs(item, attr) {
285
+ getColumnRendererInputs(component, item, attr) {
286
286
  const itemAttr = item.attributes.find(a => a.name === attr.name);
287
- return {
288
- value: itemAttr?.value,
287
+ return withDeclaredInputs(component, {
288
+ value: rendererValue(itemAttr),
289
289
  attribute: attr,
290
290
  options: attr.rendererOptions,
291
- };
291
+ item,
292
+ });
292
293
  }
293
294
  async loadLookupReferenceOptions() {
294
295
  const lookupAttrs = this.visibleAttributes().filter(a => a.lookupReferenceType);
@@ -387,13 +388,13 @@ class SparkQueryListComponent {
387
388
  this.resultCount.set(items.length);
388
389
  }
389
390
  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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\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", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsDatatableComponent, selector: "bs-datatable", inputs: ["columns", "data", "fetch", "settings", "selectionMode", "selectable", "selection", "rowKey", "resizableColumns", "pagination", "virtualScroll", "itemSize", "virtualBuffer", "isResponsive", "compareWith", "tree", "idKey", "childCountKey", "treeIndent", "expandedIds", "selectionStrategy"], outputs: ["settingsChange", "selectionChange", "rowClick", "rowDblClick", "rowContextMenu", "expandedIdsChange", "rowExpand", "rowCollapse"] }, { kind: "directive", type: BsDatatableColumnDirective, selector: "[bsDatatableColumn]", inputs: ["bsDatatableColumn", "bsDatatableColumnSortable"] }, { kind: "directive", type: BsRowTemplateDirective, selector: "[bsRowTemplate]" }, { kind: "component", type: 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", inputs: ["size"] }, { 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" }, { kind: "pipe", type: ReferenceChipsPipe, name: "referenceChips" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
391
+ 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(rendererType, 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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\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", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsDatatableComponent, selector: "bs-datatable", inputs: ["columns", "data", "fetch", "settings", "selectionMode", "selectable", "selection", "rowKey", "resizableColumns", "pagination", "virtualScroll", "itemSize", "virtualBuffer", "isResponsive", "compareWith", "tree", "idKey", "childCountKey", "treeIndent", "expandedIds", "selectionStrategy"], outputs: ["settingsChange", "selectionChange", "rowClick", "rowDblClick", "rowContextMenu", "expandedIdsChange", "rowExpand", "rowCollapse"] }, { kind: "directive", type: BsDatatableColumnDirective, selector: "[bsDatatableColumn]", inputs: ["bsDatatableColumn", "bsDatatableColumnSortable"] }, { kind: "directive", type: BsRowTemplateDirective, selector: "[bsRowTemplate]" }, { kind: "component", type: 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", inputs: ["size"] }, { 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" }, { kind: "pipe", type: ReferenceChipsPipe, name: "referenceChips" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
391
392
  }
392
393
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkQueryListComponent, decorators: [{
393
394
  type: Component,
394
395
  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, ReferenceChipsPipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
395
396
  '[class.virtual-scrolling]': 'isVirtualScrolling()'
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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\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"] }]
397
+ }, 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(rendererType, 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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\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"] }]
397
398
  }], 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"] }] } });
398
399
 
399
400
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-query-list.mjs","sources":["../../query-list/src/spark-query-list.component.ts","../../query-list/src/spark-query-list.component.html","../../query-list/mintplayer-ng-spark-query-list.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal, TemplateRef, Type } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, Router, RouterModule } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, DatatableSettings, type BsDatatableFetch } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { SortColumn } from '@mintplayer/pagination';\nimport { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n AttributeValuePipe,\n ReferenceChipsPipe,\n} from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SPARK_ATTRIBUTE_RENDERERS } from '@mintplayer/ng-spark/renderers';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n EntityType,\n EntityAttributeDefinition,\n LookupReference,\n PersistentObject,\n SparkQuery,\n ShowedOn,\n hasShowedOnFlag,\n} from '@mintplayer/ng-spark/models';\n\n@Component({\n selector: 'spark-query-list',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe, ReferenceChipsPipe],\n templateUrl: './spark-query-list.component.html',\n styleUrl: './spark-query-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.virtual-scrolling]': 'isVirtualScrolling()'\n }\n})\nexport class SparkQueryListComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly streamingService = inject(SparkStreamingService);\n protected readonly lang = inject(SparkLanguageService);\n private readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n private readonly destroyRef = inject(DestroyRef);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n rowClicked = output<PersistentObject>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n errorMessage = signal<string | null>(null);\n query = signal<SparkQuery | null>(null);\n entityType = signal<EntityType | null>(null);\n allEntityTypes = signal<EntityType[]>([]);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n resultCount = signal<number | null>(null);\n searchTerm: string = '';\n canRead = signal(false);\n canCreate = signal(false);\n customActions = signal<CustomActionDefinition[]>([]);\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n private allItems = signal<PersistentObject[]>([]);\n streamItems = signal<PersistentObject[]>([]);\n fetchFn = signal<BsDatatableFetch<PersistentObject> | null>(null);\n settings = signal(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => this.onParamsChange(params));\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n }\n\n private async onParamsChange(params: any): Promise<void> {\n // Reset prior-route state so we render the spinner (not stale rows from\n // the previous query) while the new query/entityType resolve.\n this.entityType.set(null);\n this.fetchFn.set(null);\n this.resultCount.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n let resolvedQuery: SparkQuery | null = null;\n let resolvedEntityType: EntityType | null = null;\n let resolvedEntityTypes: EntityType[] = [];\n\n if (queryId) {\n resolvedQuery = await this.sparkService.getQuery(queryId);\n if (resolvedQuery) {\n const result = await this.resolveEntityTypeForQuery(resolvedQuery);\n resolvedEntityType = result.entityType;\n resolvedEntityTypes = result.entityTypes;\n }\n } else if (typeParam) {\n resolvedEntityTypes = await this.sparkService.getEntityTypes();\n resolvedEntityType = resolvedEntityTypes.find(t =>\n t.id === typeParam || t.alias === typeParam\n ) || null;\n\n if (resolvedEntityType) {\n const queries = await this.sparkService.getQueries();\n const singularName = resolvedEntityType.name;\n resolvedQuery = queries.find(q => {\n // Match by explicit entityType\n if (q.entityType === singularName) return true;\n // Match by source name\n const sourceName = this.extractSourceName(q.source);\n const sourceSingular = this.singularize(sourceName);\n return sourceName === singularName ||\n sourceSingular === singularName ||\n sourceName === singularName + 's';\n }) || null;\n }\n }\n\n if (resolvedQuery) this.query.set(resolvedQuery);\n\n if (resolvedEntityType) {\n this.entityType.set(resolvedEntityType);\n this.allEntityTypes.set(resolvedEntityTypes);\n\n const initialSortColumns: SortColumn[] = (resolvedQuery?.sortColumns || []).map(sc => ({\n property: sc.property,\n direction: sc.direction === 'desc' ? 'descending' as const : 'ascending' as const\n }));\n\n this.settings.set(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: initialSortColumns\n }));\n\n if (resolvedQuery?.isStreamingQuery) {\n // Streaming: WebSocket feeds allItems; the datatable binds [data]=\"streamItems()\".\n this.connectStreaming(resolvedQuery.id);\n } else if (resolvedQuery) {\n // Non-streaming: the datatable drives paging/sorting via [(settings)] and calls\n // fetchFn per page. Virtual scrolling is just the [virtualScroll] template flag —\n // the datatable front-loads all pages from fetchFn when virtual.\n this.fetchFn.set(this.makeFetch(resolvedQuery));\n }\n\n this.loadLookupReferenceOptions();\n const [permissions, actions] = await Promise.all([\n this.sparkService.getPermissions(resolvedEntityType.id),\n this.sparkService.getCustomActions(resolvedEntityType.id),\n ]);\n this.canRead.set(permissions.canRead);\n this.canCreate.set(permissions.canCreate);\n this.customActions.set(actions.filter(a => a.showedOn === 'list' || a.showedOn === 'both'));\n }\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n if (action.confirmationMessageKey) {\n const message = this.lang.t(action.confirmationMessageKey) || 'Are you sure?';\n if (!confirm(message)) return;\n }\n try {\n await this.sparkService.executeCustomAction(this.entityType()!.id, action.name);\n this.customActionExecuted.emit({ action });\n if (action.refreshOnCompleted) {\n this.refresh();\n }\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || 'Action failed');\n }\n }\n\n private async resolveEntityTypeForQuery(query: SparkQuery): Promise<{ entityType: EntityType | null; entityTypes: EntityType[] }> {\n const entityTypes = await this.sparkService.getEntityTypes();\n\n // If entityType is explicitly set on the query, use it directly\n if (query.entityType) {\n const type = entityTypes.find(t =>\n t.name === query.entityType || t.alias === query.entityType?.toLowerCase()\n );\n return { entityType: type || null, entityTypes };\n }\n\n // For Database.X sources, extract the property name and try to match\n const sourceName = this.extractSourceName(query.source);\n const singularName = this.singularize(sourceName);\n const type = entityTypes.find(t =>\n t.name === sourceName ||\n t.name === singularName ||\n t.clrType.endsWith(singularName)\n );\n return { entityType: type || null, entityTypes };\n }\n\n private extractSourceName(source: string): string {\n const dotIndex = source.indexOf('.');\n return dotIndex >= 0 ? source.substring(dotIndex + 1) : source;\n }\n\n private singularize(plural: string): string {\n const irregulars: { [key: string]: string } = {\n 'People': 'Person',\n 'Children': 'Child',\n 'Men': 'Men',\n 'Women': 'Woman'\n };\n if (irregulars[plural]) return irregulars[plural];\n\n if (plural.endsWith('ies')) {\n return plural.slice(0, -3) + 'y';\n }\n if (plural.endsWith('es')) {\n return plural.slice(0, -2);\n }\n if (plural.endsWith('s')) {\n return plural.slice(0, -1);\n }\n return plural;\n }\n\n /**\n * Builds the server-side fetch callback the datatable invokes per page/sort.\n * Reads `searchTerm` live, so a settings change (or a new fetchFn identity)\n * refetches with the current search term.\n */\n private makeFetch(query: SparkQuery): BsDatatableFetch<PersistentObject> {\n return (req) => this.sparkService.executeQuery(query.id, {\n sortColumns: req.sortColumns,\n skip: (req.page - 1) * req.perPage,\n take: req.perPage,\n search: this.searchTerm || undefined,\n }).then(r => {\n this.errorMessage.set(null);\n this.resultCount.set(r.totalRecords);\n return {\n data: r.data,\n totalRecords: r.totalRecords,\n totalPages: Math.ceil(r.totalRecords / req.perPage) || 1,\n perPage: req.perPage,\n page: req.page,\n };\n }).catch((e: any) => {\n this.errorMessage.set(e.error?.error || e.message || 'An unexpected error occurred');\n this.resultCount.set(0);\n return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };\n });\n }\n\n /** Force a refetch (e.g. after a custom action) without changing page/sort. */\n private refresh(): void {\n if (this.isStreaming()) {\n this.applyFilter();\n return;\n }\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q));\n }\n\n onSearchChange(): void {\n if (this.isStreaming()) {\n this.applyFilter();\n return;\n }\n // Reset to page 1 for the new search.\n const s = this.settings();\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n // Re-assign the fetch callback so the datatable refetches even when\n // page/perPage/sort are unchanged. ng-bootstrap 22.4's web component dedupes\n // reloads by {sortColumns, perPage, page}; setting a new fetch identity resets\n // that key (set fetch → _lastReloadKey = null) and forces the reload. makeFetch\n // reads searchTerm live (mirrors refresh()).\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q));\n }\n\n clearSearch(): void {\n this.searchTerm = '';\n this.onSearchChange();\n }\n\n isVirtualScrolling = computed(() => this.query()?.renderMode === 'VirtualScrolling');\n\n visibleAttributes = computed(() => {\n return this.entityType()?.attributes\n .filter(a => a.isVisible && hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n getColumnRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.columnComponent ?? null;\n }\n\n getColumnRendererInputs(item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any> {\n const itemAttr = item.attributes.find(a => a.name === attr.name);\n return {\n value: itemAttr?.value,\n attribute: attr,\n options: attr.rendererOptions,\n };\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.visibleAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const result = await this.sparkService.getLookupReference(name);\n return [name, result] as const;\n })\n );\n this.lookupReferenceOptions.set(entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, LookupReference>));\n }\n\n onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) {\n this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n }\n\n private connectStreaming(queryId: string): void {\n this.disconnectStreaming();\n this.isStreaming.set(true);\n\n this.streamingSub = this.streamingService.connectToStreamingQuery(queryId).subscribe({\n next: (message) => this.handleStreamingMessage(message),\n error: (err) => {\n this.errorMessage.set(err?.message || 'Streaming connection failed');\n this.isStreaming.set(false);\n },\n complete: () => {\n this.isStreaming.set(false);\n }\n });\n }\n\n private disconnectStreaming(): void {\n if (this.streamingSub) {\n this.streamingSub.unsubscribe();\n this.streamingSub = null;\n }\n this.isStreaming.set(false);\n }\n\n private handleStreamingMessage(message: StreamingMessage): void {\n switch (message.type) {\n case 'snapshot':\n this.errorMessage.set(null);\n this.allItems.set(message.data);\n this.applyFilter();\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n const currentItems = this.allItems();\n const updatedItems = currentItems.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n\n // Clone the item and update only changed attribute values\n const updatedAttributes = item.attributes.map(attr => {\n if (attr.name in patch.attributes) {\n return { ...attr, value: patch.attributes[attr.name] };\n }\n return attr;\n });\n\n return { ...item, attributes: updatedAttributes };\n });\n\n this.allItems.set(updatedItems);\n this.applyFilter();\n }\n break;\n\n case 'error':\n this.errorMessage.set(message.message);\n break;\n }\n }\n\n private applyFilter(): void {\n let items = this.allItems();\n\n // Apply search filter\n if (this.searchTerm) {\n const term = this.searchTerm.toLowerCase();\n items = items.filter(item =>\n item.attributes.some(a => String(a.value ?? '').toLowerCase().includes(term))\n );\n }\n\n // Apply sorting (client-side for the streaming snapshot; the datatable in\n // [data] mode also auto-sorts on header clicks).\n const sortCols = this.settings().sortColumns;\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.attributes.find(attr => attr.name === col.property)?.value ?? '';\n const bVal = b.attributes.find(attr => attr.name === col.property)?.value ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n if (cmp !== 0) return col.direction === 'descending' ? -cmp : cmp;\n }\n return 0;\n });\n }\n\n this.streamItems.set(items);\n this.resultCount.set(items.length);\n }\n}\n","<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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\n } @else {\n {{ (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) }}\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mintplayer-ng-spark-query-list.mjs","sources":["../../query-list/src/spark-query-list.component.ts","../../query-list/src/spark-query-list.component.html","../../query-list/mintplayer-ng-spark-query-list.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, output, signal, TemplateRef, Type } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, Router, RouterModule } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, DatatableSettings, type BsDatatableFetch } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { SortColumn } from '@mintplayer/pagination';\nimport { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n AttributeValuePipe,\n ReferenceChipsPipe,\n} from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SPARK_ATTRIBUTE_RENDERERS, rendererValue, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n EntityType,\n EntityAttributeDefinition,\n LookupReference,\n PersistentObject,\n SparkQuery,\n ShowedOn,\n hasShowedOnFlag,\n} from '@mintplayer/ng-spark/models';\n\n@Component({\n selector: 'spark-query-list',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe, ReferenceChipsPipe],\n templateUrl: './spark-query-list.component.html',\n styleUrl: './spark-query-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.virtual-scrolling]': 'isVirtualScrolling()'\n }\n})\nexport class SparkQueryListComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly streamingService = inject(SparkStreamingService);\n protected readonly lang = inject(SparkLanguageService);\n private readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n private readonly destroyRef = inject(DestroyRef);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n rowClicked = output<PersistentObject>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n errorMessage = signal<string | null>(null);\n query = signal<SparkQuery | null>(null);\n entityType = signal<EntityType | null>(null);\n allEntityTypes = signal<EntityType[]>([]);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n resultCount = signal<number | null>(null);\n searchTerm: string = '';\n canRead = signal(false);\n canCreate = signal(false);\n customActions = signal<CustomActionDefinition[]>([]);\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n private allItems = signal<PersistentObject[]>([]);\n streamItems = signal<PersistentObject[]>([]);\n fetchFn = signal<BsDatatableFetch<PersistentObject> | null>(null);\n settings = signal(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => this.onParamsChange(params));\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n }\n\n private async onParamsChange(params: any): Promise<void> {\n // Reset prior-route state so we render the spinner (not stale rows from\n // the previous query) while the new query/entityType resolve.\n this.entityType.set(null);\n this.fetchFn.set(null);\n this.resultCount.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n let resolvedQuery: SparkQuery | null = null;\n let resolvedEntityType: EntityType | null = null;\n let resolvedEntityTypes: EntityType[] = [];\n\n if (queryId) {\n resolvedQuery = await this.sparkService.getQuery(queryId);\n if (resolvedQuery) {\n const result = await this.resolveEntityTypeForQuery(resolvedQuery);\n resolvedEntityType = result.entityType;\n resolvedEntityTypes = result.entityTypes;\n }\n } else if (typeParam) {\n resolvedEntityTypes = await this.sparkService.getEntityTypes();\n resolvedEntityType = resolvedEntityTypes.find(t =>\n t.id === typeParam || t.alias === typeParam\n ) || null;\n\n if (resolvedEntityType) {\n const queries = await this.sparkService.getQueries();\n const singularName = resolvedEntityType.name;\n resolvedQuery = queries.find(q => {\n // Match by explicit entityType\n if (q.entityType === singularName) return true;\n // Match by source name\n const sourceName = this.extractSourceName(q.source);\n const sourceSingular = this.singularize(sourceName);\n return sourceName === singularName ||\n sourceSingular === singularName ||\n sourceName === singularName + 's';\n }) || null;\n }\n }\n\n if (resolvedQuery) this.query.set(resolvedQuery);\n\n if (resolvedEntityType) {\n this.entityType.set(resolvedEntityType);\n this.allEntityTypes.set(resolvedEntityTypes);\n\n const initialSortColumns: SortColumn[] = (resolvedQuery?.sortColumns || []).map(sc => ({\n property: sc.property,\n direction: sc.direction === 'desc' ? 'descending' as const : 'ascending' as const\n }));\n\n this.settings.set(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: initialSortColumns\n }));\n\n if (resolvedQuery?.isStreamingQuery) {\n // Streaming: WebSocket feeds allItems; the datatable binds [data]=\"streamItems()\".\n this.connectStreaming(resolvedQuery.id);\n } else if (resolvedQuery) {\n // Non-streaming: the datatable drives paging/sorting via [(settings)] and calls\n // fetchFn per page. Virtual scrolling is just the [virtualScroll] template flag —\n // the datatable front-loads all pages from fetchFn when virtual.\n this.fetchFn.set(this.makeFetch(resolvedQuery));\n }\n\n this.loadLookupReferenceOptions();\n const [permissions, actions] = await Promise.all([\n this.sparkService.getPermissions(resolvedEntityType.id),\n this.sparkService.getCustomActions(resolvedEntityType.id),\n ]);\n this.canRead.set(permissions.canRead);\n this.canCreate.set(permissions.canCreate);\n this.customActions.set(actions.filter(a => a.showedOn === 'list' || a.showedOn === 'both'));\n }\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n if (action.confirmationMessageKey) {\n const message = this.lang.t(action.confirmationMessageKey) || 'Are you sure?';\n if (!confirm(message)) return;\n }\n try {\n await this.sparkService.executeCustomAction(this.entityType()!.id, action.name);\n this.customActionExecuted.emit({ action });\n if (action.refreshOnCompleted) {\n this.refresh();\n }\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || 'Action failed');\n }\n }\n\n private async resolveEntityTypeForQuery(query: SparkQuery): Promise<{ entityType: EntityType | null; entityTypes: EntityType[] }> {\n const entityTypes = await this.sparkService.getEntityTypes();\n\n // If entityType is explicitly set on the query, use it directly\n if (query.entityType) {\n const type = entityTypes.find(t =>\n t.name === query.entityType || t.alias === query.entityType?.toLowerCase()\n );\n return { entityType: type || null, entityTypes };\n }\n\n // For Database.X sources, extract the property name and try to match\n const sourceName = this.extractSourceName(query.source);\n const singularName = this.singularize(sourceName);\n const type = entityTypes.find(t =>\n t.name === sourceName ||\n t.name === singularName ||\n t.clrType.endsWith(singularName)\n );\n return { entityType: type || null, entityTypes };\n }\n\n private extractSourceName(source: string): string {\n const dotIndex = source.indexOf('.');\n return dotIndex >= 0 ? source.substring(dotIndex + 1) : source;\n }\n\n private singularize(plural: string): string {\n const irregulars: { [key: string]: string } = {\n 'People': 'Person',\n 'Children': 'Child',\n 'Men': 'Men',\n 'Women': 'Woman'\n };\n if (irregulars[plural]) return irregulars[plural];\n\n if (plural.endsWith('ies')) {\n return plural.slice(0, -3) + 'y';\n }\n if (plural.endsWith('es')) {\n return plural.slice(0, -2);\n }\n if (plural.endsWith('s')) {\n return plural.slice(0, -1);\n }\n return plural;\n }\n\n /**\n * Builds the server-side fetch callback the datatable invokes per page/sort.\n * Reads `searchTerm` live, so a settings change (or a new fetchFn identity)\n * refetches with the current search term.\n */\n private makeFetch(query: SparkQuery): BsDatatableFetch<PersistentObject> {\n return (req) => this.sparkService.executeQuery(query.id, {\n sortColumns: req.sortColumns,\n skip: (req.page - 1) * req.perPage,\n take: req.perPage,\n search: this.searchTerm || undefined,\n }).then(r => {\n this.errorMessage.set(null);\n this.resultCount.set(r.totalRecords);\n return {\n data: r.data,\n totalRecords: r.totalRecords,\n totalPages: Math.ceil(r.totalRecords / req.perPage) || 1,\n perPage: req.perPage,\n page: req.page,\n };\n }).catch((e: any) => {\n this.errorMessage.set(e.error?.error || e.message || 'An unexpected error occurred');\n this.resultCount.set(0);\n return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };\n });\n }\n\n /** Force a refetch (e.g. after a custom action) without changing page/sort. */\n private refresh(): void {\n if (this.isStreaming()) {\n this.applyFilter();\n return;\n }\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q));\n }\n\n onSearchChange(): void {\n if (this.isStreaming()) {\n this.applyFilter();\n return;\n }\n // Reset to page 1 for the new search.\n const s = this.settings();\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n // Re-assign the fetch callback so the datatable refetches even when\n // page/perPage/sort are unchanged. ng-bootstrap 22.4's web component dedupes\n // reloads by {sortColumns, perPage, page}; setting a new fetch identity resets\n // that key (set fetch → _lastReloadKey = null) and forces the reload. makeFetch\n // reads searchTerm live (mirrors refresh()).\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q));\n }\n\n clearSearch(): void {\n this.searchTerm = '';\n this.onSearchChange();\n }\n\n isVirtualScrolling = computed(() => this.query()?.renderMode === 'VirtualScrolling');\n\n visibleAttributes = computed(() => {\n return this.entityType()?.attributes\n .filter(a => a.isVisible && hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n getColumnRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.columnComponent ?? null;\n }\n\n getColumnRendererInputs(component: Type<any>, item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any> {\n const itemAttr = item.attributes.find(a => a.name === attr.name);\n return withDeclaredInputs(component, {\n value: rendererValue(itemAttr),\n attribute: attr,\n options: attr.rendererOptions,\n item,\n });\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.visibleAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const result = await this.sparkService.getLookupReference(name);\n return [name, result] as const;\n })\n );\n this.lookupReferenceOptions.set(entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, LookupReference>));\n }\n\n onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) {\n this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n }\n\n private connectStreaming(queryId: string): void {\n this.disconnectStreaming();\n this.isStreaming.set(true);\n\n this.streamingSub = this.streamingService.connectToStreamingQuery(queryId).subscribe({\n next: (message) => this.handleStreamingMessage(message),\n error: (err) => {\n this.errorMessage.set(err?.message || 'Streaming connection failed');\n this.isStreaming.set(false);\n },\n complete: () => {\n this.isStreaming.set(false);\n }\n });\n }\n\n private disconnectStreaming(): void {\n if (this.streamingSub) {\n this.streamingSub.unsubscribe();\n this.streamingSub = null;\n }\n this.isStreaming.set(false);\n }\n\n private handleStreamingMessage(message: StreamingMessage): void {\n switch (message.type) {\n case 'snapshot':\n this.errorMessage.set(null);\n this.allItems.set(message.data);\n this.applyFilter();\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n const currentItems = this.allItems();\n const updatedItems = currentItems.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n\n // Clone the item and update only changed attribute values\n const updatedAttributes = item.attributes.map(attr => {\n if (attr.name in patch.attributes) {\n return { ...attr, value: patch.attributes[attr.name] };\n }\n return attr;\n });\n\n return { ...item, attributes: updatedAttributes };\n });\n\n this.allItems.set(updatedItems);\n this.applyFilter();\n }\n break;\n\n case 'error':\n this.errorMessage.set(message.message);\n break;\n }\n }\n\n private applyFilter(): void {\n let items = this.allItems();\n\n // Apply search filter\n if (this.searchTerm) {\n const term = this.searchTerm.toLowerCase();\n items = items.filter(item =>\n item.attributes.some(a => String(a.value ?? '').toLowerCase().includes(term))\n );\n }\n\n // Apply sorting (client-side for the streaming snapshot; the datatable in\n // [data] mode also auto-sorts on header clicks).\n const sortCols = this.settings().sortColumns;\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.attributes.find(attr => attr.name === col.property)?.value ?? '';\n const bVal = b.attributes.find(attr => attr.name === col.property)?.value ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n if (cmp !== 0) return col.direction === 'descending' ? -cmp : cmp;\n }\n return 0;\n });\n }\n\n this.streamItems.set(items);\n this.resultCount.set(items.length);\n }\n}\n","<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(rendererType, 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 if (attr.dataType === 'Reference' && attr.isArray) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of (attr.name | referenceChips:item); track chip.id) {\n <span class=\"badge rounded-pill border bg-body-secondary text-body px-3 py-2\">{{ chip.label }}</span>\n }\n </span>\n } @else {\n {{ (attr.name | attributeValue:item:entityType():lookupReferenceOptions():allEntityTypes()) }}\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,28 @@
1
- import { InjectionToken } from '@angular/core';
1
+ import { reflectComponentType, InjectionToken } from '@angular/core';
2
+
3
+ // Reflection result cached per component type: the input builders are template
4
+ // expressions re-evaluated every CD pass (and query-list virtual-scrolls).
5
+ const declaredInputs = new Map();
6
+ /**
7
+ * Drops entries the component doesn't declare, so every contract member is
8
+ * genuinely optional — NgComponentOutlet throws on an undeclared input.
9
+ */
10
+ function withDeclaredInputs(component, inputs) {
11
+ let declared = declaredInputs.get(component);
12
+ if (!declared) {
13
+ declared = new Set(reflectComponentType(component)?.inputs.map(i => i.templateName) ?? []);
14
+ declaredInputs.set(component, declared);
15
+ }
16
+ return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared.has(k)));
17
+ }
18
+ /**
19
+ * The renderer-facing value of an attribute: the flat value, or for AsDetail
20
+ * attributes (whose flat value the server nulls on purpose) the nested
21
+ * PersistentObject (single) / PersistentObject[] (array).
22
+ */
23
+ function rendererValue(attr) {
24
+ return attr?.value ?? attr?.object ?? attr?.objects;
25
+ }
2
26
 
3
27
  const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken('SparkAttributeRenderers', { factory: () => [] });
4
28
  /**
@@ -21,5 +45,5 @@ function provideSparkAttributeRenderers(renderers) {
21
45
  * Generated bundle index. Do not edit.
22
46
  */
23
47
 
24
- export { SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers };
48
+ export { SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers, rendererValue, withDeclaredInputs };
25
49
  //# sourceMappingURL=mintplayer-ng-spark-renderers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-renderers.mjs","sources":["../../renderers/src/spark-attribute-renderer-registry.ts","../../renderers/mintplayer-ng-spark-renderers.ts"],"sourcesContent":["import { InjectionToken, Provider, Type } from '@angular/core';\n\nexport interface SparkAttributeRendererRegistration {\n /** The renderer name (must match attr.renderer in model JSON) */\n name: string;\n /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. */\n detailComponent: Type<any>;\n /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. */\n columnComponent: Type<any>;\n /** Optional component for create/edit forms. Must implement SparkAttributeEditRenderer. When omitted, the default input is used. */\n editComponent?: Type<any>;\n}\n\nexport const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken<SparkAttributeRendererRegistration[]>(\n 'SparkAttributeRenderers',\n { factory: () => [] }\n);\n\n/**\n * Register custom attribute renderers globally.\n *\n * @example\n * provideSparkAttributeRenderers([\n * { name: 'video-player', detailComponent: VideoDetailComponent, columnComponent: VideoColumnComponent },\n * { name: 'color-swatch', detailComponent: ColorDetailComponent, columnComponent: ColorColumnComponent },\n * ])\n */\nexport function provideSparkAttributeRenderers(\n renderers: SparkAttributeRendererRegistration[]\n): Provider {\n return {\n provide: SPARK_ATTRIBUTE_RENDERERS,\n useValue: renderers,\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAaO,MAAM,yBAAyB,GAAG,IAAI,cAAc,CACzD,yBAAyB,EACzB,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;AAGvB;;;;;;;;AAQG;AACG,SAAU,8BAA8B,CAC5C,SAA+C,EAAA;IAE/C,OAAO;AACL,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,QAAQ,EAAE,SAAS;KACpB;AACH;;AClCA;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-renderers.mjs","sources":["../../renderers/src/renderer-inputs.ts","../../renderers/src/spark-attribute-renderer-registry.ts","../../renderers/mintplayer-ng-spark-renderers.ts"],"sourcesContent":["import { reflectComponentType, Type } from '@angular/core';\nimport { PersistentObjectAttribute } from '@mintplayer/ng-spark/models';\n\n// Reflection result cached per component type: the input builders are template\n// expressions re-evaluated every CD pass (and query-list virtual-scrolls).\nconst declaredInputs = new Map<Type<any>, Set<string>>();\n\n/**\n * Drops entries the component doesn't declare, so every contract member is\n * genuinely optional — NgComponentOutlet throws on an undeclared input.\n */\nexport function withDeclaredInputs(component: Type<any>, inputs: Record<string, any>): Record<string, any> {\n let declared = declaredInputs.get(component);\n if (!declared) {\n declared = new Set(reflectComponentType(component)?.inputs.map(i => i.templateName) ?? []);\n declaredInputs.set(component, declared);\n }\n return Object.fromEntries(Object.entries(inputs).filter(([k]) => declared!.has(k)));\n}\n\n/**\n * The renderer-facing value of an attribute: the flat value, or for AsDetail\n * attributes (whose flat value the server nulls on purpose) the nested\n * PersistentObject (single) / PersistentObject[] (array).\n */\nexport function rendererValue(attr: PersistentObjectAttribute | undefined): any {\n return attr?.value ?? attr?.object ?? attr?.objects;\n}\n","import { InjectionToken, Provider, Type } from '@angular/core';\n\nexport interface SparkAttributeRendererRegistration {\n /** The renderer name (must match attr.renderer in model JSON) */\n name: string;\n /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. Omit for a column/edit-only renderer. */\n detailComponent?: Type<any> | null;\n /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. Omit for a detail/edit-only renderer. */\n columnComponent?: Type<any> | null;\n /** Optional component for create/edit forms. Must implement SparkAttributeEditRenderer. When omitted, the default input is used. */\n editComponent?: Type<any>;\n}\n\nexport const SPARK_ATTRIBUTE_RENDERERS = new InjectionToken<SparkAttributeRendererRegistration[]>(\n 'SparkAttributeRenderers',\n { factory: () => [] }\n);\n\n/**\n * Register custom attribute renderers globally.\n *\n * @example\n * provideSparkAttributeRenderers([\n * { name: 'video-player', detailComponent: VideoDetailComponent, columnComponent: VideoColumnComponent },\n * { name: 'color-swatch', detailComponent: ColorDetailComponent, columnComponent: ColorColumnComponent },\n * ])\n */\nexport function provideSparkAttributeRenderers(\n renderers: SparkAttributeRendererRegistration[]\n): Provider {\n return {\n provide: SPARK_ATTRIBUTE_RENDERERS,\n useValue: renderers,\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAGA;AACA;AACA,MAAM,cAAc,GAAG,IAAI,GAAG,EAA0B;AAExD;;;AAGG;AACG,SAAU,kBAAkB,CAAC,SAAoB,EAAE,MAA2B,EAAA;IAClF,IAAI,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;IAC5C,IAAI,CAAC,QAAQ,EAAE;QACb,QAAQ,GAAG,IAAI,GAAG,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;AAC1F,QAAA,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC;IACzC;AACA,IAAA,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,QAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF;AAEA;;;;AAIG;AACG,SAAU,aAAa,CAAC,IAA2C,EAAA;IACvE,OAAO,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,OAAO;AACrD;;ACdO,MAAM,yBAAyB,GAAG,IAAI,cAAc,CACzD,yBAAyB,EACzB,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;AAGvB;;;;;;;;AAQG;AACG,SAAU,8BAA8B,CAC5C,SAA+C,EAAA;IAE/C,OAAO;AACL,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,QAAQ,EAAE,SAAS;KACpB;AACH;;AClCA;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mintplayer/ng-spark",
3
3
  "private": false,
4
- "version": "22.0.10",
4
+ "version": "22.0.11",
5
5
  "description": "Angular component library for MintPlayer.Spark CRUD applications",
6
6
  "repository": {
7
7
  "type": "git",
@@ -45,10 +45,10 @@ declare class SparkPoDetailComponent {
45
45
  groupsForTab(tab: AttributeTab): AttributeGroup[];
46
46
  attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[];
47
47
  getDetailRendererComponent(attr: EntityAttributeDefinition): Type<any> | null;
48
- getDetailRendererInputs(attr: EntityAttributeDefinition, item: PersistentObject): Record<string, any>;
48
+ getDetailRendererInputs(component: Type<any>, attr: EntityAttributeDefinition, item: PersistentObject): Record<string, any>;
49
49
  /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */
50
50
  getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null;
51
- getAsDetailCellRendererInputs(row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
51
+ getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
52
52
  private loadLookupReferenceOptions;
53
53
  private loadAsDetailTypes;
54
54
  onCustomAction(action: CustomActionDefinition): Promise<void>;
@@ -81,7 +81,7 @@ declare class SparkSubQueryComponent {
81
81
  private makeFetch;
82
82
  private loadLookupReferenceOptions;
83
83
  getColumnRendererComponent(attr: EntityAttributeDefinition): Type<any> | null;
84
- getColumnRendererInputs(item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any>;
84
+ getColumnRendererInputs(component: Type<any>, item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any>;
85
85
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkSubQueryComponent, never>;
86
86
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkSubQueryComponent, "spark-sub-query", never, { "queryId": { "alias": "queryId"; "required": true; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": true; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
87
87
  }
@@ -59,13 +59,13 @@ declare class SparkPoFormComponent {
59
59
  onReferenceValueChange(attr: EntityAttributeDefinition, id: string | null): void;
60
60
  onLookupValueChange(attr: EntityAttributeDefinition, key: string | null): void;
61
61
  getEditRendererComponent(attr: EntityAttributeDefinition): Type<any> | null;
62
- getEditRendererInputs(attr: EntityAttributeDefinition): Record<string, any>;
62
+ getEditRendererInputs(component: Type<any>, attr: EntityAttributeDefinition): Record<string, any>;
63
63
  /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */
64
64
  getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null;
65
- getAsDetailCellRendererInputs(row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
65
+ getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
66
66
  /** Edit-renderer for an inline AsDetail cell (so inline editing honors `col.renderer`, not just display). */
67
67
  getAsDetailCellEditRenderer(col: EntityAttributeDefinition): Type<any> | null;
68
- getAsDetailCellEditRendererInputs(row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
68
+ getAsDetailCellEditRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
69
69
  hasError(attrName: string): boolean;
70
70
  private inlineErrorPath;
71
71
  hasInlineError(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): boolean;
@@ -56,7 +56,7 @@ declare class SparkQueryListComponent {
56
56
  isVirtualScrolling: _angular_core.Signal<boolean>;
57
57
  visibleAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
58
58
  getColumnRendererComponent(attr: EntityAttributeDefinition): Type<any> | null;
59
- getColumnRendererInputs(item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any>;
59
+ getColumnRendererInputs(component: Type<any>, item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any>;
60
60
  private loadLookupReferenceOptions;
61
61
  onCreate(): void;
62
62
  private connectStreaming;
@@ -1,31 +1,58 @@
1
- import { InputSignal, InjectionToken, Type, Provider } from '@angular/core';
2
- import { EntityAttributeDefinition } from '@mintplayer/ng-spark/models';
1
+ import { Type, InputSignal, InjectionToken, Provider } from '@angular/core';
2
+ import { PersistentObjectAttribute, EntityAttributeDefinition, PersistentObject } from '@mintplayer/ng-spark/models';
3
+
4
+ /**
5
+ * Drops entries the component doesn't declare, so every contract member is
6
+ * genuinely optional — NgComponentOutlet throws on an undeclared input.
7
+ */
8
+ declare function withDeclaredInputs(component: Type<any>, inputs: Record<string, any>): Record<string, any>;
9
+ /**
10
+ * The renderer-facing value of an attribute: the flat value, or for AsDetail
11
+ * attributes (whose flat value the server nulls on purpose) the nested
12
+ * PersistentObject (single) / PersistentObject[] (array).
13
+ */
14
+ declare function rendererValue(attr: PersistentObjectAttribute | undefined): any;
3
15
 
4
16
  /**
5
17
  * Contract for detail-page renderers (spark-po-detail).
6
18
  * Displays a single attribute value in the PO detail view.
7
19
  */
8
20
  interface SparkAttributeDetailRenderer {
9
- /** The current attribute value */
10
- value: InputSignal<any>;
21
+ /**
22
+ * The current attribute value. For an AsDetail attribute this is the nested
23
+ * PersistentObject (single) or PersistentObject[] (array).
24
+ */
25
+ value?: InputSignal<any>;
11
26
  /** The attribute definition metadata */
12
- attribute: InputSignal<EntityAttributeDefinition | undefined>;
27
+ attribute?: InputSignal<EntityAttributeDefinition | undefined>;
13
28
  /** Renderer-specific options from rendererOptions */
14
- options: InputSignal<Record<string, any> | undefined>;
15
- /** The full form data (for cross-field dependencies) */
16
- formData: InputSignal<Record<string, any>>;
29
+ options?: InputSignal<Record<string, any> | undefined>;
30
+ /** The full form data (for cross-field dependencies); AsDetail keys carry the nested PO(s) */
31
+ formData?: InputSignal<Record<string, any>>;
32
+ /** The full PersistentObject being displayed — ids/breadcrumbs the flattened formData drops */
33
+ item?: InputSignal<PersistentObject | undefined>;
17
34
  }
18
35
  /**
19
36
  * Contract for query-list column renderers (spark-query-list).
20
37
  * Displays a compact cell value in the list/grid view.
21
38
  */
22
39
  interface SparkAttributeColumnRenderer {
23
- /** The current attribute value */
24
- value: InputSignal<any>;
40
+ /**
41
+ * The current attribute value. For an AsDetail attribute this is the nested
42
+ * PersistentObject (single) / PersistentObject[] (array) in query-list and
43
+ * sub-query grids, and the flattened value in AsDetail sub-table cells.
44
+ */
45
+ value?: InputSignal<any>;
25
46
  /** The attribute definition metadata */
26
- attribute: InputSignal<EntityAttributeDefinition | undefined>;
47
+ attribute?: InputSignal<EntityAttributeDefinition | undefined>;
27
48
  /** Renderer-specific options from rendererOptions */
28
- options: InputSignal<Record<string, any> | undefined>;
49
+ options?: InputSignal<Record<string, any> | undefined>;
50
+ /**
51
+ * The row this cell belongs to: a PersistentObject in query-list/sub-query
52
+ * grids, a plain record (possibly including the reserved '__sparkBreadcrumbs'
53
+ * key) in AsDetail sub-tables. Passed only when declared.
54
+ */
55
+ item?: InputSignal<PersistentObject | Record<string, any> | undefined>;
29
56
  }
30
57
  /**
31
58
  * Contract for edit-form renderers (spark-po-form on create/edit pages).
@@ -33,22 +60,25 @@ interface SparkAttributeColumnRenderer {
33
60
  */
34
61
  interface SparkAttributeEditRenderer {
35
62
  /** The current attribute value */
36
- value: InputSignal<any>;
63
+ value?: InputSignal<any>;
37
64
  /** The attribute definition metadata */
38
- attribute: InputSignal<EntityAttributeDefinition | undefined>;
65
+ attribute?: InputSignal<EntityAttributeDefinition | undefined>;
39
66
  /** Renderer-specific options from rendererOptions */
40
- options: InputSignal<Record<string, any> | undefined>;
41
- /** Callback to notify parent form of value changes (since NgComponentOutlet doesn't support outputs) */
42
- valueChange: InputSignal<(value: any) => void>;
67
+ options?: InputSignal<Record<string, any> | undefined>;
68
+ /**
69
+ * Callback to notify parent form of value changes (since NgComponentOutlet
70
+ * doesn't support outputs). Not declaring it disables write-back.
71
+ */
72
+ valueChange?: InputSignal<(value: any) => void>;
43
73
  }
44
74
 
45
75
  interface SparkAttributeRendererRegistration {
46
76
  /** The renderer name (must match attr.renderer in model JSON) */
47
77
  name: string;
48
- /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. */
49
- detailComponent: Type<any>;
50
- /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. */
51
- columnComponent: Type<any>;
78
+ /** Component for the PO detail page. Must implement SparkAttributeDetailRenderer. Omit for a column/edit-only renderer. */
79
+ detailComponent?: Type<any> | null;
80
+ /** Component for query-list column cells. Must implement SparkAttributeColumnRenderer. Omit for a detail/edit-only renderer. */
81
+ columnComponent?: Type<any> | null;
52
82
  /** Optional component for create/edit forms. Must implement SparkAttributeEditRenderer. When omitted, the default input is used. */
53
83
  editComponent?: Type<any>;
54
84
  }
@@ -64,5 +94,5 @@ declare const SPARK_ATTRIBUTE_RENDERERS: InjectionToken<SparkAttributeRendererRe
64
94
  */
65
95
  declare function provideSparkAttributeRenderers(renderers: SparkAttributeRendererRegistration[]): Provider;
66
96
 
67
- export { SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers };
97
+ export { SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers, rendererValue, withDeclaredInputs };
68
98
  export type { SparkAttributeColumnRenderer, SparkAttributeDetailRenderer, SparkAttributeEditRenderer, SparkAttributeRendererRegistration };