fold-ng 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import { DOCUMENT, NgTemplateOutlet, NgClass, DatePipe } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, inject, ElementRef, effect, Directive, Injectable, signal, DestroyRef, model, computed, HostListener, Component, InjectionToken, Service, booleanAttribute, numberAttribute, ViewEncapsulation, output, viewChild, afterNextRender, contentChildren, TemplateRef, isDevMode, contentChild, ViewContainerRef, Injector } from '@angular/core';
3
+ import { input, inject, ElementRef, effect, Directive, Injectable, signal, DestroyRef, model, computed, HostListener, Component, InjectionToken, Service, booleanAttribute, numberAttribute, ViewEncapsulation, output, viewChild, afterNextRender, contentChildren, TemplateRef, isDevMode, contentChild, viewChildren, ViewContainerRef, Injector } from '@angular/core';
4
+ import { RouterLink, RouterLinkActive } from '@angular/router';
4
5
 
5
6
  /**
6
7
  * A matched element is only truly focusable if it's rendered — `display:none`
@@ -488,7 +489,7 @@ class FoldAppShellComponent {
488
489
  headerHeightMobileVar = computed(() => pxVar(this.headerHeightMobile()), /* @ts-ignore */
489
490
  ...(ngDevMode ? [{ debugName: "headerHeightMobileVar" }] : /* istanbul ignore next */ []));
490
491
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldAppShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
491
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldAppShellComponent, isStandalone: true, selector: "fold-app-shell", inputs: { railWidth: { classPropertyName: "railWidth", publicName: "railWidth", isSignal: true, isRequired: false, transformFunction: null }, headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: false, transformFunction: null }, headerHeightMobile: { classPropertyName: "headerHeightMobile", publicName: "headerHeightMobile", isSignal: true, isRequired: false, transformFunction: null }, headerLayout: { classPropertyName: "headerLayout", publicName: "headerLayout", isSignal: true, isRequired: false, transformFunction: null }, footerLayout: { classPropertyName: "footerLayout", publicName: "footerLayout", isSignal: true, isRequired: false, transformFunction: null }, footerBehavior: { classPropertyName: "footerBehavior", publicName: "footerBehavior", isSignal: true, isRequired: false, transformFunction: null }, contentScroll: { classPropertyName: "contentScroll", publicName: "contentScroll", isSignal: true, isRequired: false, transformFunction: null }, skipLinkLabel: { classPropertyName: "skipLinkLabel", publicName: "skipLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, drawerLabel: { classPropertyName: "drawerLabel", publicName: "drawerLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileNav: { classPropertyName: "mobileNav", publicName: "mobileNav", isSignal: true, isRequired: false, transformFunction: null }, mobileNavOpen: { classPropertyName: "mobileNavOpen", publicName: "mobileNavOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mobileNavOpen: "mobileNavOpenChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, properties: { "style.--fold-shell-rail-width": "railWidthVar()", "style.--fold-shell-header-height": "headerHeightVar()", "style.--fold-shell-header-height-mobile": "headerHeightMobileVar()", "class.header-full": "headerLayout() === \"full\"", "class.footer-full": "footerLayout() === \"full\"", "class.footer-scroll": "footerBehavior() === \"scroll\"", "class.mobile-drawer": "mobileNav() === \"drawer\"", "class.mobile-nav-open": "drawerOpen()", "class.content-auto": "contentScroll() === \"auto\"" } }, exportAs: ["foldAppShell"], ngImport: i0, template: "<!-- Skip-link: the first Tab stop \u2014 visually hidden until focused, then jumps\n keyboard users past the rails to <main> (which is focusable, tabindex=-1). -->\n<a class=\"skip-link\" [href]=\"'#' + contentId\" (click)=\"skipToContent($event)\">{{\n skipLinkLabel()\n}}</a>\n\n<!-- The primary rail. While the mobile drawer is open it is a real modal dialog\n (named, aria-modal, focus-trapped) and every sibling region is inert \u2014 the\n same bar the panel host holds; on desktop it's a plain nav rail. -->\n<div\n class=\"rail-primary\"\n [id]=\"drawerId\"\n foldSurface=\"chrome\"\n [foldFocusTrap]=\"drawerOpen()\"\n [attr.role]=\"drawerOpen() ? 'dialog' : null\"\n [attr.aria-modal]=\"drawerOpen() ? 'true' : null\"\n [attr.aria-label]=\"drawerOpen() ? drawerLabel() : null\"\n tabindex=\"-1\"\n>\n <ng-content select=\"[railPrimary]\" />\n</div>\n<div class=\"rail-secondary\" foldSurface=\"chrome\" [inert]=\"drawerOpen()\">\n <ng-content select=\"[railSecondary]\" />\n</div>\n\n<!-- Mobile drawer scrim \u2014 only rendered while the drawer is actually open (the\n shell is narrow AND mobileNavOpen). A darkening veil that dismisses on\n click; Escape and widening close it too. aria-hidden: the focus-trapped\n drawer + Escape are the accessible path, the scrim is a mouse convenience. -->\n@if (drawerOpen()) {\n <div class=\"mobile-scrim\" (click)=\"closeMobileNav()\" aria-hidden=\"true\"></div>\n}\n<header class=\"header\" foldSurface=\"chrome\" [inert]=\"drawerOpen()\">\n <ng-content select=\"[header]\" />\n</header>\n\n<!-- One footer, stamped in one of two positions \u2014 a grid row (pinned, always in\n sight) or inside the content scroll (revealed on the way down). A single\n <ng-content> in a template avoids duplicate-selector projection loss. -->\n<ng-template #footerTpl>\n <footer\n class=\"footer\"\n [class.footer-inflow]=\"footerBehavior() === 'scroll'\"\n foldSurface=\"chrome\"\n [inert]=\"drawerOpen()\"\n >\n <ng-content select=\"[footer]\" />\n </footer>\n</ng-template>\n\n<main\n class=\"content\"\n foldSurface=\"page\"\n [id]=\"contentId\"\n tabindex=\"-1\"\n [inert]=\"drawerOpen()\"\n>\n <ng-content />\n @if (footerBehavior() === \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n }\n</main>\n@if (footerBehavior() !== \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;height:100%;width:100%;position:relative;box-sizing:border-box;overflow:hidden;background:var(--fold-color-bg-page);grid-template-columns:auto auto minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height, 56px) 1fr;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\"}.skip-link{position:absolute;top:0;left:0;z-index:60;margin:var(--fold-space-sm, 8px);padding:8px 14px;border:1px solid var(--fold-color-border);border-radius:var(--fold-radius-md);background:var(--fold-color-surface-card);color:var(--fold-color-text);font-size:var(--fold-text-sm);font-weight:600;text-decoration:none;transform:translateY(-150%);transition:transform .12s ease}.skip-link:focus{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}:host(.header-full){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\"}:host(:not(.footer-scroll):has([footer])){grid-template-rows:var(--fold-shell-header-height, 56px) 1fr auto;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.header-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}.rail-primary{grid-area:rail-primary;min-height:0}.rail-secondary{grid-area:rail-secondary;min-height:0}.header{grid-area:header;min-width:0}.footer{grid-area:footer;min-width:0;min-height:0}.content{grid-area:content;position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.content:focus{outline:none}:host(.footer-scroll) .content,:host(.content-auto) .content{overflow:auto}.footer-inflow{margin-top:auto}.rail-primary:has([data-elevated]),.rail-secondary:has([data-elevated]),.header:has([data-elevated]),.footer:has([data-elevated]),.content:has([data-elevated]){padding:var(--fold-surface-inset, var(--fold-space-md))}@media(max-width:768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@container (max-width: 1024px){.rail-secondary{display:none}}@container (max-width: 768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@keyframes fold-shell-scrim-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){.rail-primary,.mobile-scrim{transition:none;animation:none}}\n"], dependencies: [{ kind: "directive", type: FoldSurfaceDirective, selector: "[foldSurface]", inputs: ["foldSurface"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FocusTrapDirective, selector: "[foldFocusTrap]", inputs: ["foldFocusTrap"] }] });
492
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldAppShellComponent, isStandalone: true, selector: "fold-app-shell", inputs: { railWidth: { classPropertyName: "railWidth", publicName: "railWidth", isSignal: true, isRequired: false, transformFunction: null }, headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: false, transformFunction: null }, headerHeightMobile: { classPropertyName: "headerHeightMobile", publicName: "headerHeightMobile", isSignal: true, isRequired: false, transformFunction: null }, headerLayout: { classPropertyName: "headerLayout", publicName: "headerLayout", isSignal: true, isRequired: false, transformFunction: null }, footerLayout: { classPropertyName: "footerLayout", publicName: "footerLayout", isSignal: true, isRequired: false, transformFunction: null }, footerBehavior: { classPropertyName: "footerBehavior", publicName: "footerBehavior", isSignal: true, isRequired: false, transformFunction: null }, contentScroll: { classPropertyName: "contentScroll", publicName: "contentScroll", isSignal: true, isRequired: false, transformFunction: null }, skipLinkLabel: { classPropertyName: "skipLinkLabel", publicName: "skipLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, drawerLabel: { classPropertyName: "drawerLabel", publicName: "drawerLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileNav: { classPropertyName: "mobileNav", publicName: "mobileNav", isSignal: true, isRequired: false, transformFunction: null }, mobileNavOpen: { classPropertyName: "mobileNavOpen", publicName: "mobileNavOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mobileNavOpen: "mobileNavOpenChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, properties: { "style.--fold-shell-rail-width": "railWidthVar()", "style.--fold-shell-header-height": "headerHeightVar()", "style.--fold-shell-header-height-mobile": "headerHeightMobileVar()", "class.header-full": "headerLayout() === \"full\"", "class.footer-full": "footerLayout() === \"full\"", "class.footer-scroll": "footerBehavior() === \"scroll\"", "class.mobile-drawer": "mobileNav() === \"drawer\"", "class.mobile-nav-open": "drawerOpen()", "class.content-auto": "contentScroll() === \"auto\"" } }, exportAs: ["foldAppShell"], ngImport: i0, template: "<!-- Skip-link: the first Tab stop \u2014 visually hidden until focused, then jumps\n keyboard users past the rails to <main> (which is focusable, tabindex=-1). -->\n<a class=\"skip-link\" [href]=\"'#' + contentId\" (click)=\"skipToContent($event)\">{{\n skipLinkLabel()\n}}</a>\n\n<!-- The primary rail. While the mobile drawer is open it is a real modal dialog\n (named, aria-modal, focus-trapped) and every sibling region is inert \u2014 the\n same bar the panel host holds; on desktop it's a plain nav rail. -->\n<div\n class=\"rail-primary\"\n [id]=\"drawerId\"\n foldSurface=\"chrome\"\n [foldFocusTrap]=\"drawerOpen()\"\n [attr.role]=\"drawerOpen() ? 'dialog' : null\"\n [attr.aria-modal]=\"drawerOpen() ? 'true' : null\"\n [attr.aria-label]=\"drawerOpen() ? drawerLabel() : null\"\n tabindex=\"-1\"\n>\n <ng-content select=\"[railPrimary]\" />\n</div>\n<div\n class=\"rail-secondary\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content select=\"[railSecondary]\" />\n</div>\n\n<!-- Mobile drawer scrim \u2014 only rendered while the drawer is actually open (the\n shell is narrow AND mobileNavOpen). A darkening veil that dismisses on\n click; Escape and widening close it too. aria-hidden: the focus-trapped\n drawer + Escape are the accessible path, the scrim is a mouse convenience. -->\n@if (drawerOpen()) {\n <div class=\"mobile-scrim\" (click)=\"closeMobileNav()\" aria-hidden=\"true\"></div>\n}\n<header\n class=\"header\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content select=\"[header]\" />\n</header>\n\n<!-- One footer, stamped in one of two positions \u2014 a grid row (pinned, always in\n sight) or inside the content scroll (revealed on the way down). A single\n <ng-content> in a template avoids duplicate-selector projection loss. -->\n<ng-template #footerTpl>\n <footer\n class=\"footer\"\n [class.footer-inflow]=\"footerBehavior() === 'scroll'\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n >\n <ng-content select=\"[footer]\" />\n </footer>\n</ng-template>\n\n<main\n class=\"content\"\n foldSurface=\"page\"\n [id]=\"contentId\"\n tabindex=\"-1\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content />\n @if (footerBehavior() === \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n }\n</main>\n@if (footerBehavior() !== \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;height:100%;width:100%;position:relative;box-sizing:border-box;overflow:hidden;background:var(--fold-color-bg-page);grid-template-columns:auto auto minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height, 56px) 1fr;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\"}.skip-link{position:absolute;top:0;left:0;z-index:60;margin:var(--fold-space-sm, 8px);padding:8px 14px;border:1px solid var(--fold-color-border);border-radius:var(--fold-radius-md);background:var(--fold-color-surface-card);color:var(--fold-color-text);font-size:var(--fold-text-sm);font-weight:600;text-decoration:none;transform:translateY(-150%);transition:transform .12s ease}.skip-link:focus{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}:host(.header-full){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\"}:host(:not(.footer-scroll):has([footer])){grid-template-rows:var(--fold-shell-header-height, 56px) 1fr auto;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.header-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}.rail-primary{grid-area:rail-primary;min-height:0}.rail-secondary{grid-area:rail-secondary;min-height:0}.header{grid-area:header;min-width:0}.footer{grid-area:footer;min-width:0;min-height:0}.content{grid-area:content;position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.content:focus{outline:none}:host(.footer-scroll) .content,:host(.content-auto) .content{overflow:auto}.footer-inflow{margin-top:auto}.rail-primary:has([data-elevated]),.rail-secondary:has([data-elevated]),.header:has([data-elevated]),.footer:has([data-elevated]),.content:has([data-elevated]){padding:var(--fold-surface-inset, var(--fold-space-md))}@media(max-width:768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@container (max-width: 1024px){.rail-secondary{display:none}}@container (max-width: 768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@keyframes fold-shell-scrim-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){.rail-primary,.mobile-scrim{transition:none;animation:none}}\n"], dependencies: [{ kind: "directive", type: FoldSurfaceDirective, selector: "[foldSurface]", inputs: ["foldSurface"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FocusTrapDirective, selector: "[foldFocusTrap]", inputs: ["foldFocusTrap"] }] });
492
493
  }
