@frame-kit/ui-ng-patterns 0.2.12 → 0.2.13
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,361 @@ 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 > </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 > </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 > </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 > </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
|
+
* The aside's toggle and the topbar's hamburger drive the underlying shell;
|
|
2339
|
+
* `collapsed` reports the rail state so projected controls can follow it.
|
|
2340
|
+
* The toggle glyphs are projection slots with a built-in fallback, so no icon
|
|
2341
|
+
* registry is required.
|
|
2342
|
+
*/
|
|
2343
|
+
class DashboardLayoutComponent {
|
|
2344
|
+
// ===== INPUTS =====
|
|
2345
|
+
/** Passed to `fk-app-shell`. `icon` collapses to a rail; `side` hides entirely. */
|
|
2346
|
+
mode = input('icon', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
2347
|
+
sidenavWidth = input('248px', ...(ngDevMode ? [{ debugName: "sidenavWidth" }] : /* istanbul ignore next */ []));
|
|
2348
|
+
collapsedWidth = input('64px', ...(ngDevMode ? [{ debugName: "collapsedWidth" }] : /* istanbul ignore next */ []));
|
|
2349
|
+
/** Veil the page area with the projected `[layoutPageLoader]` content. */
|
|
2350
|
+
pageLoading = input(false, ...(ngDevMode ? [{ debugName: "pageLoading" }] : /* istanbul ignore next */ []));
|
|
2351
|
+
/** Accessible name for the aside's collapse toggle. */
|
|
2352
|
+
toggleAriaLabel = input('Toggle navigation', ...(ngDevMode ? [{ debugName: "toggleAriaLabel" }] : /* istanbul ignore next */ []));
|
|
2353
|
+
/** Accessible name for the topbar's mobile hamburger. */
|
|
2354
|
+
menuAriaLabel = input('Open navigation', ...(ngDevMode ? [{ debugName: "menuAriaLabel" }] : /* istanbul ignore next */ []));
|
|
2355
|
+
// ===== BASE PROPS =====
|
|
2356
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
2357
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
2358
|
+
// ===== COMPUTED =====
|
|
2359
|
+
shell = viewChild(AppShellComponent, ...(ngDevMode ? [{ debugName: "shell" }] : /* istanbul ignore next */ []));
|
|
2360
|
+
/** Whether the aside is in its icon rail. Projected controls follow this. */
|
|
2361
|
+
collapsed = computed(() => this.shell()?.isCollapsed() ?? false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
2362
|
+
classes = computed(() => [
|
|
2363
|
+
'fk-dashboard-layout',
|
|
2364
|
+
this.collapsed() ? 'fk-dashboard-layout--collapsed' : '',
|
|
2365
|
+
this.pageLoading() ? 'fk-dashboard-layout--page-loading' : '',
|
|
2366
|
+
this.className(),
|
|
2367
|
+
]
|
|
2368
|
+
.filter(Boolean)
|
|
2369
|
+
.join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
2370
|
+
get hostClass() {
|
|
2371
|
+
return this.classes();
|
|
2372
|
+
}
|
|
2373
|
+
get hostId() {
|
|
2374
|
+
return this.id();
|
|
2375
|
+
}
|
|
2376
|
+
/** The aside header's toggle: collapse or expand the rail. */
|
|
2377
|
+
toggleSidenav() {
|
|
2378
|
+
this.shell()?.toggleSidenav();
|
|
2379
|
+
}
|
|
2380
|
+
/** The topbar hamburger: open the off-canvas aside on narrow viewports. */
|
|
2381
|
+
openSidenav() {
|
|
2382
|
+
this.shell()?.openSidenav();
|
|
2383
|
+
}
|
|
2384
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2385
|
+
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 }, 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>\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 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 <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:100vh;height:100dvh;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;width:var(--fk-dashboard-layout-toggle-size, 2rem);height:var(--fk-dashboard-layout-toggle-size, 2rem);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__content{display:flex;flex-direction:column;height:100%;min-height:0}.fk-dashboard-layout__body{flex:1;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__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-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 });
|
|
2386
|
+
}
|
|
2387
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardLayoutComponent, decorators: [{
|
|
2388
|
+
type: Component,
|
|
2389
|
+
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>\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 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 <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:100vh;height:100dvh;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;width:var(--fk-dashboard-layout-toggle-size, 2rem);height:var(--fk-dashboard-layout-toggle-size, 2rem);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__content{display:flex;flex-direction:column;height:100%;min-height:0}.fk-dashboard-layout__body{flex:1;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__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-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"] }]
|
|
2390
|
+
}], 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 }] }], 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: [{
|
|
2391
|
+
type: HostBinding,
|
|
2392
|
+
args: ['class']
|
|
2393
|
+
}], hostId: [{
|
|
2394
|
+
type: HostBinding,
|
|
2395
|
+
args: ['attr.id']
|
|
2396
|
+
}] } });
|
|
2397
|
+
|
|
2044
2398
|
/**
|
|
2045
2399
|
* Generated bundle index. Do not edit.
|
|
2046
2400
|
*/
|
|
2047
2401
|
|
|
2048
|
-
export { BackNavComponent, ColumnsComponent, ContentColumnComponent, ContentColumnHeadlineComponent, ContentDetailHeaderComponent, ContentDetailPageComponent, ContentPageSectionComponent, ContentRowComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, OnboardingGuideComponent, PageHeaderComponent, SearchBarLayoutComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
|
|
2402
|
+
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
2403
|
//# sourceMappingURL=frame-kit-ui-ng-patterns-dashboard.mjs.map
|