@mintplayer/ng-spark 22.8.0 → 22.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -273,13 +273,13 @@ class SparkQueryListComponent {
273
273
  this.streamItems.set(items);
274
274
  }
275
275
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
276
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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 }, rowRoute: { classPropertyName: "rowRoute", publicName: "rowRoute", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClicked: "rowClicked", createClicked: "createClicked", customActionExecuted: "customActionExecuted" }, host: { properties: { "class.virtual-scrolling": "isVirtualScrolling()" } }, viewQueries: [{ propertyName: "grid", first: true, predicate: SparkQueryGridComponent, descendants: true, isSignal: true }], 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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\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 spark-query-grid{display:flex;flex-direction:column;min-height:0}: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}.spark-live-badge{font-size:.6em}\n"], dependencies: [{ kind: "component", type: BsBadgeComponent, selector: "bs-badge", inputs: ["type", "unit", "decorative"] }, { kind: "ngmodule", type: CommonModule }, { 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: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { 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: "component", type: SparkQueryGridComponent, selector: "spark-query-grid", inputs: ["queryId", "parentId", "parentType", "data", "search", "columns", "reloadToken", "settings", "selection", "rowRoute"], outputs: ["settingsChange", "selectionChange", "error", "rowClicked", "customActionExecuted"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
276
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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 }, rowRoute: { classPropertyName: "rowRoute", publicName: "rowRoute", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClicked: "rowClicked", createClicked: "createClicked", customActionExecuted: "customActionExecuted" }, host: { properties: { "class.virtual-scrolling": "isVirtualScrolling()" } }, viewQueries: [{ propertyName: "grid", first: true, predicate: SparkQueryGridComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.spark-actionbar-wide]=\"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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n", styles: [".spark-actionbar{--_bleed-block: var(--spark-content-inset-block, 0px);--_bleed-inline: var(--spark-content-inset-inline, 0px);--_gutter: var(--spark-actionbar-gutter, 1rem);position:sticky;top:calc(-1 * var(--_bleed-block));z-index:400;background-color:var(--bs-tertiary-bg);border-bottom:1px solid var(--bs-border-color);margin-bottom:1rem;margin-inline:calc(-1 * var(--_bleed-inline));padding-inline:calc(var(--_bleed-inline) + var(--_gutter))}.spark-actionbar .btn{border-radius:0}.spark-actionbar-wide{--spark-actionbar-gutter: 1.5rem}:host{display:flex;flex-direction:column;flex:1;min-height:0}:host.virtual-scrolling{margin:0 calc(-1 * var(--spark-main-padding, 1.5rem)) calc(-1 * var(--spark-main-padding, 1.5rem));--spark-content-inset-inline: 0px}:host.virtual-scrolling spark-query-grid{display:flex;flex-direction:column;min-height:0}: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}.spark-live-badge{font-size:.6em}\n"], dependencies: [{ kind: "component", type: BsBadgeComponent, selector: "bs-badge", inputs: ["type", "unit", "decorative"] }, { kind: "ngmodule", type: CommonModule }, { 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: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { 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: "component", type: SparkQueryGridComponent, selector: "spark-query-grid", inputs: ["queryId", "parentId", "parentType", "data", "search", "columns", "reloadToken", "settings", "selection", "rowRoute"], outputs: ["settingsChange", "selectionChange", "error", "rowClicked", "customActionExecuted"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
277
277
  }
278
278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkQueryListComponent, decorators: [{
279
279
  type: Component,
280
280
  args: [{ selector: 'spark-query-list', imports: [BsBadgeComponent, CommonModule, NgTemplateOutlet, FormsModule, BsAlertComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryGridComponent, ResolveTranslationPipe, TranslateKeyPipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
281
281
  '[class.virtual-scrolling]': 'isVirtualScrolling()'
282
- }, 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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\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 spark-query-grid{display:flex;flex-direction:column;min-height:0}: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}.spark-live-badge{font-size:.6em}\n"] }]
282
+ }, template: "<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.spark-actionbar-wide]=\"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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n", styles: [".spark-actionbar{--_bleed-block: var(--spark-content-inset-block, 0px);--_bleed-inline: var(--spark-content-inset-inline, 0px);--_gutter: var(--spark-actionbar-gutter, 1rem);position:sticky;top:calc(-1 * var(--_bleed-block));z-index:400;background-color:var(--bs-tertiary-bg);border-bottom:1px solid var(--bs-border-color);margin-bottom:1rem;margin-inline:calc(-1 * var(--_bleed-inline));padding-inline:calc(var(--_bleed-inline) + var(--_gutter))}.spark-actionbar .btn{border-radius:0}.spark-actionbar-wide{--spark-actionbar-gutter: 1.5rem}:host{display:flex;flex-direction:column;flex:1;min-height:0}:host.virtual-scrolling{margin:0 calc(-1 * var(--spark-main-padding, 1.5rem)) calc(-1 * var(--spark-main-padding, 1.5rem));--spark-content-inset-inline: 0px}:host.virtual-scrolling spark-query-grid{display:flex;flex-direction:column;min-height:0}: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}.spark-live-badge{font-size:.6em}\n"] }]
283
283
  }], ctorParameters: () => [], propDecorators: { extraActionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraActionsTemplate", required: false }] }], showCustomActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomActions", required: false }] }], rowRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowRoute", required: false }] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], createClicked: [{ type: i0.Output, args: ["createClicked"] }], customActionExecuted: [{ type: i0.Output, args: ["customActionExecuted"] }], grid: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SparkQueryGridComponent), { isSignal: true }] }] } });
284
284
 