493
494
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldAppShellComponent, decorators: [{
494
495
  type: Component,
@@ -502,7 +503,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
502
503
  "[class.mobile-drawer]": 'mobileNav() === "drawer"',
503
504
  "[class.mobile-nav-open]": "drawerOpen()",
504
505
  "[class.content-auto]": 'contentScroll() === "auto"',
505
- }, template: "<!-- Skip-link: the first Tab stop \u2014 visually hidden until focused, then jumps\n keyboard users past the rails to <main> (which is focusable, tabindex=-1). -->\n<a class=\"skip-link\" [href]=\"'#' + contentId\" (click)=\"skipToContent($event)\">{{\n skipLinkLabel()\n}}</a>\n\n<!-- The primary rail. While the mobile drawer is open it is a real modal dialog\n (named, aria-modal, focus-trapped) and every sibling region is inert \u2014 the\n same bar the panel host holds; on desktop it's a plain nav rail. -->\n<div\n class=\"rail-primary\"\n [id]=\"drawerId\"\n foldSurface=\"chrome\"\n [foldFocusTrap]=\"drawerOpen()\"\n [attr.role]=\"drawerOpen() ? 'dialog' : null\"\n [attr.aria-modal]=\"drawerOpen() ? 'true' : null\"\n [attr.aria-label]=\"drawerOpen() ? drawerLabel() : null\"\n tabindex=\"-1\"\n>\n <ng-content select=\"[railPrimary]\" />\n</div>\n<div class=\"rail-secondary\" foldSurface=\"chrome\" [inert]=\"drawerOpen()\">\n <ng-content select=\"[railSecondary]\" />\n</div>\n\n<!-- Mobile drawer scrim \u2014 only rendered while the drawer is actually open (the\n shell is narrow AND mobileNavOpen). A darkening veil that dismisses on\n click; Escape and widening close it too. aria-hidden: the focus-trapped\n drawer + Escape are the accessible path, the scrim is a mouse convenience. -->\n@if (drawerOpen()) {\n <div class=\"mobile-scrim\" (click)=\"closeMobileNav()\" aria-hidden=\"true\"></div>\n}\n<header class=\"header\" foldSurface=\"chrome\" [inert]=\"drawerOpen()\">\n <ng-content select=\"[header]\" />\n</header>\n\n<!-- One footer, stamped in one of two positions \u2014 a grid row (pinned, always in\n sight) or inside the content scroll (revealed on the way down). A single\n <ng-content> in a template avoids duplicate-selector projection loss. -->\n<ng-template #footerTpl>\n <footer\n class=\"footer\"\n [class.footer-inflow]=\"footerBehavior() === 'scroll'\"\n foldSurface=\"chrome\"\n [inert]=\"drawerOpen()\"\n >\n <ng-content select=\"[footer]\" />\n </footer>\n</ng-template>\n\n<main\n class=\"content\"\n foldSurface=\"page\"\n [id]=\"contentId\"\n tabindex=\"-1\"\n [inert]=\"drawerOpen()\"\n>\n <ng-content />\n @if (footerBehavior() === \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n }\n</main>\n@if (footerBehavior() !== \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;height:100%;width:100%;position:relative;box-sizing:border-box;overflow:hidden;background:var(--fold-color-bg-page);grid-template-columns:auto auto minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height, 56px) 1fr;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\"}.skip-link{position:absolute;top:0;left:0;z-index:60;margin:var(--fold-space-sm, 8px);padding:8px 14px;border:1px solid var(--fold-color-border);border-radius:var(--fold-radius-md);background:var(--fold-color-surface-card);color:var(--fold-color-text);font-size:var(--fold-text-sm);font-weight:600;text-decoration:none;transform:translateY(-150%);transition:transform .12s ease}.skip-link:focus{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}:host(.header-full){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\"}:host(:not(.footer-scroll):has([footer])){grid-template-rows:var(--fold-shell-header-height, 56px) 1fr auto;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.header-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}.rail-primary{grid-area:rail-primary;min-height:0}.rail-secondary{grid-area:rail-secondary;min-height:0}.header{grid-area:header;min-width:0}.footer{grid-area:footer;min-width:0;min-height:0}.content{grid-area:content;position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.content:focus{outline:none}:host(.footer-scroll) .content,:host(.content-auto) .content{overflow:auto}.footer-inflow{margin-top:auto}.rail-primary:has([data-elevated]),.rail-secondary:has([data-elevated]),.header:has([data-elevated]),.footer:has([data-elevated]),.content:has([data-elevated]){padding:var(--fold-surface-inset, var(--fold-space-md))}@media(max-width:768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@container (max-width: 1024px){.rail-secondary{display:none}}@container (max-width: 768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@keyframes fold-shell-scrim-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){.rail-primary,.mobile-scrim{transition:none;animation:none}}\n"] }]
506
+ }, template: "<!-- Skip-link: the first Tab stop \u2014 visually hidden until focused, then jumps\n keyboard users past the rails to <main> (which is focusable, tabindex=-1). -->\n<a class=\"skip-link\" [href]=\"'#' + contentId\" (click)=\"skipToContent($event)\">{{\n skipLinkLabel()\n}}</a>\n\n<!-- The primary rail. While the mobile drawer is open it is a real modal dialog\n (named, aria-modal, focus-trapped) and every sibling region is inert \u2014 the\n same bar the panel host holds; on desktop it's a plain nav rail. -->\n<div\n class=\"rail-primary\"\n [id]=\"drawerId\"\n foldSurface=\"chrome\"\n [foldFocusTrap]=\"drawerOpen()\"\n [attr.role]=\"drawerOpen() ? 'dialog' : null\"\n [attr.aria-modal]=\"drawerOpen() ? 'true' : null\"\n [attr.aria-label]=\"drawerOpen() ? drawerLabel() : null\"\n tabindex=\"-1\"\n>\n <ng-content select=\"[railPrimary]\" />\n</div>\n<div\n class=\"rail-secondary\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content select=\"[railSecondary]\" />\n</div>\n\n<!-- Mobile drawer scrim \u2014 only rendered while the drawer is actually open (the\n shell is narrow AND mobileNavOpen). A darkening veil that dismisses on\n click; Escape and widening close it too. aria-hidden: the focus-trapped\n drawer + Escape are the accessible path, the scrim is a mouse convenience. -->\n@if (drawerOpen()) {\n <div class=\"mobile-scrim\" (click)=\"closeMobileNav()\" aria-hidden=\"true\"></div>\n}\n<header\n class=\"header\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content select=\"[header]\" />\n</header>\n\n<!-- One footer, stamped in one of two positions \u2014 a grid row (pinned, always in\n sight) or inside the content scroll (revealed on the way down). A single\n <ng-content> in a template avoids duplicate-selector projection loss. -->\n<ng-template #footerTpl>\n <footer\n class=\"footer\"\n [class.footer-inflow]=\"footerBehavior() === 'scroll'\"\n foldSurface=\"chrome\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n >\n <ng-content select=\"[footer]\" />\n </footer>\n</ng-template>\n\n<main\n class=\"content\"\n foldSurface=\"page\"\n [id]=\"contentId\"\n tabindex=\"-1\"\n [attr.inert]=\"drawerOpen() ? '' : null\"\n>\n <ng-content />\n @if (footerBehavior() === \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n }\n</main>\n@if (footerBehavior() !== \"scroll\") {\n <ng-container [ngTemplateOutlet]=\"footerTpl\" />\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;height:100%;width:100%;position:relative;box-sizing:border-box;overflow:hidden;background:var(--fold-color-bg-page);grid-template-columns:auto auto minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height, 56px) 1fr;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\"}.skip-link{position:absolute;top:0;left:0;z-index:60;margin:var(--fold-space-sm, 8px);padding:8px 14px;border:1px solid var(--fold-color-border);border-radius:var(--fold-radius-md);background:var(--fold-color-surface-card);color:var(--fold-color-text);font-size:var(--fold-text-sm);font-weight:600;text-decoration:none;transform:translateY(-150%);transition:transform .12s ease}.skip-link:focus{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}:host(.header-full){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\"}:host(:not(.footer-scroll):has([footer])){grid-template-rows:var(--fold-shell-header-height, 56px) 1fr auto;grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.header-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"rail-primary rail-secondary footer\"}:host(.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"rail-primary rail-secondary header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-areas:\"header header header\" \"rail-primary rail-secondary content\" \"footer footer footer\"}.rail-primary{grid-area:rail-primary;min-height:0}.rail-secondary{grid-area:rail-secondary;min-height:0}.header{grid-area:header;min-width:0}.footer{grid-area:footer;min-width:0;min-height:0}.content{grid-area:content;position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.content:focus{outline:none}:host(.footer-scroll) .content,:host(.content-auto) .content{overflow:auto}.footer-inflow{margin-top:auto}.rail-primary:has([data-elevated]),.rail-secondary:has([data-elevated]),.header:has([data-elevated]),.footer:has([data-elevated]),.content:has([data-elevated]){padding:var(--fold-surface-inset, var(--fold-space-md))}@media(max-width:768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@container (max-width: 1024px){.rail-secondary{display:none}}@container (max-width: 768px){:host,:host(.header-full){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr;grid-template-areas:\"header\" \"content\"}:host(:not(.footer-scroll):has([footer])),:host(.header-full:not(.footer-scroll):has([footer])),:host(.footer-full:not(.footer-scroll):has([footer])),:host(.header-full.footer-full:not(.footer-scroll):has([footer])){grid-template-columns:minmax(0,1fr);grid-template-rows:var(--fold-shell-header-height-mobile, 52px) 1fr auto;grid-template-areas:\"header\" \"content\" \"footer\"}.rail-primary,.rail-secondary{display:none}:host(.mobile-drawer) .rail-primary{display:block;position:absolute;top:var(--fold-shell-header-height-mobile, 52px);bottom:0;left:0;z-index:41;width:auto;max-width:85%;overflow-y:auto;visibility:hidden;transform:translate(-100%);transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}:host(.mobile-drawer.mobile-nav-open) .rail-primary{visibility:visible;transform:translate(0)}.mobile-scrim{position:absolute;inset:var(--fold-shell-header-height-mobile, 52px) 0 0 0;z-index:40;border:0;padding:0;background:var(--fold-color-scrim);animation:fold-shell-scrim-in .24s ease}:host(.mobile-nav-open) .content{overflow:hidden}}@keyframes fold-shell-scrim-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){.rail-primary,.mobile-scrim{transition:none;animation:none}}\n"] }]
506
507
  }], ctorParameters: () => [], propDecorators: { railWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "railWidth", required: false }] }], headerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeight", required: false }] }], headerHeightMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeightMobile", required: false }] }], headerLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerLayout", required: false }] }], footerLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerLayout", required: false }] }], footerBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerBehavior", required: false }] }], contentScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentScroll", required: false }] }], skipLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLinkLabel", required: false }] }], drawerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawerLabel", required: false }] }], mobileNav: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileNav", required: false }] }], mobileNavOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileNavOpen", required: false }] }, { type: i0.Output, args: ["mobileNavOpenChange"] }], onEscape: [{
507
508
  type: HostListener,
508
509
  args: ["document:keydown.escape"]
@@ -1389,7 +1390,7 @@ const FOLD_SEMANTIC_COLOR_TOKENS = [
1389
1390
  "bg-rail-primary",
1390
1391
  /** Rail 2 — the workspace menu. */
1391
1392
  "bg-rail-secondary",
1392
- /** Rail 3 — tertiary nav (e.g. a `fold-tab-nav` section sidebar). */
1393
+ /** Rail 3 — tertiary nav (e.g. a `fold-view-nav` section sidebar). */
1393
1394
  "bg-rail-tertiary",
1394
1395
  /* ── Primary / accent ─────────────────────────────────────── */
1395
1396
  /** Primary / accent — brand teal (solid). */
@@ -1472,6 +1473,10 @@ const FOLD_TEXT_TOKENS = ["xs", "sm", "md", "lg", "xl"];
1472
1473
  const FOLD_ICON_SIZE_TOKENS = ["xs", "sm", "md", "lg", "xl"];
1473
1474
  /** Space (gap / padding / margin) scale. */
1474
1475
  const FOLD_SPACE_TOKENS = ["xs", "sm", "md", "lg", "xl"];
1476
+ /** Rail-width scale — the nav-rail hierarchy's shared widths, named to pair with
1477
+ * the `--fold-color-bg-rail-*` colours (primary app menu → secondary workspace /
1478
+ * aside → tertiary in-page nav). */
1479
+ const FOLD_RAIL_TOKENS = ["primary", "secondary", "tertiary"];
1475
1480
  /** Motion scale — `transition` shorthands (duration + easing). */
1476
1481
  const FOLD_MOTION_TOKENS = ["fast", "base", "slow"];
1477
1482
  /** Backdrop-blur radii (`--fold-blur-*`). */
@@ -2421,7 +2426,7 @@ class FoldMenuComponent {
2421
2426
  this.expanded.update((v) => !v);
2422
2427
  }
2423
2428
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2424
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldMenuComponent, isStandalone: true, selector: "fold-menu", inputs: { collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, togglePlacement: { classPropertyName: "togglePlacement", publicName: "togglePlacement", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { properties: { "class.expanded": "expanded()", "attr.data-tint": "tint()", "attr.data-level": "level()" } }, viewQueries: [{ propertyName: "headRef", first: true, predicate: ["head"], descendants: true, isSignal: true }, { propertyName: "footRef", first: true, predicate: ["foot"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"menu-head\"\n [class.head-has-toggle]=\"collapsible() && resolvedPlacement() === 'header'\"\n #head\n>\n <ng-content select=\"[header]\" />\n @if (collapsible() && resolvedPlacement() === \"header\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</div>\n\n<nav class=\"menu-body\">\n <ng-content />\n @if (collapsible() && resolvedPlacement() === \"body\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</nav>\n\n<div\n class=\"menu-foot\"\n [class.foot-has-toggle]=\"collapsible() && resolvedPlacement() === 'footer'\"\n #foot\n>\n @if (collapsible() && resolvedPlacement() === \"footer\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n <ng-content select=\"[footer]\" />\n</div>\n\n<ng-template #toggleTpl>\n <button\n type=\"button\"\n class=\"menu-toggle\"\n [attr.aria-label]=\"expanded() ? 'Collapse menu' : 'Expand menu'\"\n (click)=\"toggle()\"\n >\n <fold-icon\n [name]=\"expanded() ? 'chevron-left' : 'chevron-right'\"\n [size]=\"14\"\n />\n </button>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;align-items:center;width:var(--fold-shell-rail-width, 64px);height:100%;box-sizing:border-box;padding:10px 0;gap:4px;background:var(--fold-color-bg-rail-primary);border-right:1px solid var(--fold-color-border);overflow:visible;-webkit-user-select:none;user-select:none;transition:width var(--fold-motion-base)}:host([data-level=secondary]){background:var(--fold-color-bg-rail-secondary)}:host([data-level=tertiary]){background:var(--fold-color-bg-rail-tertiary)}:host([data-elevated]){border-right:none}:host(.expanded){width:max-content;min-width:190px;max-width:260px;align-items:stretch}.menu-head,.menu-foot{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}.menu-head:empty,.menu-foot:empty{display:none}.menu-head{padding-bottom:8px;border-bottom:1px solid var(--fold-color-border-subtle)}.menu-foot{padding-top:8px;border-top:1px solid var(--fold-color-border-subtle)}:host(.expanded) .menu-head,:host(.expanded) .menu-foot{align-items:stretch;width:auto;padding-left:8px;padding-right:8px}.menu-body{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}:host(.expanded) .menu-body{align-items:stretch;width:auto;padding:0 8px;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.menu-toggle{flex-shrink:0;width:28px;height:28px;display:grid;place-items:center;border:none;border-radius:var(--fold-radius-sm);background:none;color:var(--fold-color-text-muted);cursor:pointer;transition:background var(--fold-motion-fast),color var(--fold-motion-fast)}.menu-toggle:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text-secondary)}:host(.expanded) .menu-toggle{align-self:flex-end;margin-right:8px}:host(.expanded) .menu-head.head-has-toggle,:host(.expanded) .menu-foot.foot-has-toggle{position:relative;padding-right:40px}:host(.expanded) .menu-head.head-has-toggle .menu-toggle,:host(.expanded) .menu-foot.foot-has-toggle .menu-toggle{position:absolute;right:8px;bottom:8px;margin:0}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2429
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldMenuComponent, isStandalone: true, selector: "fold-menu", inputs: { collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, togglePlacement: { classPropertyName: "togglePlacement", publicName: "togglePlacement", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { properties: { "class.expanded": "expanded()", "attr.data-tint": "tint()", "attr.data-level": "level()" } }, viewQueries: [{ propertyName: "headRef", first: true, predicate: ["head"], descendants: true, isSignal: true }, { propertyName: "footRef", first: true, predicate: ["foot"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"menu-head\"\n [class.head-has-toggle]=\"collapsible() && resolvedPlacement() === 'header'\"\n #head\n>\n <ng-content select=\"[header]\" />\n @if (collapsible() && resolvedPlacement() === \"header\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</div>\n\n<nav class=\"menu-body\">\n <ng-content />\n @if (collapsible() && resolvedPlacement() === \"body\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</nav>\n\n<div\n class=\"menu-foot\"\n [class.foot-has-toggle]=\"collapsible() && resolvedPlacement() === 'footer'\"\n #foot\n>\n @if (collapsible() && resolvedPlacement() === \"footer\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n <ng-content select=\"[footer]\" />\n</div>\n\n<ng-template #toggleTpl>\n <button\n type=\"button\"\n class=\"menu-toggle\"\n [attr.aria-label]=\"expanded() ? 'Collapse menu' : 'Expand menu'\"\n (click)=\"toggle()\"\n >\n <fold-icon\n [name]=\"expanded() ? 'chevron-left' : 'chevron-right'\"\n [size]=\"14\"\n />\n </button>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;align-items:center;width:var(--fold-shell-rail-width, var(--fold-rail-primary, 64px));height:100%;box-sizing:border-box;padding:10px 0;gap:4px;background:var(--fold-color-bg-rail-primary);border-right:1px solid var(--fold-color-border);overflow:visible;-webkit-user-select:none;user-select:none;transition:width var(--fold-motion-base)}:host([data-level=secondary]){background:var(--fold-color-bg-rail-secondary)}:host([data-level=tertiary]){background:var(--fold-color-bg-rail-tertiary)}:host([data-elevated]){border-right:none}:host(.expanded){width:max-content;min-width:190px;max-width:260px;align-items:stretch}.menu-head,.menu-foot{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}.menu-head:empty,.menu-foot:empty{display:none}.menu-head{padding-bottom:8px;border-bottom:1px solid var(--fold-color-border-subtle)}.menu-foot{padding-top:8px;border-top:1px solid var(--fold-color-border-subtle)}:host(.expanded) .menu-head,:host(.expanded) .menu-foot{align-items:stretch;width:auto;padding-left:8px;padding-right:8px}.menu-body{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}:host(.expanded) .menu-body{align-items:stretch;width:auto;padding:0 8px;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.menu-toggle{flex-shrink:0;width:28px;height:28px;display:grid;place-items:center;border:none;border-radius:var(--fold-radius-sm);background:none;color:var(--fold-color-text-muted);cursor:pointer;transition:background var(--fold-motion-fast),color var(--fold-motion-fast)}.menu-toggle:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text-secondary)}:host(.expanded) .menu-toggle{align-self:flex-end;margin-right:8px}:host(.expanded) .menu-head.head-has-toggle,:host(.expanded) .menu-foot.foot-has-toggle{position:relative;padding-right:40px}:host(.expanded) .menu-head.head-has-toggle .menu-toggle,:host(.expanded) .menu-foot.foot-has-toggle .menu-toggle{position:absolute;right:8px;bottom:8px;margin:0}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2425
2430
  }
2426
2431
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldMenuComponent, decorators: [{
2427
2432
  type: Component,
@@ -2429,7 +2434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2429
2434
  "[class.expanded]": "expanded()",
2430
2435
  "[attr.data-tint]": "tint()",
2431
2436
  "[attr.data-level]": "level()",
2432
- }, template: "<div\n class=\"menu-head\"\n [class.head-has-toggle]=\"collapsible() && resolvedPlacement() === 'header'\"\n #head\n>\n <ng-content select=\"[header]\" />\n @if (collapsible() && resolvedPlacement() === \"header\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</div>\n\n<nav class=\"menu-body\">\n <ng-content />\n @if (collapsible() && resolvedPlacement() === \"body\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</nav>\n\n<div\n class=\"menu-foot\"\n [class.foot-has-toggle]=\"collapsible() && resolvedPlacement() === 'footer'\"\n #foot\n>\n @if (collapsible() && resolvedPlacement() === \"footer\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n <ng-content select=\"[footer]\" />\n</div>\n\n<ng-template #toggleTpl>\n <button\n type=\"button\"\n class=\"menu-toggle\"\n [attr.aria-label]=\"expanded() ? 'Collapse menu' : 'Expand menu'\"\n (click)=\"toggle()\"\n >\n <fold-icon\n [name]=\"expanded() ? 'chevron-left' : 'chevron-right'\"\n [size]=\"14\"\n />\n </button>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;align-items:center;width:var(--fold-shell-rail-width, 64px);height:100%;box-sizing:border-box;padding:10px 0;gap:4px;background:var(--fold-color-bg-rail-primary);border-right:1px solid var(--fold-color-border);overflow:visible;-webkit-user-select:none;user-select:none;transition:width var(--fold-motion-base)}:host([data-level=secondary]){background:var(--fold-color-bg-rail-secondary)}:host([data-level=tertiary]){background:var(--fold-color-bg-rail-tertiary)}:host([data-elevated]){border-right:none}:host(.expanded){width:max-content;min-width:190px;max-width:260px;align-items:stretch}.menu-head,.menu-foot{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}.menu-head:empty,.menu-foot:empty{display:none}.menu-head{padding-bottom:8px;border-bottom:1px solid var(--fold-color-border-subtle)}.menu-foot{padding-top:8px;border-top:1px solid var(--fold-color-border-subtle)}:host(.expanded) .menu-head,:host(.expanded) .menu-foot{align-items:stretch;width:auto;padding-left:8px;padding-right:8px}.menu-body{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}:host(.expanded) .menu-body{align-items:stretch;width:auto;padding:0 8px;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.menu-toggle{flex-shrink:0;width:28px;height:28px;display:grid;place-items:center;border:none;border-radius:var(--fold-radius-sm);background:none;color:var(--fold-color-text-muted);cursor:pointer;transition:background var(--fold-motion-fast),color var(--fold-motion-fast)}.menu-toggle:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text-secondary)}:host(.expanded) .menu-toggle{align-self:flex-end;margin-right:8px}:host(.expanded) .menu-head.head-has-toggle,:host(.expanded) .menu-foot.foot-has-toggle{position:relative;padding-right:40px}:host(.expanded) .menu-head.head-has-toggle .menu-toggle,:host(.expanded) .menu-foot.foot-has-toggle .menu-toggle{position:absolute;right:8px;bottom:8px;margin:0}\n"] }]
2437
+ }, template: "<div\n class=\"menu-head\"\n [class.head-has-toggle]=\"collapsible() && resolvedPlacement() === 'header'\"\n #head\n>\n <ng-content select=\"[header]\" />\n @if (collapsible() && resolvedPlacement() === \"header\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</div>\n\n<nav class=\"menu-body\">\n <ng-content />\n @if (collapsible() && resolvedPlacement() === \"body\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n</nav>\n\n<div\n class=\"menu-foot\"\n [class.foot-has-toggle]=\"collapsible() && resolvedPlacement() === 'footer'\"\n #foot\n>\n @if (collapsible() && resolvedPlacement() === \"footer\") {\n <ng-container [ngTemplateOutlet]=\"toggleTpl\" />\n }\n <ng-content select=\"[footer]\" />\n</div>\n\n<ng-template #toggleTpl>\n <button\n type=\"button\"\n class=\"menu-toggle\"\n [attr.aria-label]=\"expanded() ? 'Collapse menu' : 'Expand menu'\"\n (click)=\"toggle()\"\n >\n <fold-icon\n [name]=\"expanded() ? 'chevron-left' : 'chevron-right'\"\n [size]=\"14\"\n />\n </button>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;align-items:center;width:var(--fold-shell-rail-width, var(--fold-rail-primary, 64px));height:100%;box-sizing:border-box;padding:10px 0;gap:4px;background:var(--fold-color-bg-rail-primary);border-right:1px solid var(--fold-color-border);overflow:visible;-webkit-user-select:none;user-select:none;transition:width var(--fold-motion-base)}:host([data-level=secondary]){background:var(--fold-color-bg-rail-secondary)}:host([data-level=tertiary]){background:var(--fold-color-bg-rail-tertiary)}:host([data-elevated]){border-right:none}:host(.expanded){width:max-content;min-width:190px;max-width:260px;align-items:stretch}.menu-head,.menu-foot{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}.menu-head:empty,.menu-foot:empty{display:none}.menu-head{padding-bottom:8px;border-bottom:1px solid var(--fold-color-border-subtle)}.menu-foot{padding-top:8px;border-top:1px solid var(--fold-color-border-subtle)}:host(.expanded) .menu-head,:host(.expanded) .menu-foot{align-items:stretch;width:auto;padding-left:8px;padding-right:8px}.menu-body{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;width:100%}:host(.expanded) .menu-body{align-items:stretch;width:auto;padding:0 8px;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.menu-toggle{flex-shrink:0;width:28px;height:28px;display:grid;place-items:center;border:none;border-radius:var(--fold-radius-sm);background:none;color:var(--fold-color-text-muted);cursor:pointer;transition:background var(--fold-motion-fast),color var(--fold-motion-fast)}.menu-toggle:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text-secondary)}:host(.expanded) .menu-toggle{align-self:flex-end;margin-right:8px}:host(.expanded) .menu-head.head-has-toggle,:host(.expanded) .menu-foot.foot-has-toggle{position:relative;padding-right:40px}:host(.expanded) .menu-head.head-has-toggle .menu-toggle,:host(.expanded) .menu-foot.foot-has-toggle .menu-toggle{position:absolute;right:8px;bottom:8px;margin:0}\n"] }]
2433
2438
  }], ctorParameters: () => [], propDecorators: { collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], tint: [{ type: i0.Input, args: [{ isSignal: true, alias: "tint", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], togglePlacement: [{ type: i0.Input, args: [{ isSignal: true, alias: "togglePlacement", required: false }] }], headRef: [{ type: i0.ViewChild, args: ["head", { isSignal: true }] }], footRef: [{ type: i0.ViewChild, args: ["foot", { isSignal: true }] }] } });
2434
2439
 
2435
2440
  /** Solid accent colour per semantic tone (drives the dot + follow-pill). */
@@ -4698,23 +4703,26 @@ class FoldAsideLayoutComponent {
4698
4703
  }, /* @ts-ignore */
4699
4704
  ...(ngDevMode ? [{ debugName: "topOffsetCss" }] : /* istanbul ignore next */ []));
4700
4705
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldAsideLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4701
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldAsideLayoutComponent, isStandalone: true, selector: "fold-aside-layout", inputs: { stackLeftFirst: { classPropertyName: "stackLeftFirst", publicName: "stackLeftFirst", isSignal: true, isRequired: false, transformFunction: null }, asideLeftLabel: { classPropertyName: "asideLeftLabel", publicName: "asideLeftLabel", isSignal: true, isRequired: false, transformFunction: null }, asideRightLabel: { classPropertyName: "asideRightLabel", publicName: "asideRightLabel", isSignal: true, isRequired: false, transformFunction: null }, topOffset: { classPropertyName: "topOffset", publicName: "topOffset", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--fold-aside-layout-top": "topOffsetCss()", "class.stack-left-first": "stackLeftFirst()" } }, ngImport: i0, template: "<div class=\"al-grid\">\n <div\n class=\"al-aside al-aside-left\"\n [attr.role]=\"asideLeftLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideLeftLabel() || null\"\n >\n <ng-content select=\"[asideLeft]\" />\n </div>\n <div class=\"al-center\"><ng-content /></div>\n <div\n class=\"al-aside al-aside-right\"\n [attr.role]=\"asideRightLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideRightLabel() || null\"\n >\n <ng-content select=\"[asideRight]\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;container-type:inline-size}.al-grid{display:grid;grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr));gap:var(--fold-aside-layout-gap, 28px);max-width:var(--fold-aside-layout-max, 1240px);margin-inline:auto;padding:var(--fold-aside-layout-pad, 28px 28px 64px)}:host:has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:has([asideleft]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, 220px) var(--fold-aside-layout-center-width, minmax(0, 1fr))}:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, 220px) var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:not(:has([asideleft])) .al-aside-left{display:none}:host:not(:has([asideright])) .al-aside-right{display:none}.al-center{min-width:0;display:flex;flex-direction:column;gap:var(--fold-aside-layout-stack, 18px)}.al-aside{--al-rail-top: var(--fold-aside-layout-top, 24px);align-self:start;position:sticky;top:var(--al-rail-top);display:flex;flex-direction:column;gap:var(--fold-aside-layout-rail-gap, 14px);min-width:0;max-height:var(--fold-aside-layout-rail-max, calc(100dvh - var(--al-rail-top) - 2rem));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.al-aside-left{--al-rail-top: var( --fold-aside-layout-left-top, var(--fold-aside-layout-top, 24px) )}.al-aside-right{--al-rail-top: var( --fold-aside-layout-right-top, var(--fold-aside-layout-top, 24px) )}@container (max-width: 1040px){.al-grid,:host:has([asideright]) .al-grid,:host:has([asideleft]) .al-grid,:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:minmax(0,1fr)}.al-aside{position:static;max-height:none;overflow:visible}.al-center{order:1}.al-aside-left{order:2}.al-aside-right{order:3}:host(.stack-left-first) .al-aside-left{order:0}}@container (max-width: 700px){.al-grid{padding:var(--fold-aside-layout-pad-sm, 16px 14px 48px);gap:var(--fold-aside-layout-gap-sm, 16px)}}\n"] });
4706
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldAsideLayoutComponent, isStandalone: true, selector: "fold-aside-layout", inputs: { stackLeftFirst: { classPropertyName: "stackLeftFirst", publicName: "stackLeftFirst", isSignal: true, isRequired: false, transformFunction: null }, asideLeftLabel: { classPropertyName: "asideLeftLabel", publicName: "asideLeftLabel", isSignal: true, isRequired: false, transformFunction: null }, asideRightLabel: { classPropertyName: "asideRightLabel", publicName: "asideRightLabel", isSignal: true, isRequired: false, transformFunction: null }, topOffset: { classPropertyName: "topOffset", publicName: "topOffset", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--fold-aside-layout-top": "topOffsetCss()", "class.stack-left-first": "stackLeftFirst()" } }, ngImport: i0, template: "<div class=\"al-grid\">\n <div\n class=\"al-aside al-aside-left\"\n [attr.role]=\"asideLeftLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideLeftLabel() || null\"\n >\n <ng-content select=\"[asideLeft]\" />\n </div>\n <div class=\"al-center\"><ng-content /></div>\n <div\n class=\"al-aside al-aside-right\"\n [attr.role]=\"asideRightLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideRightLabel() || null\"\n >\n <ng-content select=\"[asideRight]\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;container-type:inline-size}.al-grid{display:grid;grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr));gap:var(--fold-aside-layout-gap, 28px);max-width:var(--fold-aside-layout-max, 1240px);margin-inline:auto;padding:var(--fold-aside-layout-pad, 28px 28px 64px)}:host:has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:has([asideleft]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, var(--fold-rail-secondary, 220px)) var(--fold-aside-layout-center-width, minmax(0, 1fr))}:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, var(--fold-rail-secondary, 220px)) var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:not(:has([asideleft])) .al-aside-left{display:none}:host:not(:has([asideright])) .al-aside-right{display:none}.al-center{min-width:0;display:flex;flex-direction:column;gap:var(--fold-aside-layout-stack, 18px)}.al-aside{--al-rail-top: var(--fold-aside-layout-top, 24px);align-self:start;position:sticky;top:var(--al-rail-top);display:flex;flex-direction:column;gap:var(--fold-aside-layout-rail-gap, 14px);min-width:0;max-height:var(--fold-aside-layout-rail-max, calc(100dvh - var(--al-rail-top) - 2rem));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.al-aside-left{--al-rail-top: var( --fold-aside-layout-left-top, var(--fold-aside-layout-top, 24px) )}.al-aside-right{--al-rail-top: var( --fold-aside-layout-right-top, var(--fold-aside-layout-top, 24px) )}@container (max-width: 1040px){.al-grid,:host:has([asideright]) .al-grid,:host:has([asideleft]) .al-grid,:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:minmax(0,1fr)}.al-aside{position:static;max-height:none;overflow:visible}.al-center{order:1}.al-aside-left{order:2}.al-aside-right{order:3}:host(.stack-left-first) .al-aside-left{order:0}}@container (max-width: 700px){.al-grid{padding:var(--fold-aside-layout-pad-sm, 16px 14px 48px);gap:var(--fold-aside-layout-gap-sm, 16px)}}\n"] });
4702
4707
  }
