@frame-kit/ui-ng 0.0.2 → 0.0.3
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.
|
@@ -44,6 +44,13 @@ class NavGroupComponent {
|
|
|
44
44
|
* stays untouched regardless of this input.
|
|
45
45
|
*/
|
|
46
46
|
lockedOpen = input(false, ...(ngDevMode ? [{ debugName: "lockedOpen" }] : /* istanbul ignore next */ []));
|
|
47
|
+
/**
|
|
48
|
+
* When true, renders a chevron at the trailing edge of the trigger row
|
|
49
|
+
* that points right while collapsed and rotates to point down while
|
|
50
|
+
* expanded. Decorative (`aria-hidden`); hidden in rail mode. Off by
|
|
51
|
+
* default so existing consumers are unaffected.
|
|
52
|
+
*/
|
|
53
|
+
showChevron = input(false, ...(ngDevMode ? [{ debugName: "showChevron" }] : /* istanbul ignore next */ []));
|
|
47
54
|
// ===== MODELS =====
|
|
48
55
|
/** Whether the group is currently expanded, showing projected child links. */
|
|
49
56
|
expanded = model(false, ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
@@ -92,12 +99,12 @@ class NavGroupComponent {
|
|
|
92
99
|
this.expanded.update((v) => !v);
|
|
93
100
|
}
|
|
94
101
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NavGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
95
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: NavGroupComponent, isStandalone: true, selector: "fk-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconSize: { classPropertyName: "iconSize", publicName: "iconSize", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, lockedOpen: { classPropertyName: "lockedOpen", publicName: "lockedOpen", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", requestOpen: "requestOpen" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input \u2014\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\n", styles: [":host{display:block}.fk-nav-group__row{display:flex;align-items:center;gap:var(--fk-nav-group-row-gap, var(--fk-rhythm-1, .25rem))}.fk-nav-group__link{flex:1;min-width:0}.fk-nav-group__children{display:flex;flex-direction:column;gap:var(--fk-nav-group-children-gap, var(--fk-rhythm-1, .25rem));padding-left:var(--fk-nav-group-children-indent, var(--fk-rhythm-6, 1.5rem));margin-top:var(--fk-nav-group-children-offset, var(--fk-rhythm-1, .25rem))}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: SidenavLinkComponent, selector: "fk-sidenav-link", inputs: ["icon", "iconSize", "label", "routerLink", "exact"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
102
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: NavGroupComponent, isStandalone: true, selector: "fk-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconSize: { classPropertyName: "iconSize", publicName: "iconSize", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, lockedOpen: { classPropertyName: "lockedOpen", publicName: "lockedOpen", isSignal: true, isRequired: false, transformFunction: null }, showChevron: { classPropertyName: "showChevron", publicName: "showChevron", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", requestOpen: "requestOpen" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n\n @if (showChevron()) {\n <span\n class=\"fk-nav-group__chevron\"\n [class.fk-nav-group__chevron--open]=\"expanded()\"\n aria-hidden=\"true\"\n >\n <svg\n class=\"fk-nav-group__chevron-svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 18 18\"\n >\n <path\n d=\"M4.5 6.75L9 11.25L13.5 6.75\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n }\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input \u2014\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\n", styles: [":host{display:block}.fk-nav-group__row{display:flex;align-items:center;gap:var(--fk-nav-group-row-gap, var(--fk-rhythm-1, .25rem))}.fk-nav-group__link{flex:1;min-width:0}.fk-nav-group__chevron{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--fk-nav-group-chevron-color, var(--fk-color-muted, #64748b));transform:rotate(-90deg);transition:transform .15s ease}.fk-nav-group__chevron--open{transform:rotate(0)}.fk-nav-group__chevron-svg{display:block}:host(.fk-nav-group--collapsed) .fk-nav-group__chevron{display:none}.fk-nav-group__children{position:relative;display:flex;flex-direction:column;gap:var(--fk-nav-group-children-gap, var(--fk-rhythm-1, .25rem));padding-left:var(--fk-nav-group-children-indent, var(--fk-rhythm-6, 1.5rem));margin-top:var(--fk-nav-group-children-offset, var(--fk-rhythm-1, .25rem))}.fk-nav-group__children:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--fk-nav-group-children-border-offset, var(--fk-sidenav-link-padding-inline, var(--fk-rhythm-3, .75rem)));width:var(--fk-nav-group-children-border-width, var(--fk-border-width, 1px));background:var(--fk-nav-group-children-border-color, var(--fk-color-border, #d9e2ee))}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: SidenavLinkComponent, selector: "fk-sidenav-link", inputs: ["icon", "iconSize", "label", "routerLink", "exact"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
96
103
|
}
|
|
97
104
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NavGroupComponent, decorators: [{
|
|
98
105
|
type: Component,
|
|
99
|
-
args: [{ selector: 'fk-nav-group', standalone: true, imports: [RouterModule, SidenavLinkComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input \u2014\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\n", styles: [":host{display:block}.fk-nav-group__row{display:flex;align-items:center;gap:var(--fk-nav-group-row-gap, var(--fk-rhythm-1, .25rem))}.fk-nav-group__link{flex:1;min-width:0}.fk-nav-group__children{display:flex;flex-direction:column;gap:var(--fk-nav-group-children-gap, var(--fk-rhythm-1, .25rem));padding-left:var(--fk-nav-group-children-indent, var(--fk-rhythm-6, 1.5rem));margin-top:var(--fk-nav-group-children-offset, var(--fk-rhythm-1, .25rem))}\n"] }]
|
|
100
|
-
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconSize", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], lockedOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedOpen", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], requestOpen: [{ type: i0.Output, args: ["requestOpen"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
|
|
106
|
+
args: [{ selector: 'fk-nav-group', standalone: true, imports: [RouterModule, SidenavLinkComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n\n @if (showChevron()) {\n <span\n class=\"fk-nav-group__chevron\"\n [class.fk-nav-group__chevron--open]=\"expanded()\"\n aria-hidden=\"true\"\n >\n <svg\n class=\"fk-nav-group__chevron-svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 18 18\"\n >\n <path\n d=\"M4.5 6.75L9 11.25L13.5 6.75\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n }\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input \u2014\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\n", styles: [":host{display:block}.fk-nav-group__row{display:flex;align-items:center;gap:var(--fk-nav-group-row-gap, var(--fk-rhythm-1, .25rem))}.fk-nav-group__link{flex:1;min-width:0}.fk-nav-group__chevron{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--fk-nav-group-chevron-color, var(--fk-color-muted, #64748b));transform:rotate(-90deg);transition:transform .15s ease}.fk-nav-group__chevron--open{transform:rotate(0)}.fk-nav-group__chevron-svg{display:block}:host(.fk-nav-group--collapsed) .fk-nav-group__chevron{display:none}.fk-nav-group__children{position:relative;display:flex;flex-direction:column;gap:var(--fk-nav-group-children-gap, var(--fk-rhythm-1, .25rem));padding-left:var(--fk-nav-group-children-indent, var(--fk-rhythm-6, 1.5rem));margin-top:var(--fk-nav-group-children-offset, var(--fk-rhythm-1, .25rem))}.fk-nav-group__children:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--fk-nav-group-children-border-offset, var(--fk-sidenav-link-padding-inline, var(--fk-rhythm-3, .75rem)));width:var(--fk-nav-group-children-border-width, var(--fk-border-width, 1px));background:var(--fk-nav-group-children-border-color, var(--fk-color-border, #d9e2ee))}\n"] }]
|
|
107
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconSize", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], lockedOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedOpen", required: false }] }], showChevron: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChevron", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], requestOpen: [{ type: i0.Output, args: ["requestOpen"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
|
|
101
108
|
type: HostBinding,
|
|
102
109
|
args: ['class']
|
|
103
110
|
}] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-kit-ui-ng-ui-nav-group.mjs","sources":["../../../../packages/ui-ng/ui/nav-group/nav-group.component.ts","../../../../packages/ui-ng/ui/nav-group/nav-group.component.html","../../../../packages/ui-ng/ui/nav-group/frame-kit-ui-ng-ui-nav-group.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n model,\n output,\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\nimport type { IconSize } from '@frame-kit/ui-ng/core/icon';\nimport { SidenavLinkComponent } from '@frame-kit/ui-ng/ui/sidenav-link';\n\n/**\n * Expandable nav group — renders a trigger row (icon + label, optionally\n * linked) and reveals projected children when `expanded` is true. Designed\n * to sit inside a collapsible sidenav: when the outer shell is collapsed,\n * clicking the row emits `requestOpen` so the host can pop the sidenav\n * open, and the group auto-expands once it's visible.\n */\n@Component({\n selector: 'fk-nav-group',\n standalone: true,\n imports: [RouterModule, SidenavLinkComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './nav-group.component.html',\n styleUrl: './nav-group.component.scss',\n})\nexport class NavGroupComponent {\n // ===== INPUTS =====\n /** Primary label text shown in the group trigger row. */\n readonly label = input.required<string>();\n /** Optional icon name shown to the leading side of the label. */\n readonly icon = input<string | null>(null);\n /** Size of the leading icon. */\n readonly iconSize = input<IconSize>('sm');\n /** Optional router path; when set, the trigger navigates on click in expanded sidenav mode. */\n readonly routerLink = input<string | string[] | null>(null);\n\n /**\n * True when the containing sidenav is in its collapsed rail state.\n * In that mode, clicking the trigger BOTH navigates via\n * `routerLink` AND emits `requestOpen` so the host can expand the\n * outer shell — the user lands on the group's route with the\n * sidenav already open. The trigger never expands its own children\n * inside the rail (no horizontal room); the rail-to-expanded\n * transition is what reveals them.\n */\n readonly collapsed = input<boolean>(false);\n\n /**\n * When true and the sidenav is in its expanded state, clicking the\n * trigger row does NOT fold the group — children stay visible.\n * Useful when the group's children are permanent navigation rather\n * than a collapsible disclosure (e.g. a parent nav item that should\n * always show its sub-pages while the sidenav is open). Clicks on\n * the trigger still navigate via `routerLink`.\n *\n * Has no effect in collapsed (rail) mode — clicks there still\n * navigate via `routerLink` and emit `requestOpen`, and `expanded`\n * stays untouched regardless of this input.\n */\n readonly lockedOpen = input<boolean>(false);\n\n // ===== MODELS =====\n /** Whether the group is currently expanded, showing projected child links. */\n readonly expanded = model<boolean>(false);\n\n // ===== OUTPUTS =====\n /**\n * Emitted when the group is clicked while the outer sidenav is\n * collapsed. Hosts typically listen to open their collapsible shell\n * so the projected children become visible.\n */\n readonly requestOpen = output<void>();\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== COMPUTED =====\n readonly classes = computed(() => {\n return [\n 'fk-nav-group',\n this.expanded() ? 'fk-nav-group--expanded' : '',\n this.collapsed() ? 'fk-nav-group--collapsed' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n protected toggle(): void {\n if (this.collapsed()) {\n // In rail mode the children must not render inside the rail —\n // there's no horizontal room and the visual hierarchy collapses.\n // Just ask the host to expand the sidenav; once it does and\n // `collapsed` flips to false, the consumer's `[expanded]`\n // binding can push true and the children render in the now-open\n // sidenav. We DO NOT set `expanded` here, so a click in rail\n // mode never reveals children inside the rail itself.\n this.requestOpen.emit();\n\n return;\n }\n\n // Locked-open: in expanded sidenav mode, the trigger no longer\n // folds the group on click. Navigation via `routerLink` still\n // fires from the inner sidenav-link; this branch just suppresses\n // the toggle behavior so children stay visible.\n if (this.lockedOpen()) {\n return;\n }\n\n this.expanded.update((v) => !v);\n }\n}\n","<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input —\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\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-ui-nav-group.mjs","sources":["../../../../packages/ui-ng/ui/nav-group/nav-group.component.ts","../../../../packages/ui-ng/ui/nav-group/nav-group.component.html","../../../../packages/ui-ng/ui/nav-group/frame-kit-ui-ng-ui-nav-group.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n model,\n output,\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\nimport type { IconSize } from '@frame-kit/ui-ng/core/icon';\nimport { SidenavLinkComponent } from '@frame-kit/ui-ng/ui/sidenav-link';\n\n/**\n * Expandable nav group — renders a trigger row (icon + label, optionally\n * linked) and reveals projected children when `expanded` is true. Designed\n * to sit inside a collapsible sidenav: when the outer shell is collapsed,\n * clicking the row emits `requestOpen` so the host can pop the sidenav\n * open, and the group auto-expands once it's visible.\n */\n@Component({\n selector: 'fk-nav-group',\n standalone: true,\n imports: [RouterModule, SidenavLinkComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './nav-group.component.html',\n styleUrl: './nav-group.component.scss',\n})\nexport class NavGroupComponent {\n // ===== INPUTS =====\n /** Primary label text shown in the group trigger row. */\n readonly label = input.required<string>();\n /** Optional icon name shown to the leading side of the label. */\n readonly icon = input<string | null>(null);\n /** Size of the leading icon. */\n readonly iconSize = input<IconSize>('sm');\n /** Optional router path; when set, the trigger navigates on click in expanded sidenav mode. */\n readonly routerLink = input<string | string[] | null>(null);\n\n /**\n * True when the containing sidenav is in its collapsed rail state.\n * In that mode, clicking the trigger BOTH navigates via\n * `routerLink` AND emits `requestOpen` so the host can expand the\n * outer shell — the user lands on the group's route with the\n * sidenav already open. The trigger never expands its own children\n * inside the rail (no horizontal room); the rail-to-expanded\n * transition is what reveals them.\n */\n readonly collapsed = input<boolean>(false);\n\n /**\n * When true and the sidenav is in its expanded state, clicking the\n * trigger row does NOT fold the group — children stay visible.\n * Useful when the group's children are permanent navigation rather\n * than a collapsible disclosure (e.g. a parent nav item that should\n * always show its sub-pages while the sidenav is open). Clicks on\n * the trigger still navigate via `routerLink`.\n *\n * Has no effect in collapsed (rail) mode — clicks there still\n * navigate via `routerLink` and emit `requestOpen`, and `expanded`\n * stays untouched regardless of this input.\n */\n readonly lockedOpen = input<boolean>(false);\n\n /**\n * When true, renders a chevron at the trailing edge of the trigger row\n * that points right while collapsed and rotates to point down while\n * expanded. Decorative (`aria-hidden`); hidden in rail mode. Off by\n * default so existing consumers are unaffected.\n */\n readonly showChevron = input<boolean>(false);\n\n // ===== MODELS =====\n /** Whether the group is currently expanded, showing projected child links. */\n readonly expanded = model<boolean>(false);\n\n // ===== OUTPUTS =====\n /**\n * Emitted when the group is clicked while the outer sidenav is\n * collapsed. Hosts typically listen to open their collapsible shell\n * so the projected children become visible.\n */\n readonly requestOpen = output<void>();\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== COMPUTED =====\n readonly classes = computed(() => {\n return [\n 'fk-nav-group',\n this.expanded() ? 'fk-nav-group--expanded' : '',\n this.collapsed() ? 'fk-nav-group--collapsed' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n protected toggle(): void {\n if (this.collapsed()) {\n // In rail mode the children must not render inside the rail —\n // there's no horizontal room and the visual hierarchy collapses.\n // Just ask the host to expand the sidenav; once it does and\n // `collapsed` flips to false, the consumer's `[expanded]`\n // binding can push true and the children render in the now-open\n // sidenav. We DO NOT set `expanded` here, so a click in rail\n // mode never reveals children inside the rail itself.\n this.requestOpen.emit();\n\n return;\n }\n\n // Locked-open: in expanded sidenav mode, the trigger no longer\n // folds the group on click. Navigation via `routerLink` still\n // fires from the inner sidenav-link; this branch just suppresses\n // the toggle behavior so children stay visible.\n if (this.lockedOpen()) {\n return;\n }\n\n this.expanded.update((v) => !v);\n }\n}\n","<div class=\"fk-nav-group__row\" role=\"presentation\" (click)=\"toggle()\">\n <fk-sidenav-link\n class=\"fk-nav-group__link\"\n [icon]=\"icon() ?? ''\"\n [iconSize]=\"iconSize()\"\n [label]=\"label()\"\n [routerLink]=\"routerLink()\"\n />\n\n @if (showChevron()) {\n <span\n class=\"fk-nav-group__chevron\"\n [class.fk-nav-group__chevron--open]=\"expanded()\"\n aria-hidden=\"true\"\n >\n <svg\n class=\"fk-nav-group__chevron-svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 18 18\"\n >\n <path\n d=\"M4.5 6.75L9 11.25L13.5 6.75\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n fill=\"none\"\n />\n </svg>\n </span>\n }\n</div>\n\n@if (expanded()) {\n <!--\n Stop child clicks from bubbling to the fk-nav-group host. When a\n consumer binds [routerLink] on <fk-nav-group>, Angular attaches the\n RouterLink directive to the host element in addition to our input —\n so an un-stopped child click would re-navigate to the parent URL\n after the child's own RouterLink fires. This is an event absorber,\n not an interactive element; keyboard activation fires click events\n that bubble through the same path, so the a11y rules don't apply.\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-nav-group__children\" (click)=\"$event.stopPropagation()\">\n <ng-content />\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AAcA;;;;;;AAMG;MASU,iBAAiB,CAAA;;;AAGnB,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAU;;AAEhC,IAAA,IAAI,GAAG,KAAK,CAAgB,IAAI,2EAAC;;AAEjC,IAAA,QAAQ,GAAG,KAAK,CAAW,IAAI,+EAAC;;AAEhC,IAAA,UAAU,GAAG,KAAK,CAA2B,IAAI,iFAAC;AAE3D;;;;;;;;AAQG;AACM,IAAA,SAAS,GAAG,KAAK,CAAU,KAAK,gFAAC;AAE1C;;;;;;;;;;;AAWG;AACM,IAAA,UAAU,GAAG,KAAK,CAAU,KAAK,iFAAC;AAE3C;;;;;AAKG;AACM,IAAA,WAAW,GAAG,KAAK,CAAU,KAAK,kFAAC;;;AAInC,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,+EAAC;;AAGzC;;;;AAIG;IACM,WAAW,GAAG,MAAM,EAAQ;;AAG5B,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,gFAAC;;AAG7B,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;QAC/B,OAAO;YACL,cAAc;YACd,IAAI,CAAC,QAAQ,EAAE,GAAG,wBAAwB,GAAG,EAAE;YAC/C,IAAI,CAAC,SAAS,EAAE,GAAG,yBAAyB,GAAG,EAAE;YACjD,IAAI,CAAC,SAAS,EAAE;AACjB;aACE,MAAM,CAAC,OAAO;aACd,IAAI,CAAC,GAAG,CAAC;AACd,IAAA,CAAC,8EAAC;AAEF,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,OAAO,EAAE;IACvB;IAEU,MAAM,GAAA;AACd,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE;;;;;;;;AAQpB,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YAEvB;QACF;;;;;AAMA,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE;YACrB;QACF;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC;uGAnGW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,WAAA,EAAA,aAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,gBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC7B9B,8pDAkDA,EAAA,MAAA,EAAA,CAAA,wrCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED1BY,YAAY,gRAAE,oBAAoB,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,OAAA,EAAA,YAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAKjC,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAR7B,SAAS;+BACE,cAAc,EAAA,UAAA,EACZ,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,oBAAoB,CAAC,EAAA,eAAA,EAC5B,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,8pDAAA,EAAA,MAAA,EAAA,CAAA,wrCAAA,CAAA,EAAA;;sBA2E9C,WAAW;uBAAC,OAAO;;;AEpGtB;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -41,6 +41,13 @@ declare class NavGroupComponent {
|
|
|
41
41
|
* stays untouched regardless of this input.
|
|
42
42
|
*/
|
|
43
43
|
readonly lockedOpen: _angular_core.InputSignal<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* When true, renders a chevron at the trailing edge of the trigger row
|
|
46
|
+
* that points right while collapsed and rotates to point down while
|
|
47
|
+
* expanded. Decorative (`aria-hidden`); hidden in rail mode. Off by
|
|
48
|
+
* default so existing consumers are unaffected.
|
|
49
|
+
*/
|
|
50
|
+
readonly showChevron: _angular_core.InputSignal<boolean>;
|
|
44
51
|
/** Whether the group is currently expanded, showing projected child links. */
|
|
45
52
|
readonly expanded: _angular_core.ModelSignal<boolean>;
|
|
46
53
|
/**
|
|
@@ -54,7 +61,7 @@ declare class NavGroupComponent {
|
|
|
54
61
|
get hostClass(): string;
|
|
55
62
|
protected toggle(): void;
|
|
56
63
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NavGroupComponent, never>;
|
|
57
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NavGroupComponent, "fk-nav-group", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconSize": { "alias": "iconSize"; "required": false; "isSignal": true; }; "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "lockedOpen": { "alias": "lockedOpen"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; "requestOpen": "requestOpen"; }, never, ["*"], true, never>;
|
|
64
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NavGroupComponent, "fk-nav-group", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconSize": { "alias": "iconSize"; "required": false; "isSignal": true; }; "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "lockedOpen": { "alias": "lockedOpen"; "required": false; "isSignal": true; }; "showChevron": { "alias": "showChevron"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; "requestOpen": "requestOpen"; }, never, ["*"], true, never>;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
export { NavGroupComponent };
|
package/ui/nav-group/README.md
CHANGED
|
@@ -15,6 +15,8 @@ An expandable sidenav group — renders a clickable trigger row (icon + label, o
|
|
|
15
15
|
| `iconSize` | `IconSize` | `"sm"` | Size passed through to the trigger's `fk-sidenav-link`. |
|
|
16
16
|
| `routerLink` | `string \| string[] \| null` | `null` | Destination for the trigger's anchor. Active in both expanded and collapsed states. |
|
|
17
17
|
| `collapsed` | `boolean` | `false` | Set this to `true` when the outer sidenav is in its collapsed rail state — clicks then both navigate AND emit `requestOpen`. |
|
|
18
|
+
| `lockedOpen` | `boolean` | `false` | When `true`, clicking an expanded group's trigger no longer folds it — children stay visible (navigation via `routerLink` still fires). No effect in collapsed rail mode. |
|
|
19
|
+
| `showChevron`| `boolean` | `false` | Renders a trailing disclosure chevron that points right while collapsed and rotates to point down while expanded. Decorative; hidden in rail mode. |
|
|
18
20
|
| `className` | `string` | `''` | Additional CSS classes merged onto the host element. |
|
|
19
21
|
|
|
20
22
|
### Models
|
|
@@ -49,6 +51,8 @@ An expandable sidenav group — renders a clickable trigger row (icon + label, o
|
|
|
49
51
|
- `requestOpen` output bridges to a collapsible outer shell (`fk-app-shell` and friends) — fires alongside navigation when clicked in rail mode
|
|
50
52
|
- Content projection for arbitrary child rows — `fk-sidenav-link`, section titles, custom markers
|
|
51
53
|
- Token-driven spacing (`--fk-nav-group-row-gap`, `--fk-nav-group-children-gap`, `--fk-nav-group-children-indent`, `--fk-nav-group-children-offset`)
|
|
54
|
+
- Section rule — a vertical line brackets the expanded children, flush-left under the icon/label, tokenized via `--fk-nav-group-children-border-{width,color,offset}` (removable)
|
|
55
|
+
- Optional disclosure chevron (`showChevron`) — points right collapsed, rotates down when expanded; colour via `--fk-nav-group-chevron-color`
|
|
52
56
|
|
|
53
57
|
---
|
|
54
58
|
|
|
@@ -125,8 +129,23 @@ export class ExampleComponent {}
|
|
|
125
129
|
--fk-nav-group-children-gap;
|
|
126
130
|
--fk-nav-group-children-indent;
|
|
127
131
|
--fk-nav-group-children-offset;
|
|
132
|
+
--fk-nav-group-children-border-width; // section rule width (default --fk-border-width / 1px)
|
|
133
|
+
--fk-nav-group-children-border-color; // section rule colour (default --fk-color-border)
|
|
134
|
+
--fk-nav-group-children-border-offset; // section rule x-position (see below)
|
|
135
|
+
--fk-nav-group-chevron-color; // disclosure chevron colour (default --fk-color-muted)
|
|
128
136
|
```
|
|
129
137
|
|
|
138
|
+
The expanded children column carries a quiet vertical **section rule** by
|
|
139
|
+
default — a line that brackets the nested items, aligned flush-left with the
|
|
140
|
+
trigger row's content-left edge (the icon when present, the label text
|
|
141
|
+
otherwise — both start at the link's inline padding, so a single offset
|
|
142
|
+
covers both, independent of icon size). The colour defaults to the semantic
|
|
143
|
+
`--fk-color-border`, so the rule adapts to dark mode through the fallback
|
|
144
|
+
chain with no dedicated dark token.
|
|
145
|
+
|
|
146
|
+
Override `--fk-nav-group-children-border-offset` to pin the line elsewhere, or
|
|
147
|
+
remove the rule by zeroing the width / making the colour transparent.
|
|
148
|
+
|
|
130
149
|
Override in your app stylesheet:
|
|
131
150
|
|
|
132
151
|
```scss
|
|
@@ -134,6 +153,11 @@ Override in your app stylesheet:
|
|
|
134
153
|
--fk-nav-group-children-indent: 1rem;
|
|
135
154
|
--fk-nav-group-children-gap: var(--fk-rhythm-2);
|
|
136
155
|
}
|
|
156
|
+
|
|
157
|
+
// Remove the section rule for a particular scope:
|
|
158
|
+
.flat-nav {
|
|
159
|
+
--fk-nav-group-children-border-width: 0;
|
|
160
|
+
}
|
|
137
161
|
```
|
|
138
162
|
|
|
139
163
|
---
|
|
@@ -142,4 +166,5 @@ Override in your app stylesheet:
|
|
|
142
166
|
|
|
143
167
|
- The built-in `toggle()` decides what happens to `expanded` on click: in rail mode it leaves `expanded` alone and only emits `requestOpen` (children must not render inside the rail); in expanded mode it flips `expanded` unless `lockedOpen` is `true`. Navigation via `routerLink` happens independently on the inner sidenav-link's anchor in both states. If you want custom behavior, listen to `(requestOpen)` and drive `[(expanded)]` yourself.
|
|
144
168
|
- The trigger row composes `fk-sidenav-link` — you get the same active-route styling and keyboard semantics for free.
|
|
169
|
+
- The section rule is an absolutely-positioned `::before` on the children column (not a `border-left`), so its x-position can anchor to the icon or text. It spans the full height of the children and sits behind them; child rows render normally on top. It only shows while the group is expanded (the children column isn't rendered otherwise).
|
|
145
170
|
- No opinionated max-width — the trigger fills the row. Cap the width at the consumer level if you need it (e.g., docs shells clamp their top-level items to 18rem).
|