285
285
  /**
@@ -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, DestroyRef, computed, effect, inject, input, output, signal, viewChild, TemplateRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, ParamMap, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\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 { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkQueryGridComponent } from '@mintplayer/ng-spark/grid';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n QueryColumn,\n QueryResultItem,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * The routed query page: chrome around one {@link SparkQueryGridComponent}.\n *\n * It owns what is genuinely page-shaped and route-shaped, and nothing else:\n *\n * - **Route resolution.** It has no `queryId` input; it reads `paramMap`, and it serves two\n * routes — `query/:queryId`, and `po/:type`, which resolves an entity type to a query. That\n * second one is type-to-query resolution, not query rendering, and is why this component still\n * exists rather than the router pointing at the grid.\n * - **Streaming.** The websocket lives here so it stays out of every PO detail page's bundle;\n * the snapshot is filtered and sorted client-side and handed to the grid as `[data]`.\n * - The action bar, the caption, the LIVE badge, the search box and the New button.\n *\n * The grid itself — columns, cells, paging, the row link, selection, custom-action execution —\n * is the shared component. This page previously wrote out `<bs-datatable>` twice, once per\n * transport, with a shared row template between them; both are gone.\n */\n@Component({\n selector: 'spark-query-list',\n imports: [BsBadgeComponent, CommonModule, NgTemplateOutlet, FormsModule, BsAlertComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryGridComponent, ResolveTranslationPipe, TranslateKeyPipe],\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 private readonly destroyRef = inject(DestroyRef);\n protected readonly lang = inject(SparkLanguageService);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n /**\n * Forwarded to the grid, so a query PAGE can replace its row links — the same reason the card\n * forwards it. Without this the escape hatch existed only for a directly-embedded grid.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n rowClicked = output<QueryResultItem>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /** The query the grid should render, resolved from the route. Null until it is known. */\n queryId = signal<string | null>(null);\n errorMessage = signal<string | null>(null);\n searchTerm = signal('');\n\n private readonly grid = viewChild(SparkQueryGridComponent);\n\n /**\n * Grid state, surfaced for this page's chrome.\n *\n * Optional `viewChild`, read defensively: the action bar and caption render above the grid, so\n * on the first change-detection pass the query has not resolved yet.\n */\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n protected readonly entityType = computed(() => this.grid()?.entityType() ?? null);\n protected readonly customActions = computed(() => this.grid()?.customActions() ?? []);\n protected readonly canCreate = computed(() => this.grid()?.canCreate() ?? false);\n protected readonly resultCount = computed(() => this.grid()?.resultCount() ?? null);\n protected readonly isVirtualScrolling = computed(() => this.grid()?.isVirtualScrolling() ?? false);\n protected readonly gridError = computed(() => this.grid()?.errorMessage() ?? null);\n\n /** Whether an action's selection rule is satisfied. Delegated: the grid holds the selection. */\n protected isActionEnabled(action: CustomActionDefinition): boolean {\n return this.grid()?.isActionEnabled(action) ?? false;\n }\n\n // --- streaming ---------------------------------------------------------------\n\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n /** Columns as sent with the stream's snapshot; empty until it arrives. */\n protected readonly streamColumns = signal<QueryColumn[]>([]);\n private readonly allItems = signal<QueryResultItem[]>([]);\n private readonly streamItems = signal<QueryResultItem[]>([]);\n\n /**\n * Rows handed to the grid, or `null` to let it fetch for itself.\n *\n * Null for a normal query — an empty array would read as \"here are no rows\" and suppress the\n * fetch entirely.\n */\n protected readonly gridData = computed(() =>\n this.query()?.isStreamingQuery ? this.streamItems() : null);\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => {\n // The handler is async and this is a subscribe, so a rejection lands nowhere: the metadata\n // load would reject, the query would stay null, and the template would render a spinner\n // FOREVER — while this component has had an errorMessage surface all along that only the\n // fetch path ever reached.\n this.onParamsChange(params).catch((e: unknown) => this.reportLoadFailure(e as HttpErrorResponse));\n });\n\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n\n // Connect the socket once the grid has resolved a streaming query, and disconnect whenever it\n // resolves anything else. The grid knows not to fetch for a streaming query, so there is no\n // window in which both transports are live.\n effect(() => {\n const q = this.query();\n if (q?.isStreamingQuery) {\n this.connectStreaming(q.id);\n } else {\n this.disconnectStreaming();\n }\n });\n\n // Client-side filter and sort for the streaming snapshot. The server never sees these: there\n // is no request to attach them to.\n effect(() => {\n this.searchTerm();\n this.grid()?.settings();\n this.allItems();\n if (this.isStreaming()) this.applyFilter();\n });\n }\n\n private async onParamsChange(params: ParamMap): Promise<void> {\n this.errorMessage.set(null);\n this.queryId.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.streamColumns.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n if (queryId) {\n this.queryId.set(queryId);\n return;\n }\n\n if (!typeParam) return;\n\n // `po/:type` — the route names an entity type, so find the query that lists it. The grid takes\n // a query, and this translation is the only reason it cannot take the route directly.\n const [entityTypes, queries] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.sparkService.getQueries(),\n ]);\n const entityType = entityTypes.find(t => t.id === typeParam || t.alias === typeParam);\n if (!entityType) {\n this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n return;\n }\n\n const singularName = entityType.name;\n const match = queries.find(q => {\n if (q.entityType === singularName) return true;\n const sourceName = q.source.includes('.') ? q.source.substring(q.source.indexOf('.') + 1) : q.source;\n return sourceName === singularName || sourceName === singularName + 's';\n });\n\n if (match) this.queryId.set(match.alias || match.id);\n else this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n }\n\n /**\n * A load failure has to render, not just be swallowed: a denied query answers 404 (audit M-3, so\n * existence is not leaked), which is indistinguishable from a missing one — hence a deliberately\n * generic message rather than a guess at which it was.\n */\n private reportLoadFailure(err: HttpErrorResponse): void {\n this.errorMessage.set(\n err?.status === 404\n ? (this.lang.t('spark.query.unavailable') || 'This list is not available.')\n : (err?.error?.error || err?.message || 'An unexpected error occurred'));\n }\n\n protected async onCustomAction(action: CustomActionDefinition): Promise<void> {\n await this.grid()?.onCustomAction(action);\n }\n\n protected onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n\n protected clearSearch(): void {\n this.searchTerm.set('');\n }\n\n private connectStreaming(queryId: string): void {\n if (this.streamingSub) return;\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: () => this.isStreaming.set(false),\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 // Columns come with the snapshot and never change for the life of the stream, so they are\n // stored once and handed to the grid alongside the rows — a projection cannot describe\n // itself, and the grid has no entity-type metadata to fall back on any more.\n this.streamColumns.set(message.columns);\n this.allItems.set(message.data);\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n this.allItems.update(items => items.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n return {\n ...item,\n values: item.values.map(v =>\n v.key in patch.values ? { ...v, value: patch.values[v.key] } : v),\n };\n }));\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 const term = this.searchTerm().toLowerCase();\n if (term) {\n items = items.filter(item =>\n item.values.some(v => String(v.value ?? '').toLowerCase().includes(term)));\n }\n\n // The datatable in `[data]` mode also sorts on header clicks, but the sort must survive a\n // patch: re-deriving from `allItems` without re-applying it would silently reorder the grid\n // under the user on every update.\n const sortCols = this.grid()?.settings().sortColumns ?? [];\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.values.find(v => v.key === col.property)?.value ?? '';\n const bVal = b.values.find(v => v.key === 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 }\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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\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, DestroyRef, computed, effect, inject, input, output, signal, viewChild, TemplateRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subscription } from 'rxjs';\nimport { CommonModule, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, ParamMap, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\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 { SparkService, SparkStreamingService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkQueryGridComponent } from '@mintplayer/ng-spark/grid';\nimport {\n CustomActionDefinition,\n StreamingMessage,\n QueryColumn,\n QueryResultItem,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * The routed query page: chrome around one {@link SparkQueryGridComponent}.\n *\n * It owns what is genuinely page-shaped and route-shaped, and nothing else:\n *\n * - **Route resolution.** It has no `queryId` input; it reads `paramMap`, and it serves two\n * routes — `query/:queryId`, and `po/:type`, which resolves an entity type to a query. That\n * second one is type-to-query resolution, not query rendering, and is why this component still\n * exists rather than the router pointing at the grid.\n * - **Streaming.** The websocket lives here so it stays out of every PO detail page's bundle;\n * the snapshot is filtered and sorted client-side and handed to the grid as `[data]`.\n * - The action bar, the caption, the LIVE badge, the search box and the New button.\n *\n * The grid itself — columns, cells, paging, the row link, selection, custom-action execution —\n * is the shared component. This page previously wrote out `<bs-datatable>` twice, once per\n * transport, with a shared row template between them; both are gone.\n */\n@Component({\n selector: 'spark-query-list',\n imports: [BsBadgeComponent, CommonModule, NgTemplateOutlet, FormsModule, BsAlertComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryGridComponent, ResolveTranslationPipe, TranslateKeyPipe],\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 private readonly destroyRef = inject(DestroyRef);\n protected readonly lang = inject(SparkLanguageService);\n\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n showCustomActions = input(true);\n\n /**\n * Forwarded to the grid, so a query PAGE can replace its row links — the same reason the card\n * forwards it. Without this the escape hatch existed only for a directly-embedded grid.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n rowClicked = output<QueryResultItem>();\n createClicked = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /** The query the grid should render, resolved from the route. Null until it is known. */\n queryId = signal<string | null>(null);\n errorMessage = signal<string | null>(null);\n searchTerm = signal('');\n\n private readonly grid = viewChild(SparkQueryGridComponent);\n\n /**\n * Grid state, surfaced for this page's chrome.\n *\n * Optional `viewChild`, read defensively: the action bar and caption render above the grid, so\n * on the first change-detection pass the query has not resolved yet.\n */\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n protected readonly entityType = computed(() => this.grid()?.entityType() ?? null);\n protected readonly customActions = computed(() => this.grid()?.customActions() ?? []);\n protected readonly canCreate = computed(() => this.grid()?.canCreate() ?? false);\n protected readonly resultCount = computed(() => this.grid()?.resultCount() ?? null);\n protected readonly isVirtualScrolling = computed(() => this.grid()?.isVirtualScrolling() ?? false);\n protected readonly gridError = computed(() => this.grid()?.errorMessage() ?? null);\n\n /** Whether an action's selection rule is satisfied. Delegated: the grid holds the selection. */\n protected isActionEnabled(action: CustomActionDefinition): boolean {\n return this.grid()?.isActionEnabled(action) ?? false;\n }\n\n // --- streaming ---------------------------------------------------------------\n\n isStreaming = signal(false);\n private streamingSub: Subscription | null = null;\n /** Columns as sent with the stream's snapshot; empty until it arrives. */\n protected readonly streamColumns = signal<QueryColumn[]>([]);\n private readonly allItems = signal<QueryResultItem[]>([]);\n private readonly streamItems = signal<QueryResultItem[]>([]);\n\n /**\n * Rows handed to the grid, or `null` to let it fetch for itself.\n *\n * Null for a normal query — an empty array would read as \"here are no rows\" and suppress the\n * fetch entirely.\n */\n protected readonly gridData = computed(() =>\n this.query()?.isStreamingQuery ? this.streamItems() : null);\n\n constructor() {\n this.route.paramMap.pipe(takeUntilDestroyed()).subscribe(params => {\n // The handler is async and this is a subscribe, so a rejection lands nowhere: the metadata\n // load would reject, the query would stay null, and the template would render a spinner\n // FOREVER — while this component has had an errorMessage surface all along that only the\n // fetch path ever reached.\n this.onParamsChange(params).catch((e: unknown) => this.reportLoadFailure(e as HttpErrorResponse));\n });\n\n this.destroyRef.onDestroy(() => this.disconnectStreaming());\n\n // Connect the socket once the grid has resolved a streaming query, and disconnect whenever it\n // resolves anything else. The grid knows not to fetch for a streaming query, so there is no\n // window in which both transports are live.\n effect(() => {\n const q = this.query();\n if (q?.isStreamingQuery) {\n this.connectStreaming(q.id);\n } else {\n this.disconnectStreaming();\n }\n });\n\n // Client-side filter and sort for the streaming snapshot. The server never sees these: there\n // is no request to attach them to.\n effect(() => {\n this.searchTerm();\n this.grid()?.settings();\n this.allItems();\n if (this.isStreaming()) this.applyFilter();\n });\n }\n\n private async onParamsChange(params: ParamMap): Promise<void> {\n this.errorMessage.set(null);\n this.queryId.set(null);\n this.allItems.set([]);\n this.streamItems.set([]);\n this.streamColumns.set([]);\n this.disconnectStreaming();\n\n const queryId = params.get('queryId');\n const typeParam = params.get('type');\n\n if (queryId) {\n this.queryId.set(queryId);\n return;\n }\n\n if (!typeParam) return;\n\n // `po/:type` — the route names an entity type, so find the query that lists it. The grid takes\n // a query, and this translation is the only reason it cannot take the route directly.\n const [entityTypes, queries] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.sparkService.getQueries(),\n ]);\n const entityType = entityTypes.find(t => t.id === typeParam || t.alias === typeParam);\n if (!entityType) {\n this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n return;\n }\n\n const singularName = entityType.name;\n const match = queries.find(q => {\n if (q.entityType === singularName) return true;\n const sourceName = q.source.includes('.') ? q.source.substring(q.source.indexOf('.') + 1) : q.source;\n return sourceName === singularName || sourceName === singularName + 's';\n });\n\n if (match) this.queryId.set(match.alias || match.id);\n else this.reportLoadFailure({ status: 404 } as HttpErrorResponse);\n }\n\n /**\n * A load failure has to render, not just be swallowed: a denied query answers 404 (audit M-3, so\n * existence is not leaked), which is indistinguishable from a missing one — hence a deliberately\n * generic message rather than a guess at which it was.\n */\n private reportLoadFailure(err: HttpErrorResponse): void {\n this.errorMessage.set(\n err?.status === 404\n ? (this.lang.t('spark.query.unavailable') || 'This list is not available.')\n : (err?.error?.error || err?.message || 'An unexpected error occurred'));\n }\n\n protected async onCustomAction(action: CustomActionDefinition): Promise<void> {\n await this.grid()?.onCustomAction(action);\n }\n\n protected onCreate(): void {\n this.createClicked.emit();\n const et = this.entityType();\n if (et) this.router.navigate(['/po', et.alias || et.id, 'new']);\n }\n\n protected clearSearch(): void {\n this.searchTerm.set('');\n }\n\n private connectStreaming(queryId: string): void {\n if (this.streamingSub) return;\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: () => this.isStreaming.set(false),\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 // Columns come with the snapshot and never change for the life of the stream, so they are\n // stored once and handed to the grid alongside the rows — a projection cannot describe\n // itself, and the grid has no entity-type metadata to fall back on any more.\n this.streamColumns.set(message.columns);\n this.allItems.set(message.data);\n break;\n\n case 'patch':\n if (message.updated.length > 0) {\n this.allItems.update(items => items.map(item => {\n const patch = message.updated.find(u => u.id === item.id);\n if (!patch) return item;\n return {\n ...item,\n values: item.values.map(v =>\n v.key in patch.values ? { ...v, value: patch.values[v.key] } : v),\n };\n }));\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 const term = this.searchTerm().toLowerCase();\n if (term) {\n items = items.filter(item =>\n item.values.some(v => String(v.value ?? '').toLowerCase().includes(term)));\n }\n\n // The datatable in `[data]` mode also sorts on header clicks, but the sort must survive a\n // patch: re-deriving from `allItems` without re-applying it would silently reorder the grid\n // under the user on every update.\n const sortCols = this.grid()?.settings().sortColumns ?? [];\n if (sortCols.length > 0) {\n items = [...items].sort((a, b) => {\n for (const col of sortCols) {\n const aVal = a.values.find(v => v.key === col.property)?.value ?? '';\n const bVal = b.values.find(v => v.key === 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 }\n}\n","<div class=\"d-flex flex-column h-100\">\n <div class=\"spark-actionbar py-2 flex-shrink-0\" [class.spark-actionbar-wide]=\"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 <!-- Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-outline-primary\"\n [disabled]=\"!isActionEnabled(action)\" (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\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 <bs-badge [type]=\"colors.success\" class=\"ms-2 align-middle spark-live-badge\">LIVE</bs-badge>\n }\n </h2>\n\n @if (errorMessage(); as err) {\n <!-- The route could not be resolved to a query at all. A failure INSIDE the grid renders in\n the grid; this is the one it cannot report, because it never got a query id. -->\n <bs-alert [type]=\"colors.danger\" class=\"m-3 d-block\">\n {{ err }}\n </bs-alert>\n } @else if (queryId(); as qId) {\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]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\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 <!-- Its own full-width row rather than a column beside the box: the count is\n occasional, and reserving 8 columns for it shrank the search permanently. The\n @if moved out of the column so nothing reserves vertical space when absent. -->\n @if (searchTerm() && resultCount() !== null) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ resultCount() }} {{ resultCount() === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n </bs-form>\n </div>\n\n <!--\n One grid, both transports. `data` is null for a normal query, so the grid fetches and pages\n server-side; for a streaming query this page owns the socket and hands the filtered snapshot\n in. This used to be two near-identical <bs-datatable> blocks with a shared row template.\n -->\n <spark-query-grid class=\"flex-grow-1\"\n [queryId]=\"qId\"\n [data]=\"gridData()\"\n [columns]=\"gridData() ? streamColumns() : null\"\n [search]=\"searchTerm()\"\n [rowRoute]=\"rowRoute()\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AA0BA;;;;;;;;;;;;;;;;AAgBG;MAWU,uBAAuB,CAAA;AACjB,IAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAC9B,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACvB,IAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AACnC,IAAA,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,CAAC;AAChD,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC7B,IAAA,IAAI,GAAG,MAAM,CAAC,oBAAoB,CAAC;IAEtD,oBAAoB,GAAG,KAAK,CAA2B,IAAI;6FAAC;IAC5D,iBAAiB,GAAG,KAAK,CAAC,IAAI;0FAAC;AAE/B;;;AAGG;IACH,QAAQ,GAAG,KAAK,CAAsD,IAAI;iFAAC;IAE3E,UAAU,GAAG,MAAM,EAAmB;IACtC,aAAa,GAAG,MAAM,EAAQ;IAC9B,oBAAoB,GAAG,MAAM,EAAsC;IAEnE,MAAM,GAAG,KAAK;;IAGd,OAAO,GAAG,MAAM,CAAgB,IAAI;gFAAC;IACrC,YAAY,GAAG,MAAM,CAAgB,IAAI;qFAAC;IAC1C,UAAU,GAAG,MAAM,CAAC,EAAE;mFAAC;IAEN,IAAI,GAAG,SAAS,CAAC,uBAAuB;6EAAC;AAE1D;;;;;AAKG;AACgB,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,IAAI;8EAAC;AACpD,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI;mFAAC;AAC9D,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;sFAAC;AAClE,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,KAAK;kFAAC;AAC7D,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI;oFAAC;AAChE,IAAA,kBAAkB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,KAAK;2FAAC;AAC/E,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,EAAE,IAAI,IAAI;kFAAC;;AAGxE,IAAA,eAAe,CAAC,MAA8B,EAAA;QACtD,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,IAAI,KAAK;IACtD;;IAIA,WAAW,GAAG,MAAM,CAAC,KAAK;oFAAC;IACnB,YAAY,GAAwB,IAAI;;IAE7B,aAAa,GAAG,MAAM,CAAgB,EAAE;sFAAC;IAC3C,QAAQ,GAAG,MAAM,CAAoB,EAAE;iFAAC;IACxC,WAAW,GAAG,MAAM,CAAoB,EAAE;oFAAC;AAE5D;;;;;AAKG;IACgB,QAAQ,GAAG,QAAQ,CAAC,MACrC,IAAI,CAAC,KAAK,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI;iFAAC;AAE7D,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,IAAG;;;;;YAKhE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,KAAK,IAAI,CAAC,iBAAiB,CAAC,CAAsB,CAAC,CAAC;AACnG,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;;;;QAK3D,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;AACtB,YAAA,IAAI,CAAC,EAAE,gBAAgB,EAAE;AACvB,gBAAA,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B;iBAAO;gBACL,IAAI,CAAC,mBAAmB,EAAE;YAC5B;AACF,QAAA,CAAC,CAAC;;;QAIF,MAAM,CAAC,MAAK;YACV,IAAI,CAAC,UAAU,EAAE;AACjB,YAAA,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE;YACvB,IAAI,CAAC,QAAQ,EAAE;YACf,IAAI,IAAI,CAAC,WAAW,EAAE;gBAAE,IAAI,CAAC,WAAW,EAAE;AAC5C,QAAA,CAAC,CAAC;IACJ;IAEQ,MAAM,cAAc,CAAC,MAAgB,EAAA;AAC3C,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAC3B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACrB,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;AACxB,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,mBAAmB,EAAE;QAE1B,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QACrC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAEpC,IAAI,OAAO,EAAE;AACX,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;YACzB;QACF;AAEA,QAAA,IAAI,CAAC,SAAS;YAAE;;;QAIhB,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;AAC/C,YAAA,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;AAClC,YAAA,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE;AAC/B,SAAA,CAAC;QACF,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC;QACrF,IAAI,CAAC,UAAU,EAAE;YACf,IAAI,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAuB,CAAC;YAC5D;QACF;AAEA,QAAA,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI;QACpC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAG;AAC7B,YAAA,IAAI,CAAC,CAAC,UAAU,KAAK,YAAY;AAAE,gBAAA,OAAO,IAAI;AAC9C,YAAA,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM;YACpG,OAAO,UAAU,KAAK,YAAY,IAAI,UAAU,KAAK,YAAY,GAAG,GAAG;AACzE,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,KAAK;AAAE,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,CAAC;;YAC/C,IAAI,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAuB,CAAC;IACnE;AAEA;;;;AAIG;AACK,IAAA,iBAAiB,CAAC,GAAsB,EAAA;QAC9C,IAAI,CAAC,YAAY,CAAC,GAAG,CACnB,GAAG,EAAE,MAAM,KAAK;AACd,eAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,IAAI,6BAA6B;AAC1E,eAAG,GAAG,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,8BAA8B,CAAC,CAAC;IAC9E;IAEU,MAAM,cAAc,CAAC,MAA8B,EAAA;QAC3D,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC;IAC3C;IAEU,QAAQ,GAAA;AAChB,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;AACzB,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE;AAC5B,QAAA,IAAI,EAAE;AAAE,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACjE;IAEU,WAAW,GAAA;AACnB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;IACzB;AAEQ,IAAA,gBAAgB,CAAC,OAAe,EAAA;QACtC,IAAI,IAAI,CAAC,YAAY;YAAE;AACvB,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AAE1B,QAAA,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC;YACnF,IAAI,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC;AACvD,YAAA,KAAK,EAAE,CAAC,GAAG,KAAI;gBACb,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,IAAI,6BAA6B,CAAC;AACpE,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;YAC7B,CAAC;YACD,QAAQ,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AAC5C,SAAA,CAAC;IACJ;IAEQ,mBAAmB,GAAA;AACzB,QAAA,IAAI,IAAI,CAAC,YAAY,EAAE;AACrB,YAAA,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;AAC/B,YAAA,IAAI,CAAC,YAAY,GAAG,IAAI;QAC1B;AACA,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;IAC7B;AAEQ,IAAA,sBAAsB,CAAC,OAAyB,EAAA;AACtD,QAAA,QAAQ,OAAO,CAAC,IAAI;AAClB,YAAA,KAAK,UAAU;AACb,gBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;;;;gBAI3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;gBACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC/B;AAEF,YAAA,KAAK,OAAO;gBACV,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;AAC9B,oBAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,IAAG;wBAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;AACzD,wBAAA,IAAI,CAAC,KAAK;AAAE,4BAAA,OAAO,IAAI;wBACvB,OAAO;AACL,4BAAA,GAAG,IAAI;AACP,4BAAA,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IACvB,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;yBACpE;oBACH,CAAC,CAAC,CAAC;gBACL;gBACA;AAEF,YAAA,KAAK,OAAO;gBACV,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;gBACtC;;IAEN;IAEQ,WAAW,GAAA;AACjB,QAAA,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE;QAE3B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE;QAC5C,IAAI,IAAI,EAAE;AACR,YAAA,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,IACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9E;;;;AAKA,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,WAAW,IAAI,EAAE;AAC1D,QAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACvB,YAAA,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAI;AAC/B,gBAAA,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE;oBAC1B,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,EAAE;oBACpE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,EAAE;AACpE,oBAAA,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACpD,IAAI,GAAG,KAAK,CAAC;AAAE,wBAAA,OAAO,GAAG,CAAC,SAAS,KAAK,YAAY,GAAG,CAAC,GAAG,GAAG,GAAG;gBACnE;AACA,gBAAA,OAAO,CAAC;AACV,YAAA,CAAC,CAAC;QACJ;AAEA,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;IAC7B;uGAtPW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,kwBA4BA,uBAAuB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECjF3D,kvIAkGA,EAAA,MAAA,EAAA,CAAA,qrCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDrDY,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,gBAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAoB,WAAW,ysBAAE,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,iBAAA,EAAA,qBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,QAAA,CAAA,EAAA,OAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,sBAAsB,EAAA,QAAA,EAAA,6EAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,iBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,kBAAkB,oDAAE,qBAAqB,EAAA,QAAA,EAAA,sCAAA,EAAA,MAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,IAAA,EAAA,IAAA,EAAA,IAAA,EAAA,IAAA,EAAA,KAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,qBAAqB,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,sBAAsB,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,CAAA,WAAA,EAAA,mBAAA,EAAA,YAAA,EAAA,cAAA,EAAA,eAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,0BAA0B,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,CAAA,mBAAA,EAAA,4BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,kBAAkB,kFAAE,kBAAkB,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,UAAA,EAAA,YAAA,EAAA,MAAA,EAAA,QAAA,EAAA,SAAA,EAAA,aAAA,EAAA,UAAA,EAAA,WAAA,EAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,YAAA,EAAA,sBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAE,sBAAsB,sDAAE,gBAAgB,EAAA,IAAA,EAAA,GAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAQzW,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAVnC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,kBAAkB,WACnB,CAAC,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,sBAAsB,EAAE,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,mBAGpW,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,2BAA2B,EAAE;AAC9B,qBAAA,EAAA,QAAA,EAAA,kvIAAA,EAAA,MAAA,EAAA,CAAA,qrCAAA,CAAA,EAAA;+nBA8BiC,uBAAuB,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEjF3D;;AAEG;;;;"}
@@ -28,10 +28,12 @@ function rendererValue(attr) {
28
28
  /**
29
29
  * The renderer-facing value of a query-result cell.
30
30
  *
31
- * A grid row carries no nested objects to fall back to — a projection is flat by construction —
32
- * so this is deliberately not the same function as {@link rendererValue}. Keeping them separate
33
- * is what stops a renderer silently receiving `undefined` because it was written against the
34
- * attribute shape.
31
+ * A cell is a single channel: whatever a renderer needs is already on `value` — for a single-child
32
+ * AsDetail column that is the nested PersistentObject itself, put there by the server so this
33
+ * matches what {@link rendererValue} falls through to on a detail page. There is nothing to fall
34
+ * back to, which is why this is deliberately not the same function: keeping them separate is what
35
+ * stops a renderer silently receiving `undefined` because it was written against the attribute
36
+ * shape's `object` / `objects` fields, which a row does not carry.
35
37
  */
36
38
  function cellValue(value) {
37
39
  return value?.value;
@@ -1 +1 @@
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, QueryResultItemValue } 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 *\n * Used by the detail and edit paths, which still work in attributes.\n */\nexport function rendererValue(attr: PersistentObjectAttribute | undefined): any {\n return attr?.value ?? attr?.object ?? attr?.objects;\n}\n\n/**\n * The renderer-facing value of a query-result cell.\n *\n * A grid row carries no nested objects to fall back to — a projection is flat by construction —\n * so this is deliberately not the same function as {@link rendererValue}. Keeping them separate\n * is what stops a renderer silently receiving `undefined` because it was written against the\n * attribute shape.\n */\nexport function cellValue(value: QueryResultItemValue | undefined): any {\n return value?.value;\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;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,IAA2C,EAAA;IACvE,OAAO,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,OAAO;AACrD;AAEA;;;;;;;AAOG;AACG,SAAU,SAAS,CAAC,KAAuC,EAAA;IAC/D,OAAO,KAAK,EAAE,KAAK;AACrB;;AC5BO,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, QueryResultItemValue } 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 *\n * Used by the detail and edit paths, which still work in attributes.\n */\nexport function rendererValue(attr: PersistentObjectAttribute | undefined): any {\n return attr?.value ?? attr?.object ?? attr?.objects;\n}\n\n/**\n * The renderer-facing value of a query-result cell.\n *\n * A cell is a single channel: whatever a renderer needs is already on `value` — for a single-child\n * AsDetail column that is the nested PersistentObject itself, put there by the server so this\n * matches what {@link rendererValue} falls through to on a detail page. There is nothing to fall\n * back to, which is why this is deliberately not the same function: keeping them separate is what\n * stops a renderer silently receiving `undefined` because it was written against the attribute\n * shape's `object` / `objects` fields, which a row does not carry.\n */\nexport function cellValue(value: QueryResultItemValue | undefined): any {\n return value?.value;\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;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,IAA2C,EAAA;IACvE,OAAO,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,OAAO;AACrD;AAEA;;;;;;;;;AASG;AACG,SAAU,SAAS,CAAC,KAAuC,EAAA;IAC/D,OAAO,KAAK,EAAE,KAAK;AACrB;;AC9BO,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;;;;"}
@@ -397,7 +397,7 @@ class SparkShellComponent {
397
397
  this.isSidebarVisible.set(detail.open);
398
398
  }
399
399
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
400
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkShellComponent, isStandalone: true, selector: "spark-shell", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, breakpoint: { classPropertyName: "breakpoint", publicName: "breakpoint", isSignal: true, isRequired: false, transformFunction: null }, sidebarTheme: { classPropertyName: "sidebarTheme", publicName: "sidebarTheme", isSignal: true, isRequired: false, transformFunction: null }, reloadToken: { classPropertyName: "reloadToken", publicName: "reloadToken", isSignal: true, isRequired: false, transformFunction: null }, sidebarTabs: { classPropertyName: "sidebarTabs", publicName: "sidebarTabs", isSignal: true, isRequired: false, transformFunction: null }, topbarStartTemplate: { classPropertyName: "topbarStartTemplate", publicName: "topbarStartTemplate", isSignal: true, isRequired: false, transformFunction: null }, topbarEndTemplate: { classPropertyName: "topbarEndTemplate", publicName: "topbarEndTemplate", isSignal: true, isRequired: false, transformFunction: null }, topbarActionsTemplate: { classPropertyName: "topbarActionsTemplate", publicName: "topbarActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarHeaderTemplate: { classPropertyName: "sidebarHeaderTemplate", publicName: "sidebarHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarTopTemplate: { classPropertyName: "sidebarTopTemplate", publicName: "sidebarTopTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarFooterTemplate: { classPropertyName: "sidebarFooterTemplate", publicName: "sidebarFooterTemplate", isSignal: true, isRequired: false, transformFunction: null }, mainHeaderTemplate: { classPropertyName: "mainHeaderTemplate", publicName: "mainHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "topbarStartSlot", first: true, predicate: SparkShellTopbarStartDirective, descendants: true, isSignal: true }, { propertyName: "topbarEndSlot", first: true, predicate: SparkShellTopbarEndDirective, descendants: true, isSignal: true }, { propertyName: "topbarActionsSlot", first: true, predicate: SparkShellTopbarActionsDirective, descendants: true, isSignal: true }, { propertyName: "sidebarHeaderSlot", first: true, predicate: SparkShellSidebarHeaderDirective, descendants: true, isSignal: true }, { propertyName: "sidebarTopSlot", first: true, predicate: SparkShellSidebarTopDirective, descendants: true, isSignal: true }, { propertyName: "sidebarFooterSlot", first: true, predicate: SparkShellSidebarFooterDirective, descendants: true, isSignal: true }, { propertyName: "mainHeaderSlot", first: true, predicate: SparkShellMainHeaderDirective, descendants: true, isSignal: true }, { propertyName: "tabSlots", predicate: SparkShellTabDirective, isSignal: true }], ngImport: i0, template: "<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <main class=\"p-4\">\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n", styles: [":host{display:flex;flex-direction:column;height:100vh}bs-shell{flex:1 1 auto;min-height:0}.spark-topbar{background-color:var(--spark-shell-topbar-bg, var(--bs-dark, #212529))}::ng-deep spark-shell mp-shell::part(hamburger){display:none}bs-navbar-toggler{--bs-secondary-color: rgba(255, 255, 255, .85)}main{height:100%;background-color:var(--spark-shell-main-bg, #f8f9fa);display:flex;flex-direction:column;overflow:auto}nav.spark-sidebar{min-height:100%;background:var(--spark-shell-sidebar-bg, #f8f9fa)}nav.spark-sidebar.spark-sidebar-dark{background:var(--spark-shell-sidebar-bg, #333);color:#fff}:host ::ng-deep nav.spark-sidebar{--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep nav.spark-sidebar mp-accordion::part(content){padding:0}:host ::ng-deep nav.spark-sidebar .nav-link{color:inherit}:host ::ng-deep nav.spark-sidebar .nav-link:hover{background-color:#ffffff1a}:host ::ng-deep nav.spark-sidebar .nav-link.active{background-color:#fff3}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: BsShellComponent, selector: "bs-shell", inputs: ["state", "breakpoint", "dismissOnNavigate"], outputs: ["statechange"] }, { kind: "directive", type: BsShellSidebarDirective, selector: "[bsShellSidebar]" }, { kind: "component", type: BsNavbarTogglerComponent, selector: "bs-navbar-toggler", inputs: ["state", "toggleOnClick", "ariaLabel", "controls"], outputs: ["stateChange"] }, { kind: "component", type: SparkProgramUnitsComponent, selector: "spark-program-units", inputs: ["reloadToken", "extraTabs"] }, { kind: "component", type: SparkLanguageSelectorComponent, selector: "spark-language-selector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
400
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkShellComponent, isStandalone: true, selector: "spark-shell", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, breakpoint: { classPropertyName: "breakpoint", publicName: "breakpoint", isSignal: true, isRequired: false, transformFunction: null }, sidebarTheme: { classPropertyName: "sidebarTheme", publicName: "sidebarTheme", isSignal: true, isRequired: false, transformFunction: null }, reloadToken: { classPropertyName: "reloadToken", publicName: "reloadToken", isSignal: true, isRequired: false, transformFunction: null }, sidebarTabs: { classPropertyName: "sidebarTabs", publicName: "sidebarTabs", isSignal: true, isRequired: false, transformFunction: null }, topbarStartTemplate: { classPropertyName: "topbarStartTemplate", publicName: "topbarStartTemplate", isSignal: true, isRequired: false, transformFunction: null }, topbarEndTemplate: { classPropertyName: "topbarEndTemplate", publicName: "topbarEndTemplate", isSignal: true, isRequired: false, transformFunction: null }, topbarActionsTemplate: { classPropertyName: "topbarActionsTemplate", publicName: "topbarActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarHeaderTemplate: { classPropertyName: "sidebarHeaderTemplate", publicName: "sidebarHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarTopTemplate: { classPropertyName: "sidebarTopTemplate", publicName: "sidebarTopTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarFooterTemplate: { classPropertyName: "sidebarFooterTemplate", publicName: "sidebarFooterTemplate", isSignal: true, isRequired: false, transformFunction: null }, mainHeaderTemplate: { classPropertyName: "mainHeaderTemplate", publicName: "mainHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "topbarStartSlot", first: true, predicate: SparkShellTopbarStartDirective, descendants: true, isSignal: true }, { propertyName: "topbarEndSlot", first: true, predicate: SparkShellTopbarEndDirective, descendants: true, isSignal: true }, { propertyName: "topbarActionsSlot", first: true, predicate: SparkShellTopbarActionsDirective, descendants: true, isSignal: true }, { propertyName: "sidebarHeaderSlot", first: true, predicate: SparkShellSidebarHeaderDirective, descendants: true, isSignal: true }, { propertyName: "sidebarTopSlot", first: true, predicate: SparkShellSidebarTopDirective, descendants: true, isSignal: true }, { propertyName: "sidebarFooterSlot", first: true, predicate: SparkShellSidebarFooterDirective, descendants: true, isSignal: true }, { propertyName: "mainHeaderSlot", first: true, predicate: SparkShellMainHeaderDirective, descendants: true, isSignal: true }, { propertyName: "tabSlots", predicate: SparkShellTabDirective, isSignal: true }], ngImport: i0, template: "<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <!-- The padding lives in the stylesheet as `--spark-main-padding`, not as a `p-4` utility, so the\n sticky action bar can read it and bleed back out to the scrollport edges. See _actionbar.scss. -->\n <main>\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n", styles: [":host{display:flex;flex-direction:column;height:100vh}bs-shell{flex:1 1 auto;min-height:0}.spark-topbar{background-color:var(--spark-shell-topbar-bg, var(--bs-dark, #212529))}::ng-deep spark-shell mp-shell::part(hamburger){display:none}bs-navbar-toggler{--bs-secondary-color: rgba(255, 255, 255, .85)}main{--spark-main-padding: 1.5rem;--spark-content-inset-block: var(--spark-main-padding);--spark-content-inset-inline: var(--spark-main-padding);height:100%;padding:var(--spark-main-padding);background-color:var(--spark-shell-main-bg, #f8f9fa);display:flex;flex-direction:column;overflow:auto}nav.spark-sidebar{min-height:100%;background:var(--spark-shell-sidebar-bg, #f8f9fa)}nav.spark-sidebar.spark-sidebar-dark{background:var(--spark-shell-sidebar-bg, #333);color:#fff}:host ::ng-deep nav.spark-sidebar{--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep nav.spark-sidebar mp-accordion::part(content){padding:0}:host ::ng-deep nav.spark-sidebar .nav-link{color:inherit}:host ::ng-deep nav.spark-sidebar .nav-link:hover{background-color:#ffffff1a}:host ::ng-deep nav.spark-sidebar .nav-link.active{background-color:#fff3}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: BsShellComponent, selector: "bs-shell", inputs: ["state", "breakpoint", "dismissOnNavigate"], outputs: ["statechange"] }, { kind: "directive", type: BsShellSidebarDirective, selector: "[bsShellSidebar]" }, { kind: "component", type: BsNavbarTogglerComponent, selector: "bs-navbar-toggler", inputs: ["state", "toggleOnClick", "ariaLabel", "controls"], outputs: ["stateChange"] }, { kind: "component", type: SparkProgramUnitsComponent, selector: "spark-program-units", inputs: ["reloadToken", "extraTabs"] }, { kind: "component", type: SparkLanguageSelectorComponent, selector: "spark-language-selector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
401
401
  }
402
402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellComponent, decorators: [{
403
403
  type: Component,
@@ -405,7 +405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
405
405
  NgTemplateOutlet,
406
406
  BsShellComponent, BsShellSidebarDirective, BsNavbarTogglerComponent,
407
407
  SparkProgramUnitsComponent, SparkLanguageSelectorComponent,
408
- ], template: "<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <main class=\"p-4\">\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n", styles: [":host{display:flex;flex-direction:column;height:100vh}bs-shell{flex:1 1 auto;min-height:0}.spark-topbar{background-color:var(--spark-shell-topbar-bg, var(--bs-dark, #212529))}::ng-deep spark-shell mp-shell::part(hamburger){display:none}bs-navbar-toggler{--bs-secondary-color: rgba(255, 255, 255, .85)}main{height:100%;background-color:var(--spark-shell-main-bg, #f8f9fa);display:flex;flex-direction:column;overflow:auto}nav.spark-sidebar{min-height:100%;background:var(--spark-shell-sidebar-bg, #f8f9fa)}nav.spark-sidebar.spark-sidebar-dark{background:var(--spark-shell-sidebar-bg, #333);color:#fff}:host ::ng-deep nav.spark-sidebar{--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep nav.spark-sidebar mp-accordion::part(content){padding:0}:host ::ng-deep nav.spark-sidebar .nav-link{color:inherit}:host ::ng-deep nav.spark-sidebar .nav-link:hover{background-color:#ffffff1a}:host ::ng-deep nav.spark-sidebar .nav-link.active{background-color:#fff3}\n"] }]
408
+ ], template: "<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <!-- The padding lives in the stylesheet as `--spark-main-padding`, not as a `p-4` utility, so the\n sticky action bar can read it and bleed back out to the scrollport edges. See _actionbar.scss. -->\n <main>\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n", styles: [":host{display:flex;flex-direction:column;height:100vh}bs-shell{flex:1 1 auto;min-height:0}.spark-topbar{background-color:var(--spark-shell-topbar-bg, var(--bs-dark, #212529))}::ng-deep spark-shell mp-shell::part(hamburger){display:none}bs-navbar-toggler{--bs-secondary-color: rgba(255, 255, 255, .85)}main{--spark-main-padding: 1.5rem;--spark-content-inset-block: var(--spark-main-padding);--spark-content-inset-inline: var(--spark-main-padding);height:100%;padding:var(--spark-main-padding);background-color:var(--spark-shell-main-bg, #f8f9fa);display:flex;flex-direction:column;overflow:auto}nav.spark-sidebar{min-height:100%;background:var(--spark-shell-sidebar-bg, #f8f9fa)}nav.spark-sidebar.spark-sidebar-dark{background:var(--spark-shell-sidebar-bg, #333);color:#fff}:host ::ng-deep nav.spark-sidebar{--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep nav.spark-sidebar mp-accordion::part(content){padding:0}:host ::ng-deep nav.spark-sidebar .nav-link{color:inherit}:host ::ng-deep nav.spark-sidebar .nav-link:hover{background-color:#ffffff1a}:host ::ng-deep nav.spark-sidebar .nav-link.active{background-color:#fff3}\n"] }]
409
409
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], breakpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "breakpoint", required: false }] }], sidebarTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTheme", required: false }] }], reloadToken: [{ type: i0.Input, args: [{ isSignal: true, alias: "reloadToken", required: false }] }], sidebarTabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTabs", required: false }] }], topbarStartTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarStartTemplate", required: false }] }], topbarEndTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarEndTemplate", required: false }] }], topbarActionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarActionsTemplate", required: false }] }], sidebarHeaderTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarHeaderTemplate", required: false }] }], sidebarTopTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTopTemplate", required: false }] }], sidebarFooterTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarFooterTemplate", required: false }] }], mainHeaderTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "mainHeaderTemplate", required: false }] }], topbarStartSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellTopbarStartDirective), { isSignal: true }] }], topbarEndSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellTopbarEndDirective), { isSignal: true }] }], topbarActionsSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellTopbarActionsDirective), { isSignal: true }] }], sidebarHeaderSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellSidebarHeaderDirective), { isSignal: true }] }], sidebarTopSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellSidebarTopDirective), { isSignal: true }] }], sidebarFooterSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellSidebarFooterDirective), { isSignal: true }] }], mainHeaderSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SparkShellMainHeaderDirective), { isSignal: true }] }], tabSlots: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SparkShellTabDirective), { isSignal: true }] }] } });
410
410
 
411
411
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-shell.mjs","sources":["../../shell/src/spark-program-units.component.ts","../../shell/src/spark-program-units.component.html","../../shell/src/spark-language-selector.component.ts","../../shell/src/spark-shell-slots.ts","../../shell/src/spark-shell.component.ts","../../shell/src/spark-shell.component.html","../../shell/mintplayer-ng-spark-shell.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, effect, inject, input, signal, untracked } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\nimport { BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective } from '@mintplayer/ng-bootstrap/accordion';\nimport { SPARK_AUTH_STATE } from '@mintplayer/ng-spark';\nimport { ProgramUnitGroup } from '@mintplayer/ng-spark/models';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport { IconNamePipe, ResolveTranslationPipe, RouterLinkPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkSidebarTab } from './spark-shell-slots';\n\n/**\n * The server-driven navigation menu: an accordion of program-unit groups fetched from\n * `GET /spark/program-units`, which the server has already filtered to what the caller's rights\n * allow. Hosts write ZERO router links for navigation — every group, unit, icon, label and link\n * comes from `programUnits.json`; content around the menu belongs in `<spark-shell>`'s slots,\n * and a host tempted to hand-write a unit anchor should add a unit to `programUnits.json`\n * instead.\n *\n * Because the response is caller-scoped it must be re-fetched when the caller changes: the\n * component tracks the optional `SPARK_AUTH_STATE` signal (supplied by ng-spark-auth's\n * `provideSparkAuth()`, or by the app's own auth stack) and reloads on every change. Without a\n * provider it fetches once. `reloadToken` is the manual escape hatch (any changed value triggers\n * a reload), and `reload()` the imperative one.\n *\n * Usually rendered by `<spark-shell>`; exported standalone for hosts that own their own layout.\n */\n@Component({\n selector: 'spark-program-units',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n RouterLink, RouterLinkActive,\n BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective,\n SparkIconComponent, ResolveTranslationPipe, IconNamePipe, RouterLinkPipe,\n ],\n templateUrl: './spark-program-units.component.html',\n styleUrl: './spark-program-units.component.scss',\n})\nexport class SparkProgramUnitsComponent {\n private readonly sparkService = inject(SparkService);\n private readonly authState = inject(SPARK_AUTH_STATE, { optional: true });\n\n /** Any changed value triggers a reload — for apps whose auth state isn't a provided signal. */\n readonly reloadToken = input<unknown>(null);\n\n /**\n * Extra tabs to render after the generated groups, normally forwarded by `<spark-shell>` from\n * its `*sparkShellTab` directives. They must be rendered by THIS template — see\n * `SparkShellTabDirective` for why a host-declared `<bs-accordion-tab>` cannot work.\n */\n readonly extraTabs = input<readonly SparkSidebarTab[]>([]);\n\n protected readonly groups = signal<ProgramUnitGroup[]>([]);\n\n constructor() {\n effect(() => {\n this.authState?.();\n this.reloadToken();\n untracked(() => this.reload());\n });\n }\n\n /** Re-fetches the menu. The response is already rights-filtered per caller. */\n async reload(): Promise<void> {\n const config = await this.sparkService.getProgramUnits();\n // Order is the server file's contract, but sort both levels here so the rendered menu never\n // depends on JSON array order — groups were sorted, units were not (#324 F5).\n const groups = [...config.programUnitGroups]\n .sort((a, b) => a.order - b.order)\n .map(g => ({ ...g, programUnits: [...g.programUnits].sort((a, b) => a.order - b.order) }));\n this.groups.set(groups);\n }\n}\n","<bs-accordion class=\"d-block\">\n @for (group of groups(); track group.id) {\n <bs-accordion-tab>\n <ng-container *bsAccordionTabHeader>\n <spark-icon [name]=\"group.icon | iconName:'bi-folder'\" />\n <span class=\"ms-2\">{{ group.name | resolveTranslation }}</span>\n </ng-container>\n @for (unit of group.programUnits; track unit.id) {\n @if (unit | routerLink; as link) {\n <a [routerLink]=\"link\"\n routerLinkActive=\"active\"\n class=\"d-block px-3 py-2 text-decoration-none nav-link\">\n <spark-icon [name]=\"unit.icon | iconName:'bi-file'\" />\n <span class=\"ms-2\">{{ unit.name | resolveTranslation }}</span>\n </a>\n } @else {\n <!-- A url unit is an external address: a plain anchor, never a router link. -->\n <a [href]=\"unit.url\" target=\"_blank\" rel=\"noopener\"\n class=\"d-block px-3 py-2 text-decoration-none nav-link\">\n <spark-icon [name]=\"unit.icon | iconName:'bi-box-arrow-up-right'\" />\n <span class=\"ms-2\">{{ unit.name | resolveTranslation }}</span>\n </a>\n }\n }\n </bs-accordion-tab>\n }\n <!-- Host-contributed tabs are rendered HERE, by this template, so that the accordion's content\n query registers them and they join the generated groups' single-open group. -->\n @for (tab of extraTabs(); track tab.content) {\n <bs-accordion-tab>\n <ng-container *bsAccordionTabHeader>\n @if (tab.headerTemplate; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-icon [name]=\"tab.icon | iconName:'bi-folder'\" />\n <span class=\"ms-2\">{{ tab.header }}</span>\n }\n </ng-container>\n <ng-container *ngTemplateOutlet=\"tab.content\" />\n </bs-accordion-tab>\n }\n</bs-accordion>\n","import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { KeyValuePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { BsSelectComponent, BsSelectOption } from '@mintplayer/ng-bootstrap/select';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\n\n/**\n * The culture switcher: a `bs-select` over `SparkLanguageService`'s languages, persisting the\n * choice. Renders nothing when the app declares one language (or none), so hosts can include it\n * unconditionally — `<spark-shell>`'s topbar does exactly that as its trailing default.\n */\n@Component({\n selector: 'spark-language-selector',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [KeyValuePipe, FormsModule, BsSelectComponent, BsSelectOption, ResolveTranslationPipe],\n template: `\n @if (lang.languages() | keyvalue; as langs) {\n @if (langs.length > 1) {\n <bs-select [ngModel]=\"lang.language()\" (ngModelChange)=\"lang.setLanguage($event)\">\n @for (l of langs; track l.key) {\n <option [ngValue]=\"l.key\">{{ l.value | resolveTranslation }}</option>\n }\n </bs-select>\n }\n }\n `,\n})\nexport class SparkLanguageSelectorComponent {\n protected readonly lang = inject(SparkLanguageService);\n}\n","import { Directive, inject, input, TemplateRef } from '@angular/core';\n\n/**\n * Region slots for `<spark-shell>`.\n *\n * Each directive marks a template that REPLACES one region of the shell chrome (or fills an\n * empty one). An omitted slot is not an empty slot: the shell renders its default — the toggler,\n * the language selector, the title heading — so a host that supplies nothing still gets a\n * complete working shell, and a host that supplies one slot leaves the rest alone.\n *\n * The menu itself is deliberately NOT a slot. Navigation is sourced entirely from\n * `programUnits.json` through the rights-filtered `/spark/program-units` endpoint and re-fetched\n * on sign-in/out; a host that finds itself writing unit anchors in a slot should be adding units\n * to `programUnits.json` instead. Slots exist for the content AROUND the menu: an auth bar, a\n * user chip, branding, a one-off extra link, an alert strip above the routed content.\n *\n * Naming follows the house convention (prefix, component, slot — see `*sparkQueryIcon` in\n * `@mintplayer/ng-spark/grid`): `sparkShell` + region. Every slot also exists as a `TemplateRef`\n * input on `SparkShellComponent` for hosts that cannot use content projection.\n *\n * ```html\n * <spark-shell title=\"My App\">\n * <spark-auth-bar *sparkShellTopbarEnd />\n * <div *sparkShellSidebarTop>\n * <a routerLink=\"/github-projects\" class=\"nav-link\">GitHub projects</a>\n * </div>\n * <router-outlet />\n * </spark-shell>\n * ```\n */\n\n/** Topbar, leading edge. Default: a `bs-navbar-toggler` mirroring the shell's open state. */\n@Directive({ selector: '[sparkShellTopbarStart]' })\nexport class SparkShellTopbarStartDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * Topbar, trailing edge. Default: the language selector (which hides itself when the app has\n * one language). This is where an auth bar goes — the shell cannot ship one itself, since\n * `@mintplayer/ng-spark` does not (and must not) depend on `@mintplayer/ng-spark-auth`.\n */\n@Directive({ selector: '[sparkShellTopbarEnd]' })\nexport class SparkShellTopbarEndDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * Topbar, trailing edge, **beside** the default chrome rather than instead of it — rendered before\n * the language selector, which stays.\n *\n * ## Why this is not just `*sparkShellTopbarEnd`\n *\n * That slot REPLACES the region, which is right for an auth bar (a host taking over the trailing\n * edge wholesale) and wrong for a page-level action button. A host that only wanted to add a\n * button had to re-render the language selector itself to keep it — which means importing it,\n * knowing it hides itself in a single-language app, and keeping that copy in step with the shell.\n * Every host that did this got it slightly differently.\n *\n * Pairs with `SparkQueryActionsService`: that service resolves a query's custom actions without the\n * grid, and this is where a page-level one goes.\n *\n * ```html\n * <button *sparkShellTopbarActions class=\"btn btn-primary\" (click)=\"publish()\">Publish</button>\n * ```\n *\n * Supplying both slots is allowed and does what it says: `topbarEnd` replaces the default chrome,\n * and these actions still render ahead of whatever it put there.\n */\n@Directive({ selector: '[sparkShellTopbarActions]' })\nexport class SparkShellTopbarActionsDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Sidebar, above everything. Default: `<h5>{{ title }}</h5>`. */\n@Directive({ selector: '[sparkShellSidebarHeader]' })\nexport class SparkShellSidebarHeaderDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Sidebar, between the header and the program-units menu. No default. */\n@Directive({ selector: '[sparkShellSidebarTop]' })\nexport class SparkShellSidebarTopDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * An extra accordion tab in the sidebar menu, rendered after the generated program-unit groups\n * and sharing their single-open behavior.\n *\n * A tab is contributed as DATA (header + body template), not as markup, and that is load-bearing:\n * `bs-accordion` discovers its tabs with an Angular content query, which matches by declaration\n * view, so a `<bs-accordion-tab>` written in a host's template and inserted into the library's\n * accordion is never registered — it would land at index -1, get no hoisted header and no slot.\n * Declaring a second `<bs-accordion>` instead is what puts the tab in its own exclusivity group:\n * `mp-accordion` enforces single-open per element, over children it owns and over\n * `<details name>`, whose grouping cannot cross a shadow root. So the tab element must be created\n * by the menu itself, from what this directive carries.\n *\n * ```html\n * <ng-container *sparkShellTab=\"'Component demos'; icon: 'palette'\">\n * <a routerLink=\"/query-slots\" routerLinkActive=\"active\" class=\"nav-link\">Query card slots</a>\n * </ng-container>\n * ```\n *\n * Navigation still belongs in `programUnits.json` — this is for pages the model cannot describe\n * (client-side demos, external tools). For sidebar content that is NOT an accordion tab, use\n * `*sparkShellSidebarTop` or `*sparkShellSidebarFooter`.\n */\n@Directive({ selector: '[sparkShellTab]' })\nexport class SparkShellTabDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n\n /** The tab's header label. */\n readonly header = input.required<string>({ alias: 'sparkShellTab' });\n\n /** Bootstrap icon name for the header, as in `programUnits.json`. Defaults to a folder. */\n readonly icon = input<string | undefined>(undefined, { alias: 'sparkShellTabIcon' });\n\n /** Replaces the icon+label header entirely, for a header that needs its own markup. */\n readonly headerTemplate = input<TemplateRef<unknown> | null>(null, { alias: 'sparkShellTabHeader' });\n}\n\n/**\n * A sidebar accordion tab in the shape the menu renders it. Hosts normally contribute tabs with\n * `*sparkShellTab`; this is the same thing as data, for a host that computes its tabs.\n */\nexport interface SparkSidebarTab {\n readonly header: string;\n readonly icon?: string;\n readonly headerTemplate?: TemplateRef<unknown> | null;\n readonly content: TemplateRef<unknown>;\n}\n\n/** Sidebar, at the very bottom. No default. */\n@Directive({ selector: '[sparkShellSidebarFooter]' })\nexport class SparkShellSidebarFooterDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Main region, above the projected content (the host's `<router-outlet>`). No default. */\n@Directive({ selector: '[sparkShellMainHeader]' })\nexport class SparkShellMainHeaderDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n","import { ChangeDetectionStrategy, Component, computed, contentChild, contentChildren, input, signal, TemplateRef } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { BsShellComponent, BsShellSidebarDirective, BsShellState } from '@mintplayer/ng-bootstrap/shell';\nimport { BsNavbarTogglerComponent } from '@mintplayer/ng-bootstrap/navbar-toggler';\nimport type { Breakpoint } from '@mintplayer/ng-bootstrap';\nimport type { ShellStateChangeEventDetail } from '@mintplayer/web-components/shell';\nimport { SparkProgramUnitsComponent } from './spark-program-units.component';\nimport { SparkLanguageSelectorComponent } from './spark-language-selector.component';\nimport {\n SparkShellMainHeaderDirective,\n SparkShellSidebarFooterDirective,\n SparkShellSidebarHeaderDirective,\n SparkShellSidebarTopDirective,\n SparkShellTabDirective,\n SparkShellTopbarEndDirective,\n SparkShellTopbarActionsDirective,\n SparkShellTopbarStartDirective,\n SparkSidebarTab,\n} from './spark-shell-slots';\n\n/**\n * The application frame: topbar + sidebar + main, wrapping ng-bootstrap's `bs-shell` (whose\n * `mp-shell` web component owns ALL responsive behavior — breakpoints, the overlay drawer,\n * dismiss-on-navigate — in CSS; nothing here re-derives a pixel width). The sidebar renders the\n * server-driven program-units menu; the host projects its `<router-outlet>` as the default\n * content and customizes the chrome through the `*sparkShell*` slots (see `spark-shell-slots.ts`\n * for the doctrine: an omitted slot renders its default, and the menu itself is never a slot).\n *\n * ```html\n * <spark-shell title=\"My App\">\n * <spark-auth-bar *sparkShellTopbarEnd />\n * <router-outlet />\n * </spark-shell>\n * ```\n *\n * The one piece of state the shell keeps is the toggler↔drawer mirror: the built-in hamburger is\n * hidden (`::part(hamburger)`) in favor of a `bs-navbar-toggler` in the topbar, so the shell\n * listens to `statechange` to keep the toggler's icon truthful in `auto` mode and only forces\n * `show`/`hide` on explicit toggles.\n *\n * Theming: the chrome colors are CSS custom properties with the classic dark-sidebar defaults —\n * `--spark-shell-topbar-bg`, `--spark-shell-sidebar-bg`, `--spark-shell-main-bg` — overridable on\n * the `<spark-shell>` element. `sidebarTheme` flips the sidebar's `data-bs-theme` (which is what\n * recolors the accordion internals across the shadow boundary) together with its default palette.\n */\n@Component({\n selector: 'spark-shell',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n BsShellComponent, BsShellSidebarDirective, BsNavbarTogglerComponent,\n SparkProgramUnitsComponent, SparkLanguageSelectorComponent,\n ],\n templateUrl: './spark-shell.component.html',\n styleUrl: './spark-shell.component.scss',\n})\nexport class SparkShellComponent {\n /** The sidebar heading. Ignored when a `*sparkShellSidebarHeader` slot is supplied. */\n readonly title = input('');\n\n /** Forwarded to `bs-shell`: below it the sidebar is an overlay drawer. */\n readonly breakpoint = input<Breakpoint>('md');\n\n /**\n * `data-bs-theme` for the sidebar — what flips the accordion's shadow-DOM internals between\n * palettes — plus the matching default background. `null` sets no theme (inherit the page's).\n */\n readonly sidebarTheme = input<'dark' | 'light' | null>('dark');\n\n /** Forwarded to the menu: any changed value re-fetches the program units. */\n readonly reloadToken = input<unknown>(null);\n\n /** Extra sidebar tabs as data, for hosts that compute them; `*sparkShellTab` is the usual way. */\n readonly sidebarTabs = input<readonly SparkSidebarTab[]>([]);\n\n // One TemplateRef input per slot, for hosts that cannot use content projection\n // (the spark-query-card precedent). The projected directive wins when both are present.\n readonly topbarStartTemplate = input<TemplateRef<unknown> | null>(null);\n readonly topbarEndTemplate = input<TemplateRef<unknown> | null>(null);\n readonly topbarActionsTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarHeaderTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarTopTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarFooterTemplate = input<TemplateRef<unknown> | null>(null);\n readonly mainHeaderTemplate = input<TemplateRef<unknown> | null>(null);\n\n private readonly topbarStartSlot = contentChild(SparkShellTopbarStartDirective);\n private readonly topbarEndSlot = contentChild(SparkShellTopbarEndDirective);\n private readonly topbarActionsSlot = contentChild(SparkShellTopbarActionsDirective);\n private readonly sidebarHeaderSlot = contentChild(SparkShellSidebarHeaderDirective);\n private readonly sidebarTopSlot = contentChild(SparkShellSidebarTopDirective);\n private readonly sidebarFooterSlot = contentChild(SparkShellSidebarFooterDirective);\n private readonly mainHeaderSlot = contentChild(SparkShellMainHeaderDirective);\n\n protected readonly topbarStartTpl = computed(() => this.topbarStartSlot()?.templateRef ?? this.topbarStartTemplate());\n protected readonly topbarEndTpl = computed(() => this.topbarEndSlot()?.templateRef ?? this.topbarEndTemplate());\n protected readonly topbarActionsTpl = computed(() => this.topbarActionsSlot()?.templateRef ?? this.topbarActionsTemplate());\n protected readonly sidebarHeaderTpl = computed(() => this.sidebarHeaderSlot()?.templateRef ?? this.sidebarHeaderTemplate());\n protected readonly sidebarTopTpl = computed(() => this.sidebarTopSlot()?.templateRef ?? this.sidebarTopTemplate());\n protected readonly sidebarFooterTpl = computed(() => this.sidebarFooterSlot()?.templateRef ?? this.sidebarFooterTemplate());\n protected readonly mainHeaderTpl = computed(() => this.mainHeaderSlot()?.templateRef ?? this.mainHeaderTemplate());\n\n /**\n * Extra accordion tabs, forwarded to the menu so IT creates the `<bs-accordion-tab>` elements —\n * the only way they share the generated groups' single-open behavior (see\n * `SparkShellTabDirective`). Data-supplied tabs come first, then projected ones in declaration\n * order.\n */\n private readonly tabSlots = contentChildren(SparkShellTabDirective);\n\n protected readonly tabs = computed<readonly SparkSidebarTab[]>(() => [\n ...this.sidebarTabs(),\n ...this.tabSlots().map(slot => ({\n header: slot.header(),\n icon: slot.icon(),\n headerTemplate: slot.headerTemplate(),\n content: slot.templateRef,\n })),\n ]);\n\n protected readonly shellState = signal<BsShellState>('auto');\n protected readonly isSidebarVisible = signal(false);\n\n // Explicit toggles force show/hide; 'auto' responsive behavior is otherwise preserved by\n // never writing state on our own. The mirror below keeps the toggler icon truthful when the\n // shell opens/closes itself at the breakpoint.\n protected toggleSidebar(open: boolean): void {\n this.shellState.set(open ? 'show' : 'hide');\n }\n\n protected onShellToggle(detail: ShellStateChangeEventDetail): void {\n this.isSidebarVisible.set(detail.open);\n }\n}\n","<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <main class=\"p-4\">\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAWA;;;;;;;;;;;;;;;AAeG;MAcU,0BAA0B,CAAA;AACpB,IAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IACnC,SAAS,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;IAGhE,WAAW,GAAG,KAAK,CAAU,IAAI;oFAAC;AAE3C;;;;AAIG;IACM,SAAS,GAAG,KAAK,CAA6B,EAAE;kFAAC;IAEvC,MAAM,GAAG,MAAM,CAAqB,EAAE;+EAAC;AAE1D,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,CAAC,SAAS,IAAI;YAClB,IAAI,CAAC,WAAW,EAAE;YAClB,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;AAChC,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,MAAM,MAAM,GAAA;QACV,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE;;;AAGxD,QAAA,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,iBAAiB;AACxC,aAAA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;AAChC,aAAA,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC5F,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;IACzB;uGAjCW,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAA1B,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECxCvC,62DA0CA,EAAA,MAAA,EAAA,CAAA,wQAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDVI,gBAAgB,oJAChB,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,uBAAA,EAAA,kBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC5B,oBAAoB,kGAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,6BAA6B,EAAA,QAAA,EAAA,wBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC5E,kBAAkB,oEAAE,sBAAsB,EAAA,IAAA,EAAA,oBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAE,YAAY,EAAA,IAAA,EAAA,UAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAE,cAAc,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAK/D,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAbtC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,qBAAqB,cACnB,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC;wBACP,gBAAgB;AAChB,wBAAA,UAAU,EAAE,gBAAgB;wBAC5B,oBAAoB,EAAE,uBAAuB,EAAE,6BAA6B;AAC5E,wBAAA,kBAAkB,EAAE,sBAAsB,EAAE,YAAY,EAAE,cAAc;AACzE,qBAAA,EAAA,QAAA,EAAA,62DAAA,EAAA,MAAA,EAAA,CAAA,wQAAA,CAAA,EAAA;;;AE7BH;;;;AAIG;MAkBU,8BAA8B,CAAA;AACtB,IAAA,IAAI,GAAG,MAAM,CAAC,oBAAoB,CAAC;uGAD3C,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA9B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAZ/B;;;;;;;;;;GAUT,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAXuB,WAAW,4iBAAE,iBAAiB,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,UAAA,EAAA,eAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,cAAc,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAA5D,YAAY,4CAAkD,sBAAsB,EAAA,IAAA,EAAA,oBAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAanF,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAjB1C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,yBAAyB;AACnC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,OAAO,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,CAAC;AAC/F,oBAAA,QAAQ,EAAE;;;;;;;;;;AAUT,EAAA,CAAA;AACF,iBAAA;;;AC1BD;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;AAEH;MAEa,8BAA8B,CAAA;AAChC,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAA9B,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAA9B,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAD1C,SAAS;mBAAC,EAAE,QAAQ,EAAE,yBAAyB,EAAE;;AAKlD;;;;AAIG;MAEU,4BAA4B,CAAA;AAC9B,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,4BAA4B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAA5B,4BAA4B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAA5B,4BAA4B,EAAA,UAAA,EAAA,CAAA;kBADxC,SAAS;mBAAC,EAAE,QAAQ,EAAE,uBAAuB,EAAE;;AAKhD;;;;;;;;;;;;;;;;;;;;;AAqBG;MAEU,gCAAgC,CAAA;AAClC,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,gCAAgC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAhC,gCAAgC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAhC,gCAAgC,EAAA,UAAA,EAAA,CAAA;kBAD5C,SAAS;mBAAC,EAAE,QAAQ,EAAE,2BAA2B,EAAE;;AAKpD;MAEa,gCAAgC,CAAA;AAClC,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,gCAAgC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAhC,gCAAgC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAhC,gCAAgC,EAAA,UAAA,EAAA,CAAA;kBAD5C,SAAS;mBAAC,EAAE,QAAQ,EAAE,2BAA2B,EAAE;;AAKpD;MAEa,6BAA6B,CAAA;AAC/B,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAA7B,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAA7B,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBADzC,SAAS;mBAAC,EAAE,QAAQ,EAAE,wBAAwB,EAAE;;AAKjD;;;;;;;;;;;;;;;;;;;;;;AAsBG;MAEU,sBAAsB,CAAA;AACxB,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;;IAGvD,MAAM,GAAG,KAAK,CAAC,QAAQ,6EAAW,KAAK,EAAE,eAAe,EAAA,CAAG;;IAG3D,IAAI,GAAG,KAAK,CAAqB,SAAS,4EAAI,KAAK,EAAE,mBAAmB,EAAA,CAAG;;IAG3E,cAAc,GAAG,KAAK,CAA8B,IAAI,sFAAI,KAAK,EAAE,qBAAqB,EAAA,CAAG;uGAVzF,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAtB,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAtB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBADlC,SAAS;mBAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE;;AAyB1C;MAEa,gCAAgC,CAAA;AAClC,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,gCAAgC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAhC,gCAAgC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAhC,gCAAgC,EAAA,UAAA,EAAA,CAAA;kBAD5C,SAAS;mBAAC,EAAE,QAAQ,EAAE,2BAA2B,EAAE;;AAKpD;MAEa,6BAA6B,CAAA;AAC/B,IAAA,WAAW,GAAG,MAAM,CAAuB,WAAW,CAAC;uGADrD,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAA7B,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAA7B,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBADzC,SAAS;mBAAC,EAAE,QAAQ,EAAE,wBAAwB,EAAE;;;ACzHjD;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;MAaU,mBAAmB,CAAA;;IAErB,KAAK,GAAG,KAAK,CAAC,EAAE;8EAAC;;IAGjB,UAAU,GAAG,KAAK,CAAa,IAAI;mFAAC;AAE7C;;;AAGG;IACM,YAAY,GAAG,KAAK,CAA0B,MAAM;qFAAC;;IAGrD,WAAW,GAAG,KAAK,CAAU,IAAI;oFAAC;;IAGlC,WAAW,GAAG,KAAK,CAA6B,EAAE;oFAAC;;;IAInD,mBAAmB,GAAG,KAAK,CAA8B,IAAI;4FAAC;IAC9D,iBAAiB,GAAG,KAAK,CAA8B,IAAI;0FAAC;IAC5D,qBAAqB,GAAG,KAAK,CAA8B,IAAI;8FAAC;IAChE,qBAAqB,GAAG,KAAK,CAA8B,IAAI;8FAAC;IAChE,kBAAkB,GAAG,KAAK,CAA8B,IAAI;2FAAC;IAC7D,qBAAqB,GAAG,KAAK,CAA8B,IAAI;8FAAC;IAChE,kBAAkB,GAAG,KAAK,CAA8B,IAAI;2FAAC;IAErD,eAAe,GAAG,YAAY,CAAC,8BAA8B;wFAAC;IAC9D,aAAa,GAAG,YAAY,CAAC,4BAA4B;sFAAC;IAC1D,iBAAiB,GAAG,YAAY,CAAC,gCAAgC;0FAAC;IAClE,iBAAiB,GAAG,YAAY,CAAC,gCAAgC;0FAAC;IAClE,cAAc,GAAG,YAAY,CAAC,6BAA6B;uFAAC;IAC5D,iBAAiB,GAAG,YAAY,CAAC,gCAAgC;0FAAC;IAClE,cAAc,GAAG,YAAY,CAAC,6BAA6B;uFAAC;AAE1D,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,mBAAmB,EAAE;uFAAC;AAClG,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,iBAAiB,EAAE;qFAAC;AAC5F,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,qBAAqB,EAAE;yFAAC;AACxG,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,qBAAqB,EAAE;yFAAC;AACxG,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,kBAAkB,EAAE;sFAAC;AAC/F,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,qBAAqB,EAAE;yFAAC;AACxG,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC,kBAAkB,EAAE;sFAAC;AAElH;;;;;AAKG;IACc,QAAQ,GAAG,eAAe,CAAC,sBAAsB;iFAAC;AAEhD,IAAA,IAAI,GAAG,QAAQ,CAA6B,MAAM;QACnE,GAAG,IAAI,CAAC,WAAW,EAAE;QACrB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK;AAC9B,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;AACrB,YAAA,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACjB,YAAA,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE;YACrC,OAAO,EAAE,IAAI,CAAC,WAAW;AAC1B,SAAA,CAAC,CAAC;AACJ,KAAA;6EAAC;IAEiB,UAAU,GAAG,MAAM,CAAe,MAAM;mFAAC;IACzC,gBAAgB,GAAG,MAAM,CAAC,KAAK;yFAAC;;;;AAKzC,IAAA,aAAa,CAAC,IAAa,EAAA;AACnC,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7C;AAEU,IAAA,aAAa,CAAC,MAAmC,EAAA;QACzD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IACxC;uGA3EW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,EAAA,EAAA,iBAAA,EAAA,qBAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,qBAAA,EAAA,EAAA,iBAAA,EAAA,uBAAA,EAAA,UAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,qBAAA,EAAA,EAAA,iBAAA,EAAA,uBAAA,EAAA,UAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAAA,EAAA,oBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,qBAAA,EAAA,EAAA,iBAAA,EAAA,uBAAA,EAAA,UAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAAA,EAAA,oBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EA6BkB,8BAA8B,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,eAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAChC,4BAA4B,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,mBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EACxB,gCAAgC,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,mBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAChC,gCAAgC,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,gBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EACnC,6BAA6B,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,mBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAC1B,gCAAgC,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,gBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EACnC,6BAA6B,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,UAAA,EAAA,SAAA,EAgBhC,sBAAsB,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC5GpE,2uDA8CA,EAAA,MAAA,EAAA,CAAA,q+BAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDII,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAChB,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,YAAA,EAAA,mBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,wBAAwB,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,eAAA,EAAA,WAAA,EAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACnE,0BAA0B,sGAAE,8BAA8B,EAAA,QAAA,EAAA,yBAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAKjD,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAZ/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,aAAa,cACX,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC;wBACP,gBAAgB;wBAChB,gBAAgB,EAAE,uBAAuB,EAAE,wBAAwB;AACnE,wBAAA,0BAA0B,EAAE,8BAA8B;AAC3D,qBAAA,EAAA,QAAA,EAAA,2uDAAA,EAAA,MAAA,EAAA,CAAA,q+BAAA,CAAA,EAAA;AAiC+C,SAAA,CAAA,EAAA,cAAA,EAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,UAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,cAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,aAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,aAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,mBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,qBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,qBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,kBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,qBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,kBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAAA,8BAA8B,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,aAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAChC,4BAA4B,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MACxB,gCAAgC,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAChC,gCAAgC,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,cAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MACnC,6BAA6B,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAC1B,gCAAgC,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,cAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MACnC,6BAA6B,6FAgBhC,sBAAsB,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AE5GpE;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-shell.mjs","sources":["../../shell/src/spark-program-units.component.ts","../../shell/src/spark-program-units.component.html","../../shell/src/spark-language-selector.component.ts","../../shell/src/spark-shell-slots.ts","../../shell/src/spark-shell.component.ts","../../shell/src/spark-shell.component.html","../../shell/mintplayer-ng-spark-shell.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, effect, inject, input, signal, untracked } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\nimport { BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective } from '@mintplayer/ng-bootstrap/accordion';\nimport { SPARK_AUTH_STATE } from '@mintplayer/ng-spark';\nimport { ProgramUnitGroup } from '@mintplayer/ng-spark/models';\nimport { SparkService } from '@mintplayer/ng-spark/services';\nimport { IconNamePipe, ResolveTranslationPipe, RouterLinkPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkSidebarTab } from './spark-shell-slots';\n\n/**\n * The server-driven navigation menu: an accordion of program-unit groups fetched from\n * `GET /spark/program-units`, which the server has already filtered to what the caller's rights\n * allow. Hosts write ZERO router links for navigation — every group, unit, icon, label and link\n * comes from `programUnits.json`; content around the menu belongs in `<spark-shell>`'s slots,\n * and a host tempted to hand-write a unit anchor should add a unit to `programUnits.json`\n * instead.\n *\n * Because the response is caller-scoped it must be re-fetched when the caller changes: the\n * component tracks the optional `SPARK_AUTH_STATE` signal (supplied by ng-spark-auth's\n * `provideSparkAuth()`, or by the app's own auth stack) and reloads on every change. Without a\n * provider it fetches once. `reloadToken` is the manual escape hatch (any changed value triggers\n * a reload), and `reload()` the imperative one.\n *\n * Usually rendered by `<spark-shell>`; exported standalone for hosts that own their own layout.\n */\n@Component({\n selector: 'spark-program-units',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n RouterLink, RouterLinkActive,\n BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective,\n SparkIconComponent, ResolveTranslationPipe, IconNamePipe, RouterLinkPipe,\n ],\n templateUrl: './spark-program-units.component.html',\n styleUrl: './spark-program-units.component.scss',\n})\nexport class SparkProgramUnitsComponent {\n private readonly sparkService = inject(SparkService);\n private readonly authState = inject(SPARK_AUTH_STATE, { optional: true });\n\n /** Any changed value triggers a reload — for apps whose auth state isn't a provided signal. */\n readonly reloadToken = input<unknown>(null);\n\n /**\n * Extra tabs to render after the generated groups, normally forwarded by `<spark-shell>` from\n * its `*sparkShellTab` directives. They must be rendered by THIS template — see\n * `SparkShellTabDirective` for why a host-declared `<bs-accordion-tab>` cannot work.\n */\n readonly extraTabs = input<readonly SparkSidebarTab[]>([]);\n\n protected readonly groups = signal<ProgramUnitGroup[]>([]);\n\n constructor() {\n effect(() => {\n this.authState?.();\n this.reloadToken();\n untracked(() => this.reload());\n });\n }\n\n /** Re-fetches the menu. The response is already rights-filtered per caller. */\n async reload(): Promise<void> {\n const config = await this.sparkService.getProgramUnits();\n // Order is the server file's contract, but sort both levels here so the rendered menu never\n // depends on JSON array order — groups were sorted, units were not (#324 F5).\n const groups = [...config.programUnitGroups]\n .sort((a, b) => a.order - b.order)\n .map(g => ({ ...g, programUnits: [...g.programUnits].sort((a, b) => a.order - b.order) }));\n this.groups.set(groups);\n }\n}\n","<bs-accordion class=\"d-block\">\n @for (group of groups(); track group.id) {\n <bs-accordion-tab>\n <ng-container *bsAccordionTabHeader>\n <spark-icon [name]=\"group.icon | iconName:'bi-folder'\" />\n <span class=\"ms-2\">{{ group.name | resolveTranslation }}</span>\n </ng-container>\n @for (unit of group.programUnits; track unit.id) {\n @if (unit | routerLink; as link) {\n <a [routerLink]=\"link\"\n routerLinkActive=\"active\"\n class=\"d-block px-3 py-2 text-decoration-none nav-link\">\n <spark-icon [name]=\"unit.icon | iconName:'bi-file'\" />\n <span class=\"ms-2\">{{ unit.name | resolveTranslation }}</span>\n </a>\n } @else {\n <!-- A url unit is an external address: a plain anchor, never a router link. -->\n <a [href]=\"unit.url\" target=\"_blank\" rel=\"noopener\"\n class=\"d-block px-3 py-2 text-decoration-none nav-link\">\n <spark-icon [name]=\"unit.icon | iconName:'bi-box-arrow-up-right'\" />\n <span class=\"ms-2\">{{ unit.name | resolveTranslation }}</span>\n </a>\n }\n }\n </bs-accordion-tab>\n }\n <!-- Host-contributed tabs are rendered HERE, by this template, so that the accordion's content\n query registers them and they join the generated groups' single-open group. -->\n @for (tab of extraTabs(); track tab.content) {\n <bs-accordion-tab>\n <ng-container *bsAccordionTabHeader>\n @if (tab.headerTemplate; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-icon [name]=\"tab.icon | iconName:'bi-folder'\" />\n <span class=\"ms-2\">{{ tab.header }}</span>\n }\n </ng-container>\n <ng-container *ngTemplateOutlet=\"tab.content\" />\n </bs-accordion-tab>\n }\n</bs-accordion>\n","import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { KeyValuePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { BsSelectComponent, BsSelectOption } from '@mintplayer/ng-bootstrap/select';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\n\n/**\n * The culture switcher: a `bs-select` over `SparkLanguageService`'s languages, persisting the\n * choice. Renders nothing when the app declares one language (or none), so hosts can include it\n * unconditionally — `<spark-shell>`'s topbar does exactly that as its trailing default.\n */\n@Component({\n selector: 'spark-language-selector',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [KeyValuePipe, FormsModule, BsSelectComponent, BsSelectOption, ResolveTranslationPipe],\n template: `\n @if (lang.languages() | keyvalue; as langs) {\n @if (langs.length > 1) {\n <bs-select [ngModel]=\"lang.language()\" (ngModelChange)=\"lang.setLanguage($event)\">\n @for (l of langs; track l.key) {\n <option [ngValue]=\"l.key\">{{ l.value | resolveTranslation }}</option>\n }\n </bs-select>\n }\n }\n `,\n})\nexport class SparkLanguageSelectorComponent {\n protected readonly lang = inject(SparkLanguageService);\n}\n","import { Directive, inject, input, TemplateRef } from '@angular/core';\n\n/**\n * Region slots for `<spark-shell>`.\n *\n * Each directive marks a template that REPLACES one region of the shell chrome (or fills an\n * empty one). An omitted slot is not an empty slot: the shell renders its default — the toggler,\n * the language selector, the title heading — so a host that supplies nothing still gets a\n * complete working shell, and a host that supplies one slot leaves the rest alone.\n *\n * The menu itself is deliberately NOT a slot. Navigation is sourced entirely from\n * `programUnits.json` through the rights-filtered `/spark/program-units` endpoint and re-fetched\n * on sign-in/out; a host that finds itself writing unit anchors in a slot should be adding units\n * to `programUnits.json` instead. Slots exist for the content AROUND the menu: an auth bar, a\n * user chip, branding, a one-off extra link, an alert strip above the routed content.\n *\n * Naming follows the house convention (prefix, component, slot — see `*sparkQueryIcon` in\n * `@mintplayer/ng-spark/grid`): `sparkShell` + region. Every slot also exists as a `TemplateRef`\n * input on `SparkShellComponent` for hosts that cannot use content projection.\n *\n * ```html\n * <spark-shell title=\"My App\">\n * <spark-auth-bar *sparkShellTopbarEnd />\n * <div *sparkShellSidebarTop>\n * <a routerLink=\"/github-projects\" class=\"nav-link\">GitHub projects</a>\n * </div>\n * <router-outlet />\n * </spark-shell>\n * ```\n */\n\n/** Topbar, leading edge. Default: a `bs-navbar-toggler` mirroring the shell's open state. */\n@Directive({ selector: '[sparkShellTopbarStart]' })\nexport class SparkShellTopbarStartDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * Topbar, trailing edge. Default: the language selector (which hides itself when the app has\n * one language). This is where an auth bar goes — the shell cannot ship one itself, since\n * `@mintplayer/ng-spark` does not (and must not) depend on `@mintplayer/ng-spark-auth`.\n */\n@Directive({ selector: '[sparkShellTopbarEnd]' })\nexport class SparkShellTopbarEndDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * Topbar, trailing edge, **beside** the default chrome rather than instead of it — rendered before\n * the language selector, which stays.\n *\n * ## Why this is not just `*sparkShellTopbarEnd`\n *\n * That slot REPLACES the region, which is right for an auth bar (a host taking over the trailing\n * edge wholesale) and wrong for a page-level action button. A host that only wanted to add a\n * button had to re-render the language selector itself to keep it — which means importing it,\n * knowing it hides itself in a single-language app, and keeping that copy in step with the shell.\n * Every host that did this got it slightly differently.\n *\n * Pairs with `SparkQueryActionsService`: that service resolves a query's custom actions without the\n * grid, and this is where a page-level one goes.\n *\n * ```html\n * <button *sparkShellTopbarActions class=\"btn btn-primary\" (click)=\"publish()\">Publish</button>\n * ```\n *\n * Supplying both slots is allowed and does what it says: `topbarEnd` replaces the default chrome,\n * and these actions still render ahead of whatever it put there.\n */\n@Directive({ selector: '[sparkShellTopbarActions]' })\nexport class SparkShellTopbarActionsDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Sidebar, above everything. Default: `<h5>{{ title }}</h5>`. */\n@Directive({ selector: '[sparkShellSidebarHeader]' })\nexport class SparkShellSidebarHeaderDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Sidebar, between the header and the program-units menu. No default. */\n@Directive({ selector: '[sparkShellSidebarTop]' })\nexport class SparkShellSidebarTopDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * An extra accordion tab in the sidebar menu, rendered after the generated program-unit groups\n * and sharing their single-open behavior.\n *\n * A tab is contributed as DATA (header + body template), not as markup, and that is load-bearing:\n * `bs-accordion` discovers its tabs with an Angular content query, which matches by declaration\n * view, so a `<bs-accordion-tab>` written in a host's template and inserted into the library's\n * accordion is never registered — it would land at index -1, get no hoisted header and no slot.\n * Declaring a second `<bs-accordion>` instead is what puts the tab in its own exclusivity group:\n * `mp-accordion` enforces single-open per element, over children it owns and over\n * `<details name>`, whose grouping cannot cross a shadow root. So the tab element must be created\n * by the menu itself, from what this directive carries.\n *\n * ```html\n * <ng-container *sparkShellTab=\"'Component demos'; icon: 'palette'\">\n * <a routerLink=\"/query-slots\" routerLinkActive=\"active\" class=\"nav-link\">Query card slots</a>\n * </ng-container>\n * ```\n *\n * Navigation still belongs in `programUnits.json` — this is for pages the model cannot describe\n * (client-side demos, external tools). For sidebar content that is NOT an accordion tab, use\n * `*sparkShellSidebarTop` or `*sparkShellSidebarFooter`.\n */\n@Directive({ selector: '[sparkShellTab]' })\nexport class SparkShellTabDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n\n /** The tab's header label. */\n readonly header = input.required<string>({ alias: 'sparkShellTab' });\n\n /** Bootstrap icon name for the header, as in `programUnits.json`. Defaults to a folder. */\n readonly icon = input<string | undefined>(undefined, { alias: 'sparkShellTabIcon' });\n\n /** Replaces the icon+label header entirely, for a header that needs its own markup. */\n readonly headerTemplate = input<TemplateRef<unknown> | null>(null, { alias: 'sparkShellTabHeader' });\n}\n\n/**\n * A sidebar accordion tab in the shape the menu renders it. Hosts normally contribute tabs with\n * `*sparkShellTab`; this is the same thing as data, for a host that computes its tabs.\n */\nexport interface SparkSidebarTab {\n readonly header: string;\n readonly icon?: string;\n readonly headerTemplate?: TemplateRef<unknown> | null;\n readonly content: TemplateRef<unknown>;\n}\n\n/** Sidebar, at the very bottom. No default. */\n@Directive({ selector: '[sparkShellSidebarFooter]' })\nexport class SparkShellSidebarFooterDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/** Main region, above the projected content (the host's `<router-outlet>`). No default. */\n@Directive({ selector: '[sparkShellMainHeader]' })\nexport class SparkShellMainHeaderDirective {\n readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);\n}\n","import { ChangeDetectionStrategy, Component, computed, contentChild, contentChildren, input, signal, TemplateRef } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { BsShellComponent, BsShellSidebarDirective, BsShellState } from '@mintplayer/ng-bootstrap/shell';\nimport { BsNavbarTogglerComponent } from '@mintplayer/ng-bootstrap/navbar-toggler';\nimport type { Breakpoint } from '@mintplayer/ng-bootstrap';\nimport type { ShellStateChangeEventDetail } from '@mintplayer/web-components/shell';\nimport { SparkProgramUnitsComponent } from './spark-program-units.component';\nimport { SparkLanguageSelectorComponent } from './spark-language-selector.component';\nimport {\n SparkShellMainHeaderDirective,\n SparkShellSidebarFooterDirective,\n SparkShellSidebarHeaderDirective,\n SparkShellSidebarTopDirective,\n SparkShellTabDirective,\n SparkShellTopbarEndDirective,\n SparkShellTopbarActionsDirective,\n SparkShellTopbarStartDirective,\n SparkSidebarTab,\n} from './spark-shell-slots';\n\n/**\n * The application frame: topbar + sidebar + main, wrapping ng-bootstrap's `bs-shell` (whose\n * `mp-shell` web component owns ALL responsive behavior — breakpoints, the overlay drawer,\n * dismiss-on-navigate — in CSS; nothing here re-derives a pixel width). The sidebar renders the\n * server-driven program-units menu; the host projects its `<router-outlet>` as the default\n * content and customizes the chrome through the `*sparkShell*` slots (see `spark-shell-slots.ts`\n * for the doctrine: an omitted slot renders its default, and the menu itself is never a slot).\n *\n * ```html\n * <spark-shell title=\"My App\">\n * <spark-auth-bar *sparkShellTopbarEnd />\n * <router-outlet />\n * </spark-shell>\n * ```\n *\n * The one piece of state the shell keeps is the toggler↔drawer mirror: the built-in hamburger is\n * hidden (`::part(hamburger)`) in favor of a `bs-navbar-toggler` in the topbar, so the shell\n * listens to `statechange` to keep the toggler's icon truthful in `auto` mode and only forces\n * `show`/`hide` on explicit toggles.\n *\n * Theming: the chrome colors are CSS custom properties with the classic dark-sidebar defaults —\n * `--spark-shell-topbar-bg`, `--spark-shell-sidebar-bg`, `--spark-shell-main-bg` — overridable on\n * the `<spark-shell>` element. `sidebarTheme` flips the sidebar's `data-bs-theme` (which is what\n * recolors the accordion internals across the shadow boundary) together with its default palette.\n */\n@Component({\n selector: 'spark-shell',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgTemplateOutlet,\n BsShellComponent, BsShellSidebarDirective, BsNavbarTogglerComponent,\n SparkProgramUnitsComponent, SparkLanguageSelectorComponent,\n ],\n templateUrl: './spark-shell.component.html',\n styleUrl: './spark-shell.component.scss',\n})\nexport class SparkShellComponent {\n /** The sidebar heading. Ignored when a `*sparkShellSidebarHeader` slot is supplied. */\n readonly title = input('');\n\n /** Forwarded to `bs-shell`: below it the sidebar is an overlay drawer. */\n readonly breakpoint = input<Breakpoint>('md');\n\n /**\n * `data-bs-theme` for the sidebar — what flips the accordion's shadow-DOM internals between\n * palettes — plus the matching default background. `null` sets no theme (inherit the page's).\n */\n readonly sidebarTheme = input<'dark' | 'light' | null>('dark');\n\n /** Forwarded to the menu: any changed value re-fetches the program units. */\n readonly reloadToken = input<unknown>(null);\n\n /** Extra sidebar tabs as data, for hosts that compute them; `*sparkShellTab` is the usual way. */\n readonly sidebarTabs = input<readonly SparkSidebarTab[]>([]);\n\n // One TemplateRef input per slot, for hosts that cannot use content projection\n // (the spark-query-card precedent). The projected directive wins when both are present.\n readonly topbarStartTemplate = input<TemplateRef<unknown> | null>(null);\n readonly topbarEndTemplate = input<TemplateRef<unknown> | null>(null);\n readonly topbarActionsTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarHeaderTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarTopTemplate = input<TemplateRef<unknown> | null>(null);\n readonly sidebarFooterTemplate = input<TemplateRef<unknown> | null>(null);\n readonly mainHeaderTemplate = input<TemplateRef<unknown> | null>(null);\n\n private readonly topbarStartSlot = contentChild(SparkShellTopbarStartDirective);\n private readonly topbarEndSlot = contentChild(SparkShellTopbarEndDirective);\n private readonly topbarActionsSlot = contentChild(SparkShellTopbarActionsDirective);\n private readonly sidebarHeaderSlot = contentChild(SparkShellSidebarHeaderDirective);\n private readonly sidebarTopSlot = contentChild(SparkShellSidebarTopDirective);\n private readonly sidebarFooterSlot = contentChild(SparkShellSidebarFooterDirective);\n private readonly mainHeaderSlot = contentChild(SparkShellMainHeaderDirective);\n\n protected readonly topbarStartTpl = computed(() => this.topbarStartSlot()?.templateRef ?? this.topbarStartTemplate());\n protected readonly topbarEndTpl = computed(() => this.topbarEndSlot()?.templateRef ?? this.topbarEndTemplate());\n protected readonly topbarActionsTpl = computed(() => this.topbarActionsSlot()?.templateRef ?? this.topbarActionsTemplate());\n protected readonly sidebarHeaderTpl = computed(() => this.sidebarHeaderSlot()?.templateRef ?? this.sidebarHeaderTemplate());\n protected readonly sidebarTopTpl = computed(() => this.sidebarTopSlot()?.templateRef ?? this.sidebarTopTemplate());\n protected readonly sidebarFooterTpl = computed(() => this.sidebarFooterSlot()?.templateRef ?? this.sidebarFooterTemplate());\n protected readonly mainHeaderTpl = computed(() => this.mainHeaderSlot()?.templateRef ?? this.mainHeaderTemplate());\n\n /**\n * Extra accordion tabs, forwarded to the menu so IT creates the `<bs-accordion-tab>` elements —\n * the only way they share the generated groups' single-open behavior (see\n * `SparkShellTabDirective`). Data-supplied tabs come first, then projected ones in declaration\n * order.\n */\n private readonly tabSlots = contentChildren(SparkShellTabDirective);\n\n protected readonly tabs = computed<readonly SparkSidebarTab[]>(() => [\n ...this.sidebarTabs(),\n ...this.tabSlots().map(slot => ({\n header: slot.header(),\n icon: slot.icon(),\n headerTemplate: slot.headerTemplate(),\n content: slot.templateRef,\n })),\n ]);\n\n protected readonly shellState = signal<BsShellState>('auto');\n protected readonly isSidebarVisible = signal(false);\n\n // Explicit toggles force show/hide; 'auto' responsive behavior is otherwise preserved by\n // never writing state on our own. The mirror below keeps the toggler icon truthful when the\n // shell opens/closes itself at the breakpoint.\n protected toggleSidebar(open: boolean): void {\n this.shellState.set(open ? 'show' : 'hide');\n }\n\n protected onShellToggle(detail: ShellStateChangeEventDetail): void {\n this.isSidebarVisible.set(detail.open);\n }\n}\n","<bs-shell [state]=\"shellState()\" [breakpoint]=\"breakpoint()\" [dismissOnNavigate]=\"true\"\n (statechange)=\"onShellToggle($event)\">\n <div slot=\"topbar\" class=\"spark-topbar w-100 d-flex align-items-center\">\n @if (topbarStartTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <bs-navbar-toggler [state]=\"isSidebarVisible()\" (stateChange)=\"toggleSidebar($event)\" />\n }\n <div class=\"ms-auto me-3 d-flex align-items-center gap-2\">\n <!-- Ahead of the trailing chrome, and OUTSIDE the @if below: these actions are additive, so\n they render whether the host replaced the trailing edge or left the default selector. -->\n @if (topbarActionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n @if (topbarEndTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <spark-language-selector />\n }\n </div>\n </div>\n\n <nav bsShellSidebar class=\"spark-sidebar p-2\"\n [class.spark-sidebar-dark]=\"sidebarTheme() === 'dark'\"\n [attr.data-bs-theme]=\"sidebarTheme()\">\n @if (sidebarHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n } @else {\n <h5 class=\"p-3 border-bottom\">{{ title() }}</h5>\n }\n @if (sidebarTopTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <spark-program-units [reloadToken]=\"reloadToken()\" [extraTabs]=\"tabs()\" />\n @if (sidebarFooterTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n </nav>\n\n <!-- The padding lives in the stylesheet as `--spark-main-padding`, not as a `p-4` utility, so the\n sticky action bar can read it and bleed back out to the scrollport edges. See _actionbar.scss. -->\n <main>\n @if (mainHeaderTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl\" />\n }\n <ng-content />\n </main>\n</bs-shell>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mintplayer/ng-spark",
3
3
  "private": false,
4
- "version": "22.8.0",
4
+ "version": "22.10.0",
5
5
  "description": "Angular component library for MintPlayer.Spark CRUD applications",
6
6
  "repository": {
7
7
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "@angular/common": "^22.0.0",
15
15
  "@angular/router": "^22.0.0",
16
16
  "@angular/forms": "^22.0.0",
17
- "@mintplayer/ng-bootstrap": "^22.13.0",
17
+ "@mintplayer/ng-bootstrap": "^22.18.0",
18
18
  "@mintplayer/pagination": "*",
19
19
  "rxjs": "~7.8.0"
20
20
  },
@@ -29,6 +29,10 @@
29
29
  "types": "./types/mintplayer-ng-spark.d.ts",
30
30
  "default": "./fesm2022/mintplayer-ng-spark.mjs"
31
31
  },
32
+ "./attribute-description": {
33
+ "types": "./types/mintplayer-ng-spark-attribute-description.d.ts",
34
+ "default": "./fesm2022/mintplayer-ng-spark-attribute-description.mjs"
35
+ },
32
36
  "./client-operations": {
33
37
  "types": "./types/mintplayer-ng-spark-client-operations.d.ts",
34
38
  "default": "./fesm2022/mintplayer-ng-spark-client-operations.mjs"