4703
4708
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldAsideLayoutComponent, decorators: [{
4704
4709
  type: Component,
4705
4710
  args: [{ selector: "fold-aside-layout", standalone: true, host: {
4706
4711
  "[style.--fold-aside-layout-top]": "topOffsetCss()",
4707
4712
  "[class.stack-left-first]": "stackLeftFirst()",
4708
- }, template: "<div class=\"al-grid\">\n <div\n class=\"al-aside al-aside-left\"\n [attr.role]=\"asideLeftLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideLeftLabel() || null\"\n >\n <ng-content select=\"[asideLeft]\" />\n </div>\n <div class=\"al-center\"><ng-content /></div>\n <div\n class=\"al-aside al-aside-right\"\n [attr.role]=\"asideRightLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideRightLabel() || null\"\n >\n <ng-content select=\"[asideRight]\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;container-type:inline-size}.al-grid{display:grid;grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr));gap:var(--fold-aside-layout-gap, 28px);max-width:var(--fold-aside-layout-max, 1240px);margin-inline:auto;padding:var(--fold-aside-layout-pad, 28px 28px 64px)}:host:has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:has([asideleft]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, 220px) var(--fold-aside-layout-center-width, minmax(0, 1fr))}:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, 220px) var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:not(:has([asideleft])) .al-aside-left{display:none}:host:not(:has([asideright])) .al-aside-right{display:none}.al-center{min-width:0;display:flex;flex-direction:column;gap:var(--fold-aside-layout-stack, 18px)}.al-aside{--al-rail-top: var(--fold-aside-layout-top, 24px);align-self:start;position:sticky;top:var(--al-rail-top);display:flex;flex-direction:column;gap:var(--fold-aside-layout-rail-gap, 14px);min-width:0;max-height:var(--fold-aside-layout-rail-max, calc(100dvh - var(--al-rail-top) - 2rem));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.al-aside-left{--al-rail-top: var( --fold-aside-layout-left-top, var(--fold-aside-layout-top, 24px) )}.al-aside-right{--al-rail-top: var( --fold-aside-layout-right-top, var(--fold-aside-layout-top, 24px) )}@container (max-width: 1040px){.al-grid,:host:has([asideright]) .al-grid,:host:has([asideleft]) .al-grid,:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:minmax(0,1fr)}.al-aside{position:static;max-height:none;overflow:visible}.al-center{order:1}.al-aside-left{order:2}.al-aside-right{order:3}:host(.stack-left-first) .al-aside-left{order:0}}@container (max-width: 700px){.al-grid{padding:var(--fold-aside-layout-pad-sm, 16px 14px 48px);gap:var(--fold-aside-layout-gap-sm, 16px)}}\n"] }]
4713
+ }, template: "<div class=\"al-grid\">\n <div\n class=\"al-aside al-aside-left\"\n [attr.role]=\"asideLeftLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideLeftLabel() || null\"\n >\n <ng-content select=\"[asideLeft]\" />\n </div>\n <div class=\"al-center\"><ng-content /></div>\n <div\n class=\"al-aside al-aside-right\"\n [attr.role]=\"asideRightLabel() ? 'complementary' : null\"\n [attr.aria-label]=\"asideRightLabel() || null\"\n >\n <ng-content select=\"[asideRight]\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;container-type:inline-size}.al-grid{display:grid;grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr));gap:var(--fold-aside-layout-gap, 28px);max-width:var(--fold-aside-layout-max, 1240px);margin-inline:auto;padding:var(--fold-aside-layout-pad, 28px 28px 64px)}:host:has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:has([asideleft]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, var(--fold-rail-secondary, 220px)) var(--fold-aside-layout-center-width, minmax(0, 1fr))}:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:var(--fold-aside-layout-rail-width, var(--fold-rail-secondary, 220px)) var(--fold-aside-layout-center-width, minmax(0, 1fr)) var(--fold-aside-layout-side-width, 300px)}:host:not(:has([asideleft])) .al-aside-left{display:none}:host:not(:has([asideright])) .al-aside-right{display:none}.al-center{min-width:0;display:flex;flex-direction:column;gap:var(--fold-aside-layout-stack, 18px)}.al-aside{--al-rail-top: var(--fold-aside-layout-top, 24px);align-self:start;position:sticky;top:var(--al-rail-top);display:flex;flex-direction:column;gap:var(--fold-aside-layout-rail-gap, 14px);min-width:0;max-height:var(--fold-aside-layout-rail-max, calc(100dvh - var(--al-rail-top) - 2rem));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.al-aside-left{--al-rail-top: var( --fold-aside-layout-left-top, var(--fold-aside-layout-top, 24px) )}.al-aside-right{--al-rail-top: var( --fold-aside-layout-right-top, var(--fold-aside-layout-top, 24px) )}@container (max-width: 1040px){.al-grid,:host:has([asideright]) .al-grid,:host:has([asideleft]) .al-grid,:host:has([asideleft]):has([asideright]) .al-grid{grid-template-columns:minmax(0,1fr)}.al-aside{position:static;max-height:none;overflow:visible}.al-center{order:1}.al-aside-left{order:2}.al-aside-right{order:3}:host(.stack-left-first) .al-aside-left{order:0}}@container (max-width: 700px){.al-grid{padding:var(--fold-aside-layout-pad-sm, 16px 14px 48px);gap:var(--fold-aside-layout-gap-sm, 16px)}}\n"] }]
4709
4714
  }], propDecorators: { stackLeftFirst: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackLeftFirst", required: false }] }], asideLeftLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "asideLeftLabel", required: false }] }], asideRightLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "asideRightLabel", required: false }] }], topOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "topOffset", required: false }] }] } });
4710
4715
 
4716
+ /** DI handle for the nearest {@link FoldNavLayoutComponent}, if any. */
4717
+ const FOLD_NAV_LAYOUT = new InjectionToken("FOLD_NAV_LAYOUT");
4718
+
4711
4719
  /**
4712
4720
  * Dead band (px) between folding and unfolding. Wider than any scrollbar, so the
4713
4721
  * width a fold gives back can never flip the layout straight back.
4714
4722
  */
4715
4723
  const HYSTERESIS = 32;
4716
4724
  /**
4717
- * `<fold-tab-layout>` — pairs a tab bar with the content it drives, and owns the
4725
+ * `<fold-nav-layout>` — pairs a tab bar with the content it drives, and owns the
4718
4726
  * one thing every tabbed page hand-rolls: where the nav sits.
4719
4727
  *
4720
4728
  * - `placement="top"` (default) — the nav above the content.
@@ -4722,39 +4730,62 @@ const HYSTERESIS = 32;
4722
4730
  * top when the layout gets narrower than {@link foldAt} (so the menu always
4723
4731
  * precedes the content it drives, never below it).
4724
4732
  *
4725
- * Content projection — the nav stays yours (tabs, active key, events):
4726
- * - `[tabNav]` → the tab bar (an `fold-tab-nav`, or anything else).
4727
- * - default slot the content for the active tab.
4733
+ * Content projection — the bar stays yours (tabs, active key, events):
4734
+ * - `[tabNav]` → the bar. Project a {@link FoldViewNavComponent} if the tabs
4735
+ * **navigate** (route between views) or a {@link FoldTabsComponent} if they
4736
+ * **switch panels in place** — this layout is agnostic, it only places the bar.
4737
+ * - default slot → the content (routed views, or the tabs' `fold-tab-panel`s).
4728
4738
  *
4729
- * **A11y this owns placement, not the ARIA tabs pattern.** The projected bar
4730
- * carries the `tablist`/`tab` roles; the content is yours. If the tabs switch
4731
- * in-page panels, give that content `role="tabpanel"` named by the active tab;
4732
- * if they navigate between routes, keep plain nav semantics and add no tab
4733
- * roles (the tabs pattern is for panel-switching, not navigation).
4739
+ * **A11y lives in the bar, not here.** `fold-view-nav` brings `<nav>` +
4740
+ * `aria-current`; `fold-tabs` brings the full `role="tablist"` widget with its
4741
+ * `fold-tab-panel`s. This component adds no tab semantics it only decides where
4742
+ * the bar sits and folds it responsively.
4734
4743
  *
4735
- * A side rail needs a *vertical* bar, a folded one a *horizontal* bar. Rather
4736
- * than guess, the layout exposes {@link stacked} (`exportAs`) so the projected
4737
- * nav follows the layout in one binding:
4744
+ * A side rail needs a *vertical* bar, a folded one a *horizontal* bar. The
4745
+ * projected bar handles that itself with `direction="auto"` (its default) it
4746
+ * reads this layout through DI ({@link FoldNavLayoutContext}), no wiring:
4738
4747
  *
4739
4748
  * ```html
4740
- * <fold-tab-layout placement="side" #tl="foldTabLayout">
4741
- * <fold-tab-nav
4742
- * tabNav
4743
- * [direction]="tl.stacked() ? 'horizontal' : 'vertical'"
4744
- * [tabs]="tabs"
4745
- * [activeKey]="tab()"
4746
- * (tabChange)="tab.set($event)"
4747
- * />
4748
- * <app-tab-content />
4749
- * </fold-tab-layout>
4749
+ * <fold-nav-layout placement="side">
4750
+ * <fold-view-nav tabNav [items]="items" />
4751
+ * <router-outlet />
4752
+ * </fold-nav-layout>
4750
4753
  * ```
4751
4754
  *
4752
- * Sizing is CSS custom properties: `--fold-tab-layout-gap` (16px) and
4753
- * `--fold-tab-layout-nav-width` (200px, the side rail track).
4755
+ * The layout also exposes {@link stacked} via `exportAs` for consumers that want
4756
+ * to read the folded state themselves.
4757
+ *
4758
+ * **Two roles — same component, composed differently.**
4759
+ * - **Page scaffold** — the tab rail *is* the page's primary structure: use it
4760
+ * directly (usually `placement="side"`), the folded bar leading the content.
4761
+ * - **Tabbed section** — a tabbed block *inside* a page, among other sections.
4762
+ * Don't reach for a new mode — wrap it in a {@link FoldPageSectionComponent}.
4763
+ * The section owns the title, the `<section>` + heading semantics, the vertical
4764
+ * rhythm and the optional `bleed`; tab-layout stays pure placement. It paints
4765
+ * nothing (no card), so it sits **flat** — add `bleed` on the section for an
4766
+ * edge-to-edge band. The rail folds on the **section's** width (it measures
4767
+ * itself), not the viewport, so nested tabs collapse on their own.
4754
4768
  *
4755
- * @selector `fold-tab-layout`
4769
+ * ```html
4770
+ * <!-- a tabbed section: flat structure (page-section) + tab placement -->
4771
+ * <fold-page-section title="Settings" bleed>
4772
+ * <fold-nav-layout placement="side">
4773
+ * <fold-view-nav tabNav [items]="items" />
4774
+ * <app-settings-panel />
4775
+ * </fold-nav-layout>
4776
+ * </fold-page-section>
4777
+ * ```
4778
+ *
4779
+ * Sizing is CSS custom properties: `--fold-nav-layout-gap` (16px) and
4780
+ * `--fold-nav-layout-rail-width` — the side-rail track, defaulting to the shared
4781
+ * `--fold-rail-tertiary` (200px). That's the **tertiary** step of the rail
4782
+ * hierarchy — the same level the `--fold-color-bg-rail-tertiary` colour names
4783
+ * (app menu → workspace → in-page nav) — so every rail in the app stays on one
4784
+ * scale. Override the local var per instance.
4785
+ *
4786
+ * @selector `fold-nav-layout`
4756
4787
  */
4757
- class FoldTabLayoutComponent {
4788
+ class FoldNavLayoutComponent {
4758
4789
  /** Where the nav sits: above the content, or as a rail beside it. */
4759
4790
  placement = input("top", /* @ts-ignore */
4760
4791
  ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
@@ -4795,14 +4826,40 @@ class FoldTabLayoutComponent {
4795
4826
  this.folded.set(false);
4796
4827
  }
4797
4828
  }
4798
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4799
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldTabLayoutComponent, isStandalone: true, selector: "fold-tab-layout", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, foldAt: { classPropertyName: "foldAt", publicName: "foldAt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-row": "!stacked()" } }, exportAs: ["foldTabLayout"], ngImport: i0, template: "<div class=\"tl-nav\"><ng-content select=\"[tabNav]\" /></div>\n<div class=\"tl-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-tab-layout-gap, 16px);min-width:0}:host(.is-row){flex-direction:row}.tl-nav{display:grid;min-width:0}:host(.is-row) .tl-nav{flex:0 0 var(--fold-tab-layout-nav-width, 200px)}.tl-body{flex:1 1 auto;min-width:0}\n"] });
4829
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldNavLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4830
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldNavLayoutComponent, isStandalone: true, selector: "fold-nav-layout", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, foldAt: { classPropertyName: "foldAt", publicName: "foldAt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-row": "!stacked()" } }, providers: [
4831
+ { provide: FOLD_NAV_LAYOUT, useExisting: FoldNavLayoutComponent },
4832
+ ], exportAs: ["foldNavLayout"], ngImport: i0, template: "<div class=\"tl-nav\"><ng-content select=\"[tabNav]\" /></div>\n<div class=\"tl-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-nav-layout-gap, 16px);min-width:0}:host(.is-row){flex-direction:row}.tl-nav{display:grid;min-width:0}:host(.is-row) .tl-nav{flex:0 0 var(--fold-nav-layout-rail-width, var(--fold-rail-tertiary, 200px))}.tl-body{flex:1 1 auto;min-width:0}\n"] });
4800
4833
  }
