@mintplayer/ng-spark 22.4.0 → 22.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs +12 -12
- package/fesm2022/mintplayer-ng-spark-grid.mjs +21 -21
- package/fesm2022/mintplayer-ng-spark-icon.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-models.mjs +185 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +82 -70
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +356 -18
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +27 -15
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs +379 -0
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark.mjs +17 -1
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +5 -1
- package/types/mintplayer-ng-spark-models.d.ts +106 -2
- package/types/mintplayer-ng-spark-pipes.d.ts +9 -1
- package/types/mintplayer-ng-spark-po-form.d.ts +132 -4
- package/types/mintplayer-ng-spark-services.d.ts +10 -0
- package/types/mintplayer-ng-spark-shell.d.ts +242 -0
- package/types/mintplayer-ng-spark.d.ts +17 -2
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, input, signal, effect, untracked, ChangeDetectionStrategy, Component, TemplateRef, Directive, contentChild, computed, contentChildren } from '@angular/core';
|
|
3
|
+
import { NgTemplateOutlet, KeyValuePipe } from '@angular/common';
|
|
4
|
+
import { BsShellComponent, BsShellSidebarDirective } from '@mintplayer/ng-bootstrap/shell';
|
|
5
|
+
import { BsNavbarTogglerComponent } from '@mintplayer/ng-bootstrap/navbar-toggler';
|
|
6
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
7
|
+
import { BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective } from '@mintplayer/ng-bootstrap/accordion';
|
|
8
|
+
import { SPARK_AUTH_STATE } from '@mintplayer/ng-spark';
|
|
9
|
+
import { SparkService, SparkLanguageService } from '@mintplayer/ng-spark/services';
|
|
10
|
+
import { ResolveTranslationPipe, IconNamePipe, RouterLinkPipe } from '@mintplayer/ng-spark/pipes';
|
|
11
|
+
import { SparkIconComponent } from '@mintplayer/ng-spark/icon';
|
|
12
|
+
import * as i1 from '@angular/forms';
|
|
13
|
+
import { FormsModule } from '@angular/forms';
|
|
14
|
+
import { BsSelectComponent, BsSelectOption } from '@mintplayer/ng-bootstrap/select';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The server-driven navigation menu: an accordion of program-unit groups fetched from
|
|
18
|
+
* `GET /spark/program-units`, which the server has already filtered to what the caller's rights
|
|
19
|
+
* allow. Hosts write ZERO router links for navigation — every group, unit, icon, label and link
|
|
20
|
+
* comes from `programUnits.json`; content around the menu belongs in `<spark-shell>`'s slots,
|
|
21
|
+
* and a host tempted to hand-write a unit anchor should add a unit to `programUnits.json`
|
|
22
|
+
* instead.
|
|
23
|
+
*
|
|
24
|
+
* Because the response is caller-scoped it must be re-fetched when the caller changes: the
|
|
25
|
+
* component tracks the optional `SPARK_AUTH_STATE` signal (supplied by ng-spark-auth's
|
|
26
|
+
* `provideSparkAuth()`, or by the app's own auth stack) and reloads on every change. Without a
|
|
27
|
+
* provider it fetches once. `reloadToken` is the manual escape hatch (any changed value triggers
|
|
28
|
+
* a reload), and `reload()` the imperative one.
|
|
29
|
+
*
|
|
30
|
+
* Usually rendered by `<spark-shell>`; exported standalone for hosts that own their own layout.
|
|
31
|
+
*/
|
|
32
|
+
class SparkProgramUnitsComponent {
|
|
33
|
+
sparkService = inject(SparkService);
|
|
34
|
+
authState = inject(SPARK_AUTH_STATE, { optional: true });
|
|
35
|
+
/** Any changed value triggers a reload — for apps whose auth state isn't a provided signal. */
|
|
36
|
+
reloadToken = input(null, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "reloadToken" }] : /* istanbul ignore next */ []));
|
|
38
|
+
/**
|
|
39
|
+
* Extra tabs to render after the generated groups, normally forwarded by `<spark-shell>` from
|
|
40
|
+
* its `*sparkShellTab` directives. They must be rendered by THIS template — see
|
|
41
|
+
* `SparkShellTabDirective` for why a host-declared `<bs-accordion-tab>` cannot work.
|
|
42
|
+
*/
|
|
43
|
+
extraTabs = input([], /* @ts-ignore */
|
|
44
|
+
...(ngDevMode ? [{ debugName: "extraTabs" }] : /* istanbul ignore next */ []));
|
|
45
|
+
groups = signal([], /* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
47
|
+
constructor() {
|
|
48
|
+
effect(() => {
|
|
49
|
+
this.authState?.();
|
|
50
|
+
this.reloadToken();
|
|
51
|
+
untracked(() => this.reload());
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Re-fetches the menu. The response is already rights-filtered per caller. */
|
|
55
|
+
async reload() {
|
|
56
|
+
const config = await this.sparkService.getProgramUnits();
|
|
57
|
+
// Order is the server file's contract, but sort both levels here so the rendered menu never
|
|
58
|
+
// depends on JSON array order — groups were sorted, units were not (#324 F5).
|
|
59
|
+
const groups = [...config.programUnitGroups]
|
|
60
|
+
.sort((a, b) => a.order - b.order)
|
|
61
|
+
.map(g => ({ ...g, programUnits: [...g.programUnits].sort((a, b) => a.order - b.order) }));
|
|
62
|
+
this.groups.set(groups);
|
|
63
|
+
}
|
|
64
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkProgramUnitsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
65
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkProgramUnitsComponent, isStandalone: true, selector: "spark-program-units", inputs: { reloadToken: { classPropertyName: "reloadToken", publicName: "reloadToken", isSignal: true, isRequired: false, transformFunction: null }, extraTabs: { classPropertyName: "extraTabs", publicName: "extraTabs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<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", styles: [":host{display:block;--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep mp-accordion::part(content){padding:0}.nav-link{color:inherit}.nav-link:hover{background-color:#ffffff1a}.nav-link.active{background-color:#fff3}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: BsAccordionComponent, selector: "bs-accordion", inputs: ["multi", "highlightActiveTab"] }, { kind: "component", type: BsAccordionTabComponent, selector: "bs-accordion-tab", inputs: ["isActive", "disabled"], outputs: ["isActiveChange"] }, { kind: "directive", type: BsAccordionTabHeaderDirective, selector: "[bsAccordionTabHeader]" }, { kind: "component", type: SparkIconComponent, selector: "spark-icon", inputs: ["name"] }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }, { kind: "pipe", type: IconNamePipe, name: "iconName" }, { kind: "pipe", type: RouterLinkPipe, name: "routerLink" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
66
|
+
}
|
|
67
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkProgramUnitsComponent, decorators: [{
|
|
68
|
+
type: Component,
|
|
69
|
+
args: [{ selector: 'spark-program-units', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
70
|
+
NgTemplateOutlet,
|
|
71
|
+
RouterLink, RouterLinkActive,
|
|
72
|
+
BsAccordionComponent, BsAccordionTabComponent, BsAccordionTabHeaderDirective,
|
|
73
|
+
SparkIconComponent, ResolveTranslationPipe, IconNamePipe, RouterLinkPipe,
|
|
74
|
+
], template: "<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", styles: [":host{display:block;--bs-primary-bg-subtle: rgba(255, 255, 255, .1);--bs-border-color: transparent}:host ::ng-deep mp-accordion::part(content){padding:0}.nav-link{color:inherit}.nav-link:hover{background-color:#ffffff1a}.nav-link.active{background-color:#fff3}\n"] }]
|
|
75
|
+
}], ctorParameters: () => [], propDecorators: { reloadToken: [{ type: i0.Input, args: [{ isSignal: true, alias: "reloadToken", required: false }] }], extraTabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraTabs", required: false }] }] } });
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The culture switcher: a `bs-select` over `SparkLanguageService`'s languages, persisting the
|
|
79
|
+
* choice. Renders nothing when the app declares one language (or none), so hosts can include it
|
|
80
|
+
* unconditionally — `<spark-shell>`'s topbar does exactly that as its trailing default.
|
|
81
|
+
*/
|
|
82
|
+
class SparkLanguageSelectorComponent {
|
|
83
|
+
lang = inject(SparkLanguageService);
|
|
84
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkLanguageSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
85
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SparkLanguageSelectorComponent, isStandalone: true, selector: "spark-language-selector", ngImport: i0, template: `
|
|
86
|
+
@if (lang.languages() | keyvalue; as langs) {
|
|
87
|
+
@if (langs.length > 1) {
|
|
88
|
+
<bs-select [ngModel]="lang.language()" (ngModelChange)="lang.setLanguage($event)">
|
|
89
|
+
@for (l of langs; track l.key) {
|
|
90
|
+
<option [ngValue]="l.key">{{ l.value | resolveTranslation }}</option>
|
|
91
|
+
}
|
|
92
|
+
</bs-select>
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BsSelectComponent, selector: "bs-select", inputs: ["identifier", "size", "multiple", "numberVisible", "disabled"] }, { kind: "directive", type: BsSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: ResolveTranslationPipe, name: "resolveTranslation" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
96
|
+
}
|
|
97
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkLanguageSelectorComponent, decorators: [{
|
|
98
|
+
type: Component,
|
|
99
|
+
args: [{
|
|
100
|
+
selector: 'spark-language-selector',
|
|
101
|
+
standalone: true,
|
|
102
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
103
|
+
imports: [KeyValuePipe, FormsModule, BsSelectComponent, BsSelectOption, ResolveTranslationPipe],
|
|
104
|
+
template: `
|
|
105
|
+
@if (lang.languages() | keyvalue; as langs) {
|
|
106
|
+
@if (langs.length > 1) {
|
|
107
|
+
<bs-select [ngModel]="lang.language()" (ngModelChange)="lang.setLanguage($event)">
|
|
108
|
+
@for (l of langs; track l.key) {
|
|
109
|
+
<option [ngValue]="l.key">{{ l.value | resolveTranslation }}</option>
|
|
110
|
+
}
|
|
111
|
+
</bs-select>
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`,
|
|
115
|
+
}]
|
|
116
|
+
}] });
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Region slots for `<spark-shell>`.
|
|
120
|
+
*
|
|
121
|
+
* Each directive marks a template that REPLACES one region of the shell chrome (or fills an
|
|
122
|
+
* empty one). An omitted slot is not an empty slot: the shell renders its default — the toggler,
|
|
123
|
+
* the language selector, the title heading — so a host that supplies nothing still gets a
|
|
124
|
+
* complete working shell, and a host that supplies one slot leaves the rest alone.
|
|
125
|
+
*
|
|
126
|
+
* The menu itself is deliberately NOT a slot. Navigation is sourced entirely from
|
|
127
|
+
* `programUnits.json` through the rights-filtered `/spark/program-units` endpoint and re-fetched
|
|
128
|
+
* on sign-in/out; a host that finds itself writing unit anchors in a slot should be adding units
|
|
129
|
+
* to `programUnits.json` instead. Slots exist for the content AROUND the menu: an auth bar, a
|
|
130
|
+
* user chip, branding, a one-off extra link, an alert strip above the routed content.
|
|
131
|
+
*
|
|
132
|
+
* Naming follows the house convention (prefix, component, slot — see `*sparkQueryIcon` in
|
|
133
|
+
* `@mintplayer/ng-spark/grid`): `sparkShell` + region. Every slot also exists as a `TemplateRef`
|
|
134
|
+
* input on `SparkShellComponent` for hosts that cannot use content projection.
|
|
135
|
+
*
|
|
136
|
+
* ```html
|
|
137
|
+
* <spark-shell title="My App">
|
|
138
|
+
* <spark-auth-bar *sparkShellTopbarEnd />
|
|
139
|
+
* <div *sparkShellSidebarTop>
|
|
140
|
+
* <a routerLink="/github-projects" class="nav-link">GitHub projects</a>
|
|
141
|
+
* </div>
|
|
142
|
+
* <router-outlet />
|
|
143
|
+
* </spark-shell>
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
/** Topbar, leading edge. Default: a `bs-navbar-toggler` mirroring the shell's open state. */
|
|
147
|
+
class SparkShellTopbarStartDirective {
|
|
148
|
+
templateRef = inject(TemplateRef);
|
|
149
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTopbarStartDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
150
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellTopbarStartDirective, isStandalone: true, selector: "[sparkShellTopbarStart]", ngImport: i0 });
|
|
151
|
+
}
|
|
152
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTopbarStartDirective, decorators: [{
|
|
153
|
+
type: Directive,
|
|
154
|
+
args: [{ selector: '[sparkShellTopbarStart]' }]
|
|
155
|
+
}] });
|
|
156
|
+
/**
|
|
157
|
+
* Topbar, trailing edge. Default: the language selector (which hides itself when the app has
|
|
158
|
+
* one language). This is where an auth bar goes — the shell cannot ship one itself, since
|
|
159
|
+
* `@mintplayer/ng-spark` does not (and must not) depend on `@mintplayer/ng-spark-auth`.
|
|
160
|
+
*/
|
|
161
|
+
class SparkShellTopbarEndDirective {
|
|
162
|
+
templateRef = inject(TemplateRef);
|
|
163
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTopbarEndDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
164
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellTopbarEndDirective, isStandalone: true, selector: "[sparkShellTopbarEnd]", ngImport: i0 });
|
|
165
|
+
}
|
|
166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTopbarEndDirective, decorators: [{
|
|
167
|
+
type: Directive,
|
|
168
|
+
args: [{ selector: '[sparkShellTopbarEnd]' }]
|
|
169
|
+
}] });
|
|
170
|
+
/** Sidebar, above everything. Default: `<h5>{{ title }}</h5>`. */
|
|
171
|
+
class SparkShellSidebarHeaderDirective {
|
|
172
|
+
templateRef = inject(TemplateRef);
|
|
173
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
174
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellSidebarHeaderDirective, isStandalone: true, selector: "[sparkShellSidebarHeader]", ngImport: i0 });
|
|
175
|
+
}
|
|
176
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarHeaderDirective, decorators: [{
|
|
177
|
+
type: Directive,
|
|
178
|
+
args: [{ selector: '[sparkShellSidebarHeader]' }]
|
|
179
|
+
}] });
|
|
180
|
+
/** Sidebar, between the header and the program-units menu. No default. */
|
|
181
|
+
class SparkShellSidebarTopDirective {
|
|
182
|
+
templateRef = inject(TemplateRef);
|
|
183
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarTopDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
184
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellSidebarTopDirective, isStandalone: true, selector: "[sparkShellSidebarTop]", ngImport: i0 });
|
|
185
|
+
}
|
|
186
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarTopDirective, decorators: [{
|
|
187
|
+
type: Directive,
|
|
188
|
+
args: [{ selector: '[sparkShellSidebarTop]' }]
|
|
189
|
+
}] });
|
|
190
|
+
/**
|
|
191
|
+
* An extra accordion tab in the sidebar menu, rendered after the generated program-unit groups
|
|
192
|
+
* and sharing their single-open behavior.
|
|
193
|
+
*
|
|
194
|
+
* A tab is contributed as DATA (header + body template), not as markup, and that is load-bearing:
|
|
195
|
+
* `bs-accordion` discovers its tabs with an Angular content query, which matches by declaration
|
|
196
|
+
* view, so a `<bs-accordion-tab>` written in a host's template and inserted into the library's
|
|
197
|
+
* accordion is never registered — it would land at index -1, get no hoisted header and no slot.
|
|
198
|
+
* Declaring a second `<bs-accordion>` instead is what puts the tab in its own exclusivity group:
|
|
199
|
+
* `mp-accordion` enforces single-open per element, over children it owns and over
|
|
200
|
+
* `<details name>`, whose grouping cannot cross a shadow root. So the tab element must be created
|
|
201
|
+
* by the menu itself, from what this directive carries.
|
|
202
|
+
*
|
|
203
|
+
* ```html
|
|
204
|
+
* <ng-container *sparkShellTab="'Component demos'; icon: 'palette'">
|
|
205
|
+
* <a routerLink="/query-slots" routerLinkActive="active" class="nav-link">Query card slots</a>
|
|
206
|
+
* </ng-container>
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* Navigation still belongs in `programUnits.json` — this is for pages the model cannot describe
|
|
210
|
+
* (client-side demos, external tools). For sidebar content that is NOT an accordion tab, use
|
|
211
|
+
* `*sparkShellSidebarTop` or `*sparkShellSidebarFooter`.
|
|
212
|
+
*/
|
|
213
|
+
class SparkShellTabDirective {
|
|
214
|
+
templateRef = inject(TemplateRef);
|
|
215
|
+
/** The tab's header label. */
|
|
216
|
+
header = input.required({ ...(ngDevMode ? { debugName: "header" } : /* istanbul ignore next */ {}), alias: 'sparkShellTab' });
|
|
217
|
+
/** Bootstrap icon name for the header, as in `programUnits.json`. Defaults to a folder. */
|
|
218
|
+
icon = input(undefined, { ...(ngDevMode ? { debugName: "icon" } : /* istanbul ignore next */ {}), alias: 'sparkShellTabIcon' });
|
|
219
|
+
/** Replaces the icon+label header entirely, for a header that needs its own markup. */
|
|
220
|
+
headerTemplate = input(null, { ...(ngDevMode ? { debugName: "headerTemplate" } : /* istanbul ignore next */ {}), alias: 'sparkShellTabHeader' });
|
|
221
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTabDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
222
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: SparkShellTabDirective, isStandalone: true, selector: "[sparkShellTab]", inputs: { header: { classPropertyName: "header", publicName: "sparkShellTab", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "sparkShellTabIcon", isSignal: true, isRequired: false, transformFunction: null }, headerTemplate: { classPropertyName: "headerTemplate", publicName: "sparkShellTabHeader", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
223
|
+
}
|
|
224
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellTabDirective, decorators: [{
|
|
225
|
+
type: Directive,
|
|
226
|
+
args: [{ selector: '[sparkShellTab]' }]
|
|
227
|
+
}], propDecorators: { header: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkShellTab", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkShellTabIcon", required: false }] }], headerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkShellTabHeader", required: false }] }] } });
|
|
228
|
+
/** Sidebar, at the very bottom. No default. */
|
|
229
|
+
class SparkShellSidebarFooterDirective {
|
|
230
|
+
templateRef = inject(TemplateRef);
|
|
231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
232
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellSidebarFooterDirective, isStandalone: true, selector: "[sparkShellSidebarFooter]", ngImport: i0 });
|
|
233
|
+
}
|
|
234
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellSidebarFooterDirective, decorators: [{
|
|
235
|
+
type: Directive,
|
|
236
|
+
args: [{ selector: '[sparkShellSidebarFooter]' }]
|
|
237
|
+
}] });
|
|
238
|
+
/** Main region, above the projected content (the host's `<router-outlet>`). No default. */
|
|
239
|
+
class SparkShellMainHeaderDirective {
|
|
240
|
+
templateRef = inject(TemplateRef);
|
|
241
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellMainHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
242
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: SparkShellMainHeaderDirective, isStandalone: true, selector: "[sparkShellMainHeader]", ngImport: i0 });
|
|
243
|
+
}
|
|
244
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellMainHeaderDirective, decorators: [{
|
|
245
|
+
type: Directive,
|
|
246
|
+
args: [{ selector: '[sparkShellMainHeader]' }]
|
|
247
|
+
}] });
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The application frame: topbar + sidebar + main, wrapping ng-bootstrap's `bs-shell` (whose
|
|
251
|
+
* `mp-shell` web component owns ALL responsive behavior — breakpoints, the overlay drawer,
|
|
252
|
+
* dismiss-on-navigate — in CSS; nothing here re-derives a pixel width). The sidebar renders the
|
|
253
|
+
* server-driven program-units menu; the host projects its `<router-outlet>` as the default
|
|
254
|
+
* content and customizes the chrome through the `*sparkShell*` slots (see `spark-shell-slots.ts`
|
|
255
|
+
* for the doctrine: an omitted slot renders its default, and the menu itself is never a slot).
|
|
256
|
+
*
|
|
257
|
+
* ```html
|
|
258
|
+
* <spark-shell title="My App">
|
|
259
|
+
* <spark-auth-bar *sparkShellTopbarEnd />
|
|
260
|
+
* <router-outlet />
|
|
261
|
+
* </spark-shell>
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* The one piece of state the shell keeps is the toggler↔drawer mirror: the built-in hamburger is
|
|
265
|
+
* hidden (`::part(hamburger)`) in favor of a `bs-navbar-toggler` in the topbar, so the shell
|
|
266
|
+
* listens to `statechange` to keep the toggler's icon truthful in `auto` mode and only forces
|
|
267
|
+
* `show`/`hide` on explicit toggles.
|
|
268
|
+
*
|
|
269
|
+
* Theming: the chrome colors are CSS custom properties with the classic dark-sidebar defaults —
|
|
270
|
+
* `--spark-shell-topbar-bg`, `--spark-shell-sidebar-bg`, `--spark-shell-main-bg` — overridable on
|
|
271
|
+
* the `<spark-shell>` element. `sidebarTheme` flips the sidebar's `data-bs-theme` (which is what
|
|
272
|
+
* recolors the accordion internals across the shadow boundary) together with its default palette.
|
|
273
|
+
*/
|
|
274
|
+
class SparkShellComponent {
|
|
275
|
+
/** The sidebar heading. Ignored when a `*sparkShellSidebarHeader` slot is supplied. */
|
|
276
|
+
title = input('', /* @ts-ignore */
|
|
277
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
278
|
+
/** Forwarded to `bs-shell`: below it the sidebar is an overlay drawer. */
|
|
279
|
+
breakpoint = input('md', /* @ts-ignore */
|
|
280
|
+
...(ngDevMode ? [{ debugName: "breakpoint" }] : /* istanbul ignore next */ []));
|
|
281
|
+
/**
|
|
282
|
+
* `data-bs-theme` for the sidebar — what flips the accordion's shadow-DOM internals between
|
|
283
|
+
* palettes — plus the matching default background. `null` sets no theme (inherit the page's).
|
|
284
|
+
*/
|
|
285
|
+
sidebarTheme = input('dark', /* @ts-ignore */
|
|
286
|
+
...(ngDevMode ? [{ debugName: "sidebarTheme" }] : /* istanbul ignore next */ []));
|
|
287
|
+
/** Forwarded to the menu: any changed value re-fetches the program units. */
|
|
288
|
+
reloadToken = input(null, /* @ts-ignore */
|
|
289
|
+
...(ngDevMode ? [{ debugName: "reloadToken" }] : /* istanbul ignore next */ []));
|
|
290
|
+
/** Extra sidebar tabs as data, for hosts that compute them; `*sparkShellTab` is the usual way. */
|
|
291
|
+
sidebarTabs = input([], /* @ts-ignore */
|
|
292
|
+
...(ngDevMode ? [{ debugName: "sidebarTabs" }] : /* istanbul ignore next */ []));
|
|
293
|
+
// One TemplateRef input per slot, for hosts that cannot use content projection
|
|
294
|
+
// (the spark-query-card precedent). The projected directive wins when both are present.
|
|
295
|
+
topbarStartTemplate = input(null, /* @ts-ignore */
|
|
296
|
+
...(ngDevMode ? [{ debugName: "topbarStartTemplate" }] : /* istanbul ignore next */ []));
|
|
297
|
+
topbarEndTemplate = input(null, /* @ts-ignore */
|
|
298
|
+
...(ngDevMode ? [{ debugName: "topbarEndTemplate" }] : /* istanbul ignore next */ []));
|
|
299
|
+
sidebarHeaderTemplate = input(null, /* @ts-ignore */
|
|
300
|
+
...(ngDevMode ? [{ debugName: "sidebarHeaderTemplate" }] : /* istanbul ignore next */ []));
|
|
301
|
+
sidebarTopTemplate = input(null, /* @ts-ignore */
|
|
302
|
+
...(ngDevMode ? [{ debugName: "sidebarTopTemplate" }] : /* istanbul ignore next */ []));
|
|
303
|
+
sidebarFooterTemplate = input(null, /* @ts-ignore */
|
|
304
|
+
...(ngDevMode ? [{ debugName: "sidebarFooterTemplate" }] : /* istanbul ignore next */ []));
|
|
305
|
+
mainHeaderTemplate = input(null, /* @ts-ignore */
|
|
306
|
+
...(ngDevMode ? [{ debugName: "mainHeaderTemplate" }] : /* istanbul ignore next */ []));
|
|
307
|
+
topbarStartSlot = contentChild(SparkShellTopbarStartDirective, /* @ts-ignore */
|
|
308
|
+
...(ngDevMode ? [{ debugName: "topbarStartSlot" }] : /* istanbul ignore next */ []));
|
|
309
|
+
topbarEndSlot = contentChild(SparkShellTopbarEndDirective, /* @ts-ignore */
|
|
310
|
+
...(ngDevMode ? [{ debugName: "topbarEndSlot" }] : /* istanbul ignore next */ []));
|
|
311
|
+
sidebarHeaderSlot = contentChild(SparkShellSidebarHeaderDirective, /* @ts-ignore */
|
|
312
|
+
...(ngDevMode ? [{ debugName: "sidebarHeaderSlot" }] : /* istanbul ignore next */ []));
|
|
313
|
+
sidebarTopSlot = contentChild(SparkShellSidebarTopDirective, /* @ts-ignore */
|
|
314
|
+
...(ngDevMode ? [{ debugName: "sidebarTopSlot" }] : /* istanbul ignore next */ []));
|
|
315
|
+
sidebarFooterSlot = contentChild(SparkShellSidebarFooterDirective, /* @ts-ignore */
|
|
316
|
+
...(ngDevMode ? [{ debugName: "sidebarFooterSlot" }] : /* istanbul ignore next */ []));
|
|
317
|
+
mainHeaderSlot = contentChild(SparkShellMainHeaderDirective, /* @ts-ignore */
|
|
318
|
+
...(ngDevMode ? [{ debugName: "mainHeaderSlot" }] : /* istanbul ignore next */ []));
|
|
319
|
+
topbarStartTpl = computed(() => this.topbarStartSlot()?.templateRef ?? this.topbarStartTemplate(), /* @ts-ignore */
|
|
320
|
+
...(ngDevMode ? [{ debugName: "topbarStartTpl" }] : /* istanbul ignore next */ []));
|
|
321
|
+
topbarEndTpl = computed(() => this.topbarEndSlot()?.templateRef ?? this.topbarEndTemplate(), /* @ts-ignore */
|
|
322
|
+
...(ngDevMode ? [{ debugName: "topbarEndTpl" }] : /* istanbul ignore next */ []));
|
|
323
|
+
sidebarHeaderTpl = computed(() => this.sidebarHeaderSlot()?.templateRef ?? this.sidebarHeaderTemplate(), /* @ts-ignore */
|
|
324
|
+
...(ngDevMode ? [{ debugName: "sidebarHeaderTpl" }] : /* istanbul ignore next */ []));
|
|
325
|
+
sidebarTopTpl = computed(() => this.sidebarTopSlot()?.templateRef ?? this.sidebarTopTemplate(), /* @ts-ignore */
|
|
326
|
+
...(ngDevMode ? [{ debugName: "sidebarTopTpl" }] : /* istanbul ignore next */ []));
|
|
327
|
+
sidebarFooterTpl = computed(() => this.sidebarFooterSlot()?.templateRef ?? this.sidebarFooterTemplate(), /* @ts-ignore */
|
|
328
|
+
...(ngDevMode ? [{ debugName: "sidebarFooterTpl" }] : /* istanbul ignore next */ []));
|
|
329
|
+
mainHeaderTpl = computed(() => this.mainHeaderSlot()?.templateRef ?? this.mainHeaderTemplate(), /* @ts-ignore */
|
|
330
|
+
...(ngDevMode ? [{ debugName: "mainHeaderTpl" }] : /* istanbul ignore next */ []));
|
|
331
|
+
/**
|
|
332
|
+
* Extra accordion tabs, forwarded to the menu so IT creates the `<bs-accordion-tab>` elements —
|
|
333
|
+
* the only way they share the generated groups' single-open behavior (see
|
|
334
|
+
* `SparkShellTabDirective`). Data-supplied tabs come first, then projected ones in declaration
|
|
335
|
+
* order.
|
|
336
|
+
*/
|
|
337
|
+
tabSlots = contentChildren(SparkShellTabDirective, /* @ts-ignore */
|
|
338
|
+
...(ngDevMode ? [{ debugName: "tabSlots" }] : /* istanbul ignore next */ []));
|
|
339
|
+
tabs = computed(() => [
|
|
340
|
+
...this.sidebarTabs(),
|
|
341
|
+
...this.tabSlots().map(slot => ({
|
|
342
|
+
header: slot.header(),
|
|
343
|
+
icon: slot.icon(),
|
|
344
|
+
headerTemplate: slot.headerTemplate(),
|
|
345
|
+
content: slot.templateRef,
|
|
346
|
+
})),
|
|
347
|
+
], /* @ts-ignore */
|
|
348
|
+
...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
349
|
+
shellState = signal('auto', /* @ts-ignore */
|
|
350
|
+
...(ngDevMode ? [{ debugName: "shellState" }] : /* istanbul ignore next */ []));
|
|
351
|
+
isSidebarVisible = signal(false, /* @ts-ignore */
|
|
352
|
+
...(ngDevMode ? [{ debugName: "isSidebarVisible" }] : /* istanbul ignore next */ []));
|
|
353
|
+
// Explicit toggles force show/hide; 'auto' responsive behavior is otherwise preserved by
|
|
354
|
+
// never writing state on our own. The mirror below keeps the toggler icon truthful when the
|
|
355
|
+
// shell opens/closes itself at the breakpoint.
|
|
356
|
+
toggleSidebar(open) {
|
|
357
|
+
this.shellState.set(open ? 'show' : 'hide');
|
|
358
|
+
}
|
|
359
|
+
onShellToggle(detail) {
|
|
360
|
+
this.isSidebarVisible.set(detail.open);
|
|
361
|
+
}
|
|
362
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
363
|
+
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 }, 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: "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 @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 });
|
|
364
|
+
}
|
|
365
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkShellComponent, decorators: [{
|
|
366
|
+
type: Component,
|
|
367
|
+
args: [{ selector: 'spark-shell', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
368
|
+
NgTemplateOutlet,
|
|
369
|
+
BsShellComponent, BsShellSidebarDirective, BsNavbarTogglerComponent,
|
|
370
|
+
SparkProgramUnitsComponent, SparkLanguageSelectorComponent,
|
|
371
|
+
], 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 @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"] }]
|
|
372
|
+
}], 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 }] }], 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 }] }], 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 }] }] } });
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Generated bundle index. Do not edit.
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
export { SparkLanguageSelectorComponent, SparkProgramUnitsComponent, SparkShellComponent, SparkShellMainHeaderDirective, SparkShellSidebarFooterDirective, SparkShellSidebarHeaderDirective, SparkShellSidebarTopDirective, SparkShellTabDirective, SparkShellTopbarEndDirective, SparkShellTopbarStartDirective };
|
|
379
|
+
//# sourceMappingURL=mintplayer-ng-spark-shell.mjs.map
|
|
@@ -0,0 +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/** 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 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 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 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 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 @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":"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"}
|
|
@@ -5,6 +5,21 @@ const defaultSparkConfig = {
|
|
|
5
5
|
baseUrl: '/spark'
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* A signal that changes whenever the authenticated user changes — the bridge that lets ng-spark
|
|
10
|
+
* components react to sign-in/out without a dependency on `@mintplayer/ng-spark-auth` (no
|
|
11
|
+
* dependency exists between the two packages, in either direction, on purpose).
|
|
12
|
+
*
|
|
13
|
+
* `@mintplayer/ng-spark-auth`'s `provideSparkAuth()` supplies it from `SparkAuthService.user`;
|
|
14
|
+
* an app with its own auth stack provides any signal that changes on sign-in/out. Consumers
|
|
15
|
+
* inject it `{ optional: true }` — absent, auth-sensitive data (the program-units menu) is
|
|
16
|
+
* fetched once and never re-fetched.
|
|
17
|
+
*
|
|
18
|
+
* The signal's VALUE is deliberately opaque (`unknown`): consumers only track it for change,
|
|
19
|
+
* never read it — what "the user" looks like belongs to the auth package.
|
|
20
|
+
*/
|
|
21
|
+
const SPARK_AUTH_STATE = new InjectionToken('SPARK_AUTH_STATE');
|
|
22
|
+
|
|
8
23
|
function provideSpark(config) {
|
|
9
24
|
return [
|
|
10
25
|
{
|
|
@@ -28,10 +43,11 @@ function provideSpark(config) {
|
|
|
28
43
|
// @mintplayer/ng-spark/query-list (SparkQueryListComponent)
|
|
29
44
|
// @mintplayer/ng-spark/retry-action-modal (SparkRetryActionModalComponent)
|
|
30
45
|
// @mintplayer/ng-spark/icon (SparkIconComponent)
|
|
46
|
+
// @mintplayer/ng-spark/shell (SparkShellComponent + slots, SparkProgramUnitsComponent, SparkLanguageSelectorComponent)
|
|
31
47
|
|
|
32
48
|
/**
|
|
33
49
|
* Generated bundle index. Do not edit.
|
|
34
50
|
*/
|
|
35
51
|
|
|
36
|
-
export { SPARK_CONFIG, defaultSparkConfig, provideSpark };
|
|
52
|
+
export { SPARK_AUTH_STATE, SPARK_CONFIG, defaultSparkConfig, provideSpark };
|
|
37
53
|
//# sourceMappingURL=mintplayer-ng-spark.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark.mjs","sources":["../../src/lib/spark-config.ts","../../src/lib/provide-spark.ts","../../src/public-api.ts","../../src/mintplayer-ng-spark.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface SparkConfig {\n baseUrl: string;\n}\n\nexport const SPARK_CONFIG = new InjectionToken<SparkConfig>('SPARK_CONFIG');\n\nexport const defaultSparkConfig: SparkConfig = {\n baseUrl: '/spark'\n};\n","import { Provider } from '@angular/core';\nimport { SPARK_CONFIG, SparkConfig, defaultSparkConfig } from './spark-config';\n\nexport function provideSpark(config?: Partial<SparkConfig>): Provider[] {\n return [\n {\n provide: SPARK_CONFIG,\n useValue: { ...defaultSparkConfig, ...config }\n }\n ];\n}\n","// Root entry point — bootstrap API only.\n// For other members import from sub-paths:\n// @mintplayer/ng-spark/models (PersistentObject, EntityType, SparkQuery, etc. + currentLanguage/resolveTranslation/ShowedOn/ELookupDisplayType)\n// @mintplayer/ng-spark/services (SparkService, SparkStreamingService, SparkLanguageService, RetryActionService, SparkIconRegistry)\n// @mintplayer/ng-spark/pipes (all 22 pipes)\n// @mintplayer/ng-spark/renderers (SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers, renderer interfaces)\n// @mintplayer/ng-spark/routes (sparkRoutes, SparkRouteConfig)\n// @mintplayer/ng-spark/po-form (SparkPoFormComponent)\n// @mintplayer/ng-spark/po-create (SparkPoCreateComponent)\n// @mintplayer/ng-spark/po-edit (SparkPoEditComponent)\n// @mintplayer/ng-spark/po-detail (SparkPoDetailComponent)\n// @mintplayer/ng-spark/query-list (SparkQueryListComponent)\n// @mintplayer/ng-spark/retry-action-modal (SparkRetryActionModalComponent)\n// @mintplayer/ng-spark/icon (SparkIconComponent)\n\nexport type { SparkConfig } from './lib/spark-config';\nexport { SPARK_CONFIG, defaultSparkConfig } from './lib/spark-config';\nexport { provideSpark } from './lib/provide-spark';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;MAMa,YAAY,GAAG,IAAI,cAAc,CAAc,cAAc;AAEnE,MAAM,kBAAkB,GAAgB;AAC7C,IAAA,OAAO,EAAE;;;
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark.mjs","sources":["../../src/lib/spark-config.ts","../../src/lib/spark-auth-state.ts","../../src/lib/provide-spark.ts","../../src/public-api.ts","../../src/mintplayer-ng-spark.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface SparkConfig {\n baseUrl: string;\n}\n\nexport const SPARK_CONFIG = new InjectionToken<SparkConfig>('SPARK_CONFIG');\n\nexport const defaultSparkConfig: SparkConfig = {\n baseUrl: '/spark'\n};\n","import { InjectionToken, Signal } from '@angular/core';\n\n/**\n * A signal that changes whenever the authenticated user changes — the bridge that lets ng-spark\n * components react to sign-in/out without a dependency on `@mintplayer/ng-spark-auth` (no\n * dependency exists between the two packages, in either direction, on purpose).\n *\n * `@mintplayer/ng-spark-auth`'s `provideSparkAuth()` supplies it from `SparkAuthService.user`;\n * an app with its own auth stack provides any signal that changes on sign-in/out. Consumers\n * inject it `{ optional: true }` — absent, auth-sensitive data (the program-units menu) is\n * fetched once and never re-fetched.\n *\n * The signal's VALUE is deliberately opaque (`unknown`): consumers only track it for change,\n * never read it — what \"the user\" looks like belongs to the auth package.\n */\nexport const SPARK_AUTH_STATE = new InjectionToken<Signal<unknown>>('SPARK_AUTH_STATE');\n","import { Provider } from '@angular/core';\nimport { SPARK_CONFIG, SparkConfig, defaultSparkConfig } from './spark-config';\n\nexport function provideSpark(config?: Partial<SparkConfig>): Provider[] {\n return [\n {\n provide: SPARK_CONFIG,\n useValue: { ...defaultSparkConfig, ...config }\n }\n ];\n}\n","// Root entry point — bootstrap API only.\n// For other members import from sub-paths:\n// @mintplayer/ng-spark/models (PersistentObject, EntityType, SparkQuery, etc. + currentLanguage/resolveTranslation/ShowedOn/ELookupDisplayType)\n// @mintplayer/ng-spark/services (SparkService, SparkStreamingService, SparkLanguageService, RetryActionService, SparkIconRegistry)\n// @mintplayer/ng-spark/pipes (all 22 pipes)\n// @mintplayer/ng-spark/renderers (SPARK_ATTRIBUTE_RENDERERS, provideSparkAttributeRenderers, renderer interfaces)\n// @mintplayer/ng-spark/routes (sparkRoutes, SparkRouteConfig)\n// @mintplayer/ng-spark/po-form (SparkPoFormComponent)\n// @mintplayer/ng-spark/po-create (SparkPoCreateComponent)\n// @mintplayer/ng-spark/po-edit (SparkPoEditComponent)\n// @mintplayer/ng-spark/po-detail (SparkPoDetailComponent)\n// @mintplayer/ng-spark/query-list (SparkQueryListComponent)\n// @mintplayer/ng-spark/retry-action-modal (SparkRetryActionModalComponent)\n// @mintplayer/ng-spark/icon (SparkIconComponent)\n// @mintplayer/ng-spark/shell (SparkShellComponent + slots, SparkProgramUnitsComponent, SparkLanguageSelectorComponent)\n\nexport type { SparkConfig } from './lib/spark-config';\nexport { SPARK_CONFIG, defaultSparkConfig } from './lib/spark-config';\nexport { SPARK_AUTH_STATE } from './lib/spark-auth-state';\nexport { provideSpark } from './lib/provide-spark';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;MAMa,YAAY,GAAG,IAAI,cAAc,CAAc,cAAc;AAEnE,MAAM,kBAAkB,GAAgB;AAC7C,IAAA,OAAO,EAAE;;;ACPX;;;;;;;;;;;;AAYG;MACU,gBAAgB,GAAG,IAAI,cAAc,CAAkB,kBAAkB;;ACZhF,SAAU,YAAY,CAAC,MAA6B,EAAA;IACxD,OAAO;AACL,QAAA;AACE,YAAA,OAAO,EAAE,YAAY;AACrB,YAAA,QAAQ,EAAE,EAAE,GAAG,kBAAkB,EAAE,GAAG,MAAM;AAC7C;KACF;AACH;;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintplayer/ng-spark",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "22.
|
|
4
|
+
"version": "22.6.0",
|
|
5
5
|
"description": "Angular component library for MintPlayer.Spark CRUD applications",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
"./services": {
|
|
85
85
|
"types": "./types/mintplayer-ng-spark-services.d.ts",
|
|
86
86
|
"default": "./fesm2022/mintplayer-ng-spark-services.mjs"
|
|
87
|
+
},
|
|
88
|
+
"./shell": {
|
|
89
|
+
"types": "./types/mintplayer-ng-spark-shell.d.ts",
|
|
90
|
+
"default": "./fesm2022/mintplayer-ng-spark-shell.mjs"
|
|
87
91
|
}
|
|
88
92
|
},
|
|
89
93
|
"type": "module",
|