@frame-kit/ui-ng-patterns 0.1.4 → 0.1.6

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, computed, HostBinding, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender } from '@angular/core';
2
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, computed, HostBinding, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender, output } from '@angular/core';
3
3
  import { HeadlineComponent } from '@frame-kit/ui-ng/core/headline';
4
4
  import { TextComponent } from '@frame-kit/ui-ng/core/text';
5
5
  import { IconComponent } from '@frame-kit/ui-ng/core/icon';
@@ -11,6 +11,8 @@ import { Router, ActivatedRoute, NavigationEnd, RouterLink } from '@angular/rout
11
11
  import { filter, map, Subscription } from 'rxjs';
12
12
  import { TooltipDirective } from '@frame-kit/ui-ng/directives/tooltip';
13
13
  import { AppShellComponent } from '@frame-kit/ui-ng/layouts/app-shell';
14
+ import { ButtonComponent } from '@frame-kit/ui-ng/ui/button';
15
+ import { IconBadgeComponent } from '@frame-kit/ui-ng/ui/icon-badge';
14
16
 
15
17
  /**
16
18
  * Page-level dashboard header: title + optional description on the left, up to
@@ -645,9 +647,286 @@ function stripUrl(url) {
645
647
  return url.split('?')[0].split('#')[0];
646
648
  }
647
649
 
650
+ /**
651
+ * The fixed footer chrome for {@link DialogShellComponent} — an optional muted
652
+ * note on the left, then the projected action buttons pushed to the right,
653
+ * above a top divider. Paired with {@link DialogHeaderComponent} so every
654
+ * dialog shares the same chrome.
655
+ *
656
+ * The note icon is optional and degrades gracefully — an empty `noteIcon`
657
+ * simply renders no glyph.
658
+ *
659
+ * ```html
660
+ * <fk-dialog-footer note="Takes effect immediately" noteIcon="power">
661
+ * <fk-button variant="outline" (click)="cancel()">Cancel</fk-button>
662
+ * <fk-button variant="primary" type="submit">Save</fk-button>
663
+ * </fk-dialog-footer>
664
+ * ```
665
+ */
666
+ class DialogFooterComponent {
667
+ // ===== INPUTS =====
668
+ /** Optional muted note shown on the left (e.g. "Takes effect immediately"). */
669
+ note = input('', ...(ngDevMode ? [{ debugName: "note" }] : /* istanbul ignore next */ []));
670
+ /** Optional icon glyph rendered before the note. Empty renders no glyph. */
671
+ noteIcon = input('', ...(ngDevMode ? [{ debugName: "noteIcon" }] : /* istanbul ignore next */ []));
672
+ // ===== BASE PROPS =====
673
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
674
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
675
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
676
+ // ===== COMPUTED =====
677
+ classes = computed(() => ['fk-dialog-footer', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
678
+ get hostClass() {
679
+ return this.classes();
680
+ }
681
+ get hostId() {
682
+ return this.id();
683
+ }
684
+ get hostAriaLabel() {
685
+ return this.ariaLabel();
686
+ }
687
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
688
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogFooterComponent, isStandalone: true, selector: "fk-dialog-footer", inputs: { note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, noteIcon: { classPropertyName: "noteIcon", publicName: "noteIcon", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.aria-label": "this.hostAriaLabel" } }, ngImport: i0, template: "@if (note()) {\n <span class=\"fk-dialog-footer__note\">\n @if (noteIcon()) {\n <fk-icon\n class=\"fk-dialog-footer__note-icon\"\n [name]=\"noteIcon()\"\n size=\"sm\"\n />\n }\n {{ note() }}\n </span>\n}\n\n<span class=\"fk-dialog-footer__spacer\"></span>\n\n<ng-content />\n", styles: [":host{display:flex;align-items:center;gap:var(--fk-dialog-footer-gap, var(--fk-rhythm-3, .75rem));margin-top:var(--fk-dialog-footer-margin-top, var(--fk-rhythm-5, 1.5rem));padding-top:var(--fk-dialog-footer-padding-top, var(--fk-rhythm-5, 1.5rem));border-top:1px solid var(--fk-dialog-footer-divider-color, var(--fk-color-surface-dim, #f1f5f9))}.fk-dialog-footer__note{display:inline-flex;align-items:center;gap:.375rem;font-size:var(--fk-dialog-footer-note-font-size, .71875rem);color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__note-icon{flex:none;color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__spacer{flex:1}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
689
+ }
690
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogFooterComponent, decorators: [{
691
+ type: Component,
692
+ args: [{ selector: 'fk-dialog-footer', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (note()) {\n <span class=\"fk-dialog-footer__note\">\n @if (noteIcon()) {\n <fk-icon\n class=\"fk-dialog-footer__note-icon\"\n [name]=\"noteIcon()\"\n size=\"sm\"\n />\n }\n {{ note() }}\n </span>\n}\n\n<span class=\"fk-dialog-footer__spacer\"></span>\n\n<ng-content />\n", styles: [":host{display:flex;align-items:center;gap:var(--fk-dialog-footer-gap, var(--fk-rhythm-3, .75rem));margin-top:var(--fk-dialog-footer-margin-top, var(--fk-rhythm-5, 1.5rem));padding-top:var(--fk-dialog-footer-padding-top, var(--fk-rhythm-5, 1.5rem));border-top:1px solid var(--fk-dialog-footer-divider-color, var(--fk-color-surface-dim, #f1f5f9))}.fk-dialog-footer__note{display:inline-flex;align-items:center;gap:.375rem;font-size:var(--fk-dialog-footer-note-font-size, .71875rem);color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__note-icon{flex:none;color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__spacer{flex:1}\n"] }]
693
+ }], propDecorators: { note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], noteIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "noteIcon", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
694
+ type: HostBinding,
695
+ args: ['class']
696
+ }], hostId: [{
697
+ type: HostBinding,
698
+ args: ['attr.id']
699
+ }], hostAriaLabel: [{
700
+ type: HostBinding,
701
+ args: ['attr.aria-label']
702
+ }] } });
703
+
704
+ /** The tint tone → the CSS-variable pair driving the icon tile + title accent.
705
+ * Each reference is two-tier (component token → semantic token) so the header
706
+ * renders correctly with no theme loaded and can be retinted per theme. */
707
+ const TINTS = {
708
+ brand: {
709
+ color: 'var(--fk-dialog-header-tint-brand-color, var(--fk-color-primary, #2563eb))',
710
+ background: 'var(--fk-dialog-header-tint-brand-bg, var(--fk-color-primary-light, #eff6ff))',
711
+ },
712
+ warning: {
713
+ color: 'var(--fk-dialog-header-tint-warning-color, var(--fk-color-warning, #d97706))',
714
+ background: 'var(--fk-dialog-header-tint-warning-bg, var(--fk-color-warning-subtle, #fffbeb))',
715
+ },
716
+ success: {
717
+ color: 'var(--fk-dialog-header-tint-success-color, var(--fk-color-success, #16a34a))',
718
+ background: 'var(--fk-dialog-header-tint-success-bg, var(--fk-color-success-subtle, #f0fdf4))',
719
+ },
720
+ danger: {
721
+ color: 'var(--fk-dialog-header-tint-danger-color, var(--fk-color-danger, #dc2626))',
722
+ background: 'var(--fk-dialog-header-tint-danger-bg, var(--fk-color-danger-light, #fee2e2))',
723
+ },
724
+ accent: {
725
+ color: 'var(--fk-dialog-header-tint-accent-color, #7c3aed)',
726
+ background: 'var(--fk-dialog-header-tint-accent-bg, #f5f3ff)',
727
+ },
728
+ };
729
+ /**
730
+ * The fixed header chrome for {@link DialogShellComponent} — a tinted icon
731
+ * tile, a title with an optional trailing accent, an optional mono subtitle,
732
+ * and a close button. Every dialog wears the same chrome; only the icon, tint,
733
+ * and text differ, so this centralises it instead of each dialog hand-rolling
734
+ * the markup.
735
+ *
736
+ * Text is passed in as plain inputs (no i18n dependency), and the icon is
737
+ * optional — an empty `icon` simply renders no tile, so the header degrades
738
+ * gracefully rather than showing a broken glyph.
739
+ *
740
+ * ```html
741
+ * <fk-dialog-header
742
+ * icon="user-ban"
743
+ * tint="warning"
744
+ * title="Deactivate identity"
745
+ * subtitle="Acme Inc. · Members"
746
+ * (dismiss)="close()"
747
+ * />
748
+ * ```
749
+ */
750
+ class DialogHeaderComponent {
751
+ // ===== INPUTS =====
752
+ /** Icon glyph name for the tile. Empty renders no tile. */
753
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
754
+ /** Colour accent for the icon tile + title accent. */
755
+ tint = input('brand', ...(ngDevMode ? [{ debugName: "tint" }] : /* istanbul ignore next */ []));
756
+ /** Dialog title. */
757
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
758
+ /** Optional trailing accent appended to the title, rendered in the tint colour. */
759
+ titleAccent = input('', ...(ngDevMode ? [{ debugName: "titleAccent" }] : /* istanbul ignore next */ []));
760
+ /** Optional mono subtitle under the title. */
761
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
762
+ /** Accessible label for the close button. */
763
+ closeLabel = input('Close', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
764
+ // ===== OUTPUTS =====
765
+ /** Fires when the close button is clicked. */
766
+ dismiss = output();
767
+ // ===== BASE PROPS =====
768
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
769
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
770
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
771
+ // ===== COMPUTED =====
772
+ /** The tint's `{ color, background }` CSS-variable pair. */
773
+ tintVars = computed(() => TINTS[this.tint()], ...(ngDevMode ? [{ debugName: "tintVars" }] : /* istanbul ignore next */ []));
774
+ classes = computed(() => ['fk-dialog-header', `fk-dialog-header--${this.tint()}`, this.className()]
775
+ .filter(Boolean)
776
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
777
+ get hostClass() {
778
+ return this.classes();
779
+ }
780
+ get hostId() {
781
+ return this.id();
782
+ }
783
+ get hostAriaLabel() {
784
+ return this.ariaLabel();
785
+ }
786
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogHeaderComponent, isStandalone: true, selector: "fk-dialog-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, titleAccent: { classPropertyName: "titleAccent", publicName: "titleAccent", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.aria-label": "this.hostAriaLabel" } }, ngImport: i0, template: "@if (icon()) {\n <fk-icon-badge\n class=\"fk-dialog-header__icon\"\n [name]=\"icon()\"\n [color]=\"tintVars().color\"\n [backgroundColor]=\"tintVars().background\"\n shape=\"rounded\"\n size=\"md\"\n />\n}\n\n<div class=\"fk-dialog-header__titles\">\n <fk-headline\n class=\"fk-dialog-header__title\"\n [level]=\"2\"\n fontSize=\"var(--fk-dialog-header-title-font-size, 1.25rem)\"\n >\n {{ title() }}\n @if (titleAccent()) {\n <span class=\"fk-dialog-header__accent\" [style.color]=\"tintVars().color\">\n {{ titleAccent() }}</span\n >\n }\n </fk-headline>\n @if (subtitle()) {\n <fk-text class=\"fk-dialog-header__subtitle\" variant=\"small\">\n {{ subtitle() }}\n </fk-text>\n }\n</div>\n\n<button\n type=\"button\"\n class=\"fk-dialog-header__close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss.emit()\"\n>\n <fk-icon name=\"close\" size=\"sm\" />\n</button>\n", styles: [":host{display:flex;align-items:flex-start;gap:var(--fk-dialog-header-gap, var(--fk-rhythm-3, .75rem))}.fk-dialog-header__icon{flex:none}.fk-dialog-header__titles{flex:1;min-width:0}.fk-dialog-header__title{--fk-headline-margin-block-start: 0}.fk-dialog-header__subtitle{--fk-text-margin-block-start: .1875rem;--fk-text-color-default: var( --fk-dialog-header-subtitle-color, var(--fk-color-muted, #64748b) );--fk-typography-small-font-size: .78125rem;--fk-typography-small-letter-spacing: .02em;font-family:var(--fk-dialog-header-subtitle-font-family, var(--fk-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace))}.fk-dialog-header__close{display:grid;place-items:center;flex:none;width:2rem;height:2rem;margin:-.25rem -.25rem 0 0;border:1px solid transparent;border-radius:var(--fk-dialog-header-close-radius, var(--fk-radius-sm, .25rem));background:transparent;color:var(--fk-dialog-header-close-color, var(--fk-color-muted, #64748b));cursor:pointer;transition:.15s}.fk-dialog-header__close:hover{border-color:var(--fk-dialog-header-close-border-hover, var(--fk-color-border, #e2e8f0));background:var(--fk-dialog-header-close-bg-hover, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-dialog-header-close-color-hover, var(--fk-color-text, #1e293b))}\n"], dependencies: [{ kind: "component", type: HeadlineComponent, selector: "fk-headline", inputs: ["ariaLabel", "className", "fontSize", "id", "level", "variant", "visuallyHidden"] }, { kind: "component", type: IconBadgeComponent, selector: "fk-icon-badge", inputs: ["name", "size", "badgeSize", "shape", "color", "borderColor", "backgroundColor", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: TextComponent, selector: "fk-text", inputs: ["as", "variant", "tone", "align", "weight", "truncate", "maxLines", "gutterBottom", "italic", "visuallyHidden", "className", "id", "ariaLabel", "ariaDescribedBy", "role", "htmlFor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
788
+ }
789
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogHeaderComponent, decorators: [{
790
+ type: Component,
791
+ args: [{ selector: 'fk-dialog-header', standalone: true, imports: [
792
+ HeadlineComponent,
793
+ IconBadgeComponent,
794
+ IconComponent,
795
+ TextComponent,
796
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (icon()) {\n <fk-icon-badge\n class=\"fk-dialog-header__icon\"\n [name]=\"icon()\"\n [color]=\"tintVars().color\"\n [backgroundColor]=\"tintVars().background\"\n shape=\"rounded\"\n size=\"md\"\n />\n}\n\n<div class=\"fk-dialog-header__titles\">\n <fk-headline\n class=\"fk-dialog-header__title\"\n [level]=\"2\"\n fontSize=\"var(--fk-dialog-header-title-font-size, 1.25rem)\"\n >\n {{ title() }}\n @if (titleAccent()) {\n <span class=\"fk-dialog-header__accent\" [style.color]=\"tintVars().color\">\n {{ titleAccent() }}</span\n >\n }\n </fk-headline>\n @if (subtitle()) {\n <fk-text class=\"fk-dialog-header__subtitle\" variant=\"small\">\n {{ subtitle() }}\n </fk-text>\n }\n</div>\n\n<button\n type=\"button\"\n class=\"fk-dialog-header__close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss.emit()\"\n>\n <fk-icon name=\"close\" size=\"sm\" />\n</button>\n", styles: [":host{display:flex;align-items:flex-start;gap:var(--fk-dialog-header-gap, var(--fk-rhythm-3, .75rem))}.fk-dialog-header__icon{flex:none}.fk-dialog-header__titles{flex:1;min-width:0}.fk-dialog-header__title{--fk-headline-margin-block-start: 0}.fk-dialog-header__subtitle{--fk-text-margin-block-start: .1875rem;--fk-text-color-default: var( --fk-dialog-header-subtitle-color, var(--fk-color-muted, #64748b) );--fk-typography-small-font-size: .78125rem;--fk-typography-small-letter-spacing: .02em;font-family:var(--fk-dialog-header-subtitle-font-family, var(--fk-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace))}.fk-dialog-header__close{display:grid;place-items:center;flex:none;width:2rem;height:2rem;margin:-.25rem -.25rem 0 0;border:1px solid transparent;border-radius:var(--fk-dialog-header-close-radius, var(--fk-radius-sm, .25rem));background:transparent;color:var(--fk-dialog-header-close-color, var(--fk-color-muted, #64748b));cursor:pointer;transition:.15s}.fk-dialog-header__close:hover{border-color:var(--fk-dialog-header-close-border-hover, var(--fk-color-border, #e2e8f0));background:var(--fk-dialog-header-close-bg-hover, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-dialog-header-close-color-hover, var(--fk-color-text, #1e293b))}\n"] }]
797
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tint: [{ type: i0.Input, args: [{ isSignal: true, alias: "tint", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], titleAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleAccent", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
798
+ type: HostBinding,
799
+ args: ['class']
800
+ }], hostId: [{
801
+ type: HostBinding,
802
+ args: ['attr.id']
803
+ }], hostAriaLabel: [{
804
+ type: HostBinding,
805
+ args: ['attr.aria-label']
806
+ }] } });
807
+
808
+ /**
809
+ * The one dialog shell every dashboard dialog is built from. It owns the fixed
810
+ * chrome — the tinted icon-tile header (title / accent / subtitle + close) and
811
+ * the footer (muted note + the standard Cancel/confirm buttons) — so individual
812
+ * dialogs stop re-declaring any of it. Everything unique to a dialog lives in
813
+ * the projected **content region**.
814
+ *
815
+ * The shell carries no i18n dependency and no domain data: every label is a
816
+ * plain input (with English defaults for the built-in Cancel button), and the
817
+ * icons are inputs that degrade gracefully when empty. Render it inside the
818
+ * consuming app's own `fk-dialog` (opened bare — `header: null, closable:
819
+ * false` — with an `ariaLabel` for an accessible name).
820
+ *
821
+ * The common case needs zero footer markup — set the confirm label/variant and
822
+ * handle the outputs:
823
+ *
824
+ * ```html
825
+ * <fk-dialog-shell
826
+ * icon="user-ban"
827
+ * tint="warning"
828
+ * title="Deactivate identity"
829
+ * [subtitle]="subtitle"
830
+ * footerNote="Sign-in blocked"
831
+ * footerNoteIcon="power"
832
+ * confirmLabel="Deactivate"
833
+ * confirmIcon="user-ban"
834
+ * confirmVariant="warning"
835
+ * [confirmDisabled]="!ready()"
836
+ * (confirm)="onConfirm()"
837
+ * (dismiss)="onCancel()"
838
+ * >
839
+ * <!-- content region: lead, description, cards, callouts … -->
840
+ * </fk-dialog-shell>
841
+ * ```
842
+ *
843
+ * Escape hatches for the exceptions:
844
+ * - leave `confirmLabel` empty and project buttons into the `dialogFooter` slot
845
+ * to keep the footer chrome but use custom buttons (e.g. a form submit);
846
+ * - set `[customFooter]="true"` and project into the `footer` slot to replace
847
+ * the whole footer.
848
+ */
849
+ class DialogShellComponent {
850
+ // ===== INPUTS =====
851
+ /** Icon glyph for the header tile. Empty renders no tile. */
852
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
853
+ /** Colour accent for the header tile. */
854
+ tint = input('brand', ...(ngDevMode ? [{ debugName: "tint" }] : /* istanbul ignore next */ []));
855
+ /** Header title. */
856
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
857
+ /** Optional trailing accent appended to the title in the tint colour. */
858
+ titleAccent = input('', ...(ngDevMode ? [{ debugName: "titleAccent" }] : /* istanbul ignore next */ []));
859
+ /** Optional mono subtitle under the title. */
860
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
861
+ /** Optional muted footer note on the left (e.g. "Permanent"). */
862
+ footerNote = input('', ...(ngDevMode ? [{ debugName: "footerNote" }] : /* istanbul ignore next */ []));
863
+ /** Optional icon rendered before the footer note. */
864
+ footerNoteIcon = input('', ...(ngDevMode ? [{ debugName: "footerNoteIcon" }] : /* istanbul ignore next */ []));
865
+ /**
866
+ * Confirm-button label. When set, the shell renders the standard
867
+ * Cancel + confirm pair; leave empty to project custom buttons into the
868
+ * `dialogFooter` slot instead.
869
+ */
870
+ confirmLabel = input('', ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
871
+ /** Optional leading icon on the confirm button. */
872
+ confirmIcon = input('', ...(ngDevMode ? [{ debugName: "confirmIcon" }] : /* istanbul ignore next */ []));
873
+ /** Confirm-button colour — `primary` (default), `danger`, or `warning`. */
874
+ confirmVariant = input('primary', ...(ngDevMode ? [{ debugName: "confirmVariant" }] : /* istanbul ignore next */ []));
875
+ /** Disables the confirm button (e.g. while a type-to-confirm is unmet). */
876
+ confirmDisabled = input(false, ...(ngDevMode ? [{ debugName: "confirmDisabled" }] : /* istanbul ignore next */ []));
877
+ /** Cancel-button label. Defaults to `Cancel`. */
878
+ cancelLabel = input('Cancel', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
879
+ /** Accessible label for the header's close button. */
880
+ closeLabel = input('Close', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
881
+ /**
882
+ * Escape hatch for the rare dialog that needs a bespoke footer. When true,
883
+ * the default `fk-dialog-footer` (note + buttons) is suppressed and whatever
884
+ * is projected into the `footer` slot is rendered raw instead.
885
+ */
886
+ customFooter = input(false, ...(ngDevMode ? [{ debugName: "customFooter" }] : /* istanbul ignore next */ []));
887
+ // ===== OUTPUTS =====
888
+ /** Fires when the header close button OR the Cancel button is clicked. */
889
+ dismiss = output();
890
+ /** Fires when the confirm button is clicked. */
891
+ confirm = output();
892
+ // ===== BASE PROPS =====
893
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
894
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
895
+ // ===== COMPUTED =====
896
+ /** `warning` renders as a retinted `danger` fk-button; others map directly. */
897
+ confirmButtonVariant = computed(() => {
898
+ const variant = this.confirmVariant();
899
+ return variant === 'warning' ? 'danger' : variant;
900
+ }, ...(ngDevMode ? [{ debugName: "confirmButtonVariant" }] : /* istanbul ignore next */ []));
901
+ classes = computed(() => ['fk-dialog-shell', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
902
+ get hostClass() {
903
+ return this.classes();
904
+ }
905
+ get hostId() {
906
+ return this.id();
907
+ }
908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogShellComponent, isStandalone: true, selector: "fk-dialog-shell", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, titleAccent: { classPropertyName: "titleAccent", publicName: "titleAccent", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, footerNote: { classPropertyName: "footerNote", publicName: "footerNote", isSignal: true, isRequired: false, transformFunction: null }, footerNoteIcon: { classPropertyName: "footerNoteIcon", publicName: "footerNoteIcon", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, confirmIcon: { classPropertyName: "confirmIcon", publicName: "confirmIcon", isSignal: true, isRequired: false, transformFunction: null }, confirmVariant: { classPropertyName: "confirmVariant", publicName: "confirmVariant", isSignal: true, isRequired: false, transformFunction: null }, confirmDisabled: { classPropertyName: "confirmDisabled", publicName: "confirmDisabled", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, customFooter: { classPropertyName: "customFooter", publicName: "customFooter", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss", confirm: "confirm" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<fk-dialog-header\n class=\"fk-dialog-shell__header\"\n [icon]=\"icon()\"\n [tint]=\"tint()\"\n [title]=\"title()\"\n [titleAccent]=\"titleAccent()\"\n [subtitle]=\"subtitle()\"\n [closeLabel]=\"closeLabel()\"\n (dismiss)=\"dismiss.emit()\"\n/>\n\n<div class=\"fk-dialog-shell__body\">\n <ng-content />\n</div>\n\n@if (customFooter()) {\n <ng-content select=\"[footer]\" />\n} @else {\n <fk-dialog-footer [note]=\"footerNote()\" [noteIcon]=\"footerNoteIcon()\">\n @if (confirmLabel()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"dismiss.emit()\">\n {{ cancelLabel() }}\n </fk-button>\n <fk-button\n class=\"fk-dialog-shell__confirm\"\n [class.fk-dialog-shell__confirm--danger]=\"confirmVariant() === 'danger'\"\n [class.fk-dialog-shell__confirm--warning]=\"\n confirmVariant() === 'warning'\n \"\n [variant]=\"confirmButtonVariant()\"\n type=\"button\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"confirm.emit()\"\n >\n @if (confirmIcon()) {\n <fk-icon fkButtonIconStart [name]=\"confirmIcon()\" size=\"sm\" />\n }\n {{ confirmLabel() }}\n </fk-button>\n } @else {\n <ng-content select=\"[dialogFooter]\" />\n }\n </fk-dialog-footer>\n}\n", styles: [":host{display:flex;flex-direction:column}.fk-dialog-shell__body{margin-top:var(--fk-dialog-shell-body-margin-top, var(--fk-rhythm-4, 1rem))}.fk-dialog-shell__confirm--warning{--fk-button-danger-bg: var( --fk-dialog-confirm-warning-bg, var(--fk-color-warning, #d97706) );--fk-button-danger-bg-hover: var( --fk-dialog-confirm-warning-bg-hover, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-bg-active: var( --fk-dialog-confirm-warning-bg-active, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-color: var( --fk-dialog-confirm-warning-color, var(--fk-color-surface, #ffffff) );--fk-button-danger-border-color: transparent}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "fk-button", inputs: ["variant", "size", "loading", "disabled", "type", "fullWidth", "id", "className", "ariaLabel", "ariaDescribedBy"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: DialogFooterComponent, selector: "fk-dialog-footer", inputs: ["note", "noteIcon", "className", "id", "ariaLabel"] }, { kind: "component", type: DialogHeaderComponent, selector: "fk-dialog-header", inputs: ["icon", "tint", "title", "titleAccent", "subtitle", "closeLabel", "className", "id", "ariaLabel"], outputs: ["dismiss"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
910
+ }
911
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogShellComponent, decorators: [{
912
+ type: Component,
913
+ args: [{ selector: 'fk-dialog-shell', standalone: true, imports: [
914
+ ButtonComponent,
915
+ IconComponent,
916
+ DialogFooterComponent,
917
+ DialogHeaderComponent,
918
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fk-dialog-header\n class=\"fk-dialog-shell__header\"\n [icon]=\"icon()\"\n [tint]=\"tint()\"\n [title]=\"title()\"\n [titleAccent]=\"titleAccent()\"\n [subtitle]=\"subtitle()\"\n [closeLabel]=\"closeLabel()\"\n (dismiss)=\"dismiss.emit()\"\n/>\n\n<div class=\"fk-dialog-shell__body\">\n <ng-content />\n</div>\n\n@if (customFooter()) {\n <ng-content select=\"[footer]\" />\n} @else {\n <fk-dialog-footer [note]=\"footerNote()\" [noteIcon]=\"footerNoteIcon()\">\n @if (confirmLabel()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"dismiss.emit()\">\n {{ cancelLabel() }}\n </fk-button>\n <fk-button\n class=\"fk-dialog-shell__confirm\"\n [class.fk-dialog-shell__confirm--danger]=\"confirmVariant() === 'danger'\"\n [class.fk-dialog-shell__confirm--warning]=\"\n confirmVariant() === 'warning'\n \"\n [variant]=\"confirmButtonVariant()\"\n type=\"button\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"confirm.emit()\"\n >\n @if (confirmIcon()) {\n <fk-icon fkButtonIconStart [name]=\"confirmIcon()\" size=\"sm\" />\n }\n {{ confirmLabel() }}\n </fk-button>\n } @else {\n <ng-content select=\"[dialogFooter]\" />\n }\n </fk-dialog-footer>\n}\n", styles: [":host{display:flex;flex-direction:column}.fk-dialog-shell__body{margin-top:var(--fk-dialog-shell-body-margin-top, var(--fk-rhythm-4, 1rem))}.fk-dialog-shell__confirm--warning{--fk-button-danger-bg: var( --fk-dialog-confirm-warning-bg, var(--fk-color-warning, #d97706) );--fk-button-danger-bg-hover: var( --fk-dialog-confirm-warning-bg-hover, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-bg-active: var( --fk-dialog-confirm-warning-bg-active, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-color: var( --fk-dialog-confirm-warning-color, var(--fk-color-surface, #ffffff) );--fk-button-danger-border-color: transparent}\n"] }]
919
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tint: [{ type: i0.Input, args: [{ isSignal: true, alias: "tint", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], titleAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleAccent", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], footerNote: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerNote", required: false }] }], footerNoteIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerNoteIcon", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], confirmIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmIcon", required: false }] }], confirmVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmVariant", required: false }] }], confirmDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmDisabled", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], customFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "customFooter", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], confirm: [{ type: i0.Output, args: ["confirm"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
920
+ type: HostBinding,
921
+ args: ['class']
922
+ }], hostId: [{
923
+ type: HostBinding,
924
+ args: ['attr.id']
925
+ }] } });
926
+
648
927
  /**
649
928
  * Generated bundle index. Do not edit.
650
929
  */
651
930
 
652
- export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
931
+ export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
653
932
  //# sourceMappingURL=frame-kit-ui-ng-patterns-dashboard.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"frame-kit-ui-ng-patterns-dashboard.mjs","sources":["../../../../packages/ui-ng-patterns/dashboard/page-header/page-header.component.ts","../../../../packages/ui-ng-patterns/dashboard/page-header/page-header.component.html","../../../../packages/ui-ng-patterns/dashboard/table-empty-state/table-empty-state.component.ts","../../../../packages/ui-ng-patterns/dashboard/table-empty-state/table-empty-state.component.html","../../../../packages/ui-ng-patterns/dashboard/content-split-layout/content-split-layout.component.ts","../../../../packages/ui-ng-patterns/dashboard/content-split-layout/content-split-layout.component.html","../../../../packages/ui-ng-patterns/dashboard/columns/columns.component.ts","../../../../packages/ui-ng-patterns/dashboard/columns/columns.component.html","../../../../packages/ui-ng-patterns/dashboard/detail-panel/detail-panel.component.ts","../../../../packages/ui-ng-patterns/dashboard/detail-panel/detail-panel.component.html","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav-item.directive.ts","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav.component.ts","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav.component.html","../../../../packages/ui-ng-patterns/dashboard/frame-kit-ui-ng-patterns-dashboard.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport type { HeadlineLevel } from '@frame-kit/ui-ng/core/headline';\nimport { HeadlineComponent } from '@frame-kit/ui-ng/core/headline';\nimport { TextComponent } from '@frame-kit/ui-ng/core/text';\n\n/**\n * Page-level dashboard header: title + optional description on the left, up to\n * ~3 actions on the right.\n *\n * When the header's own width gets tight, the actions drop below the text and\n * go full-width (mobile-style). The break is driven by a CSS container query on\n * the host, so it responds to the content region's width — not the viewport,\n * which means it behaves correctly inside a narrow panel.\n *\n * Project actions by tagging each button with `pageHeaderActions`:\n *\n * <fk-page-header title=\"Users\" description=\"...\">\n * <fk-button pageHeaderActions variant=\"outline\" size=\"sm\">Import</fk-button>\n * <fk-button pageHeaderActions variant=\"primary\" size=\"sm\">Create user</fk-button>\n * </fk-page-header>\n *\n * Tagging each button (rather than wrapping them in a div) makes every button a\n * direct flex child, so full-width stacking falls out of `align-items: stretch`\n * with no `::ng-deep` reach-in.\n *\n * The title row has two projected slots — `[pageHeaderLeading]` before the\n * title and `[pageHeaderTrailing]` after it. Project a plain `fk-icon` for a\n * decorative mark, or a `button` for a clickable one (the consumer owns the\n * interaction, focus ring, and aria-label):\n *\n * <fk-page-header title=\"Permissions\">\n * <fk-icon pageHeaderLeading name=\"key-outline\" />\n * <button pageHeaderTrailing type=\"button\" aria-label=\"Docs\" (click)=\"openDocs()\">\n * <fk-icon name=\"view-api\" />\n * </button>\n * </fk-page-header>\n */\n@Component({\n selector: 'fk-page-header',\n standalone: true,\n imports: [HeadlineComponent, TextComponent],\n // Emulated (not None) so `:host` works — `:host { display: block }` is what\n // keeps the custom element from defaulting to `display: inline` and\n // collapsing to min-content under the container query below.\n encapsulation: ViewEncapsulation.Emulated,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './page-header.component.html',\n styleUrl: './page-header.component.scss',\n})\nexport class PageHeaderComponent {\n readonly title = input.required<string>();\n readonly description = input<string | null>(null);\n readonly headingLevel = input<HeadlineLevel>(1);\n}\n","<header class=\"fk-page-header\">\n <div class=\"fk-page-header__text\">\n <div class=\"fk-page-header__title-row\">\n <ng-content select=\"[pageHeaderLeading]\" />\n\n <fk-headline [level]=\"headingLevel()\">{{ title() }}</fk-headline>\n\n <ng-content select=\"[pageHeaderTrailing]\" />\n </div>\n\n @if (description()) {\n <fk-text class=\"fk-page-header__description\" tone=\"muted\">{{\n description()\n }}</fk-text>\n }\n </div>\n\n <div class=\"fk-page-header__actions\">\n <ng-content select=\"[pageHeaderActions]\" />\n </div>\n</header>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\n\n/**\n * Empty-state panel for dashboard tables and lists: a centered icon, a title,\n * an optional description, and a projected actions slot (typically one primary\n * fk-button). Bordered-card styling, meant to stand in for a table body when\n * there are no rows.\n *\n * The action row flips from a horizontal row to a centered vertical stack on\n * narrow widths, driven by a container query on the host — so it reacts to the\n * empty-state's own width (e.g. inside a split column) rather than the viewport.\n *\n * <fk-table-empty-state\n * icon=\"globe\"\n * title=\"No webhooks configured\"\n * description=\"Add a webhook to receive events.\"\n * >\n * <fk-button variant=\"primary\" size=\"sm\">Add webhook</fk-button>\n * </fk-table-empty-state>\n *\n * The actions slot is an open `ng-content`, so project as many actions as the\n * design needs (one primary is the norm; two reads cleanly).\n */\n@Component({\n selector: 'fk-table-empty-state',\n standalone: true,\n imports: [IconComponent],\n // Emulated (default) so `:host` works — the host is the bordered flex card\n // and establishes the container-query context the actions break against.\n encapsulation: ViewEncapsulation.Emulated,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './table-empty-state.component.html',\n styleUrl: './table-empty-state.component.scss',\n})\nexport class TableEmptyStateComponent {\n // ===== INPUTS =====\n readonly icon = input<string | null>(null);\n readonly title = input.required<string>();\n readonly description = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n ['fk-table-empty-state', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n}\n","@if (icon()) {\n <div class=\"fk-table-empty-state__icon\">\n <fk-icon [name]=\"icon()!\" size=\"lg\" />\n </div>\n}\n\n<h3 class=\"fk-table-empty-state__title\">{{ title() }}</h3>\n\n@if (description()) {\n <p class=\"fk-table-empty-state__description\">{{ description() }}</p>\n}\n\n<div class=\"fk-table-empty-state__actions\">\n <ng-content />\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Two-column split layout with container-query responsive stacking.\n *\n * Projects content into `[start]` (grows to fill) and `[end]` (content-sized or\n * fixed width). Below 700px container width, columns stack vertically.\n *\n * A standalone layout primitive for custom dashboard rows.\n */\n@Component({\n selector: 'fk-content-split-layout',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './content-split-layout.component.html',\n styleUrl: './content-split-layout.component.scss',\n})\nexport class ContentSplitLayoutComponent {\n readonly endWidth = input<string | null>(null);\n readonly gap = input<string>('var(--fk-rhythm-4, 1rem)');\n\n @HostBinding('class')\n readonly hostClass = 'fk-content-split-layout';\n}\n","<div\n class=\"fk-content-split-layout__row\"\n [style.--fk-content-split-gap]=\"gap()\"\n>\n <div class=\"fk-content-split-layout__start\">\n <ng-content select=\"[start]\" />\n </div>\n\n <div\n class=\"fk-content-split-layout__end\"\n [class.fk-content-split-layout__end--fixed]=\"endWidth()\"\n [style.--fk-content-split-end-width]=\"endWidth()\"\n >\n <ng-content select=\"[end]\" />\n </div>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Responsive column layout for dashboard content. Lays projected children out\n * in equal-width columns that wrap — and ultimately collapse to a single\n * stacked column — based on the layout's *own* available width, never the\n * viewport. So it reacts correctly when the dashboard content region is\n * squeezed (sidenav open, a drawer pushed in, a split pane) while the viewport\n * still reports \"desktop\".\n *\n * Implemented with an intrinsic CSS grid — `repeat(auto-fit, minmax(min, 1fr))`\n * — so there is no container query, no JS, and no viewport breakpoint to keep\n * in sync. `minColumnWidth` is the smallest a column may shrink to before the\n * grid drops a column; empty tracks collapse, so N children never produce more\n * than N columns. Spacing on both axes is a single `gutter` gap, so wrapped\n * rows and side-by-side columns separate consistently with no edge artifacts.\n *\n * <fk-columns minColumnWidth=\"18rem\">\n * <fk-field-group>…</fk-field-group>\n * <fk-field-group>…</fk-field-group>\n * </fk-columns>\n *\n * By default columns grow to fill the row (`1fr`). Set `maxColumnWidth` to cap\n * how wide a column may get — past that, columns stay that width and the extra\n * space is left empty (columns aligned to the start) rather than stretching.\n */\n@Component({\n selector: 'fk-columns',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './columns.component.html',\n styleUrl: './columns.component.scss',\n})\nexport class ColumnsComponent {\n // ===== INPUTS =====\n /**\n * The smallest width a column may shrink to before the grid drops to fewer\n * columns. This is the single knob that controls when columns wrap/stack.\n */\n readonly minColumnWidth = input<string>('16rem');\n /**\n * Optional cap on how wide a column may grow. Unset (default), columns fill\n * the row (`1fr`); when set (e.g. `'22rem'`), columns size between\n * `minColumnWidth` and this value and don't stretch past it — leftover space\n * stays empty, columns aligned to the start.\n */\n readonly maxColumnWidth = input<string | null>(null);\n /** Gap between side-by-side columns and between wrapped rows. */\n readonly gutter = input<string>('var(--fk-rhythm-4, 1rem)');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n ['fk-columns', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n @HostBinding('style.--fk-columns-min')\n get hostMin(): string {\n return this.minColumnWidth();\n }\n\n // Null leaves the property unset, so the SCSS falls back to `1fr` (fill).\n @HostBinding('style.--fk-columns-max')\n get hostMax(): string | null {\n return this.maxColumnWidth();\n }\n\n @HostBinding('style.--fk-columns-gutter')\n get hostGutter(): string {\n return this.gutter();\n }\n}\n","<ng-content />\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Padded container for an expanded dashboard detail region — typically the body\n * of a data-table row expansion, holding an `fk-columns` of form controls.\n *\n * The `disabled` input greys the panel and makes its entire subtree `inert`\n * (non-interactive, unfocusable, hidden from assistive technology) — for\n * controls gated behind a feature toggle that must not be editable until the\n * feature is enabled.\n *\n * <fk-detail-panel [disabled]=\"!feature.enabled\">\n * <fk-columns>…</fk-columns>\n * </fk-detail-panel>\n */\n@Component({\n selector: 'fk-detail-panel',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './detail-panel.component.html',\n styleUrl: './detail-panel.component.scss',\n})\nexport class DetailPanelComponent {\n // ===== INPUTS =====\n /**\n * Greys the panel and makes its content inert (locked) — for feature-gated\n * controls that should not be editable until the feature is enabled.\n */\n readonly disabled = input<boolean>(false);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n [\n 'fk-detail-panel',\n this.disabled() ? 'fk-detail-panel--disabled' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n /**\n * Lock the whole subtree when disabled. `inert` blocks pointer interaction,\n * focus, and assistive-tech access in one attribute — stronger and more\n * correct than `pointer-events: none`, which only blocks the mouse.\n */\n @HostBinding('attr.inert')\n get hostInert(): '' | null {\n return this.disabled() ? '' : null;\n }\n}\n","<ng-content />\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport type { SidebarNavLink } from './sidebar-nav.types';\n\n/**\n * Marks an `<ng-template>` whose content `fk-sidebar-nav` renders as an item's\n * leading content — e.g. a method badge or any custom adornment — receiving the\n * item as `$implicit`. The component still owns the row, label, disclosure,\n * active/branch state, collapse, and flyout; this only customizes what sits\n * before the label.\n *\n * The template is applied to any item whose `data` is set; items without `data`\n * fall back to their `icon`.\n *\n * <fk-sidebar-nav [model]=\"nav\">\n * <ng-template fkSidebarNavItem let-item>\n * <fk-method-badge [method]=\"item.data\" />\n * </ng-template>\n * </fk-sidebar-nav>\n */\n@Directive({\n selector: 'ng-template[fkSidebarNavItem]',\n standalone: true,\n})\nexport class SidebarNavItemDirective {\n readonly template =\n inject<TemplateRef<{ $implicit: SidebarNavLink }>>(TemplateRef);\n\n /** Types `let-item` as a `SidebarNavLink` in the consumer's template. */\n static ngTemplateContextGuard(\n _directive: SidebarNavItemDirective,\n _context: unknown,\n ): _context is { $implicit: SidebarNavLink } {\n return true;\n }\n}\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n effect,\n HostBinding,\n inject,\n Injector,\n input,\n signal,\n TemplateRef,\n viewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport {\n ActivatedRoute,\n NavigationEnd,\n Router,\n RouterLink,\n} from '@angular/router';\nimport { filter, map, Subscription } from 'rxjs';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\nimport { TooltipDirective } from '@frame-kit/ui-ng/directives/tooltip';\nimport { AppShellComponent } from '@frame-kit/ui-ng/layouts/app-shell';\n\nimport { SidebarNavItemDirective } from './sidebar-nav-item.directive';\nimport type {\n SidebarNavItem,\n SidebarNavLink,\n SidebarNavModel,\n} from './sidebar-nav.types';\n\n/**\n * Model-driven dashboard sidebar navigation.\n *\n * Renders a sidebar from a {@link SidebarNavModel}: labelled groups of items,\n * where each item is either a leaf link or a parent with children. The pattern\n * owns the two-state behavior that makes a dashboard rail usable:\n *\n * - **Expanded** — parents disclose their children inline; the section\n * containing the active route stays open and reads as active (tinted).\n * - **Collapsed (icon rail)** — labels collapse to icons with hover tooltips;\n * a parent click opens a **flyout** beside the rail (anchored via CDK\n * Overlay) listing its children, so nested pages stay reachable without\n * expanding the whole sidebar.\n *\n * Drop it into an `fk-app-shell` sidenav slot — it reads the shell's collapse\n * state automatically. Pass `[collapsed]` explicitly to drive the rail without\n * a shell.\n */\n@Component({\n selector: 'fk-sidebar-nav',\n standalone: true,\n imports: [RouterLink, IconComponent, TooltipDirective, NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './sidebar-nav.component.html',\n styleUrl: './sidebar-nav.component.scss',\n})\nexport class SidebarNavComponent {\n // ===== INPUTS =====\n /** The navigation model — ordered groups of items. */\n readonly model = input<SidebarNavModel>([]);\n /**\n * Collapsed (icon-rail) state. Left `null`, the component reads the collapse\n * state of an ancestor `fk-app-shell`. Set explicitly to drive the rail\n * without a shell present.\n */\n readonly collapsed = input<boolean | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== DEPENDENCIES =====\n private readonly shell = inject(AppShellComponent, { optional: true });\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly overlay = inject(Overlay);\n private readonly vcr = inject(ViewContainerRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly injector = inject(Injector);\n\n // ===== REFS =====\n private readonly flyoutTpl =\n viewChild.required<TemplateRef<unknown>>('flyoutTpl');\n /** Optional consumer template for an item's leading content (e.g. a badge). */\n private readonly itemTpl = contentChild(SidebarNavItemDirective);\n private readonly itemTemplate = computed(\n () => this.itemTpl()?.template ?? null,\n );\n\n /**\n * The leading-content template for an item, or null. Only items carrying\n * `data` use it; others fall back to their `icon`.\n */\n protected leadingTemplate(\n item: SidebarNavLink,\n ): TemplateRef<{ $implicit: SidebarNavLink }> | null {\n return item.data !== undefined && item.data !== null\n ? this.itemTemplate()\n : null;\n }\n\n // ===== STATE =====\n /** Active URL, recomputed on every navigation — drives section-active styling. */\n private readonly currentUrl = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n map(() => stripUrl(this.router.url)),\n ),\n { initialValue: stripUrl(this.router.url) },\n );\n /** Parent ids the user has explicitly opened (the active section is always open). */\n private readonly openIds = signal<ReadonlySet<string>>(new Set());\n\n private overlayRef: OverlayRef | null = null;\n /** Overlay-scoped event subscriptions — torn down with the overlay in closeFlyout(). */\n private flyoutSubs: Subscription | null = null;\n /** The trigger row the open flyout is anchored to — used to ignore its own re-click. */\n private flyoutOrigin: HTMLElement | null = null;\n /** The parent whose flyout is currently open (collapsed rail), or null. */\n protected readonly flyoutItem = signal<SidebarNavItem | null>(null);\n\n constructor() {\n // Tear down an open flyout the moment the rail expands. Clicking the shell\n // toggle would close it via the outside-pointer handler, but a programmatic\n // `[collapsed]` change, a responsive breakpoint flip, or a keyboard toggle\n // produces no pointer event — so the overlay would otherwise linger beside\n // the now-expanded sidebar.\n effect(() => {\n if (!this.isCollapsed()) {\n this.closeFlyout();\n }\n });\n\n // Close on any navigation (covers programmatic router.navigate, which\n // disposeOnNavigation does not catch, and keeps internal state in sync).\n effect(() => {\n this.currentUrl();\n this.closeFlyout();\n });\n\n // Auto-open the section that holds the active route — on first load and\n // whenever navigation lands inside a (possibly different, or previously\n // collapsed) section. This only ever *adds* to openIds, so a section the\n // user collapses by hand stays closed until they navigate back into it.\n effect(() => {\n this.currentUrl();\n\n const activeIds = this.model()\n .flatMap((group) => group.items)\n .filter((item) => this.isParent(item) && this.sectionActive(item))\n .map((item) => item.id);\n\n if (activeIds.length === 0) {\n return;\n }\n\n this.openIds.update((set) => {\n const next = new Set(set);\n activeIds.forEach((id) => next.add(id));\n\n return next;\n });\n });\n\n this.destroyRef.onDestroy(() => this.closeFlyout());\n }\n\n // ===== COMPUTED =====\n readonly isCollapsed = computed(\n () => this.collapsed() ?? this.shell?.isCollapsed() ?? false,\n );\n\n readonly classes = computed(() =>\n [\n 'fk-sidebar-nav',\n this.isCollapsed() ? 'fk-sidebar-nav--collapsed' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n // ===== ACTIVE / EXPANSION =====\n protected isParent(item: SidebarNavItem): boolean {\n return (item.children?.length ?? 0) > 0;\n }\n\n protected linkActive(link: SidebarNavLink): boolean {\n const routerLink = link.routerLink;\n\n if (!routerLink) {\n return false;\n }\n\n // Touch currentUrl so the check re-evaluates on every navigation (OnPush).\n this.currentUrl();\n\n // Resolve through the Router rather than string-joining: this handles array\n // commands, relative segments, leading slashes, and matrix/query params\n // correctly — a naive join silently mismatched for array routerLinks.\n const tree = this.router.createUrlTree(\n Array.isArray(routerLink) ? routerLink : [routerLink],\n { relativeTo: this.route },\n );\n\n return this.router.isActive(tree, {\n paths: link.exact ? 'exact' : 'subset',\n queryParams: 'ignored',\n fragment: 'ignored',\n matrixParams: 'ignored',\n });\n }\n\n /** True when the item itself, or any of its children, is the active route. */\n protected sectionActive(item: SidebarNavItem): boolean {\n if (this.linkActive(item)) {\n return true;\n }\n\n return (item.children ?? []).some((child) => this.linkActive(child));\n }\n\n /**\n * Whether a parent discloses its children inline (expanded rail only).\n * Disclosure is owned entirely by `openIds` — the active section is seeded\n * into it by the auto-open effect, so it starts open yet can still be\n * collapsed by clicking the parent (the effect only re-opens on navigation).\n */\n protected isOpen(item: SidebarNavItem): boolean {\n return this.openIds().has(item.id);\n }\n\n // ===== INTERACTIONS =====\n /** Parent row click: flyout in the rail, inline disclosure when expanded. */\n protected onParentClick(item: SidebarNavItem, rowEl: HTMLElement): void {\n if (this.isCollapsed()) {\n this.toggleFlyout(item, rowEl);\n\n return;\n }\n\n this.openIds.update((set) => {\n const next = new Set(set);\n\n if (next.has(item.id)) {\n next.delete(item.id);\n } else {\n next.add(item.id);\n }\n\n return next;\n });\n }\n\n /** Leaf / child / flyout link click — navigation is via routerLink; tidy up. */\n protected onLinkClick(): void {\n this.closeFlyout();\n this.shell?.dismissSidenav();\n }\n\n // ===== FLYOUT (collapsed parent) =====\n private toggleFlyout(item: SidebarNavItem, rowEl: HTMLElement): void {\n if (this.flyoutItem()?.id === item.id) {\n this.closeFlyout();\n\n return;\n }\n\n this.openFlyout(item, rowEl);\n }\n\n private openFlyout(item: SidebarNavItem, rowEl: HTMLElement): void {\n this.closeFlyout();\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(rowEl)\n // Nudge the panel back into the viewport when a low trigger row would push\n // its (now height-capped) box off the bottom edge.\n .withPush(true)\n .withPositions([\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'top',\n offsetX: 8,\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'top',\n offsetX: -8,\n },\n ]);\n\n const overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: this.overlay.scrollStrategies.reposition(),\n panelClass: 'fk-sidebar-nav__flyout',\n // Dispose on browser back/forward so the flyout can't linger over a new\n // page (matches fk-drawer). Programmatic navigation is covered by the\n // route-change effect in the constructor.\n disposeOnNavigation: true,\n });\n\n this.overlayRef = overlayRef;\n this.flyoutOrigin = rowEl;\n this.flyoutItem.set(item);\n overlayRef.attach(\n new TemplatePortal(this.flyoutTpl(), this.vcr, { $implicit: item }),\n );\n\n // After the panel renders: bring the active item into view (so a selection\n // deep in a long, scrolled list is visible on reopen, matching a native\n // select), and point the caret at the trigger's center now that CDK has\n // applied (and possibly pushed) the panel's final position.\n afterNextRender(\n () => {\n this.scrollActiveItemIntoView(overlayRef.overlayElement);\n this.positionFlyoutCaret(overlayRef.overlayElement);\n },\n { injector: this.injector },\n );\n\n // Scope the event subscriptions to this overlay's lifetime via a\n // Subscription torn down in closeFlyout() — not to the component lifetime,\n // and not relying on CDK completing the streams on dispose.\n const subscriptions = new Subscription();\n\n // Re-point the caret whenever CDK repositions the panel (push on open, or a\n // reposition while scrolling) so it keeps tracking the trigger's center.\n subscriptions.add(\n positionStrategy.positionChanges.subscribe(() =>\n this.positionFlyoutCaret(overlayRef.overlayElement),\n ),\n );\n\n subscriptions.add(\n overlayRef.outsidePointerEvents().subscribe((event) => {\n // Ignore clicks on the trigger row itself — the row's own click handler\n // toggles the flyout, so closing here would just let it reopen.\n const target = event.target as Node | null;\n\n if (target && this.flyoutOrigin?.contains(target)) {\n return;\n }\n\n this.closeFlyout();\n }),\n );\n\n subscriptions.add(\n overlayRef.keydownEvents().subscribe((event) => {\n if (event.key === 'Escape') {\n this.closeFlyout();\n }\n }),\n );\n\n this.flyoutSubs = subscriptions;\n }\n\n private closeFlyout(): void {\n this.flyoutSubs?.unsubscribe();\n this.flyoutSubs = null;\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.flyoutOrigin = null;\n this.flyoutItem.set(null);\n }\n\n /**\n * Center the active flyout item within its scroll viewport. No-op when there\n * is no active item (nothing selected) or the list isn't scrolled — the\n * `scrollTop` write clamps, so a short list simply stays at the top. Operates\n * only on the items container's own `scrollTop`, never `scrollIntoView`, so it\n * can't perturb the page or the overlay position.\n */\n private scrollActiveItemIntoView(overlayEl: HTMLElement): void {\n const container = overlayEl.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-items',\n );\n const active = container?.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-item--active',\n );\n\n if (!container || !active) {\n return;\n }\n\n const containerBox = container.getBoundingClientRect();\n const activeBox = active.getBoundingClientRect();\n const delta =\n activeBox.top -\n containerBox.top -\n (container.clientHeight - activeBox.height) / 2;\n\n container.scrollTop += delta;\n }\n\n /**\n * Point the flyout caret at the trigger row's vertical center. The caret is\n * absolutely positioned on the panel, so a fixed `top` drifts off the icon as\n * soon as `withPush` (or a flip) shifts the panel; deriving it from the live\n * trigger and panel boxes keeps it aligned. Clamped to the panel so it can't\n * ride off the rounded corners when the trigger sits beyond the panel's span.\n */\n private positionFlyoutCaret(overlayEl: HTMLElement): void {\n const origin = this.flyoutOrigin;\n const panel = overlayEl.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-panel',\n );\n\n if (!origin || !panel) {\n return;\n }\n\n const originBox = origin.getBoundingClientRect();\n const panelBox = panel.getBoundingClientRect();\n\n const caretSize = 11; // px — matches the ::before square\n const edge = 12; // keep the caret clear of the panel's rounded corners\n const center = originBox.top + originBox.height / 2 - panelBox.top;\n const top = Math.max(\n edge,\n Math.min(center - caretSize / 2, panelBox.height - edge - caretSize),\n );\n\n panel.style.setProperty('--fk-sidebar-nav-flyout-caret-top', `${top}px`);\n }\n}\n\n/** Strip query string and fragment so URL comparison matches route paths. */\nfunction stripUrl(url: string): string {\n return url.split('?')[0].split('#')[0];\n}\n\n","@for (group of model(); track $index) {\n <div class=\"fk-sidebar-nav__group\">\n @if (group.label) {\n <div class=\"fk-sidebar-nav__group-label\">{{ group.label }}</div>\n }\n <div class=\"fk-sidebar-nav__divider\" aria-hidden=\"true\"></div>\n\n <ul class=\"fk-sidebar-nav__list\">\n @for (item of group.items; track item.id) {\n <li class=\"fk-sidebar-nav__item\">\n @if (isParent(item)) {\n <!-- PARENT — disclosure row -->\n <button\n #row\n type=\"button\"\n class=\"fk-sidebar-nav__row fk-sidebar-nav__row--parent\"\n [class.fk-sidebar-nav__row--branch]=\"sectionActive(item)\"\n [attr.aria-expanded]=\"\n isCollapsed() ? flyoutItem()?.id === item.id : isOpen(item)\n \"\n [attr.aria-haspopup]=\"isCollapsed() ? 'true' : null\"\n [attr.aria-label]=\"isCollapsed() ? item.label : null\"\n [fkTooltip]=\"item.label\"\n [fkTooltipDisabled]=\"!isCollapsed()\"\n fkTooltipPosition=\"right\"\n (click)=\"onParentClick(item, row)\"\n >\n @if (leadingTemplate(item); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n } @else if (item.icon) {\n <fk-icon\n class=\"fk-sidebar-nav__icon\"\n [name]=\"item.icon\"\n size=\"sm\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ item.label }}</span>\n <span\n class=\"fk-sidebar-nav__chevron\"\n [class.fk-sidebar-nav__chevron--open]=\"isOpen(item)\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 18 18\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6.75 4.5L11.25 9L6.75 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n </button>\n\n @if (!isCollapsed() && isOpen(item)) {\n <ul class=\"fk-sidebar-nav__children\">\n @for (child of item.children; track child.id) {\n @if (child.heading) {\n <li class=\"fk-sidebar-nav__subgroup\">\n <span class=\"fk-sidebar-nav__subgroup-title\">{{\n child.label\n }}</span>\n </li>\n } @else {\n <li>\n <a\n class=\"fk-sidebar-nav__child\"\n [class.fk-sidebar-nav__child--active]=\"linkActive(child)\"\n [attr.aria-current]=\"linkActive(child) ? 'page' : null\"\n [routerLink]=\"child.routerLink\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(child); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: child }\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{\n child.label\n }}</span>\n </a>\n </li>\n }\n }\n </ul>\n }\n } @else {\n <!-- LEAF — direct link -->\n <a\n class=\"fk-sidebar-nav__row\"\n [class.fk-sidebar-nav__row--active]=\"linkActive(item)\"\n [attr.aria-current]=\"linkActive(item) ? 'page' : null\"\n [routerLink]=\"item.routerLink\"\n [attr.aria-label]=\"isCollapsed() ? item.label : null\"\n [fkTooltip]=\"item.label\"\n [fkTooltipDisabled]=\"!isCollapsed()\"\n fkTooltipPosition=\"right\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(item); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n } @else if (item.icon) {\n <fk-icon\n class=\"fk-sidebar-nav__icon\"\n [name]=\"item.icon\"\n size=\"sm\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ item.label }}</span>\n </a>\n }\n </li>\n }\n </ul>\n </div>\n}\n\n<!-- Flyout panel for a collapsed parent (anchored beside the rail via CDK Overlay) -->\n<ng-template #flyoutTpl let-item>\n <!--\n A labelled group of navigation links — not an ARIA `menu`. Menu semantics\n promise roving arrow-key focus we don't implement (these are Tab-navigated\n links), so `role=\"group\"` + `aria-label` is the honest, valid structure.\n -->\n <div\n class=\"fk-sidebar-nav__flyout-panel\"\n role=\"group\"\n [attr.aria-label]=\"item.label\"\n >\n <!-- Decorative: the group is already named by aria-label, so the visual\n title/icon would only repeat it to assistive tech. -->\n <div class=\"fk-sidebar-nav__flyout-head\" aria-hidden=\"true\">\n @if (item.icon) {\n <span class=\"fk-sidebar-nav__flyout-icon\">\n <fk-icon [name]=\"item.icon\" size=\"sm\" />\n </span>\n }\n <span class=\"fk-sidebar-nav__flyout-title\">{{ item.label }}</span>\n </div>\n\n <!--\n The items scroll inside their own bounded container, not the panel: the\n panel hosts the caret (::before) which sits outside its left edge, so\n putting overflow on the panel itself would clip it.\n -->\n <div class=\"fk-sidebar-nav__flyout-items\">\n @for (child of item.children; track child.id) {\n @if (child.heading) {\n <span class=\"fk-sidebar-nav__flyout-subgroup\">{{ child.label }}</span>\n } @else {\n <a\n class=\"fk-sidebar-nav__flyout-item\"\n [class.fk-sidebar-nav__flyout-item--active]=\"linkActive(child)\"\n [attr.aria-current]=\"linkActive(child) ? 'page' : null\"\n [routerLink]=\"child.routerLink\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(child); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: child }\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ child.label }}</span>\n </a>\n }\n }\n </div>\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"frame-kit-ui-ng-patterns-dashboard.mjs","sources":["../../../../packages/ui-ng-patterns/dashboard/page-header/page-header.component.ts","../../../../packages/ui-ng-patterns/dashboard/page-header/page-header.component.html","../../../../packages/ui-ng-patterns/dashboard/table-empty-state/table-empty-state.component.ts","../../../../packages/ui-ng-patterns/dashboard/table-empty-state/table-empty-state.component.html","../../../../packages/ui-ng-patterns/dashboard/content-split-layout/content-split-layout.component.ts","../../../../packages/ui-ng-patterns/dashboard/content-split-layout/content-split-layout.component.html","../../../../packages/ui-ng-patterns/dashboard/columns/columns.component.ts","../../../../packages/ui-ng-patterns/dashboard/columns/columns.component.html","../../../../packages/ui-ng-patterns/dashboard/detail-panel/detail-panel.component.ts","../../../../packages/ui-ng-patterns/dashboard/detail-panel/detail-panel.component.html","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav-item.directive.ts","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav.component.ts","../../../../packages/ui-ng-patterns/dashboard/sidebar-nav/sidebar-nav.component.html","../../../../packages/ui-ng-patterns/dashboard/dialog-footer/dialog-footer.component.ts","../../../../packages/ui-ng-patterns/dashboard/dialog-footer/dialog-footer.component.html","../../../../packages/ui-ng-patterns/dashboard/dialog-header/dialog-header.component.ts","../../../../packages/ui-ng-patterns/dashboard/dialog-header/dialog-header.component.html","../../../../packages/ui-ng-patterns/dashboard/dialog-shell/dialog-shell.component.ts","../../../../packages/ui-ng-patterns/dashboard/dialog-shell/dialog-shell.component.html","../../../../packages/ui-ng-patterns/dashboard/frame-kit-ui-ng-patterns-dashboard.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport type { HeadlineLevel } from '@frame-kit/ui-ng/core/headline';\nimport { HeadlineComponent } from '@frame-kit/ui-ng/core/headline';\nimport { TextComponent } from '@frame-kit/ui-ng/core/text';\n\n/**\n * Page-level dashboard header: title + optional description on the left, up to\n * ~3 actions on the right.\n *\n * When the header's own width gets tight, the actions drop below the text and\n * go full-width (mobile-style). The break is driven by a CSS container query on\n * the host, so it responds to the content region's width — not the viewport,\n * which means it behaves correctly inside a narrow panel.\n *\n * Project actions by tagging each button with `pageHeaderActions`:\n *\n * <fk-page-header title=\"Users\" description=\"...\">\n * <fk-button pageHeaderActions variant=\"outline\" size=\"sm\">Import</fk-button>\n * <fk-button pageHeaderActions variant=\"primary\" size=\"sm\">Create user</fk-button>\n * </fk-page-header>\n *\n * Tagging each button (rather than wrapping them in a div) makes every button a\n * direct flex child, so full-width stacking falls out of `align-items: stretch`\n * with no `::ng-deep` reach-in.\n *\n * The title row has two projected slots — `[pageHeaderLeading]` before the\n * title and `[pageHeaderTrailing]` after it. Project a plain `fk-icon` for a\n * decorative mark, or a `button` for a clickable one (the consumer owns the\n * interaction, focus ring, and aria-label):\n *\n * <fk-page-header title=\"Permissions\">\n * <fk-icon pageHeaderLeading name=\"key-outline\" />\n * <button pageHeaderTrailing type=\"button\" aria-label=\"Docs\" (click)=\"openDocs()\">\n * <fk-icon name=\"view-api\" />\n * </button>\n * </fk-page-header>\n */\n@Component({\n selector: 'fk-page-header',\n standalone: true,\n imports: [HeadlineComponent, TextComponent],\n // Emulated (not None) so `:host` works — `:host { display: block }` is what\n // keeps the custom element from defaulting to `display: inline` and\n // collapsing to min-content under the container query below.\n encapsulation: ViewEncapsulation.Emulated,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './page-header.component.html',\n styleUrl: './page-header.component.scss',\n})\nexport class PageHeaderComponent {\n readonly title = input.required<string>();\n readonly description = input<string | null>(null);\n readonly headingLevel = input<HeadlineLevel>(1);\n}\n","<header class=\"fk-page-header\">\n <div class=\"fk-page-header__text\">\n <div class=\"fk-page-header__title-row\">\n <ng-content select=\"[pageHeaderLeading]\" />\n\n <fk-headline [level]=\"headingLevel()\">{{ title() }}</fk-headline>\n\n <ng-content select=\"[pageHeaderTrailing]\" />\n </div>\n\n @if (description()) {\n <fk-text class=\"fk-page-header__description\" tone=\"muted\">{{\n description()\n }}</fk-text>\n }\n </div>\n\n <div class=\"fk-page-header__actions\">\n <ng-content select=\"[pageHeaderActions]\" />\n </div>\n</header>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\n\n/**\n * Empty-state panel for dashboard tables and lists: a centered icon, a title,\n * an optional description, and a projected actions slot (typically one primary\n * fk-button). Bordered-card styling, meant to stand in for a table body when\n * there are no rows.\n *\n * The action row flips from a horizontal row to a centered vertical stack on\n * narrow widths, driven by a container query on the host — so it reacts to the\n * empty-state's own width (e.g. inside a split column) rather than the viewport.\n *\n * <fk-table-empty-state\n * icon=\"globe\"\n * title=\"No webhooks configured\"\n * description=\"Add a webhook to receive events.\"\n * >\n * <fk-button variant=\"primary\" size=\"sm\">Add webhook</fk-button>\n * </fk-table-empty-state>\n *\n * The actions slot is an open `ng-content`, so project as many actions as the\n * design needs (one primary is the norm; two reads cleanly).\n */\n@Component({\n selector: 'fk-table-empty-state',\n standalone: true,\n imports: [IconComponent],\n // Emulated (default) so `:host` works — the host is the bordered flex card\n // and establishes the container-query context the actions break against.\n encapsulation: ViewEncapsulation.Emulated,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './table-empty-state.component.html',\n styleUrl: './table-empty-state.component.scss',\n})\nexport class TableEmptyStateComponent {\n // ===== INPUTS =====\n readonly icon = input<string | null>(null);\n readonly title = input.required<string>();\n readonly description = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n ['fk-table-empty-state', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n}\n","@if (icon()) {\n <div class=\"fk-table-empty-state__icon\">\n <fk-icon [name]=\"icon()!\" size=\"lg\" />\n </div>\n}\n\n<h3 class=\"fk-table-empty-state__title\">{{ title() }}</h3>\n\n@if (description()) {\n <p class=\"fk-table-empty-state__description\">{{ description() }}</p>\n}\n\n<div class=\"fk-table-empty-state__actions\">\n <ng-content />\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Two-column split layout with container-query responsive stacking.\n *\n * Projects content into `[start]` (grows to fill) and `[end]` (content-sized or\n * fixed width). Below 700px container width, columns stack vertically.\n *\n * A standalone layout primitive for custom dashboard rows.\n */\n@Component({\n selector: 'fk-content-split-layout',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './content-split-layout.component.html',\n styleUrl: './content-split-layout.component.scss',\n})\nexport class ContentSplitLayoutComponent {\n readonly endWidth = input<string | null>(null);\n readonly gap = input<string>('var(--fk-rhythm-4, 1rem)');\n\n @HostBinding('class')\n readonly hostClass = 'fk-content-split-layout';\n}\n","<div\n class=\"fk-content-split-layout__row\"\n [style.--fk-content-split-gap]=\"gap()\"\n>\n <div class=\"fk-content-split-layout__start\">\n <ng-content select=\"[start]\" />\n </div>\n\n <div\n class=\"fk-content-split-layout__end\"\n [class.fk-content-split-layout__end--fixed]=\"endWidth()\"\n [style.--fk-content-split-end-width]=\"endWidth()\"\n >\n <ng-content select=\"[end]\" />\n </div>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Responsive column layout for dashboard content. Lays projected children out\n * in equal-width columns that wrap — and ultimately collapse to a single\n * stacked column — based on the layout's *own* available width, never the\n * viewport. So it reacts correctly when the dashboard content region is\n * squeezed (sidenav open, a drawer pushed in, a split pane) while the viewport\n * still reports \"desktop\".\n *\n * Implemented with an intrinsic CSS grid — `repeat(auto-fit, minmax(min, 1fr))`\n * — so there is no container query, no JS, and no viewport breakpoint to keep\n * in sync. `minColumnWidth` is the smallest a column may shrink to before the\n * grid drops a column; empty tracks collapse, so N children never produce more\n * than N columns. Spacing on both axes is a single `gutter` gap, so wrapped\n * rows and side-by-side columns separate consistently with no edge artifacts.\n *\n * <fk-columns minColumnWidth=\"18rem\">\n * <fk-field-group>…</fk-field-group>\n * <fk-field-group>…</fk-field-group>\n * </fk-columns>\n *\n * By default columns grow to fill the row (`1fr`). Set `maxColumnWidth` to cap\n * how wide a column may get — past that, columns stay that width and the extra\n * space is left empty (columns aligned to the start) rather than stretching.\n */\n@Component({\n selector: 'fk-columns',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './columns.component.html',\n styleUrl: './columns.component.scss',\n})\nexport class ColumnsComponent {\n // ===== INPUTS =====\n /**\n * The smallest width a column may shrink to before the grid drops to fewer\n * columns. This is the single knob that controls when columns wrap/stack.\n */\n readonly minColumnWidth = input<string>('16rem');\n /**\n * Optional cap on how wide a column may grow. Unset (default), columns fill\n * the row (`1fr`); when set (e.g. `'22rem'`), columns size between\n * `minColumnWidth` and this value and don't stretch past it — leftover space\n * stays empty, columns aligned to the start.\n */\n readonly maxColumnWidth = input<string | null>(null);\n /** Gap between side-by-side columns and between wrapped rows. */\n readonly gutter = input<string>('var(--fk-rhythm-4, 1rem)');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n ['fk-columns', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n @HostBinding('style.--fk-columns-min')\n get hostMin(): string {\n return this.minColumnWidth();\n }\n\n // Null leaves the property unset, so the SCSS falls back to `1fr` (fill).\n @HostBinding('style.--fk-columns-max')\n get hostMax(): string | null {\n return this.maxColumnWidth();\n }\n\n @HostBinding('style.--fk-columns-gutter')\n get hostGutter(): string {\n return this.gutter();\n }\n}\n","<ng-content />\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\n/**\n * Padded container for an expanded dashboard detail region — typically the body\n * of a data-table row expansion, holding an `fk-columns` of form controls.\n *\n * The `disabled` input greys the panel and makes its entire subtree `inert`\n * (non-interactive, unfocusable, hidden from assistive technology) — for\n * controls gated behind a feature toggle that must not be editable until the\n * feature is enabled.\n *\n * <fk-detail-panel [disabled]=\"!feature.enabled\">\n * <fk-columns>…</fk-columns>\n * </fk-detail-panel>\n */\n@Component({\n selector: 'fk-detail-panel',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './detail-panel.component.html',\n styleUrl: './detail-panel.component.scss',\n})\nexport class DetailPanelComponent {\n // ===== INPUTS =====\n /**\n * Greys the panel and makes its content inert (locked) — for feature-gated\n * controls that should not be editable until the feature is enabled.\n */\n readonly disabled = input<boolean>(false);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n [\n 'fk-detail-panel',\n this.disabled() ? 'fk-detail-panel--disabled' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n /**\n * Lock the whole subtree when disabled. `inert` blocks pointer interaction,\n * focus, and assistive-tech access in one attribute — stronger and more\n * correct than `pointer-events: none`, which only blocks the mouse.\n */\n @HostBinding('attr.inert')\n get hostInert(): '' | null {\n return this.disabled() ? '' : null;\n }\n}\n","<ng-content />\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport type { SidebarNavLink } from './sidebar-nav.types';\n\n/**\n * Marks an `<ng-template>` whose content `fk-sidebar-nav` renders as an item's\n * leading content — e.g. a method badge or any custom adornment — receiving the\n * item as `$implicit`. The component still owns the row, label, disclosure,\n * active/branch state, collapse, and flyout; this only customizes what sits\n * before the label.\n *\n * The template is applied to any item whose `data` is set; items without `data`\n * fall back to their `icon`.\n *\n * <fk-sidebar-nav [model]=\"nav\">\n * <ng-template fkSidebarNavItem let-item>\n * <fk-method-badge [method]=\"item.data\" />\n * </ng-template>\n * </fk-sidebar-nav>\n */\n@Directive({\n selector: 'ng-template[fkSidebarNavItem]',\n standalone: true,\n})\nexport class SidebarNavItemDirective {\n readonly template =\n inject<TemplateRef<{ $implicit: SidebarNavLink }>>(TemplateRef);\n\n /** Types `let-item` as a `SidebarNavLink` in the consumer's template. */\n static ngTemplateContextGuard(\n _directive: SidebarNavItemDirective,\n _context: unknown,\n ): _context is { $implicit: SidebarNavLink } {\n return true;\n }\n}\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n effect,\n HostBinding,\n inject,\n Injector,\n input,\n signal,\n TemplateRef,\n viewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport {\n ActivatedRoute,\n NavigationEnd,\n Router,\n RouterLink,\n} from '@angular/router';\nimport { filter, map, Subscription } from 'rxjs';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\nimport { TooltipDirective } from '@frame-kit/ui-ng/directives/tooltip';\nimport { AppShellComponent } from '@frame-kit/ui-ng/layouts/app-shell';\n\nimport { SidebarNavItemDirective } from './sidebar-nav-item.directive';\nimport type {\n SidebarNavItem,\n SidebarNavLink,\n SidebarNavModel,\n} from './sidebar-nav.types';\n\n/**\n * Model-driven dashboard sidebar navigation.\n *\n * Renders a sidebar from a {@link SidebarNavModel}: labelled groups of items,\n * where each item is either a leaf link or a parent with children. The pattern\n * owns the two-state behavior that makes a dashboard rail usable:\n *\n * - **Expanded** — parents disclose their children inline; the section\n * containing the active route stays open and reads as active (tinted).\n * - **Collapsed (icon rail)** — labels collapse to icons with hover tooltips;\n * a parent click opens a **flyout** beside the rail (anchored via CDK\n * Overlay) listing its children, so nested pages stay reachable without\n * expanding the whole sidebar.\n *\n * Drop it into an `fk-app-shell` sidenav slot — it reads the shell's collapse\n * state automatically. Pass `[collapsed]` explicitly to drive the rail without\n * a shell.\n */\n@Component({\n selector: 'fk-sidebar-nav',\n standalone: true,\n imports: [RouterLink, IconComponent, TooltipDirective, NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './sidebar-nav.component.html',\n styleUrl: './sidebar-nav.component.scss',\n})\nexport class SidebarNavComponent {\n // ===== INPUTS =====\n /** The navigation model — ordered groups of items. */\n readonly model = input<SidebarNavModel>([]);\n /**\n * Collapsed (icon-rail) state. Left `null`, the component reads the collapse\n * state of an ancestor `fk-app-shell`. Set explicitly to drive the rail\n * without a shell present.\n */\n readonly collapsed = input<boolean | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== DEPENDENCIES =====\n private readonly shell = inject(AppShellComponent, { optional: true });\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly overlay = inject(Overlay);\n private readonly vcr = inject(ViewContainerRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly injector = inject(Injector);\n\n // ===== REFS =====\n private readonly flyoutTpl =\n viewChild.required<TemplateRef<unknown>>('flyoutTpl');\n /** Optional consumer template for an item's leading content (e.g. a badge). */\n private readonly itemTpl = contentChild(SidebarNavItemDirective);\n private readonly itemTemplate = computed(\n () => this.itemTpl()?.template ?? null,\n );\n\n /**\n * The leading-content template for an item, or null. Only items carrying\n * `data` use it; others fall back to their `icon`.\n */\n protected leadingTemplate(\n item: SidebarNavLink,\n ): TemplateRef<{ $implicit: SidebarNavLink }> | null {\n return item.data !== undefined && item.data !== null\n ? this.itemTemplate()\n : null;\n }\n\n // ===== STATE =====\n /** Active URL, recomputed on every navigation — drives section-active styling. */\n private readonly currentUrl = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n map(() => stripUrl(this.router.url)),\n ),\n { initialValue: stripUrl(this.router.url) },\n );\n /** Parent ids the user has explicitly opened (the active section is always open). */\n private readonly openIds = signal<ReadonlySet<string>>(new Set());\n\n private overlayRef: OverlayRef | null = null;\n /** Overlay-scoped event subscriptions — torn down with the overlay in closeFlyout(). */\n private flyoutSubs: Subscription | null = null;\n /** The trigger row the open flyout is anchored to — used to ignore its own re-click. */\n private flyoutOrigin: HTMLElement | null = null;\n /** The parent whose flyout is currently open (collapsed rail), or null. */\n protected readonly flyoutItem = signal<SidebarNavItem | null>(null);\n\n constructor() {\n // Tear down an open flyout the moment the rail expands. Clicking the shell\n // toggle would close it via the outside-pointer handler, but a programmatic\n // `[collapsed]` change, a responsive breakpoint flip, or a keyboard toggle\n // produces no pointer event — so the overlay would otherwise linger beside\n // the now-expanded sidebar.\n effect(() => {\n if (!this.isCollapsed()) {\n this.closeFlyout();\n }\n });\n\n // Close on any navigation (covers programmatic router.navigate, which\n // disposeOnNavigation does not catch, and keeps internal state in sync).\n effect(() => {\n this.currentUrl();\n this.closeFlyout();\n });\n\n // Auto-open the section that holds the active route — on first load and\n // whenever navigation lands inside a (possibly different, or previously\n // collapsed) section. This only ever *adds* to openIds, so a section the\n // user collapses by hand stays closed until they navigate back into it.\n effect(() => {\n this.currentUrl();\n\n const activeIds = this.model()\n .flatMap((group) => group.items)\n .filter((item) => this.isParent(item) && this.sectionActive(item))\n .map((item) => item.id);\n\n if (activeIds.length === 0) {\n return;\n }\n\n this.openIds.update((set) => {\n const next = new Set(set);\n activeIds.forEach((id) => next.add(id));\n\n return next;\n });\n });\n\n this.destroyRef.onDestroy(() => this.closeFlyout());\n }\n\n // ===== COMPUTED =====\n readonly isCollapsed = computed(\n () => this.collapsed() ?? this.shell?.isCollapsed() ?? false,\n );\n\n readonly classes = computed(() =>\n [\n 'fk-sidebar-nav',\n this.isCollapsed() ? 'fk-sidebar-nav--collapsed' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n // ===== ACTIVE / EXPANSION =====\n protected isParent(item: SidebarNavItem): boolean {\n return (item.children?.length ?? 0) > 0;\n }\n\n protected linkActive(link: SidebarNavLink): boolean {\n const routerLink = link.routerLink;\n\n if (!routerLink) {\n return false;\n }\n\n // Touch currentUrl so the check re-evaluates on every navigation (OnPush).\n this.currentUrl();\n\n // Resolve through the Router rather than string-joining: this handles array\n // commands, relative segments, leading slashes, and matrix/query params\n // correctly — a naive join silently mismatched for array routerLinks.\n const tree = this.router.createUrlTree(\n Array.isArray(routerLink) ? routerLink : [routerLink],\n { relativeTo: this.route },\n );\n\n return this.router.isActive(tree, {\n paths: link.exact ? 'exact' : 'subset',\n queryParams: 'ignored',\n fragment: 'ignored',\n matrixParams: 'ignored',\n });\n }\n\n /** True when the item itself, or any of its children, is the active route. */\n protected sectionActive(item: SidebarNavItem): boolean {\n if (this.linkActive(item)) {\n return true;\n }\n\n return (item.children ?? []).some((child) => this.linkActive(child));\n }\n\n /**\n * Whether a parent discloses its children inline (expanded rail only).\n * Disclosure is owned entirely by `openIds` — the active section is seeded\n * into it by the auto-open effect, so it starts open yet can still be\n * collapsed by clicking the parent (the effect only re-opens on navigation).\n */\n protected isOpen(item: SidebarNavItem): boolean {\n return this.openIds().has(item.id);\n }\n\n // ===== INTERACTIONS =====\n /** Parent row click: flyout in the rail, inline disclosure when expanded. */\n protected onParentClick(item: SidebarNavItem, rowEl: HTMLElement): void {\n if (this.isCollapsed()) {\n this.toggleFlyout(item, rowEl);\n\n return;\n }\n\n this.openIds.update((set) => {\n const next = new Set(set);\n\n if (next.has(item.id)) {\n next.delete(item.id);\n } else {\n next.add(item.id);\n }\n\n return next;\n });\n }\n\n /** Leaf / child / flyout link click — navigation is via routerLink; tidy up. */\n protected onLinkClick(): void {\n this.closeFlyout();\n this.shell?.dismissSidenav();\n }\n\n // ===== FLYOUT (collapsed parent) =====\n private toggleFlyout(item: SidebarNavItem, rowEl: HTMLElement): void {\n if (this.flyoutItem()?.id === item.id) {\n this.closeFlyout();\n\n return;\n }\n\n this.openFlyout(item, rowEl);\n }\n\n private openFlyout(item: SidebarNavItem, rowEl: HTMLElement): void {\n this.closeFlyout();\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(rowEl)\n // Nudge the panel back into the viewport when a low trigger row would push\n // its (now height-capped) box off the bottom edge.\n .withPush(true)\n .withPositions([\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'top',\n offsetX: 8,\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'top',\n offsetX: -8,\n },\n ]);\n\n const overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: this.overlay.scrollStrategies.reposition(),\n panelClass: 'fk-sidebar-nav__flyout',\n // Dispose on browser back/forward so the flyout can't linger over a new\n // page (matches fk-drawer). Programmatic navigation is covered by the\n // route-change effect in the constructor.\n disposeOnNavigation: true,\n });\n\n this.overlayRef = overlayRef;\n this.flyoutOrigin = rowEl;\n this.flyoutItem.set(item);\n overlayRef.attach(\n new TemplatePortal(this.flyoutTpl(), this.vcr, { $implicit: item }),\n );\n\n // After the panel renders: bring the active item into view (so a selection\n // deep in a long, scrolled list is visible on reopen, matching a native\n // select), and point the caret at the trigger's center now that CDK has\n // applied (and possibly pushed) the panel's final position.\n afterNextRender(\n () => {\n this.scrollActiveItemIntoView(overlayRef.overlayElement);\n this.positionFlyoutCaret(overlayRef.overlayElement);\n },\n { injector: this.injector },\n );\n\n // Scope the event subscriptions to this overlay's lifetime via a\n // Subscription torn down in closeFlyout() — not to the component lifetime,\n // and not relying on CDK completing the streams on dispose.\n const subscriptions = new Subscription();\n\n // Re-point the caret whenever CDK repositions the panel (push on open, or a\n // reposition while scrolling) so it keeps tracking the trigger's center.\n subscriptions.add(\n positionStrategy.positionChanges.subscribe(() =>\n this.positionFlyoutCaret(overlayRef.overlayElement),\n ),\n );\n\n subscriptions.add(\n overlayRef.outsidePointerEvents().subscribe((event) => {\n // Ignore clicks on the trigger row itself — the row's own click handler\n // toggles the flyout, so closing here would just let it reopen.\n const target = event.target as Node | null;\n\n if (target && this.flyoutOrigin?.contains(target)) {\n return;\n }\n\n this.closeFlyout();\n }),\n );\n\n subscriptions.add(\n overlayRef.keydownEvents().subscribe((event) => {\n if (event.key === 'Escape') {\n this.closeFlyout();\n }\n }),\n );\n\n this.flyoutSubs = subscriptions;\n }\n\n private closeFlyout(): void {\n this.flyoutSubs?.unsubscribe();\n this.flyoutSubs = null;\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.flyoutOrigin = null;\n this.flyoutItem.set(null);\n }\n\n /**\n * Center the active flyout item within its scroll viewport. No-op when there\n * is no active item (nothing selected) or the list isn't scrolled — the\n * `scrollTop` write clamps, so a short list simply stays at the top. Operates\n * only on the items container's own `scrollTop`, never `scrollIntoView`, so it\n * can't perturb the page or the overlay position.\n */\n private scrollActiveItemIntoView(overlayEl: HTMLElement): void {\n const container = overlayEl.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-items',\n );\n const active = container?.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-item--active',\n );\n\n if (!container || !active) {\n return;\n }\n\n const containerBox = container.getBoundingClientRect();\n const activeBox = active.getBoundingClientRect();\n const delta =\n activeBox.top -\n containerBox.top -\n (container.clientHeight - activeBox.height) / 2;\n\n container.scrollTop += delta;\n }\n\n /**\n * Point the flyout caret at the trigger row's vertical center. The caret is\n * absolutely positioned on the panel, so a fixed `top` drifts off the icon as\n * soon as `withPush` (or a flip) shifts the panel; deriving it from the live\n * trigger and panel boxes keeps it aligned. Clamped to the panel so it can't\n * ride off the rounded corners when the trigger sits beyond the panel's span.\n */\n private positionFlyoutCaret(overlayEl: HTMLElement): void {\n const origin = this.flyoutOrigin;\n const panel = overlayEl.querySelector<HTMLElement>(\n '.fk-sidebar-nav__flyout-panel',\n );\n\n if (!origin || !panel) {\n return;\n }\n\n const originBox = origin.getBoundingClientRect();\n const panelBox = panel.getBoundingClientRect();\n\n const caretSize = 11; // px — matches the ::before square\n const edge = 12; // keep the caret clear of the panel's rounded corners\n const center = originBox.top + originBox.height / 2 - panelBox.top;\n const top = Math.max(\n edge,\n Math.min(center - caretSize / 2, panelBox.height - edge - caretSize),\n );\n\n panel.style.setProperty('--fk-sidebar-nav-flyout-caret-top', `${top}px`);\n }\n}\n\n/** Strip query string and fragment so URL comparison matches route paths. */\nfunction stripUrl(url: string): string {\n return url.split('?')[0].split('#')[0];\n}\n\n","@for (group of model(); track $index) {\n <div class=\"fk-sidebar-nav__group\">\n @if (group.label) {\n <div class=\"fk-sidebar-nav__group-label\">{{ group.label }}</div>\n }\n <div class=\"fk-sidebar-nav__divider\" aria-hidden=\"true\"></div>\n\n <ul class=\"fk-sidebar-nav__list\">\n @for (item of group.items; track item.id) {\n <li class=\"fk-sidebar-nav__item\">\n @if (isParent(item)) {\n <!-- PARENT — disclosure row -->\n <button\n #row\n type=\"button\"\n class=\"fk-sidebar-nav__row fk-sidebar-nav__row--parent\"\n [class.fk-sidebar-nav__row--branch]=\"sectionActive(item)\"\n [attr.aria-expanded]=\"\n isCollapsed() ? flyoutItem()?.id === item.id : isOpen(item)\n \"\n [attr.aria-haspopup]=\"isCollapsed() ? 'true' : null\"\n [attr.aria-label]=\"isCollapsed() ? item.label : null\"\n [fkTooltip]=\"item.label\"\n [fkTooltipDisabled]=\"!isCollapsed()\"\n fkTooltipPosition=\"right\"\n (click)=\"onParentClick(item, row)\"\n >\n @if (leadingTemplate(item); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n } @else if (item.icon) {\n <fk-icon\n class=\"fk-sidebar-nav__icon\"\n [name]=\"item.icon\"\n size=\"sm\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ item.label }}</span>\n <span\n class=\"fk-sidebar-nav__chevron\"\n [class.fk-sidebar-nav__chevron--open]=\"isOpen(item)\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 18 18\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6.75 4.5L11.25 9L6.75 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n </button>\n\n @if (!isCollapsed() && isOpen(item)) {\n <ul class=\"fk-sidebar-nav__children\">\n @for (child of item.children; track child.id) {\n @if (child.heading) {\n <li class=\"fk-sidebar-nav__subgroup\">\n <span class=\"fk-sidebar-nav__subgroup-title\">{{\n child.label\n }}</span>\n </li>\n } @else {\n <li>\n <a\n class=\"fk-sidebar-nav__child\"\n [class.fk-sidebar-nav__child--active]=\"linkActive(child)\"\n [attr.aria-current]=\"linkActive(child) ? 'page' : null\"\n [routerLink]=\"child.routerLink\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(child); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: child }\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{\n child.label\n }}</span>\n </a>\n </li>\n }\n }\n </ul>\n }\n } @else {\n <!-- LEAF — direct link -->\n <a\n class=\"fk-sidebar-nav__row\"\n [class.fk-sidebar-nav__row--active]=\"linkActive(item)\"\n [attr.aria-current]=\"linkActive(item) ? 'page' : null\"\n [routerLink]=\"item.routerLink\"\n [attr.aria-label]=\"isCollapsed() ? item.label : null\"\n [fkTooltip]=\"item.label\"\n [fkTooltipDisabled]=\"!isCollapsed()\"\n fkTooltipPosition=\"right\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(item); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n } @else if (item.icon) {\n <fk-icon\n class=\"fk-sidebar-nav__icon\"\n [name]=\"item.icon\"\n size=\"sm\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ item.label }}</span>\n </a>\n }\n </li>\n }\n </ul>\n </div>\n}\n\n<!-- Flyout panel for a collapsed parent (anchored beside the rail via CDK Overlay) -->\n<ng-template #flyoutTpl let-item>\n <!--\n A labelled group of navigation links — not an ARIA `menu`. Menu semantics\n promise roving arrow-key focus we don't implement (these are Tab-navigated\n links), so `role=\"group\"` + `aria-label` is the honest, valid structure.\n -->\n <div\n class=\"fk-sidebar-nav__flyout-panel\"\n role=\"group\"\n [attr.aria-label]=\"item.label\"\n >\n <!-- Decorative: the group is already named by aria-label, so the visual\n title/icon would only repeat it to assistive tech. -->\n <div class=\"fk-sidebar-nav__flyout-head\" aria-hidden=\"true\">\n @if (item.icon) {\n <span class=\"fk-sidebar-nav__flyout-icon\">\n <fk-icon [name]=\"item.icon\" size=\"sm\" />\n </span>\n }\n <span class=\"fk-sidebar-nav__flyout-title\">{{ item.label }}</span>\n </div>\n\n <!--\n The items scroll inside their own bounded container, not the panel: the\n panel hosts the caret (::before) which sits outside its left edge, so\n putting overflow on the panel itself would clip it.\n -->\n <div class=\"fk-sidebar-nav__flyout-items\">\n @for (child of item.children; track child.id) {\n @if (child.heading) {\n <span class=\"fk-sidebar-nav__flyout-subgroup\">{{ child.label }}</span>\n } @else {\n <a\n class=\"fk-sidebar-nav__flyout-item\"\n [class.fk-sidebar-nav__flyout-item--active]=\"linkActive(child)\"\n [attr.aria-current]=\"linkActive(child) ? 'page' : null\"\n [routerLink]=\"child.routerLink\"\n (click)=\"onLinkClick()\"\n >\n @if (leadingTemplate(child); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: child }\"\n />\n }\n <span class=\"fk-sidebar-nav__label\">{{ child.label }}</span>\n </a>\n }\n }\n </div>\n </div>\n</ng-template>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\n\n/**\n * The fixed footer chrome for {@link DialogShellComponent} — an optional muted\n * note on the left, then the projected action buttons pushed to the right,\n * above a top divider. Paired with {@link DialogHeaderComponent} so every\n * dialog shares the same chrome.\n *\n * The note icon is optional and degrades gracefully — an empty `noteIcon`\n * simply renders no glyph.\n *\n * ```html\n * <fk-dialog-footer note=\"Takes effect immediately\" noteIcon=\"power\">\n * <fk-button variant=\"outline\" (click)=\"cancel()\">Cancel</fk-button>\n * <fk-button variant=\"primary\" type=\"submit\">Save</fk-button>\n * </fk-dialog-footer>\n * ```\n */\n@Component({\n selector: 'fk-dialog-footer',\n standalone: true,\n imports: [IconComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './dialog-footer.component.html',\n styleUrl: './dialog-footer.component.scss',\n})\nexport class DialogFooterComponent {\n // ===== INPUTS =====\n /** Optional muted note shown on the left (e.g. \"Takes effect immediately\"). */\n readonly note = input<string>('');\n /** Optional icon glyph rendered before the note. Empty renders no glyph. */\n readonly noteIcon = input<string>('');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() =>\n ['fk-dialog-footer', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n}\n","@if (note()) {\n <span class=\"fk-dialog-footer__note\">\n @if (noteIcon()) {\n <fk-icon\n class=\"fk-dialog-footer__note-icon\"\n [name]=\"noteIcon()\"\n size=\"sm\"\n />\n }\n {{ note() }}\n </span>\n}\n\n<span class=\"fk-dialog-footer__spacer\"></span>\n\n<ng-content />\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n output,\n} from '@angular/core';\n\nimport { HeadlineComponent } from '@frame-kit/ui-ng/core/headline';\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\nimport { TextComponent } from '@frame-kit/ui-ng/core/text';\nimport { IconBadgeComponent } from '@frame-kit/ui-ng/ui/icon-badge';\n\nimport type { DialogHeaderTint } from './dialog-header.types';\n\n/** The tint tone → the CSS-variable pair driving the icon tile + title accent.\n * Each reference is two-tier (component token → semantic token) so the header\n * renders correctly with no theme loaded and can be retinted per theme. */\nconst TINTS: Record<DialogHeaderTint, { color: string; background: string }> = {\n brand: {\n color:\n 'var(--fk-dialog-header-tint-brand-color, var(--fk-color-primary, #2563eb))',\n background:\n 'var(--fk-dialog-header-tint-brand-bg, var(--fk-color-primary-light, #eff6ff))',\n },\n warning: {\n color:\n 'var(--fk-dialog-header-tint-warning-color, var(--fk-color-warning, #d97706))',\n background:\n 'var(--fk-dialog-header-tint-warning-bg, var(--fk-color-warning-subtle, #fffbeb))',\n },\n success: {\n color:\n 'var(--fk-dialog-header-tint-success-color, var(--fk-color-success, #16a34a))',\n background:\n 'var(--fk-dialog-header-tint-success-bg, var(--fk-color-success-subtle, #f0fdf4))',\n },\n danger: {\n color:\n 'var(--fk-dialog-header-tint-danger-color, var(--fk-color-danger, #dc2626))',\n background:\n 'var(--fk-dialog-header-tint-danger-bg, var(--fk-color-danger-light, #fee2e2))',\n },\n accent: {\n color: 'var(--fk-dialog-header-tint-accent-color, #7c3aed)',\n background: 'var(--fk-dialog-header-tint-accent-bg, #f5f3ff)',\n },\n};\n\n/**\n * The fixed header chrome for {@link DialogShellComponent} — a tinted icon\n * tile, a title with an optional trailing accent, an optional mono subtitle,\n * and a close button. Every dialog wears the same chrome; only the icon, tint,\n * and text differ, so this centralises it instead of each dialog hand-rolling\n * the markup.\n *\n * Text is passed in as plain inputs (no i18n dependency), and the icon is\n * optional — an empty `icon` simply renders no tile, so the header degrades\n * gracefully rather than showing a broken glyph.\n *\n * ```html\n * <fk-dialog-header\n * icon=\"user-ban\"\n * tint=\"warning\"\n * title=\"Deactivate identity\"\n * subtitle=\"Acme Inc. · Members\"\n * (dismiss)=\"close()\"\n * />\n * ```\n */\n@Component({\n selector: 'fk-dialog-header',\n standalone: true,\n imports: [\n HeadlineComponent,\n IconBadgeComponent,\n IconComponent,\n TextComponent,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './dialog-header.component.html',\n styleUrl: './dialog-header.component.scss',\n})\nexport class DialogHeaderComponent {\n // ===== INPUTS =====\n /** Icon glyph name for the tile. Empty renders no tile. */\n readonly icon = input<string>('');\n /** Colour accent for the icon tile + title accent. */\n readonly tint = input<DialogHeaderTint>('brand');\n /** Dialog title. */\n readonly title = input.required<string>();\n /** Optional trailing accent appended to the title, rendered in the tint colour. */\n readonly titleAccent = input<string>('');\n /** Optional mono subtitle under the title. */\n readonly subtitle = input<string>('');\n /** Accessible label for the close button. */\n readonly closeLabel = input<string>('Close');\n\n // ===== OUTPUTS =====\n /** Fires when the close button is clicked. */\n readonly dismiss = output<void>();\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n /** The tint's `{ color, background }` CSS-variable pair. */\n protected readonly tintVars = computed(() => TINTS[this.tint()]);\n\n readonly classes = computed(() =>\n ['fk-dialog-header', `fk-dialog-header--${this.tint()}`, this.className()]\n .filter(Boolean)\n .join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n}\n","@if (icon()) {\n <fk-icon-badge\n class=\"fk-dialog-header__icon\"\n [name]=\"icon()\"\n [color]=\"tintVars().color\"\n [backgroundColor]=\"tintVars().background\"\n shape=\"rounded\"\n size=\"md\"\n />\n}\n\n<div class=\"fk-dialog-header__titles\">\n <fk-headline\n class=\"fk-dialog-header__title\"\n [level]=\"2\"\n fontSize=\"var(--fk-dialog-header-title-font-size, 1.25rem)\"\n >\n {{ title() }}\n @if (titleAccent()) {\n <span class=\"fk-dialog-header__accent\" [style.color]=\"tintVars().color\">\n {{ titleAccent() }}</span\n >\n }\n </fk-headline>\n @if (subtitle()) {\n <fk-text class=\"fk-dialog-header__subtitle\" variant=\"small\">\n {{ subtitle() }}\n </fk-text>\n }\n</div>\n\n<button\n type=\"button\"\n class=\"fk-dialog-header__close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss.emit()\"\n>\n <fk-icon name=\"close\" size=\"sm\" />\n</button>\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n output,\n} from '@angular/core';\n\nimport { IconComponent } from '@frame-kit/ui-ng/core/icon';\nimport { ButtonComponent } from '@frame-kit/ui-ng/ui/button';\n\nimport { DialogFooterComponent } from '../dialog-footer/dialog-footer.component';\nimport type { DialogHeaderTint } from '../dialog-header/dialog-header.types';\nimport { DialogHeaderComponent } from '../dialog-header/dialog-header.component';\nimport type { DialogConfirmVariant } from './dialog-shell.types';\n\n/**\n * The one dialog shell every dashboard dialog is built from. It owns the fixed\n * chrome — the tinted icon-tile header (title / accent / subtitle + close) and\n * the footer (muted note + the standard Cancel/confirm buttons) — so individual\n * dialogs stop re-declaring any of it. Everything unique to a dialog lives in\n * the projected **content region**.\n *\n * The shell carries no i18n dependency and no domain data: every label is a\n * plain input (with English defaults for the built-in Cancel button), and the\n * icons are inputs that degrade gracefully when empty. Render it inside the\n * consuming app's own `fk-dialog` (opened bare — `header: null, closable:\n * false` — with an `ariaLabel` for an accessible name).\n *\n * The common case needs zero footer markup — set the confirm label/variant and\n * handle the outputs:\n *\n * ```html\n * <fk-dialog-shell\n * icon=\"user-ban\"\n * tint=\"warning\"\n * title=\"Deactivate identity\"\n * [subtitle]=\"subtitle\"\n * footerNote=\"Sign-in blocked\"\n * footerNoteIcon=\"power\"\n * confirmLabel=\"Deactivate\"\n * confirmIcon=\"user-ban\"\n * confirmVariant=\"warning\"\n * [confirmDisabled]=\"!ready()\"\n * (confirm)=\"onConfirm()\"\n * (dismiss)=\"onCancel()\"\n * >\n * <!-- content region: lead, description, cards, callouts … -->\n * </fk-dialog-shell>\n * ```\n *\n * Escape hatches for the exceptions:\n * - leave `confirmLabel` empty and project buttons into the `dialogFooter` slot\n * to keep the footer chrome but use custom buttons (e.g. a form submit);\n * - set `[customFooter]=\"true\"` and project into the `footer` slot to replace\n * the whole footer.\n */\n@Component({\n selector: 'fk-dialog-shell',\n standalone: true,\n imports: [\n ButtonComponent,\n IconComponent,\n DialogFooterComponent,\n DialogHeaderComponent,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './dialog-shell.component.html',\n styleUrl: './dialog-shell.component.scss',\n})\nexport class DialogShellComponent {\n // ===== INPUTS =====\n /** Icon glyph for the header tile. Empty renders no tile. */\n readonly icon = input<string>('');\n /** Colour accent for the header tile. */\n readonly tint = input<DialogHeaderTint>('brand');\n /** Header title. */\n readonly title = input.required<string>();\n /** Optional trailing accent appended to the title in the tint colour. */\n readonly titleAccent = input<string>('');\n /** Optional mono subtitle under the title. */\n readonly subtitle = input<string>('');\n /** Optional muted footer note on the left (e.g. \"Permanent\"). */\n readonly footerNote = input<string>('');\n /** Optional icon rendered before the footer note. */\n readonly footerNoteIcon = input<string>('');\n /**\n * Confirm-button label. When set, the shell renders the standard\n * Cancel + confirm pair; leave empty to project custom buttons into the\n * `dialogFooter` slot instead.\n */\n readonly confirmLabel = input<string>('');\n /** Optional leading icon on the confirm button. */\n readonly confirmIcon = input<string>('');\n /** Confirm-button colour — `primary` (default), `danger`, or `warning`. */\n readonly confirmVariant = input<DialogConfirmVariant>('primary');\n /** Disables the confirm button (e.g. while a type-to-confirm is unmet). */\n readonly confirmDisabled = input<boolean>(false);\n /** Cancel-button label. Defaults to `Cancel`. */\n readonly cancelLabel = input<string>('Cancel');\n /** Accessible label for the header's close button. */\n readonly closeLabel = input<string>('Close');\n /**\n * Escape hatch for the rare dialog that needs a bespoke footer. When true,\n * the default `fk-dialog-footer` (note + buttons) is suppressed and whatever\n * is projected into the `footer` slot is rendered raw instead.\n */\n readonly customFooter = input<boolean>(false);\n\n // ===== OUTPUTS =====\n /** Fires when the header close button OR the Cancel button is clicked. */\n readonly dismiss = output<void>();\n /** Fires when the confirm button is clicked. */\n readonly confirm = output<void>();\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n\n // ===== COMPUTED =====\n /** `warning` renders as a retinted `danger` fk-button; others map directly. */\n protected readonly confirmButtonVariant = computed<'primary' | 'danger'>(\n () => {\n const variant = this.confirmVariant();\n\n return variant === 'warning' ? 'danger' : variant;\n },\n );\n\n readonly classes = computed(() =>\n ['fk-dialog-shell', this.className()].filter(Boolean).join(' '),\n );\n\n @HostBinding('class')\n get hostClass(): string {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n}\n","<fk-dialog-header\n class=\"fk-dialog-shell__header\"\n [icon]=\"icon()\"\n [tint]=\"tint()\"\n [title]=\"title()\"\n [titleAccent]=\"titleAccent()\"\n [subtitle]=\"subtitle()\"\n [closeLabel]=\"closeLabel()\"\n (dismiss)=\"dismiss.emit()\"\n/>\n\n<div class=\"fk-dialog-shell__body\">\n <ng-content />\n</div>\n\n@if (customFooter()) {\n <ng-content select=\"[footer]\" />\n} @else {\n <fk-dialog-footer [note]=\"footerNote()\" [noteIcon]=\"footerNoteIcon()\">\n @if (confirmLabel()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"dismiss.emit()\">\n {{ cancelLabel() }}\n </fk-button>\n <fk-button\n class=\"fk-dialog-shell__confirm\"\n [class.fk-dialog-shell__confirm--danger]=\"confirmVariant() === 'danger'\"\n [class.fk-dialog-shell__confirm--warning]=\"\n confirmVariant() === 'warning'\n \"\n [variant]=\"confirmButtonVariant()\"\n type=\"button\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"confirm.emit()\"\n >\n @if (confirmIcon()) {\n <fk-icon fkButtonIconStart [name]=\"confirmIcon()\" size=\"sm\" />\n }\n {{ confirmLabel() }}\n </fk-button>\n } @else {\n <ng-content select=\"[dialogFooter]\" />\n }\n </fk-dialog-footer>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frame-kit/ui-ng-patterns",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Opinionated, dashboard-ready Angular component patterns composed from @frame-kit/ui-ng.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,7 +17,7 @@
17
17
  "@angular/forms": "^21.2.0",
18
18
  "@angular/platform-browser": "^21.2.0",
19
19
  "@angular/router": "^21.2.0",
20
- "@frame-kit/ui-ng": "0.1.4",
20
+ "@frame-kit/ui-ng": "0.1.6",
21
21
  "rxjs": "^7.0.0"
22
22
  },
23
23
  "sideEffects": false,
@@ -378,5 +378,206 @@ declare class SidebarNavItemDirective {
378
378
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SidebarNavItemDirective, "ng-template[fkSidebarNavItem]", never, {}, {}, never, never, true, never>;
379
379
  }
380
380
 
381
- export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
382
- export type { SidebarNavChild, SidebarNavGroup, SidebarNavItem, SidebarNavLink, SidebarNavModel };
381
+ /**
382
+ * Colour accent for the dialog header's icon tile. Each value is a semantic
383
+ * tone that maps to a `--fk-color-*` token — never a literal colour name — so a
384
+ * theme can retint it. Pick by the action's meaning:
385
+ *
386
+ * - `brand` — a create / neutral primary action.
387
+ * - `warning` — a cautionary but reversible change (edit, rotate, deactivate).
388
+ * - `success` — a positive / recoverable outcome.
389
+ * - `danger` — a destructive, hard-to-reverse action.
390
+ * - `accent` — a secondary highlight tone with no danger connotation.
391
+ */
392
+ type DialogHeaderTint = 'brand' | 'warning' | 'success' | 'danger' | 'accent';
393
+
394
+ /**
395
+ * Colour of the shell's built-in confirm button:
396
+ *
397
+ * - `primary` — the default create / affirmative action.
398
+ * - `danger` — a destructive, hard-to-reverse action.
399
+ * - `warning` — a cautionary but reversible change; renders as a retinted
400
+ * `danger` button (fk-button has no warning variant, so the shell overrides
401
+ * its danger custom properties to the warning scale).
402
+ */
403
+ type DialogConfirmVariant = 'primary' | 'danger' | 'warning';
404
+
405
+ /**
406
+ * The one dialog shell every dashboard dialog is built from. It owns the fixed
407
+ * chrome — the tinted icon-tile header (title / accent / subtitle + close) and
408
+ * the footer (muted note + the standard Cancel/confirm buttons) — so individual
409
+ * dialogs stop re-declaring any of it. Everything unique to a dialog lives in
410
+ * the projected **content region**.
411
+ *
412
+ * The shell carries no i18n dependency and no domain data: every label is a
413
+ * plain input (with English defaults for the built-in Cancel button), and the
414
+ * icons are inputs that degrade gracefully when empty. Render it inside the
415
+ * consuming app's own `fk-dialog` (opened bare — `header: null, closable:
416
+ * false` — with an `ariaLabel` for an accessible name).
417
+ *
418
+ * The common case needs zero footer markup — set the confirm label/variant and
419
+ * handle the outputs:
420
+ *
421
+ * ```html
422
+ * <fk-dialog-shell
423
+ * icon="user-ban"
424
+ * tint="warning"
425
+ * title="Deactivate identity"
426
+ * [subtitle]="subtitle"
427
+ * footerNote="Sign-in blocked"
428
+ * footerNoteIcon="power"
429
+ * confirmLabel="Deactivate"
430
+ * confirmIcon="user-ban"
431
+ * confirmVariant="warning"
432
+ * [confirmDisabled]="!ready()"
433
+ * (confirm)="onConfirm()"
434
+ * (dismiss)="onCancel()"
435
+ * >
436
+ * <!-- content region: lead, description, cards, callouts … -->
437
+ * </fk-dialog-shell>
438
+ * ```
439
+ *
440
+ * Escape hatches for the exceptions:
441
+ * - leave `confirmLabel` empty and project buttons into the `dialogFooter` slot
442
+ * to keep the footer chrome but use custom buttons (e.g. a form submit);
443
+ * - set `[customFooter]="true"` and project into the `footer` slot to replace
444
+ * the whole footer.
445
+ */
446
+ declare class DialogShellComponent {
447
+ /** Icon glyph for the header tile. Empty renders no tile. */
448
+ readonly icon: _angular_core.InputSignal<string>;
449
+ /** Colour accent for the header tile. */
450
+ readonly tint: _angular_core.InputSignal<DialogHeaderTint>;
451
+ /** Header title. */
452
+ readonly title: _angular_core.InputSignal<string>;
453
+ /** Optional trailing accent appended to the title in the tint colour. */
454
+ readonly titleAccent: _angular_core.InputSignal<string>;
455
+ /** Optional mono subtitle under the title. */
456
+ readonly subtitle: _angular_core.InputSignal<string>;
457
+ /** Optional muted footer note on the left (e.g. "Permanent"). */
458
+ readonly footerNote: _angular_core.InputSignal<string>;
459
+ /** Optional icon rendered before the footer note. */
460
+ readonly footerNoteIcon: _angular_core.InputSignal<string>;
461
+ /**
462
+ * Confirm-button label. When set, the shell renders the standard
463
+ * Cancel + confirm pair; leave empty to project custom buttons into the
464
+ * `dialogFooter` slot instead.
465
+ */
466
+ readonly confirmLabel: _angular_core.InputSignal<string>;
467
+ /** Optional leading icon on the confirm button. */
468
+ readonly confirmIcon: _angular_core.InputSignal<string>;
469
+ /** Confirm-button colour — `primary` (default), `danger`, or `warning`. */
470
+ readonly confirmVariant: _angular_core.InputSignal<DialogConfirmVariant>;
471
+ /** Disables the confirm button (e.g. while a type-to-confirm is unmet). */
472
+ readonly confirmDisabled: _angular_core.InputSignal<boolean>;
473
+ /** Cancel-button label. Defaults to `Cancel`. */
474
+ readonly cancelLabel: _angular_core.InputSignal<string>;
475
+ /** Accessible label for the header's close button. */
476
+ readonly closeLabel: _angular_core.InputSignal<string>;
477
+ /**
478
+ * Escape hatch for the rare dialog that needs a bespoke footer. When true,
479
+ * the default `fk-dialog-footer` (note + buttons) is suppressed and whatever
480
+ * is projected into the `footer` slot is rendered raw instead.
481
+ */
482
+ readonly customFooter: _angular_core.InputSignal<boolean>;
483
+ /** Fires when the header close button OR the Cancel button is clicked. */
484
+ readonly dismiss: _angular_core.OutputEmitterRef<void>;
485
+ /** Fires when the confirm button is clicked. */
486
+ readonly confirm: _angular_core.OutputEmitterRef<void>;
487
+ readonly className: _angular_core.InputSignal<string>;
488
+ readonly id: _angular_core.InputSignal<string | null>;
489
+ /** `warning` renders as a retinted `danger` fk-button; others map directly. */
490
+ protected readonly confirmButtonVariant: _angular_core.Signal<"danger" | "primary">;
491
+ readonly classes: _angular_core.Signal<string>;
492
+ get hostClass(): string;
493
+ get hostId(): string | null;
494
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DialogShellComponent, never>;
495
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DialogShellComponent, "fk-dialog-shell", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tint": { "alias": "tint"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "titleAccent": { "alias": "titleAccent"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "footerNote": { "alias": "footerNote"; "required": false; "isSignal": true; }; "footerNoteIcon": { "alias": "footerNoteIcon"; "required": false; "isSignal": true; }; "confirmLabel": { "alias": "confirmLabel"; "required": false; "isSignal": true; }; "confirmIcon": { "alias": "confirmIcon"; "required": false; "isSignal": true; }; "confirmVariant": { "alias": "confirmVariant"; "required": false; "isSignal": true; }; "confirmDisabled": { "alias": "confirmDisabled"; "required": false; "isSignal": true; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; "customFooter": { "alias": "customFooter"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "dismiss": "dismiss"; "confirm": "confirm"; }, never, ["*", "[footer]", "[dialogFooter]"], true, never>;
496
+ }
497
+
498
+ /**
499
+ * The fixed header chrome for {@link DialogShellComponent} — a tinted icon
500
+ * tile, a title with an optional trailing accent, an optional mono subtitle,
501
+ * and a close button. Every dialog wears the same chrome; only the icon, tint,
502
+ * and text differ, so this centralises it instead of each dialog hand-rolling
503
+ * the markup.
504
+ *
505
+ * Text is passed in as plain inputs (no i18n dependency), and the icon is
506
+ * optional — an empty `icon` simply renders no tile, so the header degrades
507
+ * gracefully rather than showing a broken glyph.
508
+ *
509
+ * ```html
510
+ * <fk-dialog-header
511
+ * icon="user-ban"
512
+ * tint="warning"
513
+ * title="Deactivate identity"
514
+ * subtitle="Acme Inc. · Members"
515
+ * (dismiss)="close()"
516
+ * />
517
+ * ```
518
+ */
519
+ declare class DialogHeaderComponent {
520
+ /** Icon glyph name for the tile. Empty renders no tile. */
521
+ readonly icon: _angular_core.InputSignal<string>;
522
+ /** Colour accent for the icon tile + title accent. */
523
+ readonly tint: _angular_core.InputSignal<DialogHeaderTint>;
524
+ /** Dialog title. */
525
+ readonly title: _angular_core.InputSignal<string>;
526
+ /** Optional trailing accent appended to the title, rendered in the tint colour. */
527
+ readonly titleAccent: _angular_core.InputSignal<string>;
528
+ /** Optional mono subtitle under the title. */
529
+ readonly subtitle: _angular_core.InputSignal<string>;
530
+ /** Accessible label for the close button. */
531
+ readonly closeLabel: _angular_core.InputSignal<string>;
532
+ /** Fires when the close button is clicked. */
533
+ readonly dismiss: _angular_core.OutputEmitterRef<void>;
534
+ readonly className: _angular_core.InputSignal<string>;
535
+ readonly id: _angular_core.InputSignal<string | null>;
536
+ readonly ariaLabel: _angular_core.InputSignal<string | null>;
537
+ /** The tint's `{ color, background }` CSS-variable pair. */
538
+ protected readonly tintVars: _angular_core.Signal<{
539
+ color: string;
540
+ background: string;
541
+ }>;
542
+ readonly classes: _angular_core.Signal<string>;
543
+ get hostClass(): string;
544
+ get hostId(): string | null;
545
+ get hostAriaLabel(): string | null;
546
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DialogHeaderComponent, never>;
547
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DialogHeaderComponent, "fk-dialog-header", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tint": { "alias": "tint"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "titleAccent": { "alias": "titleAccent"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "dismiss": "dismiss"; }, never, never, true, never>;
548
+ }
549
+
550
+ /**
551
+ * The fixed footer chrome for {@link DialogShellComponent} — an optional muted
552
+ * note on the left, then the projected action buttons pushed to the right,
553
+ * above a top divider. Paired with {@link DialogHeaderComponent} so every
554
+ * dialog shares the same chrome.
555
+ *
556
+ * The note icon is optional and degrades gracefully — an empty `noteIcon`
557
+ * simply renders no glyph.
558
+ *
559
+ * ```html
560
+ * <fk-dialog-footer note="Takes effect immediately" noteIcon="power">
561
+ * <fk-button variant="outline" (click)="cancel()">Cancel</fk-button>
562
+ * <fk-button variant="primary" type="submit">Save</fk-button>
563
+ * </fk-dialog-footer>
564
+ * ```
565
+ */
566
+ declare class DialogFooterComponent {
567
+ /** Optional muted note shown on the left (e.g. "Takes effect immediately"). */
568
+ readonly note: _angular_core.InputSignal<string>;
569
+ /** Optional icon glyph rendered before the note. Empty renders no glyph. */
570
+ readonly noteIcon: _angular_core.InputSignal<string>;
571
+ readonly className: _angular_core.InputSignal<string>;
572
+ readonly id: _angular_core.InputSignal<string | null>;
573
+ readonly ariaLabel: _angular_core.InputSignal<string | null>;
574
+ readonly classes: _angular_core.Signal<string>;
575
+ get hostClass(): string;
576
+ get hostId(): string | null;
577
+ get hostAriaLabel(): string | null;
578
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DialogFooterComponent, never>;
579
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DialogFooterComponent, "fk-dialog-footer", never, { "note": { "alias": "note"; "required": false; "isSignal": true; }; "noteIcon": { "alias": "noteIcon"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
580
+ }
581
+
582
+ export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
583
+ export type { DialogConfirmVariant, DialogHeaderTint, SidebarNavChild, SidebarNavGroup, SidebarNavItem, SidebarNavLink, SidebarNavModel };