4801
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabLayoutComponent, decorators: [{
4834
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldNavLayoutComponent, decorators: [{
4802
4835
  type: Component,
4803
- args: [{ selector: "fold-tab-layout", standalone: true, exportAs: "foldTabLayout", host: { "[class.is-row]": "!stacked()" }, template: "<div class=\"tl-nav\"><ng-content select=\"[tabNav]\" /></div>\n<div class=\"tl-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-tab-layout-gap, 16px);min-width:0}:host(.is-row){flex-direction:row}.tl-nav{display:grid;min-width:0}:host(.is-row) .tl-nav{flex:0 0 var(--fold-tab-layout-nav-width, 200px)}.tl-body{flex:1 1 auto;min-width:0}\n"] }]
4836
+ args: [{ selector: "fold-nav-layout", standalone: true, exportAs: "foldNavLayout", host: { "[class.is-row]": "!stacked()" }, providers: [
4837
+ { provide: FOLD_NAV_LAYOUT, useExisting: FoldNavLayoutComponent },
4838
+ ], template: "<div class=\"tl-nav\"><ng-content select=\"[tabNav]\" /></div>\n<div class=\"tl-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-nav-layout-gap, 16px);min-width:0}:host(.is-row){flex-direction:row}.tl-nav{display:grid;min-width:0}:host(.is-row) .tl-nav{flex:0 0 var(--fold-nav-layout-rail-width, var(--fold-rail-tertiary, 200px))}.tl-body{flex:1 1 auto;min-width:0}\n"] }]
4804
4839
  }], ctorParameters: () => [], propDecorators: { placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], foldAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "foldAt", required: false }] }] } });
4805
4840
 
4841
+ /**
4842
+ * Marks a custom page title projected into {@link FoldPageLayoutComponent} — put
4843
+ * it on inline heading content when the plain `icon` + `title` inputs aren't
4844
+ * enough (an avatar, a two-tone title, a live status). It renders inside the
4845
+ * page's `<h1>`, so keep it inline and text-like. Its presence switches the
4846
+ * header on in place of the input-driven title.
4847
+ *
4848
+ * @example
4849
+ * ```html
4850
+ * <fold-page-layout>
4851
+ * <span pageTitle><fold-avatar … /> Acme Records</span>
4852
+ * </fold-page-layout>
4853
+ * ```
4854
+ */
4855
+ class FoldPageTitleDirective {
4856
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageTitleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4857
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: FoldPageTitleDirective, isStandalone: true, selector: "[pageTitle]", ngImport: i0 });
4858
+ }
4859
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageTitleDirective, decorators: [{
4860
+ type: Directive,
4861
+ args: [{ selector: "[pageTitle]", standalone: true }]
4862
+ }] });
4806
4863
  /**
4807
4864
  * `<fold-page-layout>` — the vertical scaffold for a settings/admin-style page:
4808
4865
  * an optional `icon` + `title` header with a description slot, an optional
@@ -4824,6 +4881,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4824
4881
  *
4825
4882
  * Content projection:
4826
4883
  * - default slot → the page body (sections, cards, banners…).
4884
+ * - `[pageTitle]` ({@link FoldPageTitleDirective}) → a custom title, rendered
4885
+ * inside the `<h1>` in place of the `icon` + `title` inputs, for when a plain
4886
+ * string won't do (an avatar, a two-tone title). Its presence alone switches
4887
+ * the header on.
4827
4888
  * - `[titleBadge]` → an inline pill beside the title (e.g. a status/kind badge).
4828
4889
  * - `p[description]` → the intro under the title. A **slot**, not a string
4829
4890
  * input: a description that needs a `<code>`, a link or a second sentence is
@@ -4854,13 +4915,17 @@ class FoldPageLayoutComponent {
4854
4915
  /** An optional leading icon shown beside the title. */
4855
4916
  icon = input(/* @ts-ignore */
4856
4917
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
4918
+ /** A projected `[pageTitle]`, if any — switches the header on for a custom
4919
+ * title even without the `title` input. */
4920
+ customTitle = contentChild(FoldPageTitleDirective, /* @ts-ignore */
4921
+ ...(ngDevMode ? [{ debugName: "customTitle" }] : /* istanbul ignore next */ []));
4857
4922
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4858
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldPageLayoutComponent, isStandalone: true, selector: "fold-page-layout", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (title()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n @if (title()) {\n <h1 class=\"page-title\">\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ title() }}</span>\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n }\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.page-head-text{display:flex;flex-direction:column;gap:5px;min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:10px;font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:6px}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:8px}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }] });
4923
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldPageLayoutComponent, isStandalone: true, selector: "fold-page-layout", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "customTitle", first: true, predicate: FoldPageTitleDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.page-head-text{display:flex;flex-direction:column;gap:5px;min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:10px;font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:6px}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:8px}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }] });
4859
4924
  }
4860
4925
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageLayoutComponent, decorators: [{
4861
4926
  type: Component,
4862
- args: [{ selector: "fold-page-layout", standalone: true, imports: [FoldIconComponent], template: "@if (title()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n @if (title()) {\n <h1 class=\"page-title\">\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ title() }}</span>\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n }\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.page-head-text{display:flex;flex-direction:column;gap:5px;min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:10px;font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:6px}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:8px}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"] }]
4863
- }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
4927
+ args: [{ selector: "fold-page-layout", standalone: true, imports: [FoldIconComponent], template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.page-head-text{display:flex;flex-direction:column;gap:5px;min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:10px;font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:6px}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:8px}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"] }]
4928
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], customTitle: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FoldPageTitleDirective), { isSignal: true }] }] } });
4864
4929
 
4865
4930
  /**
4866
4931
  * `<fold-page-section>` — a titled, semantic **`<section>`** grouping of page
@@ -5128,64 +5193,285 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
5128
5193
  }], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: true }] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: true }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], siblingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "siblingCount", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }] } });
5129
5194
 
5130
5195
  /**
5131
- * `<fold-tab-nav>` — a tab navigation bar (buttons only; the parent renders the
5132
- * content for the active key).
5196
+ * `<fold-view-nav>` — a **navigation** bar styled as tabs: a `<nav>` whose items
5197
+ * *go somewhere*. Give each item a `link` (or `href`) and it renders a real
5198
+ * `<a>` — so cmd/middle-click opens a new tab, the URL is a deep link, and the
5199
+ * active item is driven automatically by `routerLinkActive` +
5200
+ * `aria-current="page"`. Without a link an item is a `<button>` that emits
5201
+ * `activeChange` and reads its active state from `activeKey` (view switching
5202
+ * with no route).
5133
5203
  *
5134
- * - `activeStyle` `underline` (accent border on the active tab) or `fill`
5135
- * (accent background pill, good for sidebars).
5136
- * - `direction``horizontal` (row, equal-width) or `vertical` (stacked
5137
- * sidebar; auto-collapses to a horizontal icon-accordion at ≤768px).
5204
+ * **Navigation, not the tabs widget.** Reach for this when a tab routes / switches
5205
+ * views. When tabs toggle **layered panels in place** (same URL), use
5206
+ * {@link FoldTabsComponent}same look, but the ARIA Tabs pattern
5207
+ * (`role="tablist"`, arrow keys, `tabpanel`).
5138
5208
  *
5139
- * @selector `fold-tab-nav`
5209
+ * - `activeStyle` — `underline` (accent border) or `fill` (accent pill).
5210
+ * - `direction` — `auto` (default: follows a wrapping `fold-nav-layout`, else
5211
+ * vertical), `vertical`, or `horizontal`.
5212
+ *
5213
+ * @selector `fold-view-nav`
5140
5214
  *
5141
5215
  * @example
5142
5216
  * ```html
5143
- * <fold-tab-nav
5144
- * [tabs]="[{ key: 'members', label: 'Members', badge: 3 }, { key: 'settings', label: 'Settings' }]"
5145
- * [activeKey]="tab()"
5146
- * activeStyle="underline"
5147
- * direction="horizontal"
5148
- * (tabChange)="tab.set($event)"
5149
- * />
5217
+ * <!-- real navigation: link items, active state automatic -->
5218
+ * <fold-view-nav [items]="[
5219
+ * { key: 'members', label: 'Members', link: 'members', badge: 3 },
5220
+ * { key: 'settings', label: 'Settings', link: 'settings' },
5221
+ * ]" />
5222
+ * <router-outlet />
5150
5223
  * ```
5151
5224
  */
5152
- class FoldTabNavComponent {
5225
+ class FoldViewNavComponent {
5226
+ /** The items to render, in order. */
5227
+ items = input.required(/* @ts-ignore */
5228
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
5229
+ /**
5230
+ * The active item's `key` — for **button** items (no `link`). Link items ignore
5231
+ * it: their active state comes from the URL via `routerLinkActive`.
5232
+ */
5233
+ activeKey = input("", /* @ts-ignore */
5234
+ ...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
5235
+ /** How the active item reads: accent underline, or accent fill. */
5236
+ activeStyle = input("underline", /* @ts-ignore */
5237
+ ...(ngDevMode ? [{ debugName: "activeStyle" }] : /* istanbul ignore next */ []));
5238
+ /**
5239
+ * Bar orientation. Defaults to `auto`: inside a `fold-nav-layout` it follows
5240
+ * the layout (vertical rail, horizontal once folded) with no wiring; on its
5241
+ * own it is `vertical` — the readable-first shape, reading as the app's
5242
+ * **third navigation rail** (app menu → workspace → in-page views, the
5243
+ * `--fold-rail-tertiary` level). Force `horizontal` for a page-level top bar.
5244
+ */
5245
+ direction = input("auto", /* @ts-ignore */
5246
+ ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
5247
+ /**
5248
+ * Density — pure padding/typography:
5249
+ * - `compact` (default) — inline / popover bars.
5250
+ * - `comfortable` — a prominent, page-level bar.
5251
+ */
5252
+ size = input("compact", /* @ts-ignore */
5253
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
5254
+ /**
5255
+ * Collapse the bar to icons (independent of {@link size}). **Horizontal**, an
5256
+ * icon accordion: every item but the active one drops to its icon, the active
5257
+ * one keeps its label. **Vertical**, a collapsed icon rail (like a folded
5258
+ * menu): every item shows just its icon, its label a hover/focus tooltip and
5259
+ * its count a corner bubble — narrow the layout's `--fold-nav-layout-rail-width`
5260
+ * to match. An item with no icon falls back to a square glyph of its label's
5261
+ * initial, so the rail stays icon-width whether or not items carry icons.
5262
+ * Toggle it from your own breakpoint when the bar is too tight.
5263
+ */
5264
+ collapsed = input(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5265
+ /**
5266
+ * `transparent` (default — blends with the app, so the items read directly) or
5267
+ * `surface` (a filled bar that carries its own rail background).
5268
+ */
5269
+ background = input("transparent", /* @ts-ignore */
5270
+ ...(ngDevMode ? [{ debugName: "background" }] : /* istanbul ignore next */ []));
5271
+ /** Emits the `key` of a clicked **button** item (link items just navigate). */
5272
+ activeChange = output();
5273
+ /** The nearest layout, if any — lets `direction="auto"` follow it. */
5274
+ layout = inject(FOLD_NAV_LAYOUT, { optional: true });
5275
+ /** `direction` with `auto` resolved against the wrapping layout. */
5276
+ resolvedDirection = computed(() => {
5277
+ const d = this.direction();
5278
+ if (d !== "auto") {
5279
+ return d;
5280
+ }
5281
+ return this.layout?.stacked() ? "horizontal" : "vertical";
5282
+ }, /* @ts-ignore */
5283
+ ...(ngDevMode ? [{ debugName: "resolvedDirection" }] : /* istanbul ignore next */ []));
5284
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldViewNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5285
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldViewNavComponent, isStandalone: true, selector: "fold-view-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, activeStyle: { classPropertyName: "activeStyle", publicName: "activeStyle", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeChange: "activeChange" }, ngImport: i0, template: "<nav\n class=\"tab-bar\"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"resolvedDirection() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.is-collapsed]=\"collapsed()\"\n [class.bg-surface]=\"background() === 'surface'\"\n>\n @for (item of items(); track item.key) {\n @if (item.disabled) {\n <button\n type=\"button\"\n class=\"tab-bar-item is-disabled\"\n disabled\n aria-disabled=\"true\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{ $implicit: item, active: false }\"\n />\n </button>\n } @else if (item.link !== undefined) {\n <a\n class=\"tab-bar-item\"\n [routerLink]=\"item.link\"\n routerLinkActive=\"is-active\"\n #rla=\"routerLinkActive\"\n [attr.aria-current]=\"rla.isActive ? 'page' : null\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{ $implicit: item, active: rla.isActive }\"\n />\n </a>\n } @else if (item.href !== undefined) {\n <a\n class=\"tab-bar-item\"\n [href]=\"item.href\"\n [class.is-active]=\"activeKey() === item.key\"\n [attr.aria-current]=\"activeKey() === item.key ? 'page' : null\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n active: activeKey() === item.key,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"tab-bar-item\"\n [class.is-active]=\"activeKey() === item.key\"\n [attr.aria-current]=\"activeKey() === item.key ? 'page' : null\"\n (click)=\"activeChange.emit(item.key)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n active: activeKey() === item.key,\n }\"\n />\n </button>\n }\n }\n</nav>\n\n<!-- One item's inner content, shared by the link / button branches. -->\n<ng-template #body let-item let-active=\"active\">\n @if (item.icon; as ic) {\n <fold-icon class=\"tab-bar-icon\" [name]=\"ic\" size=\"sm\" />\n } @else if (collapsed()) {\n <!-- Collapsed needs a fixed glyph; an icon-less item falls back to its\n initial so the rail stays icon-width and the label still tooltips. -->\n <span class=\"tab-bar-icon tab-bar-initial\" aria-hidden=\"true\">{{\n item.label.charAt(0)\n }}</span>\n }\n <span class=\"tab-bar-label\">{{ item.label }}</span>\n @if (item.badge !== undefined && item.badge !== null) {\n <fold-badge\n class=\"tab-bar-badge\"\n [content]=\"item.badge + ''\"\n [variant]=\"active ? 'accent' : 'neutral'\"\n />\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-bar{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-bar-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-family:inherit;font-size:10px;font-weight:600;cursor:pointer;text-align:center;text-decoration:none;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-bar-item:hover{color:var(--fold-color-text-secondary)}.tab-bar-item.is-disabled{opacity:.5;cursor:default;pointer-events:none}.size-comfortable .tab-bar-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.dir-vertical.size-comfortable .tab-bar-item{padding:12px 14px;font-size:var(--fold-text-md)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-bar-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-bar-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-bar-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-bar-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-bar-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.is-collapsed .tab-bar-item{position:relative;flex:0 0 auto;gap:0}.is-collapsed .tab-bar-item:not(.is-active) .tab-bar-badge{display:none}.is-collapsed .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.is-collapsed .tab-bar-item.is-active .tab-bar-label{overflow:hidden;text-overflow:ellipsis}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{position:absolute;padding:4px 8px;border-radius:var(--fold-radius-sm);background:var(--fold-color-glass);border:1px solid var(--fold-color-glass-border);color:var(--fold-color-text);font-size:var(--fold-text-xs);font-weight:500;white-space:nowrap;opacity:0;pointer-events:none;box-shadow:var(--fold-shadow-md);transition:opacity var(--fold-motion-fast);z-index:100}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{top:calc(100% + 6px);left:50%;transform:translate(-50%)}.dir-vertical.is-collapsed .tab-bar-item:hover .tab-bar-icon~.tab-bar-label,.dir-vertical.is-collapsed .tab-bar-item:focus-visible .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):hover .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):focus-visible .tab-bar-icon~.tab-bar-label{opacity:1}.dir-vertical.is-collapsed{align-items:center}.dir-vertical.is-collapsed .tab-bar-item,.dir-vertical.is-collapsed .tab-bar-item.is-active{justify-content:center;gap:0}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-badge{display:block;position:absolute;top:-2px;right:-2px;transform:scale(.85);transform-origin:top right;pointer-events:none}:host:has(.is-collapsed){overflow:visible}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-bar-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-bar-item .tab-bar-label{display:none}.dir-vertical .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-bar-item.is-active .tab-bar-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-bar-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-bar-icon{flex-shrink:0}.tab-bar-initial{display:grid;place-items:center;width:18px;height:18px;border-radius:var(--fold-radius-sm);font-size:11px;font-weight:700;line-height:1;text-transform:uppercase}.tab-bar-badge{flex-shrink:0;font-variant-numeric:tabular-nums}.tab-bar-item:focus-visible{outline:2px solid var(--fold-color-primary);outline-offset:-2px;border-radius:var(--fold-radius-sm)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }, { kind: "component", type: FoldBadgeComponent, selector: "fold-badge", inputs: ["content", "radius", "variant"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
5286
+ }
5287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldViewNavComponent, decorators: [{
5288
+ type: Component,
5289
+ args: [{ selector: "fold-view-nav", standalone: true, imports: [
5290
+ NgTemplateOutlet,
5291
+ FoldIconComponent,
5292
+ FoldBadgeComponent,
5293
+ RouterLink,
5294
+ RouterLinkActive,
5295
+ ], template: "<nav\n class=\"tab-bar\"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"resolvedDirection() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.is-collapsed]=\"collapsed()\"\n [class.bg-surface]=\"background() === 'surface'\"\n>\n @for (item of items(); track item.key) {\n @if (item.disabled) {\n <button\n type=\"button\"\n class=\"tab-bar-item is-disabled\"\n disabled\n aria-disabled=\"true\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{ $implicit: item, active: false }\"\n />\n </button>\n } @else if (item.link !== undefined) {\n <a\n class=\"tab-bar-item\"\n [routerLink]=\"item.link\"\n routerLinkActive=\"is-active\"\n #rla=\"routerLinkActive\"\n [attr.aria-current]=\"rla.isActive ? 'page' : null\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{ $implicit: item, active: rla.isActive }\"\n />\n </a>\n } @else if (item.href !== undefined) {\n <a\n class=\"tab-bar-item\"\n [href]=\"item.href\"\n [class.is-active]=\"activeKey() === item.key\"\n [attr.aria-current]=\"activeKey() === item.key ? 'page' : null\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n active: activeKey() === item.key,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"tab-bar-item\"\n [class.is-active]=\"activeKey() === item.key\"\n [attr.aria-current]=\"activeKey() === item.key ? 'page' : null\"\n (click)=\"activeChange.emit(item.key)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"body\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n active: activeKey() === item.key,\n }\"\n />\n </button>\n }\n }\n</nav>\n\n<!-- One item's inner content, shared by the link / button branches. -->\n<ng-template #body let-item let-active=\"active\">\n @if (item.icon; as ic) {\n <fold-icon class=\"tab-bar-icon\" [name]=\"ic\" size=\"sm\" />\n } @else if (collapsed()) {\n <!-- Collapsed needs a fixed glyph; an icon-less item falls back to its\n initial so the rail stays icon-width and the label still tooltips. -->\n <span class=\"tab-bar-icon tab-bar-initial\" aria-hidden=\"true\">{{\n item.label.charAt(0)\n }}</span>\n }\n <span class=\"tab-bar-label\">{{ item.label }}</span>\n @if (item.badge !== undefined && item.badge !== null) {\n <fold-badge\n class=\"tab-bar-badge\"\n [content]=\"item.badge + ''\"\n [variant]=\"active ? 'accent' : 'neutral'\"\n />\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-bar{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-bar-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-family:inherit;font-size:10px;font-weight:600;cursor:pointer;text-align:center;text-decoration:none;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-bar-item:hover{color:var(--fold-color-text-secondary)}.tab-bar-item.is-disabled{opacity:.5;cursor:default;pointer-events:none}.size-comfortable .tab-bar-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.dir-vertical.size-comfortable .tab-bar-item{padding:12px 14px;font-size:var(--fold-text-md)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-bar-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-bar-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-bar-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-bar-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-bar-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.is-collapsed .tab-bar-item{position:relative;flex:0 0 auto;gap:0}.is-collapsed .tab-bar-item:not(.is-active) .tab-bar-badge{display:none}.is-collapsed .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.is-collapsed .tab-bar-item.is-active .tab-bar-label{overflow:hidden;text-overflow:ellipsis}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{position:absolute;padding:4px 8px;border-radius:var(--fold-radius-sm);background:var(--fold-color-glass);border:1px solid var(--fold-color-glass-border);color:var(--fold-color-text);font-size:var(--fold-text-xs);font-weight:500;white-space:nowrap;opacity:0;pointer-events:none;box-shadow:var(--fold-shadow-md);transition:opacity var(--fold-motion-fast);z-index:100}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{top:calc(100% + 6px);left:50%;transform:translate(-50%)}.dir-vertical.is-collapsed .tab-bar-item:hover .tab-bar-icon~.tab-bar-label,.dir-vertical.is-collapsed .tab-bar-item:focus-visible .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):hover .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):focus-visible .tab-bar-icon~.tab-bar-label{opacity:1}.dir-vertical.is-collapsed{align-items:center}.dir-vertical.is-collapsed .tab-bar-item,.dir-vertical.is-collapsed .tab-bar-item.is-active{justify-content:center;gap:0}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-badge{display:block;position:absolute;top:-2px;right:-2px;transform:scale(.85);transform-origin:top right;pointer-events:none}:host:has(.is-collapsed){overflow:visible}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-bar-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-bar-item .tab-bar-label{display:none}.dir-vertical .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-bar-item.is-active .tab-bar-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-bar-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-bar-icon{flex-shrink:0}.tab-bar-initial{display:grid;place-items:center;width:18px;height:18px;border-radius:var(--fold-radius-sm);font-size:11px;font-weight:700;line-height:1;text-transform:uppercase}.tab-bar-badge{flex-shrink:0;font-variant-numeric:tabular-nums}.tab-bar-item:focus-visible{outline:2px solid var(--fold-color-primary);outline-offset:-2px;border-radius:var(--fold-radius-sm)}\n"] }]
5296
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: false }] }], activeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStyle", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], background: [{ type: i0.Input, args: [{ isSignal: true, alias: "background", required: false }] }], activeChange: [{ type: i0.Output, args: ["activeChange"] }] } });
5297
+
5298
+ /**
5299
+ * `<fold-tabs>` — the **in-page tabs widget** (the ARIA Tabs pattern): a
5300
+ * `role="tablist"` of `role="tab"` buttons that switch layered panels **without
5301
+ * navigating**. Arrow keys move between tabs (roving tabindex — only the active
5302
+ * tab is in the Tab order), `Home`/`End` jump to the ends, and each tab is wired
5303
+ * to its panel via `aria-controls` ↔ `aria-labelledby`.
5304
+ *
5305
+ * Pair it with one {@link FoldTabPanelComponent} per key, handed the bar via a
5306
+ * template ref so the two coordinate across slots (e.g. inside a
5307
+ * `fold-nav-layout`). Same visual as {@link FoldViewNavComponent}; the
5308
+ * difference is **semantics** — use `fold-tabs` for in-page panel switching, and
5309
+ * `fold-view-nav` when the tabs actually navigate (route) between views.
5310
+ *
5311
+ * ```html
5312
+ * <fold-nav-layout>
5313
+ * <fold-tabs
5314
+ * nav
5315
+ * #t="foldTabs"
5316
+ * [tabs]="tabs"
5317
+ * [activeKey]="tab()"
5318
+ * (tabChange)="tab.set($event)"
5319
+ * />
5320
+ * <fold-tab-panel [tabs]="t" key="overview">…</fold-tab-panel>
5321
+ * <fold-tab-panel [tabs]="t" key="settings">…</fold-tab-panel>
5322
+ * </fold-nav-layout>
5323
+ * ```
5324
+ *
5325
+ * Same visual knobs as `fold-view-nav` (`activeStyle` · `direction` · `size` ·
5326
+ * `background`). Sizing shares the tab-bar tokens.
5327
+ *
5328
+ * @selector `fold-tabs`
5329
+ */
5330
+ class FoldTabsComponent {
5153
5331
  /** The tabs to render, in order. */
5154
5332
  tabs = input.required(/* @ts-ignore */
5155
5333
  ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
5156
- /** The `key` of the currently active tab. */
5334
+ /** The `key` of the active tab (the shown panel). */
5157
5335
  activeKey = input.required(/* @ts-ignore */
5158
5336
  ...(ngDevMode ? [{ debugName: "activeKey" }] : /* istanbul ignore next */ []));
5159
5337
  /** How the active tab reads: accent underline, or accent fill. */
5160
5338
  activeStyle = input("underline", /* @ts-ignore */
5161
5339
  ...(ngDevMode ? [{ debugName: "activeStyle" }] : /* istanbul ignore next */ []));
5162
- /** Bar orientation (vertical auto-collapses to horizontal on mobile). */
5163
- direction = input("horizontal", /* @ts-ignore */
5164
- ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
5165
5340
  /**
5166
- * Density, from tightest to roomiest:
5167
- * - `reduce` an icon accordion: every tab but the active one drops its label
5168
- * (and badge) to just its icon, while the active tab keeps its label and
5169
- * takes the remaining room. A tab with no icon keeps its label, so nothing
5170
- * is left unlabelled. Swap to it from your own breakpoint when the bar is
5171
- * too tight for every label.
5172
- * - `compact` (default) — inline / popover bars.
5173
- * - `comfortable` — a prominent, page-level bar.
5341
+ * Bar orientation (drives `aria-orientation`). Defaults to `auto`: follows a
5342
+ * wrapping `fold-nav-layout`, else `horizontal`.
5174
5343
  */
5344
+ direction = input("auto", /* @ts-ignore */
5345
+ ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
5346
+ /** Density — see {@link FoldViewNavComponent} for the same scale. */
5175
5347
  size = input("compact", /* @ts-ignore */
5176
5348
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
5177
- /** `surface` (filled bar, default) or `transparent` (blends with the app). */
5349
+ /** Collapse to icons see {@link FoldViewNavComponent.collapsed}. */
5350
+ collapsed = input(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5351
+ /** `surface` (filled bar, default) or `transparent`. */
5178
5352
  background = input("surface", /* @ts-ignore */
5179
5353
  ...(ngDevMode ? [{ debugName: "background" }] : /* istanbul ignore next */ []));
5180
- /** Emits the `key` of the clicked tab. */
5354
+ /** Emits the newly-selected tab's `key`. */
5181
5355
  tabChange = output();
5182
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5183
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldTabNavComponent, isStandalone: true, selector: "fold-tab-nav", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: true, transformFunction: null }, activeStyle: { classPropertyName: "activeStyle", publicName: "activeStyle", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabChange: "tabChange" }, ngImport: i0, template: "<nav\n class=\"tab-nav\"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"direction() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.size-reduce]=\"size() === 'reduce'\"\n [class.bg-surface]=\"background() === 'surface'\"\n>\n @for (tab of tabs(); track tab.key) {\n <button\n type=\"button\"\n class=\"tab-nav-item\"\n [class.is-active]=\"activeKey() === tab.key\"\n (click)=\"tabChange.emit(tab.key)\"\n >\n @if (tab.icon; as ic) {\n <fold-icon class=\"tab-nav-icon\" [name]=\"ic\" size=\"sm\" />\n }\n <span class=\"tab-nav-label\">{{ tab.label }}</span>\n @if (tab.badge !== undefined && tab.badge !== null) {\n <fold-badge\n class=\"tab-nav-badge\"\n [content]=\"tab.badge + ''\"\n [variant]=\"activeKey() === tab.key ? 'accent' : 'neutral'\"\n />\n }\n </button>\n }\n</nav>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-nav{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-nav-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-size:10px;font-weight:600;cursor:pointer;text-align:center;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-nav-item:hover{color:var(--fold-color-text-secondary)}.size-comfortable .tab-nav-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-nav-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-nav-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-nav-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-nav-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-nav-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-nav-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-nav-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-nav-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-nav-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.size-reduce .tab-nav-item{flex:0 0 auto;gap:0}.size-reduce .tab-nav-item:not(.is-active) .tab-nav-icon~.tab-nav-label,.size-reduce .tab-nav-item:not(.is-active) .tab-nav-badge{display:none}.size-reduce .tab-nav-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.size-reduce .tab-nav-item.is-active .tab-nav-label{overflow:hidden;text-overflow:ellipsis}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-nav-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-nav-item .tab-nav-label{display:none}.dir-vertical .tab-nav-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-nav-item.is-active .tab-nav-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-nav-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-nav-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-nav-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-nav-icon{flex-shrink:0}.tab-nav-badge{flex-shrink:0;font-variant-numeric:tabular-nums}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }, { kind: "component", type: FoldBadgeComponent, selector: "fold-badge", inputs: ["content", "radius", "variant"] }] });
5356
+ /** The nearest layout, if any lets `direction="auto"` follow it. */
5357
+ layout = inject(FOLD_NAV_LAYOUT, { optional: true });
5358
+ /** `direction` with `auto` resolved against the wrapping layout. */
5359
+ resolvedDirection = computed(() => {
5360
+ const d = this.direction();
5361
+ if (d !== "auto") {
5362
+ return d;
5363
+ }
5364
+ return this.layout?.stacked() ? "horizontal" : "vertical";
5365
+ }, /* @ts-ignore */
5366
+ ...(ngDevMode ? [{ debugName: "resolvedDirection" }] : /* istanbul ignore next */ []));
5367
+ /** Per-instance id root, so ids are unique + stable across SSR. */
5368
+ uid = inject(FoldIdService).next("fold-tabs");
5369
+ tabButtons = viewChildren("tabBtn", /* @ts-ignore */
5370
+ ...(ngDevMode ? [{ debugName: "tabButtons" }] : /* istanbul ignore next */ []));
5371
+ tabId(key) {
5372
+ return `${this.uid}-tab-${key}`;
5373
+ }
5374
+ panelId(key) {
5375
+ return `${this.uid}-panel-${key}`;
5376
+ }
5377
+ select(key) {
5378
+ this.tabChange.emit(key);
5379
+ }
5380
+ /**
5381
+ * Roving-tabindex keyboard model (APG Tabs, automatic activation): arrows move
5382
+ * — and select — the adjacent tab, `Home`/`End` the ends. Both axes work, so
5383
+ * it stays correct whether the bar is horizontal, vertical, or a
5384
+ * mobile-collapsed vertical.
5385
+ */
5386
+ onKeydown(event) {
5387
+ const keys = this.tabs().map((t) => t.key);
5388
+ if (keys.length === 0) {
5389
+ return;
5390
+ }
5391
+ const current = keys.indexOf(this.activeKey());
5392
+ let target;
5393
+ switch (event.key) {
5394
+ case "ArrowRight":
5395
+ case "ArrowDown":
5396
+ target = (current + 1) % keys.length;
5397
+ break;
5398
+ case "ArrowLeft":
5399
+ case "ArrowUp":
5400
+ target = (current - 1 + keys.length) % keys.length;
5401
+ break;
5402
+ case "Home":
5403
+ target = 0;
5404
+ break;
5405
+ case "End":
5406
+ target = keys.length - 1;
5407
+ break;
5408
+ default:
5409
+ return;
5410
+ }
5411
+ event.preventDefault();
5412
+ const key = keys[target];
5413
+ if (key === undefined) {
5414
+ return;
5415
+ }
5416
+ this.tabChange.emit(key);
5417
+ this.tabButtons()[target]?.nativeElement.focus();
5418
+ }
5419
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5420
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldTabsComponent, isStandalone: true, selector: "fold-tabs", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: true, transformFunction: null }, activeStyle: { classPropertyName: "activeStyle", publicName: "activeStyle", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabChange: "tabChange" }, viewQueries: [{ propertyName: "tabButtons", predicate: ["tabBtn"], descendants: true, isSignal: true }], exportAs: ["foldTabs"], ngImport: i0, template: "<div\n class=\"tab-bar\"\n role=\"tablist\"\n [attr.aria-orientation]=\"\n resolvedDirection() === 'vertical' ? 'vertical' : 'horizontal'\n \"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"resolvedDirection() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.is-collapsed]=\"collapsed()\"\n [class.bg-surface]=\"background() === 'surface'\"\n (keydown)=\"onKeydown($event)\"\n>\n @for (tab of tabs(); track tab.key) {\n <button\n #tabBtn\n type=\"button\"\n role=\"tab\"\n class=\"tab-bar-item\"\n [id]=\"tabId(tab.key)\"\n [class.is-active]=\"activeKey() === tab.key\"\n [attr.aria-selected]=\"activeKey() === tab.key\"\n [attr.aria-controls]=\"panelId(tab.key)\"\n [tabindex]=\"activeKey() === tab.key ? 0 : -1\"\n (click)=\"select(tab.key)\"\n >\n @if (tab.icon; as ic) {\n <fold-icon class=\"tab-bar-icon\" [name]=\"ic\" size=\"sm\" />\n } @else if (collapsed()) {\n <span class=\"tab-bar-icon tab-bar-initial\" aria-hidden=\"true\">{{\n tab.label.charAt(0)\n }}</span>\n }\n <span class=\"tab-bar-label\">{{ tab.label }}</span>\n @if (tab.badge !== undefined && tab.badge !== null) {\n <fold-badge\n class=\"tab-bar-badge\"\n [content]=\"tab.badge + ''\"\n [variant]=\"activeKey() === tab.key ? 'accent' : 'neutral'\"\n />\n }\n </button>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-bar{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-bar-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-family:inherit;font-size:10px;font-weight:600;cursor:pointer;text-align:center;text-decoration:none;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-bar-item:hover{color:var(--fold-color-text-secondary)}.tab-bar-item.is-disabled{opacity:.5;cursor:default;pointer-events:none}.size-comfortable .tab-bar-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.dir-vertical.size-comfortable .tab-bar-item{padding:12px 14px;font-size:var(--fold-text-md)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-bar-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-bar-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-bar-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-bar-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-bar-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.is-collapsed .tab-bar-item{position:relative;flex:0 0 auto;gap:0}.is-collapsed .tab-bar-item:not(.is-active) .tab-bar-badge{display:none}.is-collapsed .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.is-collapsed .tab-bar-item.is-active .tab-bar-label{overflow:hidden;text-overflow:ellipsis}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{position:absolute;padding:4px 8px;border-radius:var(--fold-radius-sm);background:var(--fold-color-glass);border:1px solid var(--fold-color-glass-border);color:var(--fold-color-text);font-size:var(--fold-text-xs);font-weight:500;white-space:nowrap;opacity:0;pointer-events:none;box-shadow:var(--fold-shadow-md);transition:opacity var(--fold-motion-fast);z-index:100}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{top:calc(100% + 6px);left:50%;transform:translate(-50%)}.dir-vertical.is-collapsed .tab-bar-item:hover .tab-bar-icon~.tab-bar-label,.dir-vertical.is-collapsed .tab-bar-item:focus-visible .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):hover .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):focus-visible .tab-bar-icon~.tab-bar-label{opacity:1}.dir-vertical.is-collapsed{align-items:center}.dir-vertical.is-collapsed .tab-bar-item,.dir-vertical.is-collapsed .tab-bar-item.is-active{justify-content:center;gap:0}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-badge{display:block;position:absolute;top:-2px;right:-2px;transform:scale(.85);transform-origin:top right;pointer-events:none}:host:has(.is-collapsed){overflow:visible}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-bar-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-bar-item .tab-bar-label{display:none}.dir-vertical .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-bar-item.is-active .tab-bar-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-bar-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-bar-icon{flex-shrink:0}.tab-bar-initial{display:grid;place-items:center;width:18px;height:18px;border-radius:var(--fold-radius-sm);font-size:11px;font-weight:700;line-height:1;text-transform:uppercase}.tab-bar-badge{flex-shrink:0;font-variant-numeric:tabular-nums}.tab-bar-item:focus-visible{outline:2px solid var(--fold-color-primary);outline-offset:-2px;border-radius:var(--fold-radius-sm)}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "title"] }, { kind: "component", type: FoldBadgeComponent, selector: "fold-badge", inputs: ["content", "radius", "variant"] }] });
5421
+ }
5422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabsComponent, decorators: [{
5423
+ type: Component,
5424
+ args: [{ selector: "fold-tabs", standalone: true, exportAs: "foldTabs", imports: [FoldIconComponent, FoldBadgeComponent], template: "<div\n class=\"tab-bar\"\n role=\"tablist\"\n [attr.aria-orientation]=\"\n resolvedDirection() === 'vertical' ? 'vertical' : 'horizontal'\n \"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"resolvedDirection() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.is-collapsed]=\"collapsed()\"\n [class.bg-surface]=\"background() === 'surface'\"\n (keydown)=\"onKeydown($event)\"\n>\n @for (tab of tabs(); track tab.key) {\n <button\n #tabBtn\n type=\"button\"\n role=\"tab\"\n class=\"tab-bar-item\"\n [id]=\"tabId(tab.key)\"\n [class.is-active]=\"activeKey() === tab.key\"\n [attr.aria-selected]=\"activeKey() === tab.key\"\n [attr.aria-controls]=\"panelId(tab.key)\"\n [tabindex]=\"activeKey() === tab.key ? 0 : -1\"\n (click)=\"select(tab.key)\"\n >\n @if (tab.icon; as ic) {\n <fold-icon class=\"tab-bar-icon\" [name]=\"ic\" size=\"sm\" />\n } @else if (collapsed()) {\n <span class=\"tab-bar-icon tab-bar-initial\" aria-hidden=\"true\">{{\n tab.label.charAt(0)\n }}</span>\n }\n <span class=\"tab-bar-label\">{{ tab.label }}</span>\n @if (tab.badge !== undefined && tab.badge !== null) {\n <fold-badge\n class=\"tab-bar-badge\"\n [content]=\"tab.badge + ''\"\n [variant]=\"activeKey() === tab.key ? 'accent' : 'neutral'\"\n />\n }\n </button>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-bar{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-bar-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-family:inherit;font-size:10px;font-weight:600;cursor:pointer;text-align:center;text-decoration:none;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-bar-item:hover{color:var(--fold-color-text-secondary)}.tab-bar-item.is-disabled{opacity:.5;cursor:default;pointer-events:none}.size-comfortable .tab-bar-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.dir-vertical.size-comfortable .tab-bar-item{padding:12px 14px;font-size:var(--fold-text-md)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-bar-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-bar-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-bar-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-bar-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-bar-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-bar-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.is-collapsed .tab-bar-item{position:relative;flex:0 0 auto;gap:0}.is-collapsed .tab-bar-item:not(.is-active) .tab-bar-badge{display:none}.is-collapsed .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.is-collapsed .tab-bar-item.is-active .tab-bar-label{overflow:hidden;text-overflow:ellipsis}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{position:absolute;padding:4px 8px;border-radius:var(--fold-radius-sm);background:var(--fold-color-glass);border:1px solid var(--fold-color-glass-border);color:var(--fold-color-text);font-size:var(--fold-text-xs);font-weight:500;white-space:nowrap;opacity:0;pointer-events:none;box-shadow:var(--fold-shadow-md);transition:opacity var(--fold-motion-fast);z-index:100}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-icon~.tab-bar-label{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active) .tab-bar-icon~.tab-bar-label{top:calc(100% + 6px);left:50%;transform:translate(-50%)}.dir-vertical.is-collapsed .tab-bar-item:hover .tab-bar-icon~.tab-bar-label,.dir-vertical.is-collapsed .tab-bar-item:focus-visible .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):hover .tab-bar-icon~.tab-bar-label,.is-collapsed:not(.dir-vertical) .tab-bar-item:not(.is-active):focus-visible .tab-bar-icon~.tab-bar-label{opacity:1}.dir-vertical.is-collapsed{align-items:center}.dir-vertical.is-collapsed .tab-bar-item,.dir-vertical.is-collapsed .tab-bar-item.is-active{justify-content:center;gap:0}.dir-vertical.is-collapsed .tab-bar-item .tab-bar-badge{display:block;position:absolute;top:-2px;right:-2px;transform:scale(.85);transform-origin:top right;pointer-events:none}:host:has(.is-collapsed){overflow:visible}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-bar-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-bar-item .tab-bar-label{display:none}.dir-vertical .tab-bar-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-bar-item.is-active .tab-bar-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-bar-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-bar-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-bar-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-bar-icon{flex-shrink:0}.tab-bar-initial{display:grid;place-items:center;width:18px;height:18px;border-radius:var(--fold-radius-sm);font-size:11px;font-weight:700;line-height:1;text-transform:uppercase}.tab-bar-badge{flex-shrink:0;font-variant-numeric:tabular-nums}.tab-bar-item:focus-visible{outline:2px solid var(--fold-color-primary);outline-offset:-2px;border-radius:var(--fold-radius-sm)}\n"] }]
5425
+ }], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: true }] }], activeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStyle", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], background: [{ type: i0.Input, args: [{ isSignal: true, alias: "background", required: false }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], tabButtons: [{ type: i0.ViewChildren, args: ["tabBtn", { isSignal: true }] }] } });
5426
+
5427
+ /**
5428
+ * `<fold-tab-panel>` — one panel of a {@link FoldTabsComponent}. Handed the bar
5429
+ * via a template ref (`[tabs]="t"`, where `#t="foldTabs"`) plus its `key`, it
5430
+ * becomes a `role="tabpanel"` named by its tab (`aria-labelledby`), reachable by
5431
+ * `aria-controls` from that tab, focusable while active, and `hidden` otherwise.
5432
+ *
5433
+ * The ref pairing is what lets the tabs bar and its panels sit in **different**
5434
+ * slots (e.g. the bar in a `fold-nav-layout`'s nav slot, the panels in its
5435
+ * content) and still coordinate.
5436
+ *
5437
+ * ```html
5438
+ * <fold-tabs #t="foldTabs" [tabs]="tabs" [activeKey]="tab()" (tabChange)="tab.set($event)" />
5439
+ * <fold-tab-panel [tabs]="t" key="overview">…</fold-tab-panel>
5440
+ * ```
5441
+ *
5442
+ * @selector `fold-tab-panel`
5443
+ */
5444
+ class FoldTabPanelComponent {
5445
+ /** The owning `fold-tabs`, read via its template ref (`#t="foldTabs"`). */
5446
+ tabs = input.required(/* @ts-ignore */
5447
+ ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
5448
+ /** This panel's tab key — must match one tab in the bar. */
5449
+ key = input.required(/* @ts-ignore */
5450
+ ...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
5451
+ isActive = computed(() => this.tabs().activeKey() === this.key(), /* @ts-ignore */
5452
+ ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
5453
+ panelId = computed(() => this.tabs().panelId(this.key()), /* @ts-ignore */
5454
+ ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
5455
+ labelledBy = computed(() => this.tabs().tabId(this.key()), /* @ts-ignore */
5456
+ ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
5457
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5458
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldTabPanelComponent, isStandalone: true, selector: "fold-tab-panel", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "tabpanel" }, properties: { "id": "panelId()", "attr.aria-labelledby": "labelledBy()", "attr.tabindex": "isActive() ? 0 : null", "hidden": "!isActive()" } }, ngImport: i0, template: "<ng-content />", isInline: true });
5184
5459
  }
5185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabNavComponent, decorators: [{
5460
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabPanelComponent, decorators: [{
5186
5461
  type: Component,
5187
- args: [{ selector: "fold-tab-nav", standalone: true, imports: [FoldIconComponent, FoldBadgeComponent], template: "<nav\n class=\"tab-nav\"\n [class.style-underline]=\"activeStyle() === 'underline'\"\n [class.style-fill]=\"activeStyle() === 'fill'\"\n [class.dir-vertical]=\"direction() === 'vertical'\"\n [class.size-comfortable]=\"size() === 'comfortable'\"\n [class.size-reduce]=\"size() === 'reduce'\"\n [class.bg-surface]=\"background() === 'surface'\"\n>\n @for (tab of tabs(); track tab.key) {\n <button\n type=\"button\"\n class=\"tab-nav-item\"\n [class.is-active]=\"activeKey() === tab.key\"\n (click)=\"tabChange.emit(tab.key)\"\n >\n @if (tab.icon; as ic) {\n <fold-icon class=\"tab-nav-icon\" [name]=\"ic\" size=\"sm\" />\n }\n <span class=\"tab-nav-label\">{{ tab.label }}</span>\n @if (tab.badge !== undefined && tab.badge !== null) {\n <fold-badge\n class=\"tab-nav-badge\"\n [content]=\"tab.badge + ''\"\n [variant]=\"activeKey() === tab.key ? 'accent' : 'neutral'\"\n />\n }\n </button>\n }\n</nav>\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100%;max-width:100%;overflow:hidden}.tab-nav{flex:1;display:flex;max-width:100%;gap:2px;-webkit-user-select:none;user-select:none}.bg-surface{background:var(--fold-color-bg-rail-tertiary);padding:4px 8px 0}.bg-surface.dir-vertical{padding:12px 8px}.tab-nav-item{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 8px;background:none;border:none;color:var(--fold-color-text-muted);font-size:10px;font-weight:600;cursor:pointer;text-align:center;transition:color .1s ease,background .1s ease,border-color .1s ease;white-space:nowrap}.tab-nav-item:hover{color:var(--fold-color-text-secondary)}.size-comfortable .tab-nav-item{flex:0 1 auto;gap:10px;padding:11px 16px;font-size:var(--fold-text-sm)}.style-underline{border-bottom:1px solid var(--fold-color-border)}.style-underline .tab-nav-item{border-bottom:2px solid transparent;margin-bottom:-1px}.style-underline .tab-nav-item.is-active{color:var(--fold-color-text);border-bottom-color:var(--fold-color-primary)}.style-fill .tab-nav-item{border-radius:var(--fold-radius-sm);border:1px solid transparent}.style-fill .tab-nav-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.style-fill .tab-nav-item:hover:not(.is-active){background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.dir-vertical{flex-direction:column}.dir-vertical .tab-nav-item{flex:none;justify-content:flex-start;text-align:left;padding:9px 12px;gap:10px;border-radius:var(--fold-radius-sm);font-size:var(--fold-text-sm);font-weight:500}.dir-vertical.style-underline{border-bottom:none;border-right:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-nav-item{border-bottom:none;border-left:2px solid transparent;margin-bottom:0;margin-right:-1px}.dir-vertical.style-underline .tab-nav-item.is-active{border-left-color:var(--fold-color-primary)}.dir-vertical.style-fill .tab-nav-item.is-active{background:var(--fold-color-primary-surface);color:var(--fold-color-primary-text);font-weight:600}.size-reduce .tab-nav-item{flex:0 0 auto;gap:0}.size-reduce .tab-nav-item:not(.is-active) .tab-nav-icon~.tab-nav-label,.size-reduce .tab-nav-item:not(.is-active) .tab-nav-badge{display:none}.size-reduce .tab-nav-item.is-active{flex:1 1 auto;min-width:0;gap:7px}.size-reduce .tab-nav-item.is-active .tab-nav-label{overflow:hidden;text-overflow:ellipsis}@media(max-width:768px){.dir-vertical{flex-direction:row;align-items:center}.dir-vertical .tab-nav-item{flex:0 0 auto;justify-content:center;text-align:center;padding:8px;gap:0;font-size:var(--fold-text-xs);font-weight:600}.dir-vertical .tab-nav-item .tab-nav-label{display:none}.dir-vertical .tab-nav-item.is-active{flex:1 1 auto;min-width:0;gap:6px;padding:8px 12px}.dir-vertical .tab-nav-item.is-active .tab-nav-label{display:inline;overflow:hidden;text-overflow:ellipsis}.dir-vertical .tab-nav-badge{display:none}.dir-vertical.style-underline{border-right:none;border-bottom:1px solid var(--fold-color-border)}.dir-vertical.style-underline .tab-nav-item{border-left:none;border-bottom:2px solid transparent;margin-right:0;margin-bottom:-1px}.dir-vertical.style-underline .tab-nav-item.is-active{border-bottom-color:var(--fold-color-primary)}.dir-vertical.style-fill{border-right:none}}.tab-nav-icon{flex-shrink:0}.tab-nav-badge{flex-shrink:0;font-variant-numeric:tabular-nums}\n"] }]
5188
- }], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: true }] }], activeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStyle", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], background: [{ type: i0.Input, args: [{ isSignal: true, alias: "background", required: false }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }] } });
5462
+ args: [{
5463
+ selector: "fold-tab-panel",
5464
+ standalone: true,
5465
+ host: {
5466
+ role: "tabpanel",
5467
+ "[id]": "panelId()",
5468
+ "[attr.aria-labelledby]": "labelledBy()",
5469
+ "[attr.tabindex]": "isActive() ? 0 : null",
5470
+ "[hidden]": "!isActive()",
5471
+ },
5472
+ template: "<ng-content />",
5473
+ }]
5474
+ }], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }] } });
5189
5475
 
5190
5476
  /** Variant → leading status icon. */
5191
5477
  const VARIANT_ICON = {
@@ -5816,5 +6102,5 @@ class FoldPanelToggle {
5816
6102
  * Generated bundle index. Do not edit.
5817
6103
  */
5818
6104
 
5819
- export { FOLD_AUTO_PALETTES, FOLD_BLUR_TOKENS, FOLD_BUILTIN_ICONS, FOLD_MOTION_TOKENS, FOLD_PALETTE_DEFAULT, FOLD_PANEL_CLOSE_LABEL, FOLD_RADIUS_TOKENS, FOLD_SEMANTIC_COLOR_TOKENS, FOLD_SHADOW_TOKENS, FOLD_SPACE_TOKENS, FOLD_TEXT_TOKENS, FOLD_TOAST_CONFIG, FoldAppShellComponent, FoldAsideLayoutComponent, FoldAvatarComponent, FoldAvatarDetailComponent, FoldAvatarListComponent, FoldBadgeComponent, FoldButtonComponent, FoldButtonIconComponent, FoldCalloutComponent, FoldCardComponent, FoldChoiceRowComponent, FoldContextCardComponent, FoldDataTableCellDirective, FoldDataTableComponent, FoldDisclosureComponent, FoldElementTitleComponent, FoldElevatedDirective, FoldEmptyStateComponent, FoldFieldComponent, FoldFieldIdDirective, FoldFieldListComponent, FoldFileDropzoneComponent, FoldHeroCardComponent, FoldHeroSectionComponent, FoldIconComponent, FoldIconRegistry, FoldIdService, FoldInputComponent, FoldLinkComponent, FoldLoadingStateComponent, FoldMenuComponent, FoldMenuItemComponent, FoldMenuSectionComponent, FoldMenuSeparatorComponent, FoldNavLauncherComponent, FoldNavTileComponent, FoldNumberInputComponent, FoldPageLayoutComponent, FoldPageSectionComponent, FoldPaginatorComponent, FoldPaletteRegistry, FoldPanelComponentOutletDirective, FoldPanelHeaderComponent, FoldPanelHostComponent, FoldPanelHostService, FoldPanelRef, FoldPanelToggle, FoldRangeSliderComponent, FoldRepeatPressDirective, FoldSearchComponent, FoldSelectComponent, FoldSliderComponent, FoldSpinnerComponent, FoldStatusBadgeComponent, FoldStickyColumnDirective, FoldSurfaceDirective, FoldTabLayoutComponent, FoldTabNavComponent, FoldTimelineComponent, FoldToastComponent, FoldToastContainerComponent, FoldToastService, FoldToggleIconComponent, foldBlurVar, foldColorProperty, foldColorVar, foldHashSeed, foldMotionVar, foldRadiusVar, foldResolvePalette, foldShadowVar, foldSpaceVar, foldTextVar, observeElementWidth, provideFoldIcons, provideFoldPalette, provideFoldPanelLabels, provideFoldToasts };
6105
+ export { FOLD_AUTO_PALETTES, FOLD_BLUR_TOKENS, FOLD_BUILTIN_ICONS, FOLD_MOTION_TOKENS, FOLD_PALETTE_DEFAULT, FOLD_PANEL_CLOSE_LABEL, FOLD_RADIUS_TOKENS, FOLD_SEMANTIC_COLOR_TOKENS, FOLD_SHADOW_TOKENS, FOLD_SPACE_TOKENS, FOLD_TEXT_TOKENS, FOLD_TOAST_CONFIG, FoldAppShellComponent, FoldAsideLayoutComponent, FoldAvatarComponent, FoldAvatarDetailComponent, FoldAvatarListComponent, FoldBadgeComponent, FoldButtonComponent, FoldButtonIconComponent, FoldCalloutComponent, FoldCardComponent, FoldChoiceRowComponent, FoldContextCardComponent, FoldDataTableCellDirective, FoldDataTableComponent, FoldDisclosureComponent, FoldElementTitleComponent, FoldElevatedDirective, FoldEmptyStateComponent, FoldFieldComponent, FoldFieldIdDirective, FoldFieldListComponent, FoldFileDropzoneComponent, FoldHeroCardComponent, FoldHeroSectionComponent, FoldIconComponent, FoldIconRegistry, FoldIdService, FoldInputComponent, FoldLinkComponent, FoldLoadingStateComponent, FoldMenuComponent, FoldMenuItemComponent, FoldMenuSectionComponent, FoldMenuSeparatorComponent, FoldNavLauncherComponent, FoldNavLayoutComponent, FoldNavTileComponent, FoldNumberInputComponent, FoldPageLayoutComponent, FoldPageSectionComponent, FoldPageTitleDirective, FoldPaginatorComponent, FoldPaletteRegistry, FoldPanelComponentOutletDirective, FoldPanelHeaderComponent, FoldPanelHostComponent, FoldPanelHostService, FoldPanelRef, FoldPanelToggle, FoldRangeSliderComponent, FoldRepeatPressDirective, FoldSearchComponent, FoldSelectComponent, FoldSliderComponent, FoldSpinnerComponent, FoldStatusBadgeComponent, FoldStickyColumnDirective, FoldSurfaceDirective, FoldTabPanelComponent, FoldTabsComponent, FoldTimelineComponent, FoldToastComponent, FoldToastContainerComponent, FoldToastService, FoldToggleIconComponent, FoldViewNavComponent, foldBlurVar, foldColorProperty, foldColorVar, foldHashSeed, foldMotionVar, foldRadiusVar, foldResolvePalette, foldShadowVar, foldSpaceVar, foldTextVar, observeElementWidth, provideFoldIcons, provideFoldPalette, provideFoldPanelLabels, provideFoldToasts };
5820
6106
  //# sourceMappingURL=fold-ng.mjs.map