@frame-kit/ui-ng-patterns 0.2.12 → 0.2.14

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.
@@ -15,6 +15,8 @@ import { ButtonComponent } from '@frame-kit/ui-ng/ui/button';
15
15
  import { IconBadgeComponent } from '@frame-kit/ui-ng/ui/icon-badge';
16
16
  import * as i1 from '@angular/cdk/drag-drop';
17
17
  import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
18
+ import { LoaderComponent } from '@frame-kit/ui-ng/ui/loader';
19
+ import { DropdownMenuComponent, DropdownTriggerDirective, DropdownPanelDirective } from '@frame-kit/ui-ng/ui/dropdown-menu';
18
20
 
19
21
  /**
20
22
  * Page-level dashboard header: title + optional description on the left, up to
@@ -2041,9 +2043,392 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2041
2043
  args: ['attr.aria-label']
2042
2044
  }] } });
2043
2045
 
2046
+ /**
2047
+ * The line a region shows while it fetches something of its own: a small
2048
+ * spinner beside a label, centered where the content will be.
2049
+ *
2050
+ * Sits in place of the content so a section that is still resolving reads as
2051
+ * working rather than as empty. Mono and faint because it is machinery
2052
+ * reporting itself, not content that has arrived.
2053
+ *
2054
+ * Not for a whole page — a page has its own loading state. This is for a
2055
+ * section whose data comes separately from the rest: a drawer panel, a
2056
+ * capability list, an audit page inside a detail view.
2057
+ *
2058
+ * <fk-inline-loader label="Loading capabilities…" />
2059
+ *
2060
+ * The label doubles as the spinner's accessible name — one string, so the two
2061
+ * cannot drift.
2062
+ */
2063
+ class InlineLoaderComponent {
2064
+ // ===== INPUTS =====
2065
+ /** What is being fetched, already translated. Also the spinner's aria-label. */
2066
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2067
+ // ===== BASE PROPS =====
2068
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2069
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2070
+ // ===== COMPUTED =====
2071
+ classes = computed(() => ['fk-inline-loader', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2072
+ get hostClass() {
2073
+ return this.classes();
2074
+ }
2075
+ get hostId() {
2076
+ return this.id();
2077
+ }
2078
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InlineLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2079
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: InlineLoaderComponent, isStandalone: true, selector: "fk-inline-loader", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<fk-loader size=\"sm\" [ariaLabel]=\"label()\" />\n<span class=\"fk-inline-loader__label\">{{ label() }}</span>\n", styles: [":host{display:flex;align-items:center;justify-content:center;gap:var(--fk-inline-loader-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-inline-loader-padding, var(--fk-rhythm-3, .75rem));font-family:var(--fk-inline-loader-font-family, var(--fk-font-family-mono, monospace));font-size:var(--fk-inline-loader-font-size, .75rem);letter-spacing:var(--fk-inline-loader-letter-spacing, .02em);color:var(--fk-inline-loader-color, var(--fk-color-faint, #b4bfcb))}\n"], dependencies: [{ kind: "component", type: LoaderComponent, selector: "fk-loader", inputs: ["size", "color", "className", "id", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2080
+ }
2081
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InlineLoaderComponent, decorators: [{
2082
+ type: Component,
2083
+ args: [{ selector: 'fk-inline-loader', standalone: true, imports: [LoaderComponent], encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<fk-loader size=\"sm\" [ariaLabel]=\"label()\" />\n<span class=\"fk-inline-loader__label\">{{ label() }}</span>\n", styles: [":host{display:flex;align-items:center;justify-content:center;gap:var(--fk-inline-loader-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-inline-loader-padding, var(--fk-rhythm-3, .75rem));font-family:var(--fk-inline-loader-font-family, var(--fk-font-family-mono, monospace));font-size:var(--fk-inline-loader-font-size, .75rem);letter-spacing:var(--fk-inline-loader-letter-spacing, .02em);color:var(--fk-inline-loader-color, var(--fk-color-faint, #b4bfcb))}\n"] }]
2084
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
2085
+ type: HostBinding,
2086
+ args: ['class']
2087
+ }], hostId: [{
2088
+ type: HostBinding,
2089
+ args: ['attr.id']
2090
+ }] } });
2091
+
2092
+ /**
2093
+ * The sidebar control for "which context am I working in": a trigger showing
2094
+ * the current name over a small category line, and a panel of one or more
2095
+ * titled option columns.
2096
+ *
2097
+ * Several of these stack in a rail (an account, an application, an
2098
+ * environment), and any visible difference between them reads as a mistake
2099
+ * rather than a distinction — so the chrome lives here once, and the meaning
2100
+ * stays with the caller. This holds no data, fetches nothing, and knows
2101
+ * nothing about what the options are; it reports what was clicked.
2102
+ *
2103
+ * <fk-context-switcher
2104
+ * [name]="accountName()"
2105
+ * sublabel="Account"
2106
+ * ariaLabel="Switch account"
2107
+ * [columns]="columns()"
2108
+ * (optionSelect)="onSelect($event)"
2109
+ * >
2110
+ * <fk-icon contextSwitcherIcon name="organization" />
2111
+ * </fk-context-switcher>
2112
+ *
2113
+ * Closing is the caller's call: choosing an account commits and should close,
2114
+ * while choosing an application in a two-column switcher only re-focuses the
2115
+ * second column and must stay open. Call `close()` when you mean to.
2116
+ */
2117
+ class ContextSwitcherComponent {
2118
+ // ===== INPUTS =====
2119
+ /**
2120
+ * The trigger's headline — the context you are in. Null renders a
2121
+ * skeleton rather than an empty row, for a first load with nothing cached.
2122
+ */
2123
+ name = input(null, ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
2124
+ /**
2125
+ * The small mono line beneath the name — the kind of thing the name is
2126
+ * ("Account"), or a second piece of live context. Null renders a skeleton;
2127
+ * empty renders nothing.
2128
+ */
2129
+ sublabel = input('', ...(ngDevMode ? [{ debugName: "sublabel" }] : /* istanbul ignore next */ []));
2130
+ iconTone = input('brand', ...(ngDevMode ? [{ debugName: "iconTone" }] : /* istanbul ignore next */ []));
2131
+ sublabelTone = input('muted', ...(ngDevMode ? [{ debugName: "sublabelTone" }] : /* istanbul ignore next */ []));
2132
+ /** Icon-rail mode — the trigger collapses to its badge. */
2133
+ collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
2134
+ /**
2135
+ * The collapsed trigger shows no text, so it carries the current context in
2136
+ * its label instead. Falls back to `ariaLabel`.
2137
+ */
2138
+ collapsedAriaLabel = input('', ...(ngDevMode ? [{ debugName: "collapsedAriaLabel" }] : /* istanbul ignore next */ []));
2139
+ /** One column, or several. Empty columns render nothing. */
2140
+ columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
2141
+ // ===== OUTPUTS =====
2142
+ optionSelect = output();
2143
+ // ===== BASE PROPS =====
2144
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2145
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2146
+ /** Describes the control itself. */
2147
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
2148
+ // ===== COMPUTED =====
2149
+ dropdown = viewChild.required(DropdownMenuComponent);
2150
+ /** Two columns sit side by side; one fills the panel. */
2151
+ multiColumn = computed(() => this.columns().length > 1, ...(ngDevMode ? [{ debugName: "multiColumn" }] : /* istanbul ignore next */ []));
2152
+ triggerAriaLabel = computed(() => this.collapsed()
2153
+ ? this.collapsedAriaLabel() || this.ariaLabel()
2154
+ : this.ariaLabel(), ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
2155
+ classes = computed(() => [
2156
+ 'fk-context-switcher',
2157
+ this.collapsed() ? 'fk-context-switcher--collapsed' : '',
2158
+ this.className(),
2159
+ ]
2160
+ .filter(Boolean)
2161
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2162
+ get hostClass() {
2163
+ return this.classes();
2164
+ }
2165
+ get hostId() {
2166
+ return this.id();
2167
+ }
2168
+ /** Close the panel. The caller decides when a selection is final. */
2169
+ close() {
2170
+ this.dropdown().close();
2171
+ }
2172
+ onSelect(column, option) {
2173
+ this.optionSelect.emit({ columnKey: column.key, option });
2174
+ }
2175
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ContextSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2176
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ContextSwitcherComponent, isStandalone: true, selector: "fk-context-switcher", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, sublabel: { classPropertyName: "sublabel", publicName: "sublabel", isSignal: true, isRequired: false, transformFunction: null }, iconTone: { classPropertyName: "iconTone", publicName: "iconTone", isSignal: true, isRequired: false, transformFunction: null }, sublabelTone: { classPropertyName: "sublabelTone", publicName: "sublabelTone", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, collapsedAriaLabel: { classPropertyName: "collapsedAriaLabel", publicName: "collapsedAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelect: "optionSelect" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, viewQueries: [{ propertyName: "dropdown", first: true, predicate: DropdownMenuComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<fk-dropdown-menu\n [ariaLabel]=\"ariaLabel() || null\"\n placement=\"bottom-start\"\n [portal]=\"true\"\n>\n <button\n fkDropdownTrigger\n type=\"button\"\n class=\"fk-context-switcher__trigger\"\n [class.fk-context-switcher__trigger--collapsed]=\"collapsed()\"\n [attr.aria-label]=\"triggerAriaLabel() || null\"\n >\n <!--\n One badge for both states, so the projected icon has exactly one outlet.\n The fallback glyph shows only while nothing is projected.\n -->\n <span\n class=\"fk-context-switcher__icon\"\n [class.fk-context-switcher__icon--solid]=\"iconTone() === 'solid'\"\n aria-hidden=\"true\"\n >\n <ng-content select=\"[contextSwitcherIcon]\" />\n </span>\n\n @if (!collapsed()) {\n <span class=\"fk-context-switcher__labels\">\n <span class=\"fk-context-switcher__name\">\n @if (name(); as resolved) {\n {{ resolved }}\n } @else {\n <span class=\"fk-context-switcher__skeleton\" aria-hidden=\"true\"\n >&nbsp;</span\n >\n }\n </span>\n <span\n class=\"fk-context-switcher__sublabel\"\n [class.fk-context-switcher__sublabel--primary]=\"\n sublabelTone() === 'primary'\n \"\n >\n @if (sublabel(); as resolved) {\n {{ resolved }}\n } @else if (sublabel() === null) {\n <span\n class=\"fk-context-switcher__skeleton fk-context-switcher__skeleton--sublabel\"\n aria-hidden=\"true\"\n >&nbsp;</span\n >\n }\n </span>\n </span>\n\n <span class=\"fk-context-switcher__chevron\" aria-hidden=\"true\">\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 18 18\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M4.5 6.75L9 11.25L13.5 6.75\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n }\n </button>\n\n <ng-template fkDropdownPanel>\n <div\n class=\"fk-context-switcher__panel\"\n [class.fk-context-switcher__panel--multi]=\"multiColumn()\"\n >\n @for (column of columns(); track column.key) {\n <div class=\"fk-context-switcher__column\">\n <div class=\"fk-context-switcher__column-title\">\n {{ column.title }}\n </div>\n <div class=\"fk-context-switcher__list\" role=\"listbox\">\n @for (option of column.options; track option.id) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fk-context-switcher__item\"\n [class.fk-context-switcher__item--active]=\"\n option.id === column.activeId\n \"\n [attr.aria-selected]=\"option.id === column.activeId\"\n (click)=\"onSelect(column, option)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n</fk-dropdown-menu>\n", styles: [":host{display:block;width:100%}fk-dropdown-menu{display:block;width:100%}.fk-context-switcher__trigger{display:flex;align-items:center;gap:var(--fk-context-switcher-gap, var(--fk-rhythm-2, .5rem));width:100%;padding:var(--fk-context-switcher-trigger-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));background:transparent;border:1px solid var(--fk-context-switcher-trigger-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-context-switcher-trigger-radius, var(--fk-radius-md, .5rem));cursor:pointer;color:inherit;text-align:left;font:inherit}.fk-context-switcher__trigger--collapsed{justify-content:center;padding:var(--fk-rhythm-2, .5rem)}.fk-context-switcher__icon{flex:none;display:grid;place-items:center;width:var(--fk-context-switcher-icon-size, 1.625rem);height:var(--fk-context-switcher-icon-size, 1.625rem);border-radius:var(--fk-context-switcher-icon-radius, .4375rem);color:var(--fk-context-switcher-icon-color, var(--fk-color-primary, #0a84ff));background:var(--fk-context-switcher-icon-bg, var(--fk-color-primary-light, #e5f1ff))}.fk-context-switcher__icon:not(:has(*)):before{content:\"\";width:.5rem;height:.5rem;border-radius:50%;background:currentColor}.fk-context-switcher__icon--solid{color:var(--fk-context-switcher-icon-solid-color, #ffffff);background:var(--fk-context-switcher-icon-solid-bg, var(--fk-color-text, #1f2d3d))}.fk-context-switcher__labels{flex:1;display:flex;flex-direction:column;min-width:0;line-height:1.2}.fk-context-switcher__name{font-size:var(--fk-context-switcher-name-font-size, .8125rem);font-weight:var(--fk-context-switcher-name-font-weight, var(--fk-font-weight-semibold, 600));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-context-switcher__sublabel{margin-top:1px;font-family:var(--fk-context-switcher-sublabel-font-family, var(--fk-font-family-mono, monospace));font-size:var(--fk-context-switcher-sublabel-font-size, .5625rem);letter-spacing:var(--fk-context-switcher-sublabel-letter-spacing, .04em);text-transform:uppercase;color:var(--fk-context-switcher-sublabel-color, var(--fk-color-muted, #8a98a8));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-context-switcher__sublabel--primary{color:var(--fk-context-switcher-sublabel-primary-color, var(--fk-color-primary, #0a84ff))}.fk-context-switcher__skeleton{display:inline-block;width:9ch;border-radius:var(--fk-radius-sm, .25rem);background:var(--fk-context-switcher-skeleton-bg, var(--fk-color-border, #d9e2ee));animation:fk-context-switcher-pulse 1.2s ease-in-out infinite}.fk-context-switcher__skeleton--sublabel{width:7ch}.fk-context-switcher__chevron{display:inline-flex;color:inherit}.fk-context-switcher__panel{display:grid;min-width:var(--fk-context-switcher-panel-min-width, 15rem)}.fk-context-switcher__panel--multi{grid-template-columns:repeat(2,var(--fk-context-switcher-panel-column-width, 12.5rem));min-width:calc(2 * var(--fk-context-switcher-panel-column-width, 12.5rem))}.fk-context-switcher__column{display:flex;flex-direction:column;min-width:0}.fk-context-switcher__panel--multi .fk-context-switcher__column:first-child{border-right:1px solid var(--fk-context-switcher-column-border-color, var(--fk-color-border, #d9e2ee))}.fk-context-switcher__column-title{padding:var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem);font-size:var(--fk-context-switcher-column-title-font-size, .7rem);text-transform:uppercase;letter-spacing:.05em;color:var(--fk-context-switcher-column-title-color, inherit);border-bottom:1px solid var(--fk-context-switcher-column-border-color, var(--fk-color-border, #d9e2ee))}.fk-context-switcher__list{display:flex;flex-direction:column;gap:var(--fk-rhythm-1, .25rem);max-height:var(--fk-context-switcher-list-max-height, 18.75rem);padding:var(--fk-rhythm-2, .5rem);overflow-y:auto}.fk-context-switcher__item{display:flex;align-items:center;width:100%;padding:var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem);border:0;border-radius:var(--fk-context-switcher-item-radius, var(--fk-radius-md, .5rem));background:transparent;color:inherit;font-family:inherit;font-size:var(--fk-context-switcher-item-font-size, .875rem);text-align:left;cursor:pointer;transition:background-color .12s ease,color .12s ease}.fk-context-switcher__item:hover,.fk-context-switcher__item:focus-visible{outline:none;background:var(--fk-context-switcher-item-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-context-switcher__item--active,.fk-context-switcher__item--active:hover{background:var(--fk-context-switcher-item-active-bg, var(--fk-color-primary-light, #e5f1ff));color:var(--fk-context-switcher-item-active-color, var(--fk-color-primary, #0a84ff));font-weight:var(--fk-font-weight-semibold, 600)}@keyframes fk-context-switcher-pulse{0%,to{opacity:.6}50%{opacity:.25}}@media(prefers-reduced-motion:reduce){.fk-context-switcher__skeleton{animation:none}}\n"], dependencies: [{ kind: "component", type: DropdownMenuComponent, selector: "fk-dropdown-menu", inputs: ["placement", "disabled", "portal", "panelClass", "ariaLabel", "className", "id"], outputs: ["menuOpen", "menuClose"] }, { kind: "directive", type: DropdownTriggerDirective, selector: "[fkDropdownTrigger]" }, { kind: "directive", type: DropdownPanelDirective, selector: "[fkDropdownPanel]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2177
+ }
2178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ContextSwitcherComponent, decorators: [{
2179
+ type: Component,
2180
+ args: [{ selector: 'fk-context-switcher', standalone: true, imports: [
2181
+ DropdownMenuComponent,
2182
+ DropdownTriggerDirective,
2183
+ DropdownPanelDirective,
2184
+ ], encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<fk-dropdown-menu\n [ariaLabel]=\"ariaLabel() || null\"\n placement=\"bottom-start\"\n [portal]=\"true\"\n>\n <button\n fkDropdownTrigger\n type=\"button\"\n class=\"fk-context-switcher__trigger\"\n [class.fk-context-switcher__trigger--collapsed]=\"collapsed()\"\n [attr.aria-label]=\"triggerAriaLabel() || null\"\n >\n <!--\n One badge for both states, so the projected icon has exactly one outlet.\n The fallback glyph shows only while nothing is projected.\n -->\n <span\n class=\"fk-context-switcher__icon\"\n [class.fk-context-switcher__icon--solid]=\"iconTone() === 'solid'\"\n aria-hidden=\"true\"\n >\n <ng-content select=\"[contextSwitcherIcon]\" />\n </span>\n\n @if (!collapsed()) {\n <span class=\"fk-context-switcher__labels\">\n <span class=\"fk-context-switcher__name\">\n @if (name(); as resolved) {\n {{ resolved }}\n } @else {\n <span class=\"fk-context-switcher__skeleton\" aria-hidden=\"true\"\n >&nbsp;</span\n >\n }\n </span>\n <span\n class=\"fk-context-switcher__sublabel\"\n [class.fk-context-switcher__sublabel--primary]=\"\n sublabelTone() === 'primary'\n \"\n >\n @if (sublabel(); as resolved) {\n {{ resolved }}\n } @else if (sublabel() === null) {\n <span\n class=\"fk-context-switcher__skeleton fk-context-switcher__skeleton--sublabel\"\n aria-hidden=\"true\"\n >&nbsp;</span\n >\n }\n </span>\n </span>\n\n <span class=\"fk-context-switcher__chevron\" aria-hidden=\"true\">\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 18 18\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M4.5 6.75L9 11.25L13.5 6.75\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n }\n </button>\n\n <ng-template fkDropdownPanel>\n <div\n class=\"fk-context-switcher__panel\"\n [class.fk-context-switcher__panel--multi]=\"multiColumn()\"\n >\n @for (column of columns(); track column.key) {\n <div class=\"fk-context-switcher__column\">\n <div class=\"fk-context-switcher__column-title\">\n {{ column.title }}\n </div>\n <div class=\"fk-context-switcher__list\" role=\"listbox\">\n @for (option of column.options; track option.id) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fk-context-switcher__item\"\n [class.fk-context-switcher__item--active]=\"\n option.id === column.activeId\n \"\n [attr.aria-selected]=\"option.id === column.activeId\"\n (click)=\"onSelect(column, option)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n</fk-dropdown-menu>\n", styles: [":host{display:block;width:100%}fk-dropdown-menu{display:block;width:100%}.fk-context-switcher__trigger{display:flex;align-items:center;gap:var(--fk-context-switcher-gap, var(--fk-rhythm-2, .5rem));width:100%;padding:var(--fk-context-switcher-trigger-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));background:transparent;border:1px solid var(--fk-context-switcher-trigger-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-context-switcher-trigger-radius, var(--fk-radius-md, .5rem));cursor:pointer;color:inherit;text-align:left;font:inherit}.fk-context-switcher__trigger--collapsed{justify-content:center;padding:var(--fk-rhythm-2, .5rem)}.fk-context-switcher__icon{flex:none;display:grid;place-items:center;width:var(--fk-context-switcher-icon-size, 1.625rem);height:var(--fk-context-switcher-icon-size, 1.625rem);border-radius:var(--fk-context-switcher-icon-radius, .4375rem);color:var(--fk-context-switcher-icon-color, var(--fk-color-primary, #0a84ff));background:var(--fk-context-switcher-icon-bg, var(--fk-color-primary-light, #e5f1ff))}.fk-context-switcher__icon:not(:has(*)):before{content:\"\";width:.5rem;height:.5rem;border-radius:50%;background:currentColor}.fk-context-switcher__icon--solid{color:var(--fk-context-switcher-icon-solid-color, #ffffff);background:var(--fk-context-switcher-icon-solid-bg, var(--fk-color-text, #1f2d3d))}.fk-context-switcher__labels{flex:1;display:flex;flex-direction:column;min-width:0;line-height:1.2}.fk-context-switcher__name{font-size:var(--fk-context-switcher-name-font-size, .8125rem);font-weight:var(--fk-context-switcher-name-font-weight, var(--fk-font-weight-semibold, 600));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-context-switcher__sublabel{margin-top:1px;font-family:var(--fk-context-switcher-sublabel-font-family, var(--fk-font-family-mono, monospace));font-size:var(--fk-context-switcher-sublabel-font-size, .5625rem);letter-spacing:var(--fk-context-switcher-sublabel-letter-spacing, .04em);text-transform:uppercase;color:var(--fk-context-switcher-sublabel-color, var(--fk-color-muted, #8a98a8));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-context-switcher__sublabel--primary{color:var(--fk-context-switcher-sublabel-primary-color, var(--fk-color-primary, #0a84ff))}.fk-context-switcher__skeleton{display:inline-block;width:9ch;border-radius:var(--fk-radius-sm, .25rem);background:var(--fk-context-switcher-skeleton-bg, var(--fk-color-border, #d9e2ee));animation:fk-context-switcher-pulse 1.2s ease-in-out infinite}.fk-context-switcher__skeleton--sublabel{width:7ch}.fk-context-switcher__chevron{display:inline-flex;color:inherit}.fk-context-switcher__panel{display:grid;min-width:var(--fk-context-switcher-panel-min-width, 15rem)}.fk-context-switcher__panel--multi{grid-template-columns:repeat(2,var(--fk-context-switcher-panel-column-width, 12.5rem));min-width:calc(2 * var(--fk-context-switcher-panel-column-width, 12.5rem))}.fk-context-switcher__column{display:flex;flex-direction:column;min-width:0}.fk-context-switcher__panel--multi .fk-context-switcher__column:first-child{border-right:1px solid var(--fk-context-switcher-column-border-color, var(--fk-color-border, #d9e2ee))}.fk-context-switcher__column-title{padding:var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem);font-size:var(--fk-context-switcher-column-title-font-size, .7rem);text-transform:uppercase;letter-spacing:.05em;color:var(--fk-context-switcher-column-title-color, inherit);border-bottom:1px solid var(--fk-context-switcher-column-border-color, var(--fk-color-border, #d9e2ee))}.fk-context-switcher__list{display:flex;flex-direction:column;gap:var(--fk-rhythm-1, .25rem);max-height:var(--fk-context-switcher-list-max-height, 18.75rem);padding:var(--fk-rhythm-2, .5rem);overflow-y:auto}.fk-context-switcher__item{display:flex;align-items:center;width:100%;padding:var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem);border:0;border-radius:var(--fk-context-switcher-item-radius, var(--fk-radius-md, .5rem));background:transparent;color:inherit;font-family:inherit;font-size:var(--fk-context-switcher-item-font-size, .875rem);text-align:left;cursor:pointer;transition:background-color .12s ease,color .12s ease}.fk-context-switcher__item:hover,.fk-context-switcher__item:focus-visible{outline:none;background:var(--fk-context-switcher-item-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-context-switcher__item--active,.fk-context-switcher__item--active:hover{background:var(--fk-context-switcher-item-active-bg, var(--fk-color-primary-light, #e5f1ff));color:var(--fk-context-switcher-item-active-color, var(--fk-color-primary, #0a84ff));font-weight:var(--fk-font-weight-semibold, 600)}@keyframes fk-context-switcher-pulse{0%,to{opacity:.6}50%{opacity:.25}}@media(prefers-reduced-motion:reduce){.fk-context-switcher__skeleton{animation:none}}\n"] }]
2185
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], sublabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sublabel", required: false }] }], iconTone: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTone", required: false }] }], sublabelTone: [{ type: i0.Input, args: [{ isSignal: true, alias: "sublabelTone", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], collapsedAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedAriaLabel", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], optionSelect: [{ type: i0.Output, args: ["optionSelect"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], dropdown: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DropdownMenuComponent), { isSignal: true }] }], hostClass: [{
2186
+ type: HostBinding,
2187
+ args: ['class']
2188
+ }], hostId: [{
2189
+ type: HostBinding,
2190
+ args: ['attr.id']
2191
+ }] } });
2192
+
2193
+ /**
2194
+ * A dashboard's sidebar navigation: the caller's groups, with items the
2195
+ * caller may not open already removed, rendered as one `fk-sidebar-nav` per
2196
+ * group so that projected controls — an account switcher, an environment
2197
+ * switcher — can sit between groups.
2198
+ *
2199
+ * <fk-dashboard-nav
2200
+ * [groups]="groups()"
2201
+ * [collapsed]="collapsed()"
2202
+ * slotBeforeGroupId="workspace"
2203
+ * slotAfterGroupId="access"
2204
+ * >
2205
+ * <div navSlotBefore><fk-context-switcher … /></div>
2206
+ * <div><fk-context-switcher … /></div>
2207
+ * </fk-dashboard-nav>
2208
+ *
2209
+ * Visibility is a plain boolean on each item, decided by the caller from
2210
+ * whatever authority it has; this component only removes what is hidden. A
2211
+ * parent whose children have all been removed goes too — it would expand to
2212
+ * nothing. Disclosure, active state and the collapsed-rail flyout are owned
2213
+ * by `fk-sidebar-nav`.
2214
+ */
2215
+ class DashboardNavComponent {
2216
+ // ===== INPUTS =====
2217
+ groups = input.required(...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
2218
+ /** Icon-rail mode, passed through to every segment. */
2219
+ collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
2220
+ /** Project `[navSlotBefore]` content immediately before this group. */
2221
+ slotBeforeGroupId = input(null, ...(ngDevMode ? [{ debugName: "slotBeforeGroupId" }] : /* istanbul ignore next */ []));
2222
+ /** Project default `<ng-content>` immediately after this group. */
2223
+ slotAfterGroupId = input(null, ...(ngDevMode ? [{ debugName: "slotAfterGroupId" }] : /* istanbul ignore next */ []));
2224
+ // ===== BASE PROPS =====
2225
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2226
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2227
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
2228
+ // ===== COMPUTED =====
2229
+ /**
2230
+ * Visible groups sliced into render segments, each carrying a single-group
2231
+ * `SidebarNavModel`.
2232
+ */
2233
+ segments = computed(() => {
2234
+ const before = this.slotBeforeGroupId();
2235
+ const after = this.slotAfterGroupId();
2236
+ return this.groups()
2237
+ .map((group) => ({
2238
+ group,
2239
+ items: group.items
2240
+ .filter((item) => item.visible !== false)
2241
+ .map((item) => item.children
2242
+ ? {
2243
+ ...item,
2244
+ children: item.children.filter((c) => c.visible !== false),
2245
+ }
2246
+ : item)
2247
+ .filter((item) => !item.children || item.children.length > 0),
2248
+ }))
2249
+ .filter((entry) => entry.items.length > 0)
2250
+ .map((entry, index) => ({
2251
+ id: entry.group.id,
2252
+ before: before === entry.group.id,
2253
+ after: after === entry.group.id,
2254
+ separated: index > 0 && (entry.group.separatorBefore ?? false),
2255
+ model: [
2256
+ {
2257
+ label: entry.group.label,
2258
+ items: entry.items.map(toModelItem),
2259
+ },
2260
+ ],
2261
+ }));
2262
+ }, ...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
2263
+ /**
2264
+ * Groups above the projected `[navSlotBefore]` content, and those below it.
2265
+ * Split rather than conditionally projecting inside the loop: the slot's
2266
+ * position is expressed by what is emitted around it, so the template needs
2267
+ * exactly one outlet — Angular projects to the first matching `ng-content`,
2268
+ * and a second copy (or one inside a false `@if`) swallows the content.
2269
+ */
2270
+ segmentsBeforeSlot = computed(() => {
2271
+ const all = this.segments();
2272
+ const anchor = all.findIndex((segment) => segment.before);
2273
+ return anchor < 0 ? [] : all.slice(0, anchor);
2274
+ }, ...(ngDevMode ? [{ debugName: "segmentsBeforeSlot" }] : /* istanbul ignore next */ []));
2275
+ segmentsAfterSlot = computed(() => {
2276
+ const all = this.segments();
2277
+ const anchor = all.findIndex((segment) => segment.before);
2278
+ return anchor < 0 ? all : all.slice(anchor);
2279
+ }, ...(ngDevMode ? [{ debugName: "segmentsAfterSlot" }] : /* istanbul ignore next */ []));
2280
+ classes = computed(() => [
2281
+ 'fk-dashboard-nav',
2282
+ this.collapsed() ? 'fk-dashboard-nav--collapsed' : '',
2283
+ this.className(),
2284
+ ]
2285
+ .filter(Boolean)
2286
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2287
+ get hostClass() {
2288
+ return this.classes();
2289
+ }
2290
+ get hostId() {
2291
+ return this.id();
2292
+ }
2293
+ get hostAriaLabel() {
2294
+ return this.ariaLabel();
2295
+ }
2296
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2297
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DashboardNavComponent, isStandalone: true, selector: "fk-dashboard-nav", inputs: { groups: { classPropertyName: "groups", publicName: "groups", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, slotBeforeGroupId: { classPropertyName: "slotBeforeGroupId", publicName: "slotBeforeGroupId", isSignal: true, isRequired: false, transformFunction: null }, slotAfterGroupId: { classPropertyName: "slotAfterGroupId", publicName: "slotAfterGroupId", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.aria-label": "this.hostAriaLabel" } }, ngImport: i0, template: "@for (segment of segmentsBeforeSlot(); track segment.id) {\n @if (segment.separated) {\n <div class=\"fk-dashboard-nav__separator\" aria-hidden=\"true\"></div>\n }\n <fk-sidebar-nav [model]=\"segment.model\" [collapsed]=\"collapsed()\" />\n}\n\n<!--\n Exactly one outlet for `[navSlotBefore]`, never inside a conditional. With\n no anchored group, or no groups at all, both loops are empty and the slot\n lands at the top \u2014 so a caller with nothing to navigate to still gets its\n switcher, which is the one way back to somewhere it can use.\n-->\n<ng-content select=\"[navSlotBefore]\" />\n\n@for (segment of segmentsAfterSlot(); track segment.id) {\n @if (segment.separated) {\n <div class=\"fk-dashboard-nav__separator\" aria-hidden=\"true\"></div>\n }\n <fk-sidebar-nav [model]=\"segment.model\" [collapsed]=\"collapsed()\" />\n @if (segment.after) {\n <ng-content />\n }\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--fk-dashboard-nav-gap, var(--fk-rhythm-6, 1.5rem));padding:var(--fk-dashboard-nav-padding, var(--fk-rhythm-4, 1rem))}:host(.fk-dashboard-nav--collapsed){padding-inline:var(--fk-dashboard-nav-padding-inline-collapsed, var(--fk-rhythm-2, .5rem))}.fk-dashboard-nav__separator{border-top:1px solid var(--fk-dashboard-nav-separator-color, var(--fk-color-border, #d9e2ee))}\n"], dependencies: [{ kind: "component", type: SidebarNavComponent, selector: "fk-sidebar-nav", inputs: ["model", "collapsed", "className"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2298
+ }
2299
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardNavComponent, decorators: [{
2300
+ type: Component,
2301
+ args: [{ selector: 'fk-dashboard-nav', standalone: true, imports: [SidebarNavComponent], encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (segment of segmentsBeforeSlot(); track segment.id) {\n @if (segment.separated) {\n <div class=\"fk-dashboard-nav__separator\" aria-hidden=\"true\"></div>\n }\n <fk-sidebar-nav [model]=\"segment.model\" [collapsed]=\"collapsed()\" />\n}\n\n<!--\n Exactly one outlet for `[navSlotBefore]`, never inside a conditional. With\n no anchored group, or no groups at all, both loops are empty and the slot\n lands at the top \u2014 so a caller with nothing to navigate to still gets its\n switcher, which is the one way back to somewhere it can use.\n-->\n<ng-content select=\"[navSlotBefore]\" />\n\n@for (segment of segmentsAfterSlot(); track segment.id) {\n @if (segment.separated) {\n <div class=\"fk-dashboard-nav__separator\" aria-hidden=\"true\"></div>\n }\n <fk-sidebar-nav [model]=\"segment.model\" [collapsed]=\"collapsed()\" />\n @if (segment.after) {\n <ng-content />\n }\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--fk-dashboard-nav-gap, var(--fk-rhythm-6, 1.5rem));padding:var(--fk-dashboard-nav-padding, var(--fk-rhythm-4, 1rem))}:host(.fk-dashboard-nav--collapsed){padding-inline:var(--fk-dashboard-nav-padding-inline-collapsed, var(--fk-rhythm-2, .5rem))}.fk-dashboard-nav__separator{border-top:1px solid var(--fk-dashboard-nav-separator-color, var(--fk-color-border, #d9e2ee))}\n"] }]
2302
+ }], propDecorators: { groups: [{ type: i0.Input, args: [{ isSignal: true, alias: "groups", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], slotBeforeGroupId: [{ type: i0.Input, args: [{ isSignal: true, alias: "slotBeforeGroupId", required: false }] }], slotAfterGroupId: [{ type: i0.Input, args: [{ isSignal: true, alias: "slotAfterGroupId", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
2303
+ type: HostBinding,
2304
+ args: ['class']
2305
+ }], hostId: [{
2306
+ type: HostBinding,
2307
+ args: ['attr.id']
2308
+ }], hostAriaLabel: [{
2309
+ type: HostBinding,
2310
+ args: ['attr.aria-label']
2311
+ }] } });
2312
+ /** Strip the visibility flags; `fk-sidebar-nav` never sees a hidden row. */
2313
+ function toModelItem(item) {
2314
+ const { visible: _visible, children, ...rest } = item;
2315
+ return {
2316
+ ...rest,
2317
+ children: children?.map(({ visible: _v, ...child }) => child),
2318
+ };
2319
+ }
2320
+
2321
+ /**
2322
+ * The chrome of a signed-in dashboard, built on `fk-app-shell`: a full-height
2323
+ * aside with a brand header and collapse toggle, a sticky frosted topbar with
2324
+ * a mobile hamburger, a capped page area with an optional loading veil, and a
2325
+ * footer. Every dashboard that uses it gets the identical structure — the
2326
+ * same hamburger, the same collapse, the same scroll behaviour — and differs
2327
+ * only in what it projects.
2328
+ *
2329
+ * <fk-dashboard-layout [pageLoading]="navigating()">
2330
+ * <fk-nav-brand layoutBrand brandText="Acme" />
2331
+ * <fk-dashboard-nav layoutAside [groups]="groups()" [collapsed]="collapsed()" />
2332
+ * <nav layoutTopbarStart>Acme / Overview</nav>
2333
+ * <fk-user-menu layoutTopbarEnd … />
2334
+ * <router-outlet />
2335
+ * <span layoutFooter>© Acme</span>
2336
+ * </fk-dashboard-layout>
2337
+ *
2338
+ * A dashboard that needs a trailing panel — a documentation reader, a detail
2339
+ * inspector — projects it into `[layoutEnd]` and chooses its `endMode`. One
2340
+ * that needs a panel beside the page, such as a secondary navigation, projects
2341
+ * it into `[layoutContentPanel]`, which sits in the content row ahead of the
2342
+ * scroll container so the page scrolls under it rather than with it.
2343
+ *
2344
+ * The aside's toggle and the topbar's hamburger drive the underlying shell;
2345
+ * `collapsed` reports the rail state so projected controls can follow it.
2346
+ * The toggle glyphs are projection slots with a built-in fallback, so no icon
2347
+ * registry is required.
2348
+ */
2349
+ class DashboardLayoutComponent {
2350
+ // ===== INPUTS =====
2351
+ /** Passed to `fk-app-shell`. `icon` collapses to a rail; `side` hides entirely. */
2352
+ mode = input('icon', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
2353
+ sidenavWidth = input('260px', ...(ngDevMode ? [{ debugName: "sidenavWidth" }] : /* istanbul ignore next */ []));
2354
+ collapsedWidth = input('64px', ...(ngDevMode ? [{ debugName: "collapsedWidth" }] : /* istanbul ignore next */ []));
2355
+ /** Layout mode for the trailing panel, when `[layoutEnd]` is projected. */
2356
+ endMode = input('over', ...(ngDevMode ? [{ debugName: "endMode" }] : /* istanbul ignore next */ []));
2357
+ endWidth = input('400px', ...(ngDevMode ? [{ debugName: "endWidth" }] : /* istanbul ignore next */ []));
2358
+ endCollapsedWidth = input('64px', ...(ngDevMode ? [{ debugName: "endCollapsedWidth" }] : /* istanbul ignore next */ []));
2359
+ /** Veil the page area with the projected `[layoutPageLoader]` content. */
2360
+ pageLoading = input(false, ...(ngDevMode ? [{ debugName: "pageLoading" }] : /* istanbul ignore next */ []));
2361
+ /** Accessible name for the aside's collapse toggle. */
2362
+ toggleAriaLabel = input('Toggle navigation', ...(ngDevMode ? [{ debugName: "toggleAriaLabel" }] : /* istanbul ignore next */ []));
2363
+ /** Accessible name for the topbar's mobile hamburger. */
2364
+ menuAriaLabel = input('Open navigation', ...(ngDevMode ? [{ debugName: "menuAriaLabel" }] : /* istanbul ignore next */ []));
2365
+ // ===== BASE PROPS =====
2366
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2367
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2368
+ // ===== COMPUTED =====
2369
+ shell = viewChild(AppShellComponent, ...(ngDevMode ? [{ debugName: "shell" }] : /* istanbul ignore next */ []));
2370
+ /** Whether the aside is in its icon rail. Projected controls follow this. */
2371
+ collapsed = computed(() => this.shell()?.isCollapsed() ?? false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
2372
+ /** Whether the trailing panel is showing. Projected controls follow this. */
2373
+ endOpen = computed(() => this.shell()?.endOpen() ?? false, ...(ngDevMode ? [{ debugName: "endOpen" }] : /* istanbul ignore next */ []));
2374
+ classes = computed(() => [
2375
+ 'fk-dashboard-layout',
2376
+ this.collapsed() ? 'fk-dashboard-layout--collapsed' : '',
2377
+ this.pageLoading() ? 'fk-dashboard-layout--page-loading' : '',
2378
+ this.className(),
2379
+ ]
2380
+ .filter(Boolean)
2381
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2382
+ get hostClass() {
2383
+ return this.classes();
2384
+ }
2385
+ get hostId() {
2386
+ return this.id();
2387
+ }
2388
+ /** The aside header's toggle: collapse or expand the rail. */
2389
+ toggleSidenav() {
2390
+ this.shell()?.toggleSidenav();
2391
+ }
2392
+ /** The topbar hamburger: open the off-canvas aside on narrow viewports. */
2393
+ openSidenav() {
2394
+ this.shell()?.openSidenav();
2395
+ }
2396
+ /**
2397
+ * Collapse the rail from outside the header's own toggle — what a dashboard
2398
+ * calls when it opens the trailing panel and wants the room.
2399
+ */
2400
+ closeSidenav() {
2401
+ this.shell()?.closeSidenav();
2402
+ }
2403
+ /** Show the projected `[layoutEnd]` panel. */
2404
+ openEnd() {
2405
+ this.shell()?.openEnd();
2406
+ }
2407
+ /** Hide it. */
2408
+ closeEnd() {
2409
+ this.shell()?.closeEnd();
2410
+ }
2411
+ /** Hide it if it is showing, show it if it is not. */
2412
+ toggleEnd() {
2413
+ this.shell()?.toggleEnd();
2414
+ }
2415
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2416
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DashboardLayoutComponent, isStandalone: true, selector: "fk-dashboard-layout", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, sidenavWidth: { classPropertyName: "sidenavWidth", publicName: "sidenavWidth", isSignal: true, isRequired: false, transformFunction: null }, collapsedWidth: { classPropertyName: "collapsedWidth", publicName: "collapsedWidth", isSignal: true, isRequired: false, transformFunction: null }, endMode: { classPropertyName: "endMode", publicName: "endMode", isSignal: true, isRequired: false, transformFunction: null }, endWidth: { classPropertyName: "endWidth", publicName: "endWidth", isSignal: true, isRequired: false, transformFunction: null }, endCollapsedWidth: { classPropertyName: "endCollapsedWidth", publicName: "endCollapsedWidth", isSignal: true, isRequired: false, transformFunction: null }, pageLoading: { classPropertyName: "pageLoading", publicName: "pageLoading", isSignal: true, isRequired: false, transformFunction: null }, toggleAriaLabel: { classPropertyName: "toggleAriaLabel", publicName: "toggleAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, menuAriaLabel: { classPropertyName: "menuAriaLabel", publicName: "menuAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, viewQueries: [{ propertyName: "shell", first: true, predicate: AppShellComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Banners sit above the shell, in flow, inside the fixed-height host: the\n shell absorbs whatever height they leave, so a banner appearing never adds a\n document scrollbar.\n-->\n<ng-content select=\"[layoutBanner]\" />\n\n<fk-app-shell\n [mode]=\"mode()\"\n [sidenavWidth]=\"sidenavWidth()\"\n [collapsedWidth]=\"collapsedWidth()\"\n [endMode]=\"endMode()\"\n [endWidth]=\"endWidth()\"\n [endCollapsedWidth]=\"endCollapsedWidth()\"\n>\n <div appShellSidenav class=\"fk-dashboard-layout__aside\">\n <div class=\"fk-dashboard-layout__aside-header\">\n <div class=\"fk-dashboard-layout__brand\">\n <ng-content select=\"[layoutBrand]\" />\n </div>\n\n <button\n type=\"button\"\n class=\"fk-dashboard-layout__toggle\"\n [attr.aria-label]=\"toggleAriaLabel()\"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleSidenav()\"\n >\n <ng-content select=\"[layoutToggleIcon]\" />\n </button>\n </div>\n\n <div class=\"fk-dashboard-layout__aside-body\">\n <ng-content select=\"[layoutAside]\" />\n </div>\n\n <div class=\"fk-dashboard-layout__aside-footer\">\n <ng-content select=\"[layoutAsideFooter]\" />\n </div>\n </div>\n\n <div appShellContent class=\"fk-dashboard-layout__content\">\n <!--\n A panel beside the page \u2014 a secondary navigation, a filter rail \u2014 sits in\n the content row ahead of the scroll container, so the topbar and the page\n scroll under it rather than it scrolling with them.\n -->\n <ng-content select=\"[layoutContentPanel]\" />\n\n <!--\n The scroll container. The topbar sticks at its top and the page scrolls\n under it, which is what lets the frosted bar show content through.\n -->\n <div class=\"fk-dashboard-layout__body\">\n <header class=\"fk-dashboard-layout__topbar\">\n <div class=\"fk-dashboard-layout__topbar-inner\">\n <button\n type=\"button\"\n class=\"fk-dashboard-layout__menu\"\n [attr.aria-label]=\"menuAriaLabel()\"\n (click)=\"openSidenav()\"\n >\n <ng-content select=\"[layoutMenuIcon]\" />\n </button>\n\n <div class=\"fk-dashboard-layout__topbar-start\">\n <ng-content select=\"[layoutTopbarStart]\" />\n </div>\n\n <div class=\"fk-dashboard-layout__topbar-end\">\n <ng-content select=\"[layoutTopbarEnd]\" />\n </div>\n </div>\n </header>\n\n <div class=\"fk-dashboard-layout__page\">\n <ng-content />\n\n @if (pageLoading()) {\n <div class=\"fk-dashboard-layout__page-loader\" aria-live=\"polite\">\n <ng-content select=\"[layoutPageLoader]\" />\n </div>\n }\n </div>\n </div>\n </div>\n\n <!--\n A plain div, not a second `aside`: `fk-app-shell` already makes its end\n region an aside, and nesting one inside it would declare two landmarks for\n one panel.\n -->\n <div appShellEnd class=\"fk-dashboard-layout__end\">\n <ng-content select=\"[layoutEnd]\" />\n </div>\n\n <footer appShellFooter class=\"fk-dashboard-layout__footer\">\n <ng-content select=\"[layoutFooter]\" />\n </footer>\n</fk-app-shell>\n", styles: [":host{display:flex;flex-direction:column;height:var(--fk-dashboard-layout-height, 100vh);overflow:hidden;--_max-width: var(--fk-dashboard-layout-max-width, 1400px);--_topbar-height: var(--fk-dashboard-layout-topbar-height, 4rem)}fk-app-shell{flex:1;min-height:0}.fk-dashboard-layout__aside{display:flex;flex-direction:column;height:100%;background:var(--fk-dashboard-layout-aside-bg, var(--fk-color-surface, #ffffff));border-right:1px solid var(--fk-dashboard-layout-aside-border-color, var(--fk-color-border, #d9e2ee))}.fk-dashboard-layout__aside-header{display:flex;align-items:center;gap:var(--fk-rhythm-2, .5rem);flex-shrink:0;padding:var(--fk-dashboard-layout-aside-header-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-5, 1.25rem))}.fk-dashboard-layout__brand{flex:1 1 auto;min-width:0;display:flex;align-items:center}.fk-dashboard-layout__aside-body{flex:1;min-height:0;overflow-y:auto}.fk-dashboard-layout__aside-footer{flex-shrink:0}.fk-dashboard-layout__aside-footer:empty{display:none}.fk-dashboard-layout__toggle,.fk-dashboard-layout__menu{display:grid;place-items:center;flex:none;padding:0;border:1px solid transparent;border-radius:var(--fk-radius-sm, .25rem);background:none;color:var(--fk-dashboard-layout-toggle-color, var(--fk-color-muted, #8a98a8));cursor:pointer;transition:border-color .15s,background-color .15s,color .15s}.fk-dashboard-layout__toggle:hover,.fk-dashboard-layout__menu:hover{border-color:var(--fk-dashboard-layout-toggle-hover-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-dashboard-layout-toggle-hover-bg, var(--fk-color-surface, #ffffff));color:var(--fk-dashboard-layout-toggle-hover-color, var(--fk-color-text, #1f2d3d))}.fk-dashboard-layout__toggle:focus-visible,.fk-dashboard-layout__menu:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-dashboard-layout__toggle:not(:has(*)):before,.fk-dashboard-layout__menu:not(:has(*)):before{content:\"\";width:1rem;height:.75rem;background:linear-gradient(currentColor 0 2px,transparent 2px 5px,currentColor 5px 7px,transparent 7px 10px,currentColor 10px 12px)}.fk-dashboard-layout__toggle{width:var(--fk-dashboard-layout-toggle-size, 2rem);height:var(--fk-dashboard-layout-toggle-size, 2rem)}.fk-dashboard-layout__menu{width:var(--fk-dashboard-layout-menu-size, 2.25rem);height:var(--fk-dashboard-layout-menu-size, 2.25rem)}.fk-dashboard-layout__content{display:flex;flex-direction:row;width:100%;height:100%;min-height:0}.fk-dashboard-layout__body{flex:1;min-width:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fk-dashboard-layout__topbar{position:sticky;top:0;z-index:20;height:var(--_topbar-height);background:var(--fk-dashboard-layout-topbar-bg, rgba(251, 251, 253, .82));backdrop-filter:blur(var(--fk-dashboard-layout-topbar-blur, 14px));-webkit-backdrop-filter:blur(var(--fk-dashboard-layout-topbar-blur, 14px));border-bottom:1px solid var(--fk-dashboard-layout-topbar-border-color, var(--fk-color-border, #d9e2ee))}.fk-dashboard-layout__topbar-inner{display:flex;align-items:center;gap:var(--fk-rhythm-4, 1rem);height:100%;max-width:var(--_max-width);margin-inline:auto;padding-inline:var(--fk-dashboard-layout-topbar-padding-inline, var(--fk-rhythm-8, 2rem))}.fk-dashboard-layout__topbar-start{display:flex;align-items:center;min-width:0}.fk-dashboard-layout__topbar-end{display:flex;align-items:center;gap:var(--fk-rhythm-3, .75rem);margin-left:auto}.fk-dashboard-layout__menu{display:none}.fk-dashboard-layout__end{display:flex;flex-direction:column;height:100%;min-height:0}.fk-dashboard-layout__end:empty{display:none}.fk-dashboard-layout__page{container-type:inline-size;position:relative;max-width:var(--_max-width);margin-inline:auto;padding:var(--fk-dashboard-layout-page-padding, var(--fk-rhythm-4, 1rem));min-height:calc(100% - var(--_topbar-height))}.fk-dashboard-layout__page-loader{position:absolute;inset:0;z-index:10;display:flex;align-items:center;justify-content:center;min-height:12rem;background:var(--fk-dashboard-layout-page-loader-bg, color-mix(in srgb, var(--fk-color-bg, #fbfbfd) 72%, transparent));backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}.fk-dashboard-layout__footer{position:relative;z-index:100;display:flex;align-items:center;justify-content:center;flex-shrink:0;height:var(--fk-dashboard-layout-footer-height, 2.5rem);background:var(--fk-dashboard-layout-footer-bg, var(--fk-color-surface, #ffffff));border-top:1px solid var(--fk-dashboard-layout-footer-border-color, var(--fk-color-border, #d9e2ee));font-family:var(--fk-dashboard-layout-footer-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-dashboard-layout-footer-font-size, var(--fk-typography-small-font-size, .75rem));color:var(--fk-dashboard-layout-footer-color, var(--fk-color-muted, #8a98a8))}.fk-dashboard-layout__footer:empty{display:none}:host(.fk-dashboard-layout--collapsed) .fk-dashboard-layout__aside-header{justify-content:center;padding-inline:0}:host(.fk-dashboard-layout--collapsed) .fk-dashboard-layout__brand{display:none}@media(min-width:48em){.fk-dashboard-layout__page{padding:var(--fk-dashboard-layout-page-padding-wide, var(--fk-rhythm-6, 1.5rem) var(--fk-rhythm-8, 2rem))}}@media(max-width:760px){.fk-dashboard-layout__menu{display:grid}.fk-dashboard-layout__topbar-inner{padding-inline:var(--fk-rhythm-4, 1rem)}}@media(prefers-reduced-motion:reduce){.fk-dashboard-layout__toggle,.fk-dashboard-layout__menu{transition:none}}\n"], dependencies: [{ kind: "component", type: AppShellComponent, selector: "fk-app-shell", inputs: ["mode", "sidenavWidth", "collapsedWidth", "breakpoint", "sticky", "collapseOnMobile", "endMode", "endWidth", "endCollapsedWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2417
+ }
2418
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardLayoutComponent, decorators: [{
2419
+ type: Component,
2420
+ args: [{ selector: 'fk-dashboard-layout', standalone: true, imports: [AppShellComponent], encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Banners sit above the shell, in flow, inside the fixed-height host: the\n shell absorbs whatever height they leave, so a banner appearing never adds a\n document scrollbar.\n-->\n<ng-content select=\"[layoutBanner]\" />\n\n<fk-app-shell\n [mode]=\"mode()\"\n [sidenavWidth]=\"sidenavWidth()\"\n [collapsedWidth]=\"collapsedWidth()\"\n [endMode]=\"endMode()\"\n [endWidth]=\"endWidth()\"\n [endCollapsedWidth]=\"endCollapsedWidth()\"\n>\n <div appShellSidenav class=\"fk-dashboard-layout__aside\">\n <div class=\"fk-dashboard-layout__aside-header\">\n <div class=\"fk-dashboard-layout__brand\">\n <ng-content select=\"[layoutBrand]\" />\n </div>\n\n <button\n type=\"button\"\n class=\"fk-dashboard-layout__toggle\"\n [attr.aria-label]=\"toggleAriaLabel()\"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleSidenav()\"\n >\n <ng-content select=\"[layoutToggleIcon]\" />\n </button>\n </div>\n\n <div class=\"fk-dashboard-layout__aside-body\">\n <ng-content select=\"[layoutAside]\" />\n </div>\n\n <div class=\"fk-dashboard-layout__aside-footer\">\n <ng-content select=\"[layoutAsideFooter]\" />\n </div>\n </div>\n\n <div appShellContent class=\"fk-dashboard-layout__content\">\n <!--\n A panel beside the page \u2014 a secondary navigation, a filter rail \u2014 sits in\n the content row ahead of the scroll container, so the topbar and the page\n scroll under it rather than it scrolling with them.\n -->\n <ng-content select=\"[layoutContentPanel]\" />\n\n <!--\n The scroll container. The topbar sticks at its top and the page scrolls\n under it, which is what lets the frosted bar show content through.\n -->\n <div class=\"fk-dashboard-layout__body\">\n <header class=\"fk-dashboard-layout__topbar\">\n <div class=\"fk-dashboard-layout__topbar-inner\">\n <button\n type=\"button\"\n class=\"fk-dashboard-layout__menu\"\n [attr.aria-label]=\"menuAriaLabel()\"\n (click)=\"openSidenav()\"\n >\n <ng-content select=\"[layoutMenuIcon]\" />\n </button>\n\n <div class=\"fk-dashboard-layout__topbar-start\">\n <ng-content select=\"[layoutTopbarStart]\" />\n </div>\n\n <div class=\"fk-dashboard-layout__topbar-end\">\n <ng-content select=\"[layoutTopbarEnd]\" />\n </div>\n </div>\n </header>\n\n <div class=\"fk-dashboard-layout__page\">\n <ng-content />\n\n @if (pageLoading()) {\n <div class=\"fk-dashboard-layout__page-loader\" aria-live=\"polite\">\n <ng-content select=\"[layoutPageLoader]\" />\n </div>\n }\n </div>\n </div>\n </div>\n\n <!--\n A plain div, not a second `aside`: `fk-app-shell` already makes its end\n region an aside, and nesting one inside it would declare two landmarks for\n one panel.\n -->\n <div appShellEnd class=\"fk-dashboard-layout__end\">\n <ng-content select=\"[layoutEnd]\" />\n </div>\n\n <footer appShellFooter class=\"fk-dashboard-layout__footer\">\n <ng-content select=\"[layoutFooter]\" />\n </footer>\n</fk-app-shell>\n", styles: [":host{display:flex;flex-direction:column;height:var(--fk-dashboard-layout-height, 100vh);overflow:hidden;--_max-width: var(--fk-dashboard-layout-max-width, 1400px);--_topbar-height: var(--fk-dashboard-layout-topbar-height, 4rem)}fk-app-shell{flex:1;min-height:0}.fk-dashboard-layout__aside{display:flex;flex-direction:column;height:100%;background:var(--fk-dashboard-layout-aside-bg, var(--fk-color-surface, #ffffff));border-right:1px solid var(--fk-dashboard-layout-aside-border-color, var(--fk-color-border, #d9e2ee))}.fk-dashboard-layout__aside-header{display:flex;align-items:center;gap:var(--fk-rhythm-2, .5rem);flex-shrink:0;padding:var(--fk-dashboard-layout-aside-header-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-5, 1.25rem))}.fk-dashboard-layout__brand{flex:1 1 auto;min-width:0;display:flex;align-items:center}.fk-dashboard-layout__aside-body{flex:1;min-height:0;overflow-y:auto}.fk-dashboard-layout__aside-footer{flex-shrink:0}.fk-dashboard-layout__aside-footer:empty{display:none}.fk-dashboard-layout__toggle,.fk-dashboard-layout__menu{display:grid;place-items:center;flex:none;padding:0;border:1px solid transparent;border-radius:var(--fk-radius-sm, .25rem);background:none;color:var(--fk-dashboard-layout-toggle-color, var(--fk-color-muted, #8a98a8));cursor:pointer;transition:border-color .15s,background-color .15s,color .15s}.fk-dashboard-layout__toggle:hover,.fk-dashboard-layout__menu:hover{border-color:var(--fk-dashboard-layout-toggle-hover-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-dashboard-layout-toggle-hover-bg, var(--fk-color-surface, #ffffff));color:var(--fk-dashboard-layout-toggle-hover-color, var(--fk-color-text, #1f2d3d))}.fk-dashboard-layout__toggle:focus-visible,.fk-dashboard-layout__menu:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-dashboard-layout__toggle:not(:has(*)):before,.fk-dashboard-layout__menu:not(:has(*)):before{content:\"\";width:1rem;height:.75rem;background:linear-gradient(currentColor 0 2px,transparent 2px 5px,currentColor 5px 7px,transparent 7px 10px,currentColor 10px 12px)}.fk-dashboard-layout__toggle{width:var(--fk-dashboard-layout-toggle-size, 2rem);height:var(--fk-dashboard-layout-toggle-size, 2rem)}.fk-dashboard-layout__menu{width:var(--fk-dashboard-layout-menu-size, 2.25rem);height:var(--fk-dashboard-layout-menu-size, 2.25rem)}.fk-dashboard-layout__content{display:flex;flex-direction:row;width:100%;height:100%;min-height:0}.fk-dashboard-layout__body{flex:1;min-width:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fk-dashboard-layout__topbar{position:sticky;top:0;z-index:20;height:var(--_topbar-height);background:var(--fk-dashboard-layout-topbar-bg, rgba(251, 251, 253, .82));backdrop-filter:blur(var(--fk-dashboard-layout-topbar-blur, 14px));-webkit-backdrop-filter:blur(var(--fk-dashboard-layout-topbar-blur, 14px));border-bottom:1px solid var(--fk-dashboard-layout-topbar-border-color, var(--fk-color-border, #d9e2ee))}.fk-dashboard-layout__topbar-inner{display:flex;align-items:center;gap:var(--fk-rhythm-4, 1rem);height:100%;max-width:var(--_max-width);margin-inline:auto;padding-inline:var(--fk-dashboard-layout-topbar-padding-inline, var(--fk-rhythm-8, 2rem))}.fk-dashboard-layout__topbar-start{display:flex;align-items:center;min-width:0}.fk-dashboard-layout__topbar-end{display:flex;align-items:center;gap:var(--fk-rhythm-3, .75rem);margin-left:auto}.fk-dashboard-layout__menu{display:none}.fk-dashboard-layout__end{display:flex;flex-direction:column;height:100%;min-height:0}.fk-dashboard-layout__end:empty{display:none}.fk-dashboard-layout__page{container-type:inline-size;position:relative;max-width:var(--_max-width);margin-inline:auto;padding:var(--fk-dashboard-layout-page-padding, var(--fk-rhythm-4, 1rem));min-height:calc(100% - var(--_topbar-height))}.fk-dashboard-layout__page-loader{position:absolute;inset:0;z-index:10;display:flex;align-items:center;justify-content:center;min-height:12rem;background:var(--fk-dashboard-layout-page-loader-bg, color-mix(in srgb, var(--fk-color-bg, #fbfbfd) 72%, transparent));backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}.fk-dashboard-layout__footer{position:relative;z-index:100;display:flex;align-items:center;justify-content:center;flex-shrink:0;height:var(--fk-dashboard-layout-footer-height, 2.5rem);background:var(--fk-dashboard-layout-footer-bg, var(--fk-color-surface, #ffffff));border-top:1px solid var(--fk-dashboard-layout-footer-border-color, var(--fk-color-border, #d9e2ee));font-family:var(--fk-dashboard-layout-footer-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-dashboard-layout-footer-font-size, var(--fk-typography-small-font-size, .75rem));color:var(--fk-dashboard-layout-footer-color, var(--fk-color-muted, #8a98a8))}.fk-dashboard-layout__footer:empty{display:none}:host(.fk-dashboard-layout--collapsed) .fk-dashboard-layout__aside-header{justify-content:center;padding-inline:0}:host(.fk-dashboard-layout--collapsed) .fk-dashboard-layout__brand{display:none}@media(min-width:48em){.fk-dashboard-layout__page{padding:var(--fk-dashboard-layout-page-padding-wide, var(--fk-rhythm-6, 1.5rem) var(--fk-rhythm-8, 2rem))}}@media(max-width:760px){.fk-dashboard-layout__menu{display:grid}.fk-dashboard-layout__topbar-inner{padding-inline:var(--fk-rhythm-4, 1rem)}}@media(prefers-reduced-motion:reduce){.fk-dashboard-layout__toggle,.fk-dashboard-layout__menu{transition:none}}\n"] }]
2421
+ }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], sidenavWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidenavWidth", required: false }] }], collapsedWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedWidth", required: false }] }], endMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "endMode", required: false }] }], endWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "endWidth", required: false }] }], endCollapsedWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "endCollapsedWidth", required: false }] }], pageLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLoading", required: false }] }], toggleAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleAriaLabel", required: false }] }], menuAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuAriaLabel", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], shell: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AppShellComponent), { isSignal: true }] }], hostClass: [{
2422
+ type: HostBinding,
2423
+ args: ['class']
2424
+ }], hostId: [{
2425
+ type: HostBinding,
2426
+ args: ['attr.id']
2427
+ }] } });
2428
+
2044
2429
  /**
2045
2430
  * Generated bundle index. Do not edit.
2046
2431
  */
2047
2432
 
2048
- export { BackNavComponent, ColumnsComponent, ContentColumnComponent, ContentColumnHeadlineComponent, ContentDetailHeaderComponent, ContentDetailPageComponent, ContentPageSectionComponent, ContentRowComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, OnboardingGuideComponent, PageHeaderComponent, SearchBarLayoutComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
2433
+ export { BackNavComponent, ColumnsComponent, ContentColumnComponent, ContentColumnHeadlineComponent, ContentDetailHeaderComponent, ContentDetailPageComponent, ContentPageSectionComponent, ContentRowComponent, ContentSplitLayoutComponent, ContextSwitcherComponent, DashboardLayoutComponent, DashboardNavComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, InlineLoaderComponent, OnboardingGuideComponent, PageHeaderComponent, SearchBarLayoutComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
2049
2434
  //# sourceMappingURL=frame-kit-ui-ng-patterns-dashboard.mjs.map