@lifecorner/angular-ui-kit 3.10.1 → 3.11.1
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.
- package/fesm2022/lifecorner-angular-ui-kit-badge.mjs +19 -2
- package/fesm2022/lifecorner-angular-ui-kit-badge.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs +2 -1
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs +4 -4
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-footer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-footer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +14 -5
- package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-page-header.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-page-header.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +2 -1
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-tabs.mjs +2 -1
- package/fesm2022/lifecorner-angular-ui-kit-tabs.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-timeline.mjs +2 -1
- package/fesm2022/lifecorner-angular-ui-kit-timeline.mjs.map +1 -1
- package/package.json +1 -1
- package/types/lifecorner-angular-ui-kit-badge.d.ts +16 -0
- package/types/lifecorner-angular-ui-kit-button.d.ts +9 -0
- package/types/lifecorner-angular-ui-kit-icon.d.ts +19 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-next-step-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/next-step-card/next-step-card.component.ts","../../../../libs/angular-ui-kit/src/lib/next-step-card/next-step-card.component.html"],"sourcesContent":["import {\n AfterViewChecked,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n effect,\n input,\n isDevMode,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_RIGHT, ICON_EXTERNAL_LINK, ICON_INFORMATION_CIRCLE } from '@lifecorner/angular-ui-kit/icons';\n\nexport type NextStepCardTone = 'neutral' | 'primary' | 'success' | 'warning' | 'info';\n\n/**\n * Appearance of the card.\n * - `default`: white surface, hairline border, 4 px tonal rail, 36 px tinted bubble\n * - `soft`: tinted fill per tone, radius 24 px (28 px from 1024 px), no rail,\n * border or shadow, a larger white bubble with a 1 px ring (the \"Weite\" look)\n */\nexport type NextStepCardVariant = 'default' | 'soft';\n\n/**\n * How several actions are laid out.\n * - `inline`: side by side at the end of the card; below 480 px card width in\n * their own row, stretched\n * - `stacked`: always in their own row, one per line at equal width, 8 px apart\n */\nexport type NextStepCardActionsLayout = 'inline' | 'stacked';\n\n/** Element that renders the title (semantics only, the look stays the same). */\nexport type NextStepCardTitleTag = 'div' | 'h2' | 'h3';\n\n/** Size of the built-in action buttons (`sm` 32 px, `md` 42 px, `lg` 48.5 px). */\nexport type NextStepCardActionSize = 'sm' | 'md' | 'lg';\n\n/**\n * Next-step card — the single status-and-action surface of a page: \"where does\n * this stand, and what is the one next step?\".\n *\n * Features:\n * - Tonal accent rail (4px left) + tinted icon bubble, token colors only\n * - Title, message (string input or default slot for markup)\n * - Primary action (with loading / disabled + mandatory disabled reason),\n * ghost secondary action, or an external link instead of the primary button\n * - `[slot='blockers']`: rows inside the card under a subtle divider (dot\n * prefix); each projected element with `slot=\"blockers\"` becomes one row —\n * blockers are rows IN the card, never additional banners\n * - `[slot='meta']`: one footer line (icon + text)\n * - `[slot='actions']`: fully replaces the built-in action buttons\n * - `[slot='body']`: content below the message without the message's text\n * styles, so form fields, lists or a description list keep their own look\n * - `role=\"region\"` labelled by the title; optional polite live announcement\n * of title changes via `announce`\n * - Narrow layout: below 480 px of card width (a CSS container query on the\n * host) the icon sits beside the title, message, body, blockers and meta\n * take the full inner width, the inline padding drops to `padding-md`, and\n * the actions take their own row with every action stretched. A projected\n * `fullWidth` button gets its own row at every width.\n * - `variant=\"soft\"`: the tinted card of the \"Weite\" look; `actionsLayout`\n * `stacked` for several equal-width actions; `titleTag` for a heading\n *\n * The host is a size container (`container-type: inline-size`) and takes the\n * full inline size of its parent.\n *\n * Usage rule: exactly ONE next-step card per page.\n *\n * @example\n * ```html\n * <lc-next-step-card\n * tone=\"warning\"\n * icon=\"clock\"\n * cardTitle=\"Wartet auf Freigabe\"\n * message=\"Der Entwurf ist vollständig und kann geprüft werden.\"\n * primaryLabel=\"Freigeben\"\n * secondaryLabel=\"Änderungen anfordern\"\n * (primaryClick)=\"approve()\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-next-step-card',\n standalone: true,\n imports: [ButtonComponent, IconComponent],\n // Its default `icon` and the action glyphs.\n viewProviders: [ɵprovideKitIcons([ICON_INFORMATION_CIRCLE, ICON_CHEVRON_RIGHT, ICON_EXTERNAL_LINK])],\n templateUrl: './next-step-card.component.html',\n styleUrls: ['./next-step-card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NextStepCardComponent implements AfterViewChecked {\n /**\n * Tone of the accent rail and icon bubble. Colors come exclusively from the\n * semantic tokens (`--color-*` rail, `--color-*-subtle` bubble tint,\n * `--color-on-*-subtle` icon ink).\n * @default 'neutral'\n */\n tone = input<NextStepCardTone>('neutral');\n\n /**\n * Appearance: `default` (surface, border, 4 px rail) or `soft` (tinted fill\n * per tone, radius 24 px / 28 px from 1024 px, no rail, border or shadow,\n * a 48 px white bubble with a 1 px ring; 56 px from 1024 px, 60 px from\n * 1440 px). Soft fills come from `--color-next-step-soft-neutral` /\n * `--color-next-step-soft-primary` and the tone's `-subtle` token; the bubble\n * from `--color-bubble`, `--color-bubble-ring`, `--color-bubble-icon`.\n * @default 'default'\n */\n variant = input<NextStepCardVariant>('default');\n\n /** Icon name for the round bubble (36 px; 48–60 px in the soft variant). */\n icon = input<string>('information-circle');\n\n /** Title (1rem semibold; 20–28 px bold in the soft variant). Also the card's accessible name. */\n cardTitle = input.required<string>();\n\n /**\n * Element that renders the title: `div` (default), `h2` or `h3`. Use a\n * heading when the page has sections, so heading navigation reaches the\n * most important item. Semantics only, the look stays the same.\n * @default 'div'\n */\n titleTag = input<NextStepCardTitleTag>('div');\n\n /**\n * Layout of several actions. `inline`: side by side at the end of the card\n * (below 480 px card width in their own row, stretched). `stacked`: always\n * in their own row, one per line at equal width, 8 px apart (e.g. a list of\n * sign-in methods).\n * @default 'inline'\n */\n actionsLayout = input<NextStepCardActionsLayout>('inline');\n\n /**\n * Size of the built-in action buttons and link: `sm` (32 px, the 3.2\n * default), `md` (42 px) or `lg` (48.5 px, meets the 44 px touch target).\n * @default 'sm'\n */\n actionSize = input<NextStepCardActionSize>('sm');\n\n /** Message below the title. For markup, use the default slot instead. */\n message = input<string>();\n\n /** Label of the primary action. Omit to render no primary button. */\n primaryLabel = input<string>();\n\n /** Optional leading icon of the primary action. */\n primaryIcon = input<string>();\n\n /** Shows a spinner on the primary action. */\n primaryLoading = input<boolean>(false);\n\n /**\n * Disables the primary action. A disabled primary REQUIRES\n * `primaryDisabledReason`; disabled without a reason logs a dev warning.\n */\n primaryDisabled = input<boolean>(false);\n\n /** Why the primary action is disabled — shown as tooltip on the button. */\n primaryDisabledReason = input<string>('');\n\n /** Label of the ghost secondary action (rendered with a chevron). */\n secondaryLabel = input<string>();\n\n /**\n * Label of an external link rendered instead of the primary button\n * (opens in a new tab). Requires `linkHref`.\n */\n linkLabel = input<string>();\n\n /** Target URL of the external link. */\n linkHref = input<string>();\n\n /**\n * Where the link opens: `_blank` (a new tab, with `rel=\"noopener noreferrer\"`,\n * the 3.2 behaviour) or `_self` (the same tab, no `rel`).\n * @default '_blank'\n */\n linkTarget = input<'_blank' | '_self'>('_blank');\n\n /**\n * Announce title changes to screen readers via `aria-live=\"polite\"` —\n * for cards whose state advances in place.\n * @default false\n */\n announce = input<boolean>(false);\n\n /** Emits when the primary action is clicked. */\n primaryClick = output<void>();\n\n /** Emits when the secondary action is clicked. */\n secondaryClick = output<void>();\n\n private actionsSlot = viewChild<ElementRef<HTMLElement>>('actionsSlot');\n\n /**\n * Whether content was projected into `[slot='actions']` — projected actions\n * fully replace the built-in buttons.\n */\n protected hasProjectedActions = signal(false);\n\n protected cardClasses = computed(() =>\n [\n 'lc-next-step-card',\n `lc-next-step-card--${this.tone()}`,\n this.variant() === 'soft' ? 'lc-next-step-card--soft' : null,\n this.actionsLayout() === 'stacked' ? 'lc-next-step-card--actions-stacked' : null,\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n /** `rel` of the external link: only a new tab needs `noopener noreferrer`. */\n protected linkRel = computed(() => (this.linkTarget() === '_blank' ? 'noopener noreferrer' : null));\n\n protected showLink = computed(() => !!(this.linkLabel() && this.linkHref()));\n\n /**\n * Whether the card renders any action: hides the empty actions box, so the\n * narrow layout gets no empty row.\n */\n protected hasActions = computed(\n () =>\n this.hasProjectedActions() ||\n !!this.secondaryLabel() ||\n this.showLink() ||\n !!this.primaryLabel(),\n );\n\n constructor() {\n effect(() => {\n if (isDevMode() && this.primaryDisabled() && !this.primaryDisabledReason()) {\n console.warn(\n '[lc-next-step-card] `primaryDisabled` is set without a `primaryDisabledReason`. ' +\n 'A disabled primary action must explain why (shown as tooltip).',\n );\n }\n });\n }\n\n ngAfterViewChecked(): void {\n // Projected [slot='actions'] content replaces the built-in buttons. The\n // signal write is equality-guarded, so unchanged checks are no-ops.\n const el = this.actionsSlot()?.nativeElement;\n this.hasProjectedActions.set(!!el && el.childElementCount > 0);\n }\n}\n","<div [class]=\"cardClasses()\" role=\"region\" [attr.aria-label]=\"cardTitle()\">\n <div class=\"lc-next-step-card__icon\" aria-hidden=\"true\">\n <lc-icon [name]=\"icon()\" size=\"md\" [decorative]=\"true\" />\n </div>\n\n <div class=\"lc-next-step-card__body\">\n @switch (titleTag()) {\n @case ('h2') {\n <h2 class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </h3>\n }\n @default {\n <div class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </div>\n }\n }\n @if (message()) {\n <p class=\"lc-next-step-card__message\">{{ message() }}</p>\n }\n <div class=\"lc-next-step-card__custom-message\">\n <ng-content />\n </div>\n <!-- Form-safe body: no inherited message text styles. -->\n <div class=\"lc-next-step-card__content\">\n <ng-content select=\"[slot='body']\" />\n </div>\n <div class=\"lc-next-step-card__blockers\">\n <ng-content select=\"[slot='blockers']\" />\n </div>\n <div class=\"lc-next-step-card__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n </div>\n\n <div class=\"lc-next-step-card__actions\" [class.lc-next-step-card__actions--empty]=\"!hasActions()\">\n <div #actionsSlot class=\"lc-next-step-card__actions-slot\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n @if (!hasProjectedActions()) {\n @if (secondaryLabel()) {\n <lc-button variant=\"ghost\" [size]=\"actionSize()\" (clicked)=\"secondaryClick.emit()\">\n {{ secondaryLabel() }}\n <lc-icon slot=\"icon-after\" name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </lc-button>\n }\n @if (showLink()) {\n <a\n class=\"btn btn-secondary btn-{{ actionSize() }} lc-next-step-card__link\"\n [href]=\"linkHref()\"\n [attr.target]=\"linkTarget()\"\n [attr.rel]=\"linkRel()\"\n >\n <lc-icon name=\"external-link\" size=\"sm\" [decorative]=\"true\" />\n {{ linkLabel() }}\n </a>\n } @else if (primaryLabel()) {\n <lc-button\n variant=\"primary\"\n [size]=\"actionSize()\"\n [loading]=\"primaryLoading()\"\n [disabled]=\"primaryDisabled()\"\n [disabledReason]=\"primaryDisabledReason()\"\n (clicked)=\"primaryClick.emit()\"\n >\n @if (primaryIcon()) {\n <lc-icon slot=\"icon-before\" [name]=\"primaryIcon()!\" size=\"sm\" [decorative]=\"true\" />\n }\n {{ primaryLabel() }}\n </lc-button>\n }\n }\n </div>\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-next-step-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/next-step-card/next-step-card.component.ts","../../../../libs/angular-ui-kit/src/lib/next-step-card/next-step-card.component.html"],"sourcesContent":["import {\n AfterViewChecked,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n effect,\n input,\n isDevMode,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_RIGHT, ICON_EXTERNAL_LINK, ICON_INFORMATION_CIRCLE } from '@lifecorner/angular-ui-kit/icons';\n\nexport type NextStepCardTone = 'neutral' | 'primary' | 'success' | 'warning' | 'info';\n\n/**\n * Appearance of the card.\n * - `default`: white surface, hairline border, 4 px tonal rail, 36 px tinted bubble\n * - `soft`: tinted fill per tone, radius 24 px (28 px from 1024 px), no rail,\n * border or shadow, a larger white bubble with a 1 px ring (the \"Weite\" look)\n */\nexport type NextStepCardVariant = 'default' | 'soft';\n\n/**\n * How several actions are laid out.\n * - `inline`: side by side at the end of the card; below 480 px card width in\n * their own row, stretched\n * - `stacked`: always in their own row, one per line at equal width, 8 px apart\n */\nexport type NextStepCardActionsLayout = 'inline' | 'stacked';\n\n/** Element that renders the title (semantics only, the look stays the same). */\nexport type NextStepCardTitleTag = 'div' | 'h2' | 'h3';\n\n/** Size of the built-in action buttons (`sm` 32 px, `md` 42 px, `lg` 48.5 px). */\nexport type NextStepCardActionSize = 'sm' | 'md' | 'lg';\n\n/**\n * Next-step card — the single status-and-action surface of a page: \"where does\n * this stand, and what is the one next step?\".\n *\n * Features:\n * - Tonal accent rail (4px left) + tinted icon bubble, token colors only\n * - Title, message (string input or default slot for markup)\n * - Primary action (with loading / disabled + mandatory disabled reason),\n * ghost secondary action, or an external link instead of the primary button\n * - `[slot='blockers']`: rows inside the card under a subtle divider (dot\n * prefix); each projected element with `slot=\"blockers\"` becomes one row —\n * blockers are rows IN the card, never additional banners\n * - `[slot='meta']`: one footer line (icon + text)\n * - `[slot='actions']`: fully replaces the built-in action buttons\n * - `[slot='body']`: content below the message without the message's text\n * styles, so form fields, lists or a description list keep their own look\n * - `role=\"region\"` labelled by the title; optional polite live announcement\n * of title changes via `announce`\n * - Narrow layout: below 480 px of card width (a CSS container query on the\n * host) the icon sits beside the title, message, body, blockers and meta\n * take the full inner width, the inline padding drops to `padding-md`, and\n * the actions take their own row with every action stretched. A projected\n * `fullWidth` button gets its own row at every width.\n * - `variant=\"soft\"`: the tinted card of the \"Weite\" look; `actionsLayout`\n * `stacked` for several equal-width actions; `titleTag` for a heading\n *\n * The host is a size container (`container-type: inline-size`) and takes the\n * full inline size of its parent.\n *\n * Usage rule: exactly ONE next-step card per page.\n *\n * @example\n * ```html\n * <lc-next-step-card\n * tone=\"warning\"\n * icon=\"clock\"\n * cardTitle=\"Wartet auf Freigabe\"\n * message=\"Der Entwurf ist vollständig und kann geprüft werden.\"\n * primaryLabel=\"Freigeben\"\n * secondaryLabel=\"Änderungen anfordern\"\n * (primaryClick)=\"approve()\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-next-step-card',\n standalone: true,\n imports: [ButtonComponent, IconComponent],\n // Its default `icon` and the action glyphs.\n viewProviders: [ɵprovideKitIcons([ICON_INFORMATION_CIRCLE, ICON_CHEVRON_RIGHT, ICON_EXTERNAL_LINK])],\n templateUrl: './next-step-card.component.html',\n styleUrls: ['./next-step-card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NextStepCardComponent implements AfterViewChecked {\n /**\n * Tone of the accent rail and icon bubble. Colors come exclusively from the\n * semantic tokens (`--color-*` rail, `--color-*-subtle` bubble tint,\n * `--color-on-*-subtle` icon ink).\n * @default 'neutral'\n */\n tone = input<NextStepCardTone>('neutral');\n\n /**\n * Appearance: `default` (surface, border, 4 px rail) or `soft` (tinted fill\n * per tone, radius 24 px / 28 px from 1024 px, no rail, border or shadow,\n * a 48 px white bubble with a 1 px ring; 56 px from 1024 px, 60 px from\n * 1440 px). Soft fills come from `--color-next-step-soft-neutral` /\n * `--color-next-step-soft-primary` and the tone's `-subtle` token; the bubble\n * from `--color-bubble`, `--color-bubble-ring`, `--color-bubble-icon`.\n * @default 'default'\n */\n variant = input<NextStepCardVariant>('default');\n\n /** Icon name for the round bubble (36 px; 48–60 px in the soft variant). */\n icon = input<string>('information-circle');\n\n /** Title (1rem semibold; 20–28 px bold in the soft variant). Also the card's accessible name. */\n cardTitle = input.required<string>();\n\n /**\n * Element that renders the title: `div` (default), `h2` or `h3`. Use a\n * heading when the page has sections, so heading navigation reaches the\n * most important item. Semantics only, the look stays the same.\n * @default 'div'\n */\n titleTag = input<NextStepCardTitleTag>('div');\n\n /**\n * Layout of several actions. `inline`: side by side at the end of the card\n * (below 480 px card width in their own row, stretched). `stacked`: always\n * in their own row, one per line at equal width, 8 px apart (e.g. a list of\n * sign-in methods).\n * @default 'inline'\n */\n actionsLayout = input<NextStepCardActionsLayout>('inline');\n\n /**\n * Size of the built-in action buttons and link: `sm` (32 px, the 3.2\n * default), `md` (42 px) or `lg` (48.5 px, meets the 44 px touch target).\n * @default 'sm'\n */\n actionSize = input<NextStepCardActionSize>('sm');\n\n /** Message below the title. For markup, use the default slot instead. */\n message = input<string>();\n\n /** Label of the primary action. Omit to render no primary button. */\n primaryLabel = input<string>();\n\n /** Optional leading icon of the primary action. */\n primaryIcon = input<string>();\n\n /** Shows a spinner on the primary action. */\n primaryLoading = input<boolean>(false);\n\n /**\n * Disables the primary action. A disabled primary REQUIRES\n * `primaryDisabledReason`; disabled without a reason logs a dev warning.\n */\n primaryDisabled = input<boolean>(false);\n\n /** Why the primary action is disabled — shown as tooltip on the button. */\n primaryDisabledReason = input<string>('');\n\n /** Label of the ghost secondary action (rendered with a chevron). */\n secondaryLabel = input<string>();\n\n /**\n * Label of an external link rendered instead of the primary button\n * (opens in a new tab). Requires `linkHref`.\n */\n linkLabel = input<string>();\n\n /** Target URL of the external link. */\n linkHref = input<string>();\n\n /**\n * Where the link opens: `_blank` (a new tab, with `rel=\"noopener noreferrer\"`,\n * the 3.2 behaviour) or `_self` (the same tab, no `rel`).\n * @default '_blank'\n */\n linkTarget = input<'_blank' | '_self'>('_blank');\n\n /**\n * Announce title changes to screen readers via `aria-live=\"polite\"` —\n * for cards whose state advances in place.\n * @default false\n */\n announce = input<boolean>(false);\n\n /** Emits when the primary action is clicked. */\n primaryClick = output<void>();\n\n /** Emits when the secondary action is clicked. */\n secondaryClick = output<void>();\n\n private actionsSlot = viewChild<ElementRef<HTMLElement>>('actionsSlot');\n\n /**\n * Whether content was projected into `[slot='actions']` — projected actions\n * fully replace the built-in buttons.\n */\n protected hasProjectedActions = signal(false);\n\n protected cardClasses = computed(() =>\n [\n 'lc-next-step-card',\n `lc-next-step-card--${this.tone()}`,\n this.variant() === 'soft' ? 'lc-next-step-card--soft' : null,\n this.actionsLayout() === 'stacked' ? 'lc-next-step-card--actions-stacked' : null,\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n /** `rel` of the external link: only a new tab needs `noopener noreferrer`. */\n protected linkRel = computed(() => (this.linkTarget() === '_blank' ? 'noopener noreferrer' : null));\n\n protected showLink = computed(() => !!(this.linkLabel() && this.linkHref()));\n\n /**\n * Whether the card renders any action: hides the empty actions box, so the\n * narrow layout gets no empty row.\n */\n protected hasActions = computed(\n () =>\n this.hasProjectedActions() ||\n !!this.secondaryLabel() ||\n this.showLink() ||\n !!this.primaryLabel(),\n );\n\n constructor() {\n effect(() => {\n if (isDevMode() && this.primaryDisabled() && !this.primaryDisabledReason()) {\n console.warn(\n '[lc-next-step-card] `primaryDisabled` is set without a `primaryDisabledReason`. ' +\n 'A disabled primary action must explain why (shown as tooltip).',\n );\n }\n });\n }\n\n ngAfterViewChecked(): void {\n // Projected [slot='actions'] content replaces the built-in buttons. The\n // signal write is equality-guarded, so unchanged checks are no-ops.\n const el = this.actionsSlot()?.nativeElement;\n this.hasProjectedActions.set(!!el && el.childElementCount > 0);\n }\n}\n","<div [class]=\"cardClasses()\" role=\"region\" [attr.aria-label]=\"cardTitle()\">\n <div class=\"lc-next-step-card__icon\" aria-hidden=\"true\">\n <lc-icon [name]=\"icon()\" size=\"md\" [decorative]=\"true\" />\n </div>\n\n <div class=\"lc-next-step-card__body\">\n @switch (titleTag()) {\n @case ('h2') {\n <h2 class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </h3>\n }\n @default {\n <div class=\"lc-next-step-card__title\" [attr.aria-live]=\"announce() ? 'polite' : null\">\n {{ cardTitle() }}\n </div>\n }\n }\n @if (message()) {\n <p class=\"lc-next-step-card__message\">{{ message() }}</p>\n }\n <div class=\"lc-next-step-card__custom-message\">\n <ng-content />\n </div>\n <!-- Form-safe body: no inherited message text styles. -->\n <div class=\"lc-next-step-card__content\">\n <ng-content select=\"[slot='body']\" />\n </div>\n <div class=\"lc-next-step-card__blockers\">\n <ng-content select=\"[slot='blockers']\" />\n </div>\n <div class=\"lc-next-step-card__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n </div>\n\n <div class=\"lc-next-step-card__actions\" [class.lc-next-step-card__actions--empty]=\"!hasActions()\">\n <div #actionsSlot class=\"lc-next-step-card__actions-slot\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n @if (!hasProjectedActions()) {\n @if (secondaryLabel()) {\n <lc-button variant=\"ghost\" [size]=\"actionSize()\" (clicked)=\"secondaryClick.emit()\">\n {{ secondaryLabel() }}\n <lc-icon slot=\"icon-after\" name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </lc-button>\n }\n @if (showLink()) {\n <a\n class=\"btn btn-secondary btn-{{ actionSize() }} lc-next-step-card__link\"\n [href]=\"linkHref()\"\n [attr.target]=\"linkTarget()\"\n [attr.rel]=\"linkRel()\"\n >\n <lc-icon name=\"external-link\" size=\"sm\" [decorative]=\"true\" />\n {{ linkLabel() }}\n </a>\n } @else if (primaryLabel()) {\n <lc-button\n variant=\"primary\"\n [size]=\"actionSize()\"\n [loading]=\"primaryLoading()\"\n [disabled]=\"primaryDisabled()\"\n [disabledReason]=\"primaryDisabledReason()\"\n (clicked)=\"primaryClick.emit()\"\n >\n @if (primaryIcon()) {\n <lc-icon slot=\"icon-before\" [name]=\"primaryIcon()!\" size=\"sm\" [decorative]=\"true\" />\n }\n {{ primaryLabel() }}\n </lc-button>\n }\n }\n </div>\n</div>\n"],"mappings":"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"}
|
|
@@ -124,7 +124,7 @@ var PageHeaderComponent = class PageHeaderComponent {
|
|
|
124
124
|
host: { properties: { "class": "hostClasses()" } },
|
|
125
125
|
ngImport: i0,
|
|
126
126
|
template: "@if (true) {\n <div class=\"lc-page-header__breadcrumbs\">\n <ng-content select=\"[slot='breadcrumbs']\" />\n </div>\n\n <div class=\"lc-page-header__row\">\n @if (icon()) {\n <div\n class=\"lc-page-header__icon lc-page-header__icon--{{ iconVariant() }} lc-page-header__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"lc-page-header__titles\">\n <div class=\"lc-page-header__title-row\">\n @if (title()) {\n <!-- One projection slot for the suffix: a slot inside a non-rendered\n @switch branch would swallow it for levels 2 and 3. -->\n <ng-template #titleSuffix><ng-content select=\"[slot='title-suffix']\" /></ng-template>\n @switch (level()) {\n @case (1) {\n <h1 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h1>\n }\n @case (2) {\n <h2 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h2>\n }\n @case (3) {\n <h3 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h3>\n }\n }\n <!-- The overline follows the heading in the DOM (read after it) and\n is shown above it (order: -1 and a full line in the wrapping row). -->\n @if (overline()) {\n <p class=\"lc-page-header__overline lc-overline\">{{ overline() }}</p>\n }\n }\n\n <div class=\"lc-page-header__actions\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n </div>\n\n @if (subtitle()) {\n <p class=\"lc-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n </div>\n\n <div class=\"lc-page-header__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n\n <div class=\"lc-page-header__body\">\n <ng-content />\n </div>\n}\n",
|
|
127
|
-
styles: [".lc-page-header{display:flex;flex-direction:column;gap:var(--lc-density-gap-md, 1rem);padding-block:var(--lc-density-padding-md, 1.5rem);padding-inline:var(--lc-density-padding-md, 1rem);box-sizing:border-box;width:100%;min-width:0}@media(min-width:640px){.lc-page-header{padding-inline:var(--lc-density-padding-lg, 1.5rem)}}@media(min-width:1024px){.lc-page-header{padding-inline:var(--lc-density-padding-xl, 2rem)}}.lc-page-header--flush-x{padding-inline:0}.lc-page-header__breadcrumbs,.lc-page-header__body{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__meta{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__breadcrumbs:empty,.lc-page-header__meta:empty,.lc-page-header__body:empty,.lc-page-header__actions:empty{display:none}.lc-page-header__row{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, 1rem);flex-wrap:wrap;min-width:0}.lc-page-header__titles{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, .25rem);min-width:0;flex:1 1 auto}.lc-page-header__title-row{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:var(--lc-density-gap-md, 1rem);min-width:0}.lc-page-header__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:var(--border-radius-md)}.lc-page-header__icon--size-sm{width:2rem;height:2rem;border-radius:var(--border-radius-sm)}.lc-page-header__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.lc-page-header__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.lc-page-header__title{margin:0;font-family:var(--font-family-sans, inherit);font-weight:var(--font-weight-bold, 700);color:var(--color-text-primary, var(--color-neutral-900, #111));line-height:var(--line-height-tight, 1.2);display:inline-flex;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0;hyphens:var(--lc-page-header-title-hyphens, auto);overflow-wrap:
|
|
127
|
+
styles: [".lc-page-header{display:flex;flex-direction:column;gap:var(--lc-density-gap-md, 1rem);padding-block:var(--lc-density-padding-md, 1.5rem);padding-inline:var(--lc-density-padding-md, 1rem);box-sizing:border-box;width:100%;min-width:0}@media(min-width:640px){.lc-page-header{padding-inline:var(--lc-density-padding-lg, 1.5rem)}}@media(min-width:1024px){.lc-page-header{padding-inline:var(--lc-density-padding-xl, 2rem)}}.lc-page-header--flush-x{padding-inline:0}.lc-page-header__breadcrumbs,.lc-page-header__body{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__meta{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__breadcrumbs:empty,.lc-page-header__meta:empty,.lc-page-header__body:empty,.lc-page-header__actions:empty{display:none}.lc-page-header__row{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, 1rem);flex-wrap:wrap;min-width:0}.lc-page-header__titles{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, .25rem);min-width:0;flex:1 1 auto}.lc-page-header__title-row{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:var(--lc-density-gap-md, 1rem);min-width:0}.lc-page-header__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:var(--border-radius-md)}.lc-page-header__icon--size-sm{width:2rem;height:2rem;border-radius:var(--border-radius-sm)}.lc-page-header__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.lc-page-header__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.lc-page-header__title{margin:0;font-family:var(--font-family-sans, inherit);font-weight:var(--font-weight-bold, 700);color:var(--color-text-primary, var(--color-neutral-900, #111));line-height:var(--line-height-tight, 1.2);display:inline-flex;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0;hyphens:var(--lc-page-header-title-hyphens, auto);overflow-wrap:anywhere}.lc-page-header__subtitle{margin:0;display:var(--lc-page-header-subtitle-display, -webkit-box);-webkit-box-orient:vertical;-webkit-line-clamp:var(--lc-page-header-subtitle-line-clamp, none);overflow:hidden;font-family:var(--font-family-sans, inherit);color:var(--lc-page-header-subtitle-fg, var(--color-text-secondary, var(--color-neutral-600, #666)));line-height:var(--line-height-normal, 1.5)}.lc-page-header__title-suffix{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem)}.lc-page-header__title-suffix:empty{display:none}.lc-page-header__overline{order:-1;flex:1 0 100%;margin:0;min-width:0;font-family:var(--font-family-sans, inherit);font-size:var(--font-size-overline, .8125rem);font-weight:var(--font-weight-bold, 700);line-height:var(--line-height-overline, 1.3);letter-spacing:var(--letter-spacing-overline, .12em);text-transform:uppercase;color:var(--color-overline, var(--color-primary-700));overflow-wrap:break-word}.lc-page-header--has-overline .lc-page-header__title-row{row-gap:.75rem}.lc-page-header__badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:var(--border-radius-full, 999px);background:var(--color-primary-100, #e0f2fe);color:var(--color-primary-700, #0369a1);font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);line-height:1.4;text-transform:uppercase;letter-spacing:.04em}.lc-page-header__actions{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);flex:0 0 auto;justify-content:flex-end;margin-inline-start:auto;max-width:100%;--lc-btn-overflow-wrap: anywhere}.lc-page-header--size-compact{--lc-density-gap-md: .5rem;--lc-density-gap-sm: .375rem;--lc-density-padding-md: .75rem}.lc-page-header--size-compact .lc-page-header__title{font-size:var(--font-size-lg, 1.125rem)}.lc-page-header--size-compact .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-sm, .875rem))}.lc-page-header--size-secondary{--lc-density-gap-md: .5rem;--lc-density-gap-sm: .375rem;--lc-density-padding-md: .75rem}.lc-page-header--size-secondary .lc-page-header__title{font-size:var(--font-size-xl, 1.25rem)}.lc-page-header--size-secondary .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-sm, .875rem))}.lc-page-header--size-default .lc-page-header__title{font-size:var(--font-size-2xl, 1.5rem)}.lc-page-header--size-default .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-base, 1rem))}.lc-page-header--size-comfortable{--lc-density-gap-md: 1.5rem;--lc-density-padding-md: 2.5rem}.lc-page-header--size-comfortable .lc-page-header__title{font-size:var(--font-size-3xl, 1.875rem)}.lc-page-header--size-comfortable .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-title .lc-page-header__title{font-size:var(--font-size-title, 2rem);line-height:var(--line-height-title, 1.08);letter-spacing:var(--letter-spacing-title, -.03em)}.lc-page-header--size-title .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-display .lc-page-header__title{font-size:var(--font-size-display, 2.5rem);line-height:var(--line-height-display, 1.05);letter-spacing:var(--letter-spacing-display, -.035em)}.lc-page-header--size-display .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-auth .lc-page-header__title{font-size:var(--font-size-auth-title, 1.875rem);line-height:var(--line-height-auth-title, 1.12);letter-spacing:var(--letter-spacing-auth-title, -.03em)}.lc-page-header--size-auth .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-base, 1rem))}.lc-page-header--divided{border-bottom:1px solid var(--color-border, var(--color-neutral-200, #e5e5e5))}\n"],
|
|
128
128
|
dependencies: [{
|
|
129
129
|
kind: "component",
|
|
130
130
|
type: IconComponent,
|
|
@@ -167,7 +167,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
167
167
|
encapsulation: ViewEncapsulation.None,
|
|
168
168
|
host: { "[class]": "hostClasses()" },
|
|
169
169
|
template: "@if (true) {\n <div class=\"lc-page-header__breadcrumbs\">\n <ng-content select=\"[slot='breadcrumbs']\" />\n </div>\n\n <div class=\"lc-page-header__row\">\n @if (icon()) {\n <div\n class=\"lc-page-header__icon lc-page-header__icon--{{ iconVariant() }} lc-page-header__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"lc-page-header__titles\">\n <div class=\"lc-page-header__title-row\">\n @if (title()) {\n <!-- One projection slot for the suffix: a slot inside a non-rendered\n @switch branch would swallow it for levels 2 and 3. -->\n <ng-template #titleSuffix><ng-content select=\"[slot='title-suffix']\" /></ng-template>\n @switch (level()) {\n @case (1) {\n <h1 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h1>\n }\n @case (2) {\n <h2 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h2>\n }\n @case (3) {\n <h3 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h3>\n }\n }\n <!-- The overline follows the heading in the DOM (read after it) and\n is shown above it (order: -1 and a full line in the wrapping row). -->\n @if (overline()) {\n <p class=\"lc-page-header__overline lc-overline\">{{ overline() }}</p>\n }\n }\n\n <div class=\"lc-page-header__actions\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n </div>\n\n @if (subtitle()) {\n <p class=\"lc-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n </div>\n\n <div class=\"lc-page-header__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n\n <div class=\"lc-page-header__body\">\n <ng-content />\n </div>\n}\n",
|
|
170
|
-
styles: [".lc-page-header{display:flex;flex-direction:column;gap:var(--lc-density-gap-md, 1rem);padding-block:var(--lc-density-padding-md, 1.5rem);padding-inline:var(--lc-density-padding-md, 1rem);box-sizing:border-box;width:100%;min-width:0}@media(min-width:640px){.lc-page-header{padding-inline:var(--lc-density-padding-lg, 1.5rem)}}@media(min-width:1024px){.lc-page-header{padding-inline:var(--lc-density-padding-xl, 2rem)}}.lc-page-header--flush-x{padding-inline:0}.lc-page-header__breadcrumbs,.lc-page-header__body{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__meta{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__breadcrumbs:empty,.lc-page-header__meta:empty,.lc-page-header__body:empty,.lc-page-header__actions:empty{display:none}.lc-page-header__row{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, 1rem);flex-wrap:wrap;min-width:0}.lc-page-header__titles{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, .25rem);min-width:0;flex:1 1 auto}.lc-page-header__title-row{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:var(--lc-density-gap-md, 1rem);min-width:0}.lc-page-header__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:var(--border-radius-md)}.lc-page-header__icon--size-sm{width:2rem;height:2rem;border-radius:var(--border-radius-sm)}.lc-page-header__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.lc-page-header__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.lc-page-header__title{margin:0;font-family:var(--font-family-sans, inherit);font-weight:var(--font-weight-bold, 700);color:var(--color-text-primary, var(--color-neutral-900, #111));line-height:var(--line-height-tight, 1.2);display:inline-flex;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0;hyphens:var(--lc-page-header-title-hyphens, auto);overflow-wrap:
|
|
170
|
+
styles: [".lc-page-header{display:flex;flex-direction:column;gap:var(--lc-density-gap-md, 1rem);padding-block:var(--lc-density-padding-md, 1.5rem);padding-inline:var(--lc-density-padding-md, 1rem);box-sizing:border-box;width:100%;min-width:0}@media(min-width:640px){.lc-page-header{padding-inline:var(--lc-density-padding-lg, 1.5rem)}}@media(min-width:1024px){.lc-page-header{padding-inline:var(--lc-density-padding-xl, 2rem)}}.lc-page-header--flush-x{padding-inline:0}.lc-page-header__breadcrumbs,.lc-page-header__body{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__meta{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0}.lc-page-header__breadcrumbs:empty,.lc-page-header__meta:empty,.lc-page-header__body:empty,.lc-page-header__actions:empty{display:none}.lc-page-header__row{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, 1rem);flex-wrap:wrap;min-width:0}.lc-page-header__titles{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, .25rem);min-width:0;flex:1 1 auto}.lc-page-header__title-row{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:var(--lc-density-gap-md, 1rem);min-width:0}.lc-page-header__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:var(--border-radius-md)}.lc-page-header__icon--size-sm{width:2rem;height:2rem;border-radius:var(--border-radius-sm)}.lc-page-header__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.lc-page-header__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.lc-page-header__title{margin:0;font-family:var(--font-family-sans, inherit);font-weight:var(--font-weight-bold, 700);color:var(--color-text-primary, var(--color-neutral-900, #111));line-height:var(--line-height-tight, 1.2);display:inline-flex;align-items:center;gap:var(--lc-density-gap-sm, .5rem);min-width:0;hyphens:var(--lc-page-header-title-hyphens, auto);overflow-wrap:anywhere}.lc-page-header__subtitle{margin:0;display:var(--lc-page-header-subtitle-display, -webkit-box);-webkit-box-orient:vertical;-webkit-line-clamp:var(--lc-page-header-subtitle-line-clamp, none);overflow:hidden;font-family:var(--font-family-sans, inherit);color:var(--lc-page-header-subtitle-fg, var(--color-text-secondary, var(--color-neutral-600, #666)));line-height:var(--line-height-normal, 1.5)}.lc-page-header__title-suffix{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem)}.lc-page-header__title-suffix:empty{display:none}.lc-page-header__overline{order:-1;flex:1 0 100%;margin:0;min-width:0;font-family:var(--font-family-sans, inherit);font-size:var(--font-size-overline, .8125rem);font-weight:var(--font-weight-bold, 700);line-height:var(--line-height-overline, 1.3);letter-spacing:var(--letter-spacing-overline, .12em);text-transform:uppercase;color:var(--color-overline, var(--color-primary-700));overflow-wrap:break-word}.lc-page-header--has-overline .lc-page-header__title-row{row-gap:.75rem}.lc-page-header__badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:var(--border-radius-full, 999px);background:var(--color-primary-100, #e0f2fe);color:var(--color-primary-700, #0369a1);font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);line-height:1.4;text-transform:uppercase;letter-spacing:.04em}.lc-page-header__actions{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:var(--lc-density-gap-sm, .5rem);flex:0 0 auto;justify-content:flex-end;margin-inline-start:auto;max-width:100%;--lc-btn-overflow-wrap: anywhere}.lc-page-header--size-compact{--lc-density-gap-md: .5rem;--lc-density-gap-sm: .375rem;--lc-density-padding-md: .75rem}.lc-page-header--size-compact .lc-page-header__title{font-size:var(--font-size-lg, 1.125rem)}.lc-page-header--size-compact .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-sm, .875rem))}.lc-page-header--size-secondary{--lc-density-gap-md: .5rem;--lc-density-gap-sm: .375rem;--lc-density-padding-md: .75rem}.lc-page-header--size-secondary .lc-page-header__title{font-size:var(--font-size-xl, 1.25rem)}.lc-page-header--size-secondary .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-sm, .875rem))}.lc-page-header--size-default .lc-page-header__title{font-size:var(--font-size-2xl, 1.5rem)}.lc-page-header--size-default .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-base, 1rem))}.lc-page-header--size-comfortable{--lc-density-gap-md: 1.5rem;--lc-density-padding-md: 2.5rem}.lc-page-header--size-comfortable .lc-page-header__title{font-size:var(--font-size-3xl, 1.875rem)}.lc-page-header--size-comfortable .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-title .lc-page-header__title{font-size:var(--font-size-title, 2rem);line-height:var(--line-height-title, 1.08);letter-spacing:var(--letter-spacing-title, -.03em)}.lc-page-header--size-title .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-display .lc-page-header__title{font-size:var(--font-size-display, 2.5rem);line-height:var(--line-height-display, 1.05);letter-spacing:var(--letter-spacing-display, -.035em)}.lc-page-header--size-display .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-lg, 1.125rem))}.lc-page-header--size-auth .lc-page-header__title{font-size:var(--font-size-auth-title, 1.875rem);line-height:var(--line-height-auth-title, 1.12);letter-spacing:var(--letter-spacing-auth-title, -.03em)}.lc-page-header--size-auth .lc-page-header__subtitle{font-size:var(--lc-page-header-subtitle-font-size, var(--font-size-base, 1rem))}.lc-page-header--divided{border-bottom:1px solid var(--color-border, var(--color-neutral-200, #e5e5e5))}\n"]
|
|
171
171
|
}]
|
|
172
172
|
}],
|
|
173
173
|
propDecorators: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-page-header.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/page-header/page-header.component.ts","../../../../libs/angular-ui-kit/src/lib/page-header/page-header.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/**\n * Visual size of the page header.\n * - `compact` — dense data-tool pages and section headings (18 px)\n * - `secondary` — a second title under the page's main one, e.g. the other\n * language of a bilingual page (20 px, `--font-size-xl`;\n * the `compact` spacing; 3.9, AS-25)\n * - `default` — standard app pages (24 px)\n * - `comfortable` — landing-page hero feel (30 px)\n * - `title` — the \"Weite\" page title: 32 px on phones, 40 / 44 / 48 px\n * from 1024 / 1440 / 1920 px (`--font-size-title`)\n * - `display` — the display greeting: 40 px on phones, 52 / 56 / 60 / 72 px\n * from 1024 / 1280 / 1440 / 1920 px (`--font-size-display`)\n * - `auth` — the sign-in and registration `<h1>`: 30 px, 34 px from\n * 1024 px (`--font-size-auth-title`)\n */\nexport type PageHeaderSize = 'compact' | 'secondary' | 'default' | 'comfortable' | 'title' | 'display' | 'auth';\n\n/**\n * Page header — top-of-page title block with optional subtitle,\n * breadcrumbs, badge, description and an actions area.\n *\n * Density-aware, slot-based, no Tailwind. Drop it at the top of any route\n * to get consistent rhythm between title, metadata and content below.\n *\n * Slots:\n * - `[slot=\"breadcrumbs\"]` — rendered above the title (e.g. `<lc-breadcrumbs>`)\n * - `[slot=\"title-suffix\"]` — inline beside the title, after the optional `badge`\n * (for one or more status chips/badges)\n * - `[slot=\"actions\"]` — right-aligned actions (buttons, menus)\n * - `[slot=\"meta\"]` — secondary metadata under the title row (chips, tags)\n * - default — short header-internal description text\n *\n * `overline` renders a short uppercase label (a date, a section) **above** the\n * title but **after** it in the DOM, so a screen reader that lands on the\n * heading reads the title first and the overline next.\n *\n * Full-blown navigation primitives like `lc-tabs` — which render both the tab\n * strip **and** their panel content — belong **below** the page header, never\n * inside it. The header stays focused on page identity.\n *\n * @example\n * ```html\n * <lc-page-header\n * title=\"Reports\"\n * subtitle=\"Compare period-over-period metrics\"\n * [showDivider]=\"true\"\n * >\n * <lc-breadcrumbs slot=\"breadcrumbs\" [items]=\"crumbs\" />\n * <lc-chip slot=\"title-suffix\" size=\"sm\">Beta</lc-chip>\n * <lc-chip slot=\"title-suffix\" size=\"sm\" variant=\"success\">Ready</lc-chip>\n * <lc-button slot=\"actions\" variant=\"primary\">New report</lc-button>\n * <lc-chip slot=\"meta\">12 active</lc-chip>\n * </lc-page-header>\n * ```\n */\n@Component({\n selector: 'lc-page-header',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet],\n templateUrl: './page-header.component.html',\n styleUrl: './page-header.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'hostClasses()',\n },\n})\nexport class PageHeaderComponent {\n /** Main heading text. */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a brand tile to the\n * left of the title block, mirroring `lc-card`'s header icon. Purely additive.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /** Short supporting line below the title. */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Short label shown above the title (e.g. the date on a daily view, a\n * section name): uppercase through CSS (write it in sentence case), bold,\n * letter spacing 0.12 em, in `--color-overline`; 13 px on phones, 14 / 15 /\n * 16 px from 1024 / 1440 / 1920 px. Rendered right after the heading in the\n * DOM, so the heading is read first.\n */\n overline = input<string | undefined>(undefined);\n\n /**\n * Heading level for the title — affects semantics, not size.\n * @default 1\n */\n level = input<1 | 2 | 3>(1);\n\n /**\n * Visual size of the page header.\n * - `compact` — dense data-tool pages and section headings (18 px)\n * - `secondary` — a second title under the main one, e.g. the other\n * language of a bilingual page (20 px, compact spacing)\n * - `default` — standard app pages (24 px)\n * - `comfortable` — landing-page hero feel (30 px)\n * - `title` — page title on the responsive title scale, 32–48 px\n * - `display` — the display greeting, 40–72 px, weight 700\n * - `auth` — the sign-in and registration `<h1>`, 30 / 34 px\n * @default 'default'\n */\n size = input<PageHeaderSize>('default');\n\n /**\n * Whether to render a divider line below the header.\n * @default false\n */\n showDivider = input<boolean>(false);\n\n /** Optional badge text next to the title (e.g. `Beta`). */\n badge = input<string | undefined>(undefined);\n\n /**\n * Remove the header's own horizontal padding so its content sits flush with\n * the container edge. Use when the header is already inside a padded wrapper\n * (e.g. `<lc-container>`) and you'd otherwise get a double inset. The divider,\n * when shown, always spans the full width regardless of this setting.\n * @default false\n */\n noPaddingX = input<boolean>(false);\n\n /**\n * Classes of the heading: the global type-scale class (`lc-title`,\n * `lc-display`, `lc-auth-title`) joins the component class for the 3.3 sizes.\n */\n protected titleClasses = computed(() => {\n const scale: Partial<Record<PageHeaderSize, string>> = {\n title: 'lc-title',\n display: 'lc-display',\n auth: 'lc-auth-title',\n };\n return ['lc-page-header__title', scale[this.size()]].filter(Boolean).join(' ');\n });\n\n protected hostClasses = computed(() =>\n [\n 'lc-page-header',\n `lc-page-header--size-${this.size()}`,\n this.showDivider() ? 'lc-page-header--divided' : null,\n this.overline() ? 'lc-page-header--has-overline' : null,\n this.noPaddingX() ? 'lc-page-header--flush-x' : null,\n ]\n .filter(Boolean)\n .join(' '),\n );\n}\n","@if (true) {\n <div class=\"lc-page-header__breadcrumbs\">\n <ng-content select=\"[slot='breadcrumbs']\" />\n </div>\n\n <div class=\"lc-page-header__row\">\n @if (icon()) {\n <div\n class=\"lc-page-header__icon lc-page-header__icon--{{ iconVariant() }} lc-page-header__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"lc-page-header__titles\">\n <div class=\"lc-page-header__title-row\">\n @if (title()) {\n <!-- One projection slot for the suffix: a slot inside a non-rendered\n @switch branch would swallow it for levels 2 and 3. -->\n <ng-template #titleSuffix><ng-content select=\"[slot='title-suffix']\" /></ng-template>\n @switch (level()) {\n @case (1) {\n <h1 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h1>\n }\n @case (2) {\n <h2 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h2>\n }\n @case (3) {\n <h3 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h3>\n }\n }\n <!-- The overline follows the heading in the DOM (read after it) and\n is shown above it (order: -1 and a full line in the wrapping row). -->\n @if (overline()) {\n <p class=\"lc-page-header__overline lc-overline\">{{ overline() }}</p>\n }\n }\n\n <div class=\"lc-page-header__actions\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n </div>\n\n @if (subtitle()) {\n <p class=\"lc-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n </div>\n\n <div class=\"lc-page-header__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n\n <div class=\"lc-page-header__body\">\n <ng-content />\n </div>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-page-header.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/page-header/page-header.component.ts","../../../../libs/angular-ui-kit/src/lib/page-header/page-header.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/**\n * Visual size of the page header.\n * - `compact` — dense data-tool pages and section headings (18 px)\n * - `secondary` — a second title under the page's main one, e.g. the other\n * language of a bilingual page (20 px, `--font-size-xl`;\n * the `compact` spacing; 3.9, AS-25)\n * - `default` — standard app pages (24 px)\n * - `comfortable` — landing-page hero feel (30 px)\n * - `title` — the \"Weite\" page title: 32 px on phones, 40 / 44 / 48 px\n * from 1024 / 1440 / 1920 px (`--font-size-title`)\n * - `display` — the display greeting: 40 px on phones, 52 / 56 / 60 / 72 px\n * from 1024 / 1280 / 1440 / 1920 px (`--font-size-display`)\n * - `auth` — the sign-in and registration `<h1>`: 30 px, 34 px from\n * 1024 px (`--font-size-auth-title`)\n */\nexport type PageHeaderSize = 'compact' | 'secondary' | 'default' | 'comfortable' | 'title' | 'display' | 'auth';\n\n/**\n * Page header — top-of-page title block with optional subtitle,\n * breadcrumbs, badge, description and an actions area.\n *\n * Density-aware, slot-based, no Tailwind. Drop it at the top of any route\n * to get consistent rhythm between title, metadata and content below.\n *\n * Slots:\n * - `[slot=\"breadcrumbs\"]` — rendered above the title (e.g. `<lc-breadcrumbs>`)\n * - `[slot=\"title-suffix\"]` — inline beside the title, after the optional `badge`\n * (for one or more status chips/badges)\n * - `[slot=\"actions\"]` — right-aligned actions (buttons, menus)\n * - `[slot=\"meta\"]` — secondary metadata under the title row (chips, tags)\n * - default — short header-internal description text\n *\n * `overline` renders a short uppercase label (a date, a section) **above** the\n * title but **after** it in the DOM, so a screen reader that lands on the\n * heading reads the title first and the overline next.\n *\n * Full-blown navigation primitives like `lc-tabs` — which render both the tab\n * strip **and** their panel content — belong **below** the page header, never\n * inside it. The header stays focused on page identity.\n *\n * @example\n * ```html\n * <lc-page-header\n * title=\"Reports\"\n * subtitle=\"Compare period-over-period metrics\"\n * [showDivider]=\"true\"\n * >\n * <lc-breadcrumbs slot=\"breadcrumbs\" [items]=\"crumbs\" />\n * <lc-chip slot=\"title-suffix\" size=\"sm\">Beta</lc-chip>\n * <lc-chip slot=\"title-suffix\" size=\"sm\" variant=\"success\">Ready</lc-chip>\n * <lc-button slot=\"actions\" variant=\"primary\">New report</lc-button>\n * <lc-chip slot=\"meta\">12 active</lc-chip>\n * </lc-page-header>\n * ```\n */\n@Component({\n selector: 'lc-page-header',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet],\n templateUrl: './page-header.component.html',\n styleUrl: './page-header.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'hostClasses()',\n },\n})\nexport class PageHeaderComponent {\n /** Main heading text. */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a brand tile to the\n * left of the title block, mirroring `lc-card`'s header icon. Purely additive.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /** Short supporting line below the title. */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Short label shown above the title (e.g. the date on a daily view, a\n * section name): uppercase through CSS (write it in sentence case), bold,\n * letter spacing 0.12 em, in `--color-overline`; 13 px on phones, 14 / 15 /\n * 16 px from 1024 / 1440 / 1920 px. Rendered right after the heading in the\n * DOM, so the heading is read first.\n */\n overline = input<string | undefined>(undefined);\n\n /**\n * Heading level for the title — affects semantics, not size.\n * @default 1\n */\n level = input<1 | 2 | 3>(1);\n\n /**\n * Visual size of the page header.\n * - `compact` — dense data-tool pages and section headings (18 px)\n * - `secondary` — a second title under the main one, e.g. the other\n * language of a bilingual page (20 px, compact spacing)\n * - `default` — standard app pages (24 px)\n * - `comfortable` — landing-page hero feel (30 px)\n * - `title` — page title on the responsive title scale, 32–48 px\n * - `display` — the display greeting, 40–72 px, weight 700\n * - `auth` — the sign-in and registration `<h1>`, 30 / 34 px\n * @default 'default'\n */\n size = input<PageHeaderSize>('default');\n\n /**\n * Whether to render a divider line below the header.\n * @default false\n */\n showDivider = input<boolean>(false);\n\n /** Optional badge text next to the title (e.g. `Beta`). */\n badge = input<string | undefined>(undefined);\n\n /**\n * Remove the header's own horizontal padding so its content sits flush with\n * the container edge. Use when the header is already inside a padded wrapper\n * (e.g. `<lc-container>`) and you'd otherwise get a double inset. The divider,\n * when shown, always spans the full width regardless of this setting.\n * @default false\n */\n noPaddingX = input<boolean>(false);\n\n /**\n * Classes of the heading: the global type-scale class (`lc-title`,\n * `lc-display`, `lc-auth-title`) joins the component class for the 3.3 sizes.\n */\n protected titleClasses = computed(() => {\n const scale: Partial<Record<PageHeaderSize, string>> = {\n title: 'lc-title',\n display: 'lc-display',\n auth: 'lc-auth-title',\n };\n return ['lc-page-header__title', scale[this.size()]].filter(Boolean).join(' ');\n });\n\n protected hostClasses = computed(() =>\n [\n 'lc-page-header',\n `lc-page-header--size-${this.size()}`,\n this.showDivider() ? 'lc-page-header--divided' : null,\n this.overline() ? 'lc-page-header--has-overline' : null,\n this.noPaddingX() ? 'lc-page-header--flush-x' : null,\n ]\n .filter(Boolean)\n .join(' '),\n );\n}\n","@if (true) {\n <div class=\"lc-page-header__breadcrumbs\">\n <ng-content select=\"[slot='breadcrumbs']\" />\n </div>\n\n <div class=\"lc-page-header__row\">\n @if (icon()) {\n <div\n class=\"lc-page-header__icon lc-page-header__icon--{{ iconVariant() }} lc-page-header__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"lc-page-header__titles\">\n <div class=\"lc-page-header__title-row\">\n @if (title()) {\n <!-- One projection slot for the suffix: a slot inside a non-rendered\n @switch branch would swallow it for levels 2 and 3. -->\n <ng-template #titleSuffix><ng-content select=\"[slot='title-suffix']\" /></ng-template>\n @switch (level()) {\n @case (1) {\n <h1 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h1>\n }\n @case (2) {\n <h2 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h2>\n }\n @case (3) {\n <h3 [class]=\"titleClasses()\">\n {{ title() }}\n @if (badge()) {\n <span class=\"lc-page-header__badge\">{{ badge() }}</span>\n }\n <span class=\"lc-page-header__title-suffix\">\n <ng-container [ngTemplateOutlet]=\"titleSuffix\" />\n </span>\n </h3>\n }\n }\n <!-- The overline follows the heading in the DOM (read after it) and\n is shown above it (order: -1 and a full line in the wrapping row). -->\n @if (overline()) {\n <p class=\"lc-page-header__overline lc-overline\">{{ overline() }}</p>\n }\n }\n\n <div class=\"lc-page-header__actions\">\n <ng-content select=\"[slot='actions']\" />\n </div>\n </div>\n\n @if (subtitle()) {\n <p class=\"lc-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n </div>\n\n <div class=\"lc-page-header__meta\">\n <ng-content select=\"[slot='meta']\" />\n </div>\n\n <div class=\"lc-page-header__body\">\n <ng-content />\n </div>\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-sidenav.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/sidenav/sidenav.component.ts","../../../../libs/angular-ui-kit/src/lib/sidenav/sidenav.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n signal,\n computed,\n input,\n output,\n model,\n effect,\n untracked,\n HostListener,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { NgTemplateOutlet, NgStyle } from '@angular/common';\nimport { NavigationEnd, Router, RouterLink } from '@angular/router';\nimport { filter } from 'rxjs';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { NavigationItem } from '@lifecorner/angular-ui-kit/models';\nimport {\n currentItemParents,\n currentNavigationItem,\n navigationLinks,\n navigationPath,\n sortNavigationItems,\n} from '@lifecorner/angular-ui-kit/internal/navigation';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_DOWN, ICON_CHEVRON_LEFT, ICON_CHEVRON_RIGHT } from '@lifecorner/angular-ui-kit/icons';\nimport { BadgeComponent } from '@lifecorner/angular-ui-kit/badge';\nimport { LogoComponent } from '@lifecorner/angular-ui-kit/logo';\n\nexport type SidenavPosition = 'left' | 'right';\nexport type SidenavMode = 'drawer' | 'docked';\n\n/**\n * Surface of the docked sidenav: `default` is the rail on `--color-sidebar`\n * with a border; `transparent` has no background and no border and draws the\n * items as 48 px pills (the side navigation on the page, \"Weite\"). A drawer\n * keeps its surface in both.\n */\nexport type SidenavVariant = 'default' | 'transparent';\n\n/**\n * Sidenav component for application navigation sidebar.\n *\n * Features:\n * - Drawer (overlay) and docked (persistent) modes\n * - Responsive mobile mode — docked automatically switches to drawer below a configurable breakpoint\n * - Collapsed icon-rail mode (narrow 56px sidebar with icons only and hover tooltips)\n * - Integrated logo area with collapse-toggle (sidebar-first layouts)\n * - Hierarchical navigation with collapsible groups (up to 3 levels)\n * - Section headlines for item grouping with optional action buttons\n * - Action buttons on any item (hover-reveal)\n * - Badge support on items (count / status indicator)\n * - One current item, announced and painted alike: the link for `activeRoute`\n * (or, without it, the one the router's URL matches) gets\n * `aria-current=\"page\"` and the current look, every other link neither\n * (AS-16). No hover color change on the current item\n * - Keyboard navigation support (Escape closes drawer)\n * - Configurable width and position (left/right)\n * - Optional backdrop overlay\n * - Theme variants: auto, light, dark (with teal accent for dark mode)\n * - `variant=\"transparent\"`: no rail background or border, items as 48 px\n * pills with 16 px labels, the current one on `--color-surface-selected`\n * - Items are at least 44 px high in every density; the current item's ink is\n * `--color-on-surface-selected` (AA on the selected tint)\n * - Accessible: the inner `<nav>` is the navigation landmark; in drawer mode\n * the panel is a modal dialog (`role=\"dialog\"`, focus moved to the close\n * button on open, trapped while open, restored on close)\n *\n * @example\n * ```html\n * <lc-sidenav\n * [isOpen]=\"isOpen\"\n * mode=\"docked\"\n * [(collapsed)]=\"isCollapsed\"\n * [showLogo]=\"true\"\n * logoSize=\"md\"\n * [mobileBreakpoint]=\"768\"\n * [items]=\"navItems\"\n * activeRoute=\"/dashboard\"\n * theme=\"dark\"\n * (closed)=\"isOpen = false\"\n * (itemClicked)=\"navigate($event)\"\n * (itemAction)=\"onAction($event)\" />\n * ```\n */\n@Component({\n selector: 'lc-sidenav',\n standalone: true,\n imports: [NgTemplateOutlet, NgStyle, RouterLink, A11yModule, IconComponent, BadgeComponent, LogoComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_DOWN, ICON_CHEVRON_LEFT, ICON_CHEVRON_RIGHT])],\n templateUrl: './sidenav.component.html',\n styleUrl: './sidenav.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.lc-sidenav-container]': 'true',\n '[class.lc-sidenav-container--docked]': \"effectiveMode() === 'docked'\",\n '[class.lc-sidenav-container--open]': 'isOpen()',\n },\n})\nexport class SidenavComponent implements OnDestroy {\n private readonly router = inject(Router);\n\n /**\n * The router's URL, updated on every `NavigationEnd`. Before the first\n * navigation it is the router's initial URL (`/`). Read only when\n * `activeRoute` is empty.\n */\n private readonly routerUrl = signal(this.router.url);\n\n /** Whether the sidenav is collapsed to icon-only rail (two-way bindable) */\n readonly collapsed = model(false);\n\n /** Whether to show the logo at the top of the sidenav */\n readonly showLogo = input(false);\n\n /** Custom URL for the full brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoSrc = input('');\n /** Custom URL for the emblem-only brand logo. */\n readonly logoEmblemSrc = input('');\n /** Optional dark-theme URL for the full brand logo. */\n readonly logoDarkSrc = input('');\n /** Optional dark-theme URL for the emblem-only brand logo. */\n readonly logoDarkEmblemSrc = input('');\n /** Alt text for the brand logo. */\n readonly logoAlt = input('');\n\n /** Size of the brand logo (forwarded to inner `<lc-logo>`). Defaults to `md` for a prominent brand block aligned with `<lc-header size=\"md\">` (both 64px). */\n readonly logoSize = input<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('md');\n\n /** Whether the sidenav is open */\n readonly isOpen = input(false);\n\n /** Display mode: 'drawer' (overlay) or 'docked' (persistent sidebar) */\n readonly mode = input<SidenavMode>('drawer');\n\n /** Mobile breakpoint in pixels. Below this width, docked mode switches to drawer. */\n readonly mobileBreakpoint = input(768);\n\n /** Whether the viewport is below the mobile breakpoint */\n readonly isMobile = signal(false);\n\n /** Effective mode: switches docked → drawer on mobile */\n readonly effectiveMode = computed(() => {\n if (this.isMobile() && this.mode() === 'docked') {\n return 'drawer' as SidenavMode;\n }\n return this.mode();\n });\n\n private mediaQuery: MediaQueryList | null = null;\n private mediaHandler = (e: MediaQueryListEvent) => this.isMobile.set(e.matches);\n\n /** Position of the sidenav (left or right) */\n readonly position = input<SidenavPosition>('left');\n\n /** Width of the sidenav drawer */\n readonly width = input('320px');\n\n /**\n * ARIA label of the navigation landmark (the inner `<nav>`) and, in drawer\n * mode, of the dialog panel.\n */\n readonly ariaLabel = input('Side navigation');\n\n /** Whether to show the overlay backdrop */\n readonly hasOverlay = input(true);\n\n /**\n * Navigation items (`NavigationItem[]`): `id`, `icon`, `label`, `route`,\n * `displayOrder` (sort order, 1 = first); optional `children`\n * (`NavigationItem[]`, a collapsible group, up to 3 levels), `isSection`\n * (renders `label` as a section headline over its `children`), `badge`\n * (`{ value: string | number; variant?: 'default' | 'primary' | 'success' |\n * 'warning' | 'error' | 'info' }`), `action` (`{ icon: string; ariaLabel?:\n * string }`, a button that emits `itemAction`), `requiredRole` (for the\n * app's own filtering; not evaluated here). Items link with `routerLink`;\n * exactly one link, the current item (see `activeRoute`), gets\n * `aria-current=\"page\"`.\n * @default []\n */\n readonly items = input<NavigationItem[]>([]);\n\n /**\n * The current route. When set, the first link (in display order) whose\n * `route` equals it is the current item: it alone gets `aria-current=\"page\"`\n * and the current look, whatever the router matches. When empty, the\n * sidenav follows the router: the link whose route equals the URL's path is\n * current, otherwise the one with the longest route the path starts with\n * (`/household` on `/household/members`; `/` only on `/`). Routes are\n * compared as written, so give them as absolute paths (`'/household'`).\n * @default ''\n */\n readonly activeRoute = input('');\n\n /** Theme variant for the sidenav */\n readonly theme = input<'light' | 'dark' | 'auto'>('auto');\n\n /**\n * Surface of the docked sidenav: `default` (the rail with background and\n * border) or `transparent` (no background, no border, items as 48 px pills\n * with radius full and 16 px labels; the current one on\n * `--color-surface-selected`). A drawer keeps its surface.\n */\n readonly variant = input<SidenavVariant>('default');\n\n /** Accessible name of the logo button that collapses/expands the sidebar. */\n readonly toggleAriaLabel = input('Toggle sidebar');\n\n /** Accessible name of the collapse button. Empty: \"Collapse navigation to the left/right\" by `position`. */\n readonly collapseAriaLabel = input('');\n\n /** Tooltip of the collapse button. */\n readonly collapseTitle = input('Collapse sidebar');\n\n /** Accessible name of the close button (drawer mode). */\n readonly closeAriaLabel = input('Close navigation');\n\n /**\n * Ids of the expanded parent items — the single source of truth for\n * `isExpanded`. Seeded with the current item's parent whenever it changes\n * (see constructor), so a group opens on navigation but can still\n * be collapsed by the user. (It used to be OR-ed with `hasActiveChild` at\n * read time, so toggling such a group flipped the set without ever closing\n * it — the set drifted out of sync with what was on screen.)\n */\n readonly expandedItems = signal<Set<string>>(new Set());\n\n /** Ids of the collapsible parents (top-level groups, section sub-groups) that contain the current item. */\n private readonly activeChildParents = computed(() => currentItemParents(this.items(), this.currentItem()));\n\n /**\n * Computed sorted navigation items by displayOrder\n */\n readonly sortedItems = computed(() => sortNavigationItems(this.items()));\n\n /**\n * Every item rendered as a link, in display order (depth first): simple\n * items, the children of a group, the children of a section, and the\n * children of a section's sub-group.\n */\n private readonly linkItems = computed(() => navigationLinks(this.sortedItems()));\n\n /** The router URL's path: primary outlet only, no query, fragment or matrix parameters. */\n private readonly routerPath = computed(() => navigationPath(this.router, this.routerUrl()));\n\n /**\n * The one current link (or none). The single source of both the current\n * look (`lc-sidenav__nav-item--active`) and `aria-current=\"page\"` (AS-16).\n * With `activeRoute` set: the first link whose route equals it. Without:\n * the router's path — the exact match wins, otherwise the longest link\n * route the path continues with a `/` (`/` matches only `/`); ties go to\n * the first link in display order. The rules live in\n * `internal/navigation` since 3.6, shared with `lc-sidenav-placeholder`.\n */\n private readonly currentItem = computed<NavigationItem | null>(() =>\n currentNavigationItem(this.linkItems(), this.activeRoute(), this.activeRoute() === '' ? this.routerPath() : ''),\n );\n\n /**\n * Event emitted when the sidenav should close\n */\n readonly closed = output<void>();\n\n /**\n * Event emitted when a navigation item is clicked\n */\n readonly itemClicked = output<NavigationItem>();\n\n /**\n * Event emitted when an item's action button is clicked\n */\n readonly itemAction = output<NavigationItem>();\n\n constructor() {\n this.router.events\n .pipe(\n filter((event): event is NavigationEnd => event instanceof NavigationEnd),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.routerUrl.set(this.router.url));\n\n // React to mobileBreakpoint changes\n effect(() => {\n this.mobileBreakpoint(); // track dependency\n this.setupMediaQuery();\n });\n\n // Navigating into a group opens it: add the active child's parent(s) to\n // the expanded set. Only additive — the user's own collapses/expands of\n // other groups survive a route change.\n effect(() => {\n const parents = this.activeChildParents();\n if (parents.length === 0) return;\n const expanded = untracked(this.expandedItems);\n if (parents.every((id) => expanded.has(id))) return;\n this.expandedItems.set(new Set([...expanded, ...parents]));\n });\n }\n\n ngOnDestroy(): void {\n this.teardownMediaQuery();\n }\n\n private setupMediaQuery(): void {\n if (typeof window === 'undefined') return;\n this.teardownMediaQuery();\n this.mediaQuery = window.matchMedia(`(max-width: ${this.mobileBreakpoint()}px)`);\n this.isMobile.set(this.mediaQuery.matches);\n this.mediaQuery.addEventListener('change', this.mediaHandler);\n }\n\n private teardownMediaQuery(): void {\n this.mediaQuery?.removeEventListener('change', this.mediaHandler);\n this.mediaQuery = null;\n }\n\n /**\n * Computed CSS classes for the sidenav\n */\n sidenavClasses = computed(() => {\n const classes = ['lc-sidenav'];\n classes.push(`lc-sidenav--${this.position()}`);\n classes.push(`lc-sidenav--${this.effectiveMode()}`);\n if (this.isOpen()) {\n classes.push('lc-sidenav--open');\n }\n if (this.collapsed() && !this.isMobile()) {\n classes.push('lc-sidenav--collapsed');\n }\n if (this.theme() !== 'auto') {\n classes.push(`lc-sidenav--${this.theme()}`);\n }\n if (this.variant() === 'transparent') {\n classes.push('lc-sidenav--transparent');\n }\n return classes.join(' ');\n });\n\n /**\n * Computed inline styles for the sidenav\n */\n sidenavStyles = computed(() => ({\n width: this.isMobile() ? this.width() : (this.collapsed() ? '56px' : this.width()),\n }));\n\n /**\n * Toggle collapsed state\n */\n toggleCollapsed(): void {\n this.collapsed.set(!this.collapsed());\n }\n\n /**\n * Handle close action\n */\n handleClose(): void {\n this.closed.emit();\n }\n\n /**\n * Handle navigation item click\n */\n handleItemClick(item: NavigationItem): void {\n this.itemClicked.emit(item);\n // Auto-close drawer on mobile after navigation\n if (this.isMobile()) {\n this.handleClose();\n }\n }\n\n /**\n * Handle action button click on a navigation item\n */\n handleItemAction(event: Event, item: NavigationItem): void {\n event.stopPropagation();\n this.itemAction.emit(item);\n }\n\n /**\n * Whether the item is the current link (see `currentItem`): it alone is\n * painted current and carries `aria-current=\"page\"`.\n */\n isItemActive(item: NavigationItem): boolean {\n return this.currentItem() === item;\n }\n\n /**\n * Check if a parent item contains the current link\n */\n hasActiveChild(item: NavigationItem): boolean {\n const current = this.currentItem();\n return !!current && !!item.children?.includes(current);\n }\n\n /**\n * Toggle expansion of a parent item.\n * If the sidenav is collapsed, expand it first.\n */\n toggleExpanded(item: NavigationItem): void {\n if (this.collapsed()) {\n this.collapsed.set(false);\n // Expand the clicked item after uncollapsing\n const expanded = new Set(this.expandedItems());\n expanded.add(item.id);\n this.expandedItems.set(expanded);\n return;\n }\n const expanded = new Set(this.expandedItems());\n if (expanded.has(item.id)) {\n expanded.delete(item.id);\n } else {\n expanded.add(item.id);\n }\n this.expandedItems.set(expanded);\n }\n\n /**\n * Check if an item is expanded\n */\n isExpanded(item: NavigationItem): boolean {\n return this.expandedItems().has(item.id);\n }\n\n /**\n * Handle keyboard navigation. Keyed on the EFFECTIVE mode: a docked sidenav\n * that has become a drawer below the mobile breakpoint must close on Escape\n * like any drawer.\n */\n @HostListener('document:keydown', ['$event'])\n handleKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.isOpen() && this.effectiveMode() === 'drawer') {\n this.handleClose();\n }\n }\n}\n","<!-- Drawer mode: render with overlay and conditional visibility -->\n@if (effectiveMode() === 'drawer') {\n @if (isOpen()) {\n <!-- Overlay backdrop -->\n @if (hasOverlay()) {\n <div\n class=\"lc-sidenav__overlay\"\n (click)=\"handleClose()\"\n aria-hidden=\"true\">\n </div>\n }\n\n <ng-container *ngTemplateOutlet=\"sidenavPanel\" />\n }\n}\n\n<!-- Docked mode: always render, visibility controlled by CSS -->\n@if (effectiveMode() === 'docked') {\n <ng-container *ngTemplateOutlet=\"sidenavPanel\" />\n}\n\n<!-- Shared sidenav panel template.\n The <aside> is a plain complementary region; the navigation landmark is\n the inner <nav> (nesting a role=\"navigation\" wrapper around a <nav> gave\n two navigation landmarks). In drawer mode the panel is a modal dialog:\n focus is captured (close button first, see cdkFocusInitial), trapped\n while open and restored to the opener when the panel is destroyed. -->\n<ng-template #sidenavPanel>\n <aside\n [class]=\"sidenavClasses()\"\n [ngStyle]=\"sidenavStyles()\"\n [attr.role]=\"effectiveMode() === 'drawer' ? 'dialog' : null\"\n [attr.aria-modal]=\"effectiveMode() === 'drawer' ? 'true' : null\"\n [attr.aria-label]=\"effectiveMode() === 'drawer' ? ariaLabel() : null\"\n [cdkTrapFocus]=\"effectiveMode() === 'drawer'\"\n [cdkTrapFocusAutoCapture]=\"effectiveMode() === 'drawer'\">\n\n <!-- Logo area (sidebar-first layout) -->\n @if (showLogo()) {\n <div\n class=\"lc-sidenav__logo\"\n [class.lc-sidenav__logo--size-xs]=\"logoSize() === 'xs'\"\n [class.lc-sidenav__logo--size-sm]=\"logoSize() === 'sm'\"\n [class.lc-sidenav__logo--size-md]=\"logoSize() === 'md'\"\n [class.lc-sidenav__logo--size-lg]=\"logoSize() === 'lg'\"\n [class.lc-sidenav__logo--size-xl]=\"logoSize() === 'xl'\"\n >\n <button type=\"button\" class=\"lc-sidenav__logo-link\" [attr.aria-label]=\"toggleAriaLabel()\" (click)=\"toggleCollapsed()\">\n <lc-logo\n [variant]=\"collapsed() ? 'emblem' : 'full'\"\n [size]=\"collapsed() ? 'sm' : logoSize()\"\n [clickable]=\"false\"\n [colorMode]=\"theme() === 'dark' ? 'dark' : theme() === 'light' ? 'light' : 'auto'\"\n [src]=\"logoSrc()\"\n [emblemSrc]=\"logoEmblemSrc()\"\n [darkSrc]=\"logoDarkSrc()\"\n [darkEmblemSrc]=\"logoDarkEmblemSrc()\"\n [alt]=\"logoAlt() || 'Logo'\"\n />\n </button>\n @if (!collapsed() && effectiveMode() === 'docked') {\n <button\n type=\"button\"\n class=\"lc-sidenav__logo-toggle\"\n (click)=\"toggleCollapsed()\"\n [attr.aria-label]=\"collapseAriaLabel() || (position() === 'right' ? 'Collapse navigation to the right' : 'Collapse navigation to the left')\"\n [attr.title]=\"collapseTitle()\">\n <lc-icon\n [name]=\"position() === 'right' ? 'chevron-right' : 'chevron-left'\"\n size=\"sm\"\n [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n\n <!-- Close button (drawer mode only) -->\n @if (effectiveMode() === 'drawer') {\n <button\n type=\"button\"\n class=\"lc-sidenav__close\"\n cdkFocusInitial\n (click)=\"handleClose()\"\n [attr.aria-label]=\"closeAriaLabel()\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n }\n\n <!-- Navigation items (if provided) -->\n @if (items().length > 0) {\n <nav\n class=\"lc-sidenav__nav\"\n [class.lc-sidenav__nav--docked]=\"effectiveMode() === 'docked'\"\n [attr.aria-label]=\"ariaLabel()\">\n @for (item of sortedItems(); track item.id) {\n <!-- Section headline -->\n @if (item.isSection) {\n <div class=\"lc-sidenav__section\">\n <div class=\"lc-sidenav__section-header\">\n <h2 class=\"lc-sidenav__section-title\">{{ item.label }}</h2>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (item.children?.length) {\n <ul class=\"lc-sidenav__section-items\">\n @for (child of item.children; track child.id) {\n <li>\n @if (child.children?.length) {\n <!-- Section child with nested children (collapsible) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <button\n type=\"button\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--parent\"\n [class.lc-sidenav__nav-item--expanded]=\"isExpanded(child)\"\n [class.lc-sidenav__nav-item--has-active-child]=\"hasActiveChild(child)\"\n (click)=\"toggleExpanded(child)\"\n [attr.aria-expanded]=\"isExpanded(child)\"\n [title]=\"collapsed() ? child.label : ''\">\n @if (child.icon) {\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" class=\"lc-sidenav__nav-chevron\" />\n </button>\n @if (child.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"child.action.ariaLabel || child.label + ' action'\"\n (click)=\"handleItemAction($event, child)\">\n <lc-icon [name]=\"child.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (isExpanded(child)) {\n <div class=\"lc-sidenav__nav-children\">\n @for (grandchild of child.children; track grandchild.id) {\n <a\n [routerLink]=\"grandchild.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(grandchild)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(grandchild)\"\n [attr.aria-current]=\"isItemActive(grandchild) ? 'page' : null\">\n @if (grandchild.icon) {\n <lc-icon [name]=\"grandchild.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ grandchild.label }}</span>\n @if (grandchild.badge) {\n <lc-badge [variant]=\"grandchild.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ grandchild.badge.value }}</lc-badge>\n }\n </a>\n }\n </div>\n }\n } @else {\n <!-- Section child (simple link) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <a\n [routerLink]=\"child.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(child)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(child)\"\n [attr.aria-current]=\"isItemActive(child) ? 'page' : null\"\n [title]=\"collapsed() ? child.label : ''\">\n @if (child.icon) {\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n </a>\n @if (child.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"child.action.ariaLabel || child.label + ' action'\"\n (click)=\"handleItemAction($event, child)\">\n <lc-icon [name]=\"child.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n </li>\n }\n </ul>\n }\n </div>\n } @else if (item.children?.length) {\n <!-- Parent item with children (collapsible group) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <button\n type=\"button\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--parent\"\n [class.lc-sidenav__nav-item--expanded]=\"isExpanded(item)\"\n [class.lc-sidenav__nav-item--has-active-child]=\"hasActiveChild(item)\"\n (click)=\"toggleExpanded(item)\"\n [attr.aria-expanded]=\"isExpanded(item)\"\n [title]=\"collapsed() ? item.label : ''\">\n <lc-icon [name]=\"item.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ item.label }}</span>\n @if (item.badge) {\n <lc-badge [variant]=\"item.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ item.badge.value }}</lc-badge>\n }\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" class=\"lc-sidenav__nav-chevron\" />\n </button>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (isExpanded(item)) {\n <div class=\"lc-sidenav__nav-children\">\n @for (child of item.children; track child.id) {\n <a\n [routerLink]=\"child.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(child)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(child)\"\n [attr.aria-current]=\"isItemActive(child) ? 'page' : null\">\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n </a>\n }\n </div>\n }\n } @else {\n <!-- Simple item without children -->\n <div class=\"lc-sidenav__nav-item-row\">\n <a\n [routerLink]=\"item.route\"\n class=\"lc-sidenav__nav-item\"\n (click)=\"handleItemClick(item)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(item)\"\n [attr.aria-current]=\"isItemActive(item) ? 'page' : null\"\n [title]=\"collapsed() ? item.label : ''\">\n <lc-icon [name]=\"item.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ item.label }}</span>\n @if (item.badge) {\n <lc-badge [variant]=\"item.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ item.badge.value }}</lc-badge>\n }\n </a>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n }\n </nav>\n }\n\n <!-- Content projection (for custom content) -->\n <div class=\"lc-sidenav__content\" [class.lc-sidenav__content--docked]=\"effectiveMode() === 'docked'\">\n <ng-content></ng-content>\n </div>\n </aside>\n</ng-template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-sidenav.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/sidenav/sidenav.component.ts","../../../../libs/angular-ui-kit/src/lib/sidenav/sidenav.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n signal,\n computed,\n input,\n output,\n model,\n effect,\n untracked,\n HostListener,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { NgTemplateOutlet, NgStyle } from '@angular/common';\nimport { NavigationEnd, Router, RouterLink } from '@angular/router';\nimport { filter } from 'rxjs';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { NavigationItem } from '@lifecorner/angular-ui-kit/models';\nimport {\n currentItemParents,\n currentNavigationItem,\n navigationLinks,\n navigationPath,\n sortNavigationItems,\n} from '@lifecorner/angular-ui-kit/internal/navigation';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_DOWN, ICON_CHEVRON_LEFT, ICON_CHEVRON_RIGHT } from '@lifecorner/angular-ui-kit/icons';\nimport { BadgeComponent } from '@lifecorner/angular-ui-kit/badge';\nimport { LogoComponent } from '@lifecorner/angular-ui-kit/logo';\n\nexport type SidenavPosition = 'left' | 'right';\nexport type SidenavMode = 'drawer' | 'docked';\n\n/**\n * Surface of the docked sidenav: `default` is the rail on `--color-sidebar`\n * with a border; `transparent` has no background and no border and draws the\n * items as 48 px pills (the side navigation on the page, \"Weite\"). A drawer\n * keeps its surface in both.\n */\nexport type SidenavVariant = 'default' | 'transparent';\n\n/**\n * Sidenav component for application navigation sidebar.\n *\n * Features:\n * - Drawer (overlay) and docked (persistent) modes\n * - Responsive mobile mode — docked automatically switches to drawer below a configurable breakpoint\n * - Collapsed icon-rail mode (narrow 56px sidebar with icons only and hover tooltips)\n * - Integrated logo area with collapse-toggle (sidebar-first layouts)\n * - Hierarchical navigation with collapsible groups (up to 3 levels)\n * - Section headlines for item grouping with optional action buttons\n * - Action buttons on any item (hover-reveal)\n * - Badge support on items (count / status indicator)\n * - One current item, announced and painted alike: the link for `activeRoute`\n * (or, without it, the one the router's URL matches) gets\n * `aria-current=\"page\"` and the current look, every other link neither\n * (AS-16). No hover color change on the current item\n * - Keyboard navigation support (Escape closes drawer)\n * - Configurable width and position (left/right)\n * - Optional backdrop overlay\n * - Theme variants: auto, light, dark (with teal accent for dark mode)\n * - `variant=\"transparent\"`: no rail background or border, items as 48 px\n * pills with 16 px labels, the current one on `--color-surface-selected`\n * - Items are at least 44 px high in every density; the current item's ink is\n * `--color-on-surface-selected` (AA on the selected tint)\n * - Accessible: the inner `<nav>` is the navigation landmark; in drawer mode\n * the panel is a modal dialog (`role=\"dialog\"`, focus moved to the close\n * button on open, trapped while open, restored on close)\n *\n * @example\n * ```html\n * <lc-sidenav\n * [isOpen]=\"isOpen\"\n * mode=\"docked\"\n * [(collapsed)]=\"isCollapsed\"\n * [showLogo]=\"true\"\n * logoSize=\"md\"\n * [mobileBreakpoint]=\"768\"\n * [items]=\"navItems\"\n * activeRoute=\"/dashboard\"\n * theme=\"dark\"\n * (closed)=\"isOpen = false\"\n * (itemClicked)=\"navigate($event)\"\n * (itemAction)=\"onAction($event)\" />\n * ```\n */\n@Component({\n selector: 'lc-sidenav',\n standalone: true,\n imports: [NgTemplateOutlet, NgStyle, RouterLink, A11yModule, IconComponent, BadgeComponent, LogoComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_DOWN, ICON_CHEVRON_LEFT, ICON_CHEVRON_RIGHT])],\n templateUrl: './sidenav.component.html',\n styleUrl: './sidenav.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.lc-sidenav-container]': 'true',\n '[class.lc-sidenav-container--docked]': \"effectiveMode() === 'docked'\",\n '[class.lc-sidenav-container--open]': 'isOpen()',\n },\n})\nexport class SidenavComponent implements OnDestroy {\n private readonly router = inject(Router);\n\n /**\n * The router's URL, updated on every `NavigationEnd`. Before the first\n * navigation it is the router's initial URL (`/`). Read only when\n * `activeRoute` is empty.\n */\n private readonly routerUrl = signal(this.router.url);\n\n /** Whether the sidenav is collapsed to icon-only rail (two-way bindable) */\n readonly collapsed = model(false);\n\n /** Whether to show the logo at the top of the sidenav */\n readonly showLogo = input(false);\n\n /** Custom URL for the full brand logo (forwarded to inner `<lc-logo>`). */\n readonly logoSrc = input('');\n /** Custom URL for the emblem-only brand logo. */\n readonly logoEmblemSrc = input('');\n /** Optional dark-theme URL for the full brand logo. */\n readonly logoDarkSrc = input('');\n /** Optional dark-theme URL for the emblem-only brand logo. */\n readonly logoDarkEmblemSrc = input('');\n /** Alt text for the brand logo. */\n readonly logoAlt = input('');\n\n /** Size of the brand logo (forwarded to inner `<lc-logo>`). Defaults to `md` for a prominent brand block aligned with `<lc-header size=\"md\">` (both 64px). */\n readonly logoSize = input<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('md');\n\n /** Whether the sidenav is open */\n readonly isOpen = input(false);\n\n /** Display mode: 'drawer' (overlay) or 'docked' (persistent sidebar) */\n readonly mode = input<SidenavMode>('drawer');\n\n /** Mobile breakpoint in pixels. Below this width, docked mode switches to drawer. */\n readonly mobileBreakpoint = input(768);\n\n /** Whether the viewport is below the mobile breakpoint */\n readonly isMobile = signal(false);\n\n /** Effective mode: switches docked → drawer on mobile */\n readonly effectiveMode = computed(() => {\n if (this.isMobile() && this.mode() === 'docked') {\n return 'drawer' as SidenavMode;\n }\n return this.mode();\n });\n\n private mediaQuery: MediaQueryList | null = null;\n private mediaHandler = (e: MediaQueryListEvent) => this.isMobile.set(e.matches);\n\n /** Position of the sidenav (left or right) */\n readonly position = input<SidenavPosition>('left');\n\n /** Width of the sidenav drawer */\n readonly width = input('320px');\n\n /**\n * ARIA label of the navigation landmark (the inner `<nav>`) and, in drawer\n * mode, of the dialog panel.\n */\n readonly ariaLabel = input('Side navigation');\n\n /** Whether to show the overlay backdrop */\n readonly hasOverlay = input(true);\n\n /**\n * Navigation items (`NavigationItem[]`): `id`, `icon`, `label`, `route`,\n * `displayOrder` (sort order, 1 = first); optional `children`\n * (`NavigationItem[]`, a collapsible group, up to 3 levels), `isSection`\n * (renders `label` as a section headline over its `children`), `badge`\n * (`{ value: string | number; variant?: 'default' | 'primary' | 'success' |\n * 'warning' | 'error' | 'info' }`), `action` (`{ icon: string; ariaLabel?:\n * string }`, a button that emits `itemAction`), `requiredRole` (for the\n * app's own filtering; not evaluated here). Items link with `routerLink`;\n * exactly one link, the current item (see `activeRoute`), gets\n * `aria-current=\"page\"`.\n * @default []\n */\n readonly items = input<NavigationItem[]>([]);\n\n /**\n * The current route. When set, the first link (in display order) whose\n * `route` equals it is the current item: it alone gets `aria-current=\"page\"`\n * and the current look, whatever the router matches. When empty, the\n * sidenav follows the router: the link whose route equals the URL's path is\n * current, otherwise the one with the longest route the path starts with\n * (`/household` on `/household/members`; `/` only on `/`). Routes are\n * compared as written, so give them as absolute paths (`'/household'`).\n * @default ''\n */\n readonly activeRoute = input('');\n\n /** Theme variant for the sidenav */\n readonly theme = input<'light' | 'dark' | 'auto'>('auto');\n\n /**\n * Surface of the docked sidenav: `default` (the rail with background and\n * border) or `transparent` (no background, no border, items as 48 px pills\n * with radius full and 16 px labels; the current one on\n * `--color-surface-selected`). A drawer keeps its surface.\n */\n readonly variant = input<SidenavVariant>('default');\n\n /** Accessible name of the logo button that collapses/expands the sidebar. */\n readonly toggleAriaLabel = input('Toggle sidebar');\n\n /** Accessible name of the collapse button. Empty: \"Collapse navigation to the left/right\" by `position`. */\n readonly collapseAriaLabel = input('');\n\n /** Tooltip of the collapse button. */\n readonly collapseTitle = input('Collapse sidebar');\n\n /** Accessible name of the close button (drawer mode). */\n readonly closeAriaLabel = input('Close navigation');\n\n /**\n * Ids of the expanded parent items — the single source of truth for\n * `isExpanded`. Seeded with the current item's parent whenever it changes\n * (see constructor), so a group opens on navigation but can still\n * be collapsed by the user. (It used to be OR-ed with `hasActiveChild` at\n * read time, so toggling such a group flipped the set without ever closing\n * it — the set drifted out of sync with what was on screen.)\n */\n readonly expandedItems = signal<Set<string>>(new Set());\n\n /** Ids of the collapsible parents (top-level groups, section sub-groups) that contain the current item. */\n private readonly activeChildParents = computed(() => currentItemParents(this.items(), this.currentItem()));\n\n /**\n * Computed sorted navigation items by displayOrder\n */\n readonly sortedItems = computed(() => sortNavigationItems(this.items()));\n\n /**\n * Every item rendered as a link, in display order (depth first): simple\n * items, the children of a group, the children of a section, and the\n * children of a section's sub-group.\n */\n private readonly linkItems = computed(() => navigationLinks(this.sortedItems()));\n\n /** The router URL's path: primary outlet only, no query, fragment or matrix parameters. */\n private readonly routerPath = computed(() => navigationPath(this.router, this.routerUrl()));\n\n /**\n * The one current link (or none). The single source of both the current\n * look (`lc-sidenav__nav-item--active`) and `aria-current=\"page\"` (AS-16).\n * With `activeRoute` set: the first link whose route equals it. Without:\n * the router's path — the exact match wins, otherwise the longest link\n * route the path continues with a `/` (`/` matches only `/`); ties go to\n * the first link in display order. The rules live in\n * `internal/navigation` since 3.6, shared with `lc-sidenav-placeholder`.\n */\n private readonly currentItem = computed<NavigationItem | null>(() =>\n currentNavigationItem(this.linkItems(), this.activeRoute(), this.activeRoute() === '' ? this.routerPath() : ''),\n );\n\n /**\n * Event emitted when the sidenav should close\n */\n readonly closed = output<void>();\n\n /**\n * Event emitted when a navigation item is clicked\n */\n readonly itemClicked = output<NavigationItem>();\n\n /**\n * Event emitted when an item's action button is clicked\n */\n readonly itemAction = output<NavigationItem>();\n\n constructor() {\n this.router.events\n .pipe(\n filter((event): event is NavigationEnd => event instanceof NavigationEnd),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.routerUrl.set(this.router.url));\n\n // React to mobileBreakpoint changes\n effect(() => {\n this.mobileBreakpoint(); // track dependency\n this.setupMediaQuery();\n });\n\n // Navigating into a group opens it: add the active child's parent(s) to\n // the expanded set. Only additive — the user's own collapses/expands of\n // other groups survive a route change.\n effect(() => {\n const parents = this.activeChildParents();\n if (parents.length === 0) return;\n const expanded = untracked(this.expandedItems);\n if (parents.every((id) => expanded.has(id))) return;\n this.expandedItems.set(new Set([...expanded, ...parents]));\n });\n }\n\n ngOnDestroy(): void {\n this.teardownMediaQuery();\n }\n\n private setupMediaQuery(): void {\n if (typeof window === 'undefined') return;\n this.teardownMediaQuery();\n this.mediaQuery = window.matchMedia(`(max-width: ${this.mobileBreakpoint()}px)`);\n this.isMobile.set(this.mediaQuery.matches);\n this.mediaQuery.addEventListener('change', this.mediaHandler);\n }\n\n private teardownMediaQuery(): void {\n this.mediaQuery?.removeEventListener('change', this.mediaHandler);\n this.mediaQuery = null;\n }\n\n /**\n * Computed CSS classes for the sidenav\n */\n sidenavClasses = computed(() => {\n const classes = ['lc-sidenav'];\n classes.push(`lc-sidenav--${this.position()}`);\n classes.push(`lc-sidenav--${this.effectiveMode()}`);\n if (this.isOpen()) {\n classes.push('lc-sidenav--open');\n }\n if (this.collapsed() && !this.isMobile()) {\n classes.push('lc-sidenav--collapsed');\n }\n if (this.theme() !== 'auto') {\n classes.push(`lc-sidenav--${this.theme()}`);\n }\n if (this.variant() === 'transparent') {\n classes.push('lc-sidenav--transparent');\n }\n return classes.join(' ');\n });\n\n /**\n * Computed inline styles for the sidenav\n */\n sidenavStyles = computed(() => ({\n width: this.isMobile() ? this.width() : (this.collapsed() ? '56px' : this.width()),\n }));\n\n /**\n * Toggle collapsed state\n */\n toggleCollapsed(): void {\n this.collapsed.set(!this.collapsed());\n }\n\n /**\n * Handle close action\n */\n handleClose(): void {\n this.closed.emit();\n }\n\n /**\n * Handle navigation item click\n */\n handleItemClick(item: NavigationItem): void {\n this.itemClicked.emit(item);\n // Auto-close drawer on mobile after navigation\n if (this.isMobile()) {\n this.handleClose();\n }\n }\n\n /**\n * Handle action button click on a navigation item\n */\n handleItemAction(event: Event, item: NavigationItem): void {\n event.stopPropagation();\n this.itemAction.emit(item);\n }\n\n /**\n * Whether the item is the current link (see `currentItem`): it alone is\n * painted current and carries `aria-current=\"page\"`.\n */\n isItemActive(item: NavigationItem): boolean {\n return this.currentItem() === item;\n }\n\n /**\n * Check if a parent item contains the current link\n */\n hasActiveChild(item: NavigationItem): boolean {\n const current = this.currentItem();\n return !!current && !!item.children?.includes(current);\n }\n\n /**\n * Toggle expansion of a parent item.\n * If the sidenav is collapsed, expand it first.\n */\n toggleExpanded(item: NavigationItem): void {\n if (this.collapsed()) {\n this.collapsed.set(false);\n // Expand the clicked item after uncollapsing\n const expanded = new Set(this.expandedItems());\n expanded.add(item.id);\n this.expandedItems.set(expanded);\n return;\n }\n const expanded = new Set(this.expandedItems());\n if (expanded.has(item.id)) {\n expanded.delete(item.id);\n } else {\n expanded.add(item.id);\n }\n this.expandedItems.set(expanded);\n }\n\n /**\n * Check if an item is expanded\n */\n isExpanded(item: NavigationItem): boolean {\n return this.expandedItems().has(item.id);\n }\n\n /**\n * Handle keyboard navigation. Keyed on the EFFECTIVE mode: a docked sidenav\n * that has become a drawer below the mobile breakpoint must close on Escape\n * like any drawer.\n */\n @HostListener('document:keydown', ['$event'])\n handleKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.isOpen() && this.effectiveMode() === 'drawer') {\n this.handleClose();\n }\n }\n}\n","<!-- Drawer mode: render with overlay and conditional visibility -->\n@if (effectiveMode() === 'drawer') {\n @if (isOpen()) {\n <!-- Overlay backdrop -->\n @if (hasOverlay()) {\n <div\n class=\"lc-sidenav__overlay\"\n (click)=\"handleClose()\"\n aria-hidden=\"true\">\n </div>\n }\n\n <ng-container *ngTemplateOutlet=\"sidenavPanel\" />\n }\n}\n\n<!-- Docked mode: always render, visibility controlled by CSS -->\n@if (effectiveMode() === 'docked') {\n <ng-container *ngTemplateOutlet=\"sidenavPanel\" />\n}\n\n<!-- Shared sidenav panel template.\n The <aside> is a plain complementary region; the navigation landmark is\n the inner <nav> (nesting a role=\"navigation\" wrapper around a <nav> gave\n two navigation landmarks). In drawer mode the panel is a modal dialog:\n focus is captured (close button first, see cdkFocusInitial), trapped\n while open and restored to the opener when the panel is destroyed. -->\n<ng-template #sidenavPanel>\n <aside\n [class]=\"sidenavClasses()\"\n [ngStyle]=\"sidenavStyles()\"\n [attr.role]=\"effectiveMode() === 'drawer' ? 'dialog' : null\"\n [attr.aria-modal]=\"effectiveMode() === 'drawer' ? 'true' : null\"\n [attr.aria-label]=\"effectiveMode() === 'drawer' ? ariaLabel() : null\"\n [cdkTrapFocus]=\"effectiveMode() === 'drawer'\"\n [cdkTrapFocusAutoCapture]=\"effectiveMode() === 'drawer'\">\n\n <!-- Logo area (sidebar-first layout) -->\n @if (showLogo()) {\n <div\n class=\"lc-sidenav__logo\"\n [class.lc-sidenav__logo--size-xs]=\"logoSize() === 'xs'\"\n [class.lc-sidenav__logo--size-sm]=\"logoSize() === 'sm'\"\n [class.lc-sidenav__logo--size-md]=\"logoSize() === 'md'\"\n [class.lc-sidenav__logo--size-lg]=\"logoSize() === 'lg'\"\n [class.lc-sidenav__logo--size-xl]=\"logoSize() === 'xl'\"\n >\n <button type=\"button\" class=\"lc-sidenav__logo-link\" [attr.aria-label]=\"toggleAriaLabel()\" (click)=\"toggleCollapsed()\">\n <lc-logo\n [variant]=\"collapsed() ? 'emblem' : 'full'\"\n [size]=\"collapsed() ? 'sm' : logoSize()\"\n [clickable]=\"false\"\n [colorMode]=\"theme() === 'dark' ? 'dark' : theme() === 'light' ? 'light' : 'auto'\"\n [src]=\"logoSrc()\"\n [emblemSrc]=\"logoEmblemSrc()\"\n [darkSrc]=\"logoDarkSrc()\"\n [darkEmblemSrc]=\"logoDarkEmblemSrc()\"\n [alt]=\"logoAlt() || 'Logo'\"\n />\n </button>\n @if (!collapsed() && effectiveMode() === 'docked') {\n <button\n type=\"button\"\n class=\"lc-sidenav__logo-toggle\"\n (click)=\"toggleCollapsed()\"\n [attr.aria-label]=\"collapseAriaLabel() || (position() === 'right' ? 'Collapse navigation to the right' : 'Collapse navigation to the left')\"\n [attr.title]=\"collapseTitle()\">\n <lc-icon\n [name]=\"position() === 'right' ? 'chevron-right' : 'chevron-left'\"\n size=\"sm\"\n [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n\n <!-- Close button (drawer mode only) -->\n @if (effectiveMode() === 'drawer') {\n <button\n type=\"button\"\n class=\"lc-sidenav__close\"\n cdkFocusInitial\n (click)=\"handleClose()\"\n [attr.aria-label]=\"closeAriaLabel()\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n }\n\n <!-- Navigation items (if provided) -->\n @if (items().length > 0) {\n <nav\n class=\"lc-sidenav__nav\"\n [class.lc-sidenav__nav--docked]=\"effectiveMode() === 'docked'\"\n [attr.aria-label]=\"ariaLabel()\">\n @for (item of sortedItems(); track item.id) {\n <!-- Section headline -->\n @if (item.isSection) {\n <div class=\"lc-sidenav__section\">\n <div class=\"lc-sidenav__section-header\">\n <h2 class=\"lc-sidenav__section-title\">{{ item.label }}</h2>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (item.children?.length) {\n <ul class=\"lc-sidenav__section-items\">\n @for (child of item.children; track child.id) {\n <li>\n @if (child.children?.length) {\n <!-- Section child with nested children (collapsible) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <button\n type=\"button\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--parent\"\n [class.lc-sidenav__nav-item--expanded]=\"isExpanded(child)\"\n [class.lc-sidenav__nav-item--has-active-child]=\"hasActiveChild(child)\"\n (click)=\"toggleExpanded(child)\"\n [attr.aria-expanded]=\"isExpanded(child)\"\n [title]=\"collapsed() ? child.label : ''\">\n @if (child.icon) {\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" class=\"lc-sidenav__nav-chevron\" />\n </button>\n @if (child.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"child.action.ariaLabel || child.label + ' action'\"\n (click)=\"handleItemAction($event, child)\">\n <lc-icon [name]=\"child.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (isExpanded(child)) {\n <div class=\"lc-sidenav__nav-children\">\n @for (grandchild of child.children; track grandchild.id) {\n <a\n [routerLink]=\"grandchild.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(grandchild)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(grandchild)\"\n [attr.aria-current]=\"isItemActive(grandchild) ? 'page' : null\">\n @if (grandchild.icon) {\n <lc-icon [name]=\"grandchild.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ grandchild.label }}</span>\n @if (grandchild.badge) {\n <lc-badge [variant]=\"grandchild.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ grandchild.badge.value }}</lc-badge>\n }\n </a>\n }\n </div>\n }\n } @else {\n <!-- Section child (simple link) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <a\n [routerLink]=\"child.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(child)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(child)\"\n [attr.aria-current]=\"isItemActive(child) ? 'page' : null\"\n [title]=\"collapsed() ? child.label : ''\">\n @if (child.icon) {\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n }\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n </a>\n @if (child.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"child.action.ariaLabel || child.label + ' action'\"\n (click)=\"handleItemAction($event, child)\">\n <lc-icon [name]=\"child.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n </li>\n }\n </ul>\n }\n </div>\n } @else if (item.children?.length) {\n <!-- Parent item with children (collapsible group) -->\n <div class=\"lc-sidenav__nav-item-row\">\n <button\n type=\"button\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--parent\"\n [class.lc-sidenav__nav-item--expanded]=\"isExpanded(item)\"\n [class.lc-sidenav__nav-item--has-active-child]=\"hasActiveChild(item)\"\n (click)=\"toggleExpanded(item)\"\n [attr.aria-expanded]=\"isExpanded(item)\"\n [title]=\"collapsed() ? item.label : ''\">\n <lc-icon [name]=\"item.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ item.label }}</span>\n @if (item.badge) {\n <lc-badge [variant]=\"item.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ item.badge.value }}</lc-badge>\n }\n <lc-icon name=\"chevron-down\" size=\"xs\" [decorative]=\"true\" class=\"lc-sidenav__nav-chevron\" />\n </button>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n @if (isExpanded(item)) {\n <div class=\"lc-sidenav__nav-children\">\n @for (child of item.children; track child.id) {\n <a\n [routerLink]=\"child.route\"\n class=\"lc-sidenav__nav-item lc-sidenav__nav-item--child\"\n (click)=\"handleItemClick(child)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(child)\"\n [attr.aria-current]=\"isItemActive(child) ? 'page' : null\">\n <lc-icon [name]=\"child.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ child.label }}</span>\n @if (child.badge) {\n <lc-badge [variant]=\"child.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ child.badge.value }}</lc-badge>\n }\n </a>\n }\n </div>\n }\n } @else {\n <!-- Simple item without children -->\n <div class=\"lc-sidenav__nav-item-row\">\n <a\n [routerLink]=\"item.route\"\n class=\"lc-sidenav__nav-item\"\n (click)=\"handleItemClick(item)\"\n [class.lc-sidenav__nav-item--active]=\"isItemActive(item)\"\n [attr.aria-current]=\"isItemActive(item) ? 'page' : null\"\n [title]=\"collapsed() ? item.label : ''\">\n <lc-icon [name]=\"item.icon\" size=\"sm\" [decorative]=\"true\" class=\"lc-sidenav__nav-icon\" />\n <span class=\"lc-sidenav__nav-label\">{{ item.label }}</span>\n @if (item.badge) {\n <lc-badge [variant]=\"item.badge.variant || 'default'\" size=\"xs\" [rounded]=\"true\" class=\"lc-sidenav__nav-badge\">{{ item.badge.value }}</lc-badge>\n }\n </a>\n @if (item.action) {\n <button\n type=\"button\"\n class=\"lc-sidenav__action-btn\"\n [attr.aria-label]=\"item.action.ariaLabel || item.label + ' action'\"\n (click)=\"handleItemAction($event, item)\">\n <lc-icon [name]=\"item.action.icon\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n }\n }\n </nav>\n }\n\n <!-- Content projection (for custom content) -->\n <div class=\"lc-sidenav__content\" [class.lc-sidenav__content--docked]=\"effectiveMode() === 'docked'\">\n <ng-content></ng-content>\n </div>\n </aside>\n</ng-template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-tabs.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tabs/tabs.component.ts","../../../../libs/angular-ui-kit/src/lib/tabs/tabs.component.html"],"sourcesContent":["import {\n Component,\n input,\n model,\n contentChildren,\n viewChild,\n viewChildren,\n computed,\n effect,\n untracked,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n TemplateRef,\n ElementRef,\n} from '@angular/core';\nimport { NgClass, NgTemplateOutlet } from '@angular/common';\nimport { BadgeComponent, BadgeVariant } from '@lifecorner/angular-ui-kit/badge';\n\nlet nextUniqueId = 0;\n\nexport type TabOrientation = 'horizontal' | 'vertical';\n\n/**\n * Individual tab component\n */\n@Component({\n selector: 'lc-tab',\n standalone: true,\n imports: [],\n template: `\n <ng-template>\n <ng-content></ng-content>\n </ng-template>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TabComponent {\n /**\n * Tab label displayed in tab button\n */\n readonly label = input('');\n\n /**\n * Whether the tab is disabled\n * @default false\n */\n readonly disabled = input(false);\n\n /**\n * Optional icon name (Tabler Icons)\n */\n readonly icon = input<string | undefined>();\n\n /**\n * Optional badge content shown after the label (e.g. a count or \"New\").\n * `0` is rendered; `undefined`, `null`, and `''` hide the badge.\n */\n readonly badge = input<string | number | undefined | null>();\n\n /**\n * Badge color variant\n * @default 'default'\n */\n readonly badgeVariant = input<BadgeVariant>('default');\n\n /**\n * Whether the badge should be rendered\n */\n readonly hasBadge = computed(() => {\n const badge = this.badge();\n return badge !== undefined && badge !== null && badge !== '';\n });\n\n /**\n * Unique ID for accessibility\n */\n readonly id = `lc-tab-${nextUniqueId++}`;\n\n /**\n * Unique ID for the panel\n */\n readonly panelId = `lc-tabpanel-${this.id}`;\n\n /**\n * Template reference for tab content\n */\n readonly template = viewChild.required(TemplateRef);\n}\n\n/**\n * Tabs component for organizing content into switchable views.\n *\n * Features:\n * - Dynamic tab registration via content projection (tabs added or removed\n * after init are picked up automatically)\n * - Active tab tracking with two-way binding (`[(selectedIndex)]`)\n * - Accessible with ARIA tablist/tab/tabpanel roles\n * - Keyboard navigation between tabs (arrow keys, Home, End) moves both the\n * selection and DOM focus\n * - Lazy content rendering per tab\n * - Optional badge per tab (counts or status labels)\n *\n * @example\n * ```html\n * <lc-tabs [(selectedIndex)]=\"active\">\n * <lc-tab label=\"Account\">Account settings</lc-tab>\n * <lc-tab label=\"Inbox\" [badge]=\"12\" badgeVariant=\"primary\">Messages</lc-tab>\n * <lc-tab label=\"Security\">Security settings</lc-tab>\n * </lc-tabs>\n * ```\n */\n@Component({\n selector: 'lc-tabs',\n standalone: true,\n imports: [NgClass, NgTemplateOutlet, BadgeComponent],\n templateUrl: './tabs.component.html',\n styleUrl: './tabs.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-tabs',\n },\n})\nexport class TabsComponent {\n /**\n * Orientation of tabs\n * @default 'horizontal'\n */\n readonly orientation = input<TabOrientation>('horizontal');\n\n /**\n * Currently selected tab index. Two-way bindable via `[(selectedIndex)]`;\n * `selectedIndexChange` emits only when the selection actually changes\n * (never during initialisation).\n * @default 0\n */\n readonly selectedIndex = model(0);\n\n /**\n * Legacy one-way input for the selected index.\n * @deprecated Bind `[selectedIndex]` / `[(selectedIndex)]` instead.\n */\n readonly selectedIndexInput = input<number | undefined>(undefined);\n\n /**\n * Projected tab components (signal query — updates when tabs are added or\n * removed after init).\n */\n readonly tabs = contentChildren(TabComponent);\n\n /**\n * Tab list for template access\n */\n readonly tabList = computed(() => this.tabs());\n\n /**\n * Rendered tab buttons — used to move DOM focus on keyboard navigation.\n */\n private readonly tabButtons = viewChildren<ElementRef<HTMLButtonElement>>('tabButton');\n\n /**\n * CSS classes for tab list\n */\n readonly tabListClasses = computed(() => ({\n 'lc-tabs__list': true,\n 'lc-tabs--horizontal': this.orientation() === 'horizontal',\n 'lc-tabs--vertical': this.orientation() === 'vertical',\n }));\n\n constructor() {\n // Mirror the deprecated one-way input into the model. `model.set` is a\n // no-op (no emit) when the value is unchanged, so binding the same index\n // does not fire `selectedIndexChange`.\n effect(() => {\n const legacy = this.selectedIndexInput();\n if (legacy !== undefined) {\n untracked(() => this.selectedIndex.set(legacy));\n }\n });\n\n // Keep the selection valid when tabs are removed at runtime: falling off\n // the end would leave no panel visible and no tab reachable via Tab.\n effect(() => {\n const count = this.tabs().length;\n const index = untracked(() => this.selectedIndex());\n if (count > 0 && index >= count) {\n untracked(() => this.selectTab(this.getLastEnabledTab()));\n }\n });\n }\n\n /**\n * Select a tab by index\n */\n selectTab(index: number): void {\n const tabs = this.tabList();\n const tab = tabs[index];\n if (index >= 0 && index < tabs.length && tab && !tab.disabled()) {\n this.selectedIndex.set(index);\n }\n }\n\n /**\n * Check if tab is selected\n */\n isSelected(index: number): boolean {\n return this.selectedIndex() === index;\n }\n\n /**\n * Handle keyboard navigation (automatic activation: focus and selection\n * move together).\n */\n handleKeyDown(event: KeyboardEvent): void {\n const currentIndex = this.selectedIndex();\n let nextIndex = currentIndex;\n\n const isHorizontal = this.orientation() === 'horizontal';\n const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';\n const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';\n\n switch (event.key) {\n case nextKey:\n event.preventDefault();\n nextIndex = this.getNextEnabledTab(currentIndex);\n break;\n\n case prevKey:\n event.preventDefault();\n nextIndex = this.getPreviousEnabledTab(currentIndex);\n break;\n\n case 'Home':\n event.preventDefault();\n nextIndex = this.getFirstEnabledTab();\n break;\n\n case 'End':\n event.preventDefault();\n nextIndex = this.getLastEnabledTab();\n break;\n\n default:\n return;\n }\n\n if (nextIndex !== currentIndex) {\n this.selectTab(nextIndex);\n this.focusTab(nextIndex);\n }\n }\n\n /**\n * Get tabindex for tab button\n */\n getTabIndex(index: number): number {\n return this.isSelected(index) ? 0 : -1;\n }\n\n /**\n * Move DOM focus to the tab button at `index` (roving tabindex).\n */\n private focusTab(index: number): void {\n this.tabButtons()[index]?.nativeElement.focus();\n }\n\n /**\n * Get next enabled tab index (with wrapping)\n */\n private getNextEnabledTab(currentIndex: number): number {\n const tabs = this.tabList();\n let nextIndex = (currentIndex + 1) % tabs.length;\n const startIndex = nextIndex;\n\n while (tabs[nextIndex]?.disabled()) {\n nextIndex = (nextIndex + 1) % tabs.length;\n if (nextIndex === startIndex) {\n return currentIndex; // No enabled tabs found\n }\n }\n\n return nextIndex;\n }\n\n /**\n * Get previous enabled tab index (with wrapping)\n */\n private getPreviousEnabledTab(currentIndex: number): number {\n const tabs = this.tabList();\n let prevIndex = currentIndex - 1;\n if (prevIndex < 0) {\n prevIndex = tabs.length - 1;\n }\n const startIndex = prevIndex;\n\n while (tabs[prevIndex]?.disabled()) {\n prevIndex = prevIndex - 1;\n if (prevIndex < 0) {\n prevIndex = tabs.length - 1;\n }\n if (prevIndex === startIndex) {\n return currentIndex; // No enabled tabs found\n }\n }\n\n return prevIndex;\n }\n\n /**\n * Get first enabled tab index\n */\n private getFirstEnabledTab(): number {\n const tabs = this.tabList();\n for (let i = 0; i < tabs.length; i++) {\n const tab = tabs[i];\n if (tab && !tab.disabled()) {\n return i;\n }\n }\n return 0;\n }\n\n /**\n * Get last enabled tab index\n */\n private getLastEnabledTab(): number {\n const tabs = this.tabList();\n for (let i = tabs.length - 1; i >= 0; i--) {\n const tab = tabs[i];\n if (tab && !tab.disabled()) {\n return i;\n }\n }\n return tabs.length - 1;\n }\n}\n","<div\n class=\"lc-tabs__container\"\n [class.lc-tabs__container--horizontal]=\"orientation() === 'horizontal'\"\n [class.lc-tabs__container--vertical]=\"orientation() === 'vertical'\"\n>\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- keydown bubbles from the focused tab (roving tabindex) -->\n <div\n [ngClass]=\"tabListClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"handleKeyDown($event)\"\n >\n @for (tab of tabList(); track tab.id; let i = $index) {\n <button\n #tabButton\n type=\"button\"\n class=\"lc-tabs__tab\"\n [class.lc-tabs__tab--selected]=\"isSelected(i)\"\n [class.lc-tabs__tab--disabled]=\"tab.disabled()\"\n role=\"tab\"\n [attr.id]=\"tab.id\"\n [attr.aria-selected]=\"isSelected(i)\"\n [attr.aria-controls]=\"tab.panelId\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [tabindex]=\"getTabIndex(i)\"\n [disabled]=\"tab.disabled()\"\n (click)=\"selectTab(i)\"\n >\n {{ tab.label() }}\n @if (tab.hasBadge()) {\n <lc-badge\n class=\"lc-tabs__badge\"\n [variant]=\"tab.badgeVariant()\"\n size=\"xs\"\n [rounded]=\"true\"\n >{{ tab.badge() }}</lc-badge\n >\n }\n </button>\n }\n </div>\n\n <div class=\"lc-tabs__panels\">\n @for (tab of tabList(); track tab.id; let i = $index) {\n <div\n class=\"lc-tabs__panel\"\n [class.lc-tabs__panel--active]=\"isSelected(i)\"\n role=\"tabpanel\"\n [attr.id]=\"tab.panelId\"\n [attr.aria-labelledby]=\"tab.id\"\n [attr.hidden]=\"!isSelected(i) || null\"\n [tabindex]=\"0\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.template()\"></ng-container>\n </div>\n }\n </div>\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-tabs.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tabs/tabs.component.ts","../../../../libs/angular-ui-kit/src/lib/tabs/tabs.component.html"],"sourcesContent":["import {\n Component,\n input,\n model,\n contentChildren,\n viewChild,\n viewChildren,\n computed,\n effect,\n untracked,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n TemplateRef,\n ElementRef,\n} from '@angular/core';\nimport { NgClass, NgTemplateOutlet } from '@angular/common';\nimport { BadgeComponent, BadgeVariant } from '@lifecorner/angular-ui-kit/badge';\n\nlet nextUniqueId = 0;\n\nexport type TabOrientation = 'horizontal' | 'vertical';\n\n/**\n * Individual tab component\n */\n@Component({\n selector: 'lc-tab',\n standalone: true,\n imports: [],\n template: `\n <ng-template>\n <ng-content></ng-content>\n </ng-template>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TabComponent {\n /**\n * Tab label displayed in tab button\n */\n readonly label = input('');\n\n /**\n * Whether the tab is disabled\n * @default false\n */\n readonly disabled = input(false);\n\n /**\n * Optional icon name (Tabler Icons)\n */\n readonly icon = input<string | undefined>();\n\n /**\n * Optional badge content shown after the label (e.g. a count or \"New\").\n * `0` is rendered; `undefined`, `null`, and `''` hide the badge.\n */\n readonly badge = input<string | number | undefined | null>();\n\n /**\n * Badge color variant\n * @default 'default'\n */\n readonly badgeVariant = input<BadgeVariant>('default');\n\n /**\n * Whether the badge should be rendered\n */\n readonly hasBadge = computed(() => {\n const badge = this.badge();\n return badge !== undefined && badge !== null && badge !== '';\n });\n\n /**\n * Unique ID for accessibility\n */\n readonly id = `lc-tab-${nextUniqueId++}`;\n\n /**\n * Unique ID for the panel\n */\n readonly panelId = `lc-tabpanel-${this.id}`;\n\n /**\n * Template reference for tab content\n */\n readonly template = viewChild.required(TemplateRef);\n}\n\n/**\n * Tabs component for organizing content into switchable views.\n *\n * Features:\n * - Dynamic tab registration via content projection (tabs added or removed\n * after init are picked up automatically)\n * - Active tab tracking with two-way binding (`[(selectedIndex)]`)\n * - Accessible with ARIA tablist/tab/tabpanel roles\n * - Keyboard navigation between tabs (arrow keys, Home, End) moves both the\n * selection and DOM focus\n * - Lazy content rendering per tab\n * - Optional badge per tab (counts or status labels)\n *\n * @example\n * ```html\n * <lc-tabs [(selectedIndex)]=\"active\">\n * <lc-tab label=\"Account\">Account settings</lc-tab>\n * <lc-tab label=\"Inbox\" [badge]=\"12\" badgeVariant=\"primary\">Messages</lc-tab>\n * <lc-tab label=\"Security\">Security settings</lc-tab>\n * </lc-tabs>\n * ```\n */\n@Component({\n selector: 'lc-tabs',\n standalone: true,\n imports: [NgClass, NgTemplateOutlet, BadgeComponent],\n templateUrl: './tabs.component.html',\n styleUrl: './tabs.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-tabs',\n },\n})\nexport class TabsComponent {\n /**\n * Orientation of tabs\n * @default 'horizontal'\n */\n readonly orientation = input<TabOrientation>('horizontal');\n\n /**\n * Currently selected tab index. Two-way bindable via `[(selectedIndex)]`;\n * `selectedIndexChange` emits only when the selection actually changes\n * (never during initialisation).\n * @default 0\n */\n readonly selectedIndex = model(0);\n\n /**\n * Legacy one-way input for the selected index.\n * @deprecated Bind `[selectedIndex]` / `[(selectedIndex)]` instead.\n */\n readonly selectedIndexInput = input<number | undefined>(undefined);\n\n /**\n * Projected tab components (signal query — updates when tabs are added or\n * removed after init).\n */\n readonly tabs = contentChildren(TabComponent);\n\n /**\n * Tab list for template access\n */\n readonly tabList = computed(() => this.tabs());\n\n /**\n * Rendered tab buttons — used to move DOM focus on keyboard navigation.\n */\n private readonly tabButtons = viewChildren<ElementRef<HTMLButtonElement>>('tabButton');\n\n /**\n * CSS classes for tab list\n */\n readonly tabListClasses = computed(() => ({\n 'lc-tabs__list': true,\n 'lc-tabs--horizontal': this.orientation() === 'horizontal',\n 'lc-tabs--vertical': this.orientation() === 'vertical',\n }));\n\n constructor() {\n // Mirror the deprecated one-way input into the model. `model.set` is a\n // no-op (no emit) when the value is unchanged, so binding the same index\n // does not fire `selectedIndexChange`.\n effect(() => {\n const legacy = this.selectedIndexInput();\n if (legacy !== undefined) {\n untracked(() => this.selectedIndex.set(legacy));\n }\n });\n\n // Keep the selection valid when tabs are removed at runtime: falling off\n // the end would leave no panel visible and no tab reachable via Tab.\n effect(() => {\n const count = this.tabs().length;\n const index = untracked(() => this.selectedIndex());\n if (count > 0 && index >= count) {\n untracked(() => this.selectTab(this.getLastEnabledTab()));\n }\n });\n }\n\n /**\n * Select a tab by index\n */\n selectTab(index: number): void {\n const tabs = this.tabList();\n const tab = tabs[index];\n if (index >= 0 && index < tabs.length && tab && !tab.disabled()) {\n this.selectedIndex.set(index);\n }\n }\n\n /**\n * Check if tab is selected\n */\n isSelected(index: number): boolean {\n return this.selectedIndex() === index;\n }\n\n /**\n * Handle keyboard navigation (automatic activation: focus and selection\n * move together).\n */\n handleKeyDown(event: KeyboardEvent): void {\n const currentIndex = this.selectedIndex();\n let nextIndex = currentIndex;\n\n const isHorizontal = this.orientation() === 'horizontal';\n const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';\n const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';\n\n switch (event.key) {\n case nextKey:\n event.preventDefault();\n nextIndex = this.getNextEnabledTab(currentIndex);\n break;\n\n case prevKey:\n event.preventDefault();\n nextIndex = this.getPreviousEnabledTab(currentIndex);\n break;\n\n case 'Home':\n event.preventDefault();\n nextIndex = this.getFirstEnabledTab();\n break;\n\n case 'End':\n event.preventDefault();\n nextIndex = this.getLastEnabledTab();\n break;\n\n default:\n return;\n }\n\n if (nextIndex !== currentIndex) {\n this.selectTab(nextIndex);\n this.focusTab(nextIndex);\n }\n }\n\n /**\n * Get tabindex for tab button\n */\n getTabIndex(index: number): number {\n return this.isSelected(index) ? 0 : -1;\n }\n\n /**\n * Move DOM focus to the tab button at `index` (roving tabindex).\n */\n private focusTab(index: number): void {\n this.tabButtons()[index]?.nativeElement.focus();\n }\n\n /**\n * Get next enabled tab index (with wrapping)\n */\n private getNextEnabledTab(currentIndex: number): number {\n const tabs = this.tabList();\n let nextIndex = (currentIndex + 1) % tabs.length;\n const startIndex = nextIndex;\n\n while (tabs[nextIndex]?.disabled()) {\n nextIndex = (nextIndex + 1) % tabs.length;\n if (nextIndex === startIndex) {\n return currentIndex; // No enabled tabs found\n }\n }\n\n return nextIndex;\n }\n\n /**\n * Get previous enabled tab index (with wrapping)\n */\n private getPreviousEnabledTab(currentIndex: number): number {\n const tabs = this.tabList();\n let prevIndex = currentIndex - 1;\n if (prevIndex < 0) {\n prevIndex = tabs.length - 1;\n }\n const startIndex = prevIndex;\n\n while (tabs[prevIndex]?.disabled()) {\n prevIndex = prevIndex - 1;\n if (prevIndex < 0) {\n prevIndex = tabs.length - 1;\n }\n if (prevIndex === startIndex) {\n return currentIndex; // No enabled tabs found\n }\n }\n\n return prevIndex;\n }\n\n /**\n * Get first enabled tab index\n */\n private getFirstEnabledTab(): number {\n const tabs = this.tabList();\n for (let i = 0; i < tabs.length; i++) {\n const tab = tabs[i];\n if (tab && !tab.disabled()) {\n return i;\n }\n }\n return 0;\n }\n\n /**\n * Get last enabled tab index\n */\n private getLastEnabledTab(): number {\n const tabs = this.tabList();\n for (let i = tabs.length - 1; i >= 0; i--) {\n const tab = tabs[i];\n if (tab && !tab.disabled()) {\n return i;\n }\n }\n return tabs.length - 1;\n }\n}\n","<div\n class=\"lc-tabs__container\"\n [class.lc-tabs__container--horizontal]=\"orientation() === 'horizontal'\"\n [class.lc-tabs__container--vertical]=\"orientation() === 'vertical'\"\n>\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- keydown bubbles from the focused tab (roving tabindex) -->\n <div\n [ngClass]=\"tabListClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"handleKeyDown($event)\"\n >\n @for (tab of tabList(); track tab.id; let i = $index) {\n <button\n #tabButton\n type=\"button\"\n class=\"lc-tabs__tab\"\n [class.lc-tabs__tab--selected]=\"isSelected(i)\"\n [class.lc-tabs__tab--disabled]=\"tab.disabled()\"\n role=\"tab\"\n [attr.id]=\"tab.id\"\n [attr.aria-selected]=\"isSelected(i)\"\n [attr.aria-controls]=\"tab.panelId\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [tabindex]=\"getTabIndex(i)\"\n [disabled]=\"tab.disabled()\"\n (click)=\"selectTab(i)\"\n >\n {{ tab.label() }}\n @if (tab.hasBadge()) {\n <lc-badge\n class=\"lc-tabs__badge\"\n [variant]=\"tab.badgeVariant()\"\n size=\"xs\"\n [rounded]=\"true\"\n >{{ tab.badge() }}</lc-badge\n >\n }\n </button>\n }\n </div>\n\n <div class=\"lc-tabs__panels\">\n @for (tab of tabList(); track tab.id; let i = $index) {\n <div\n class=\"lc-tabs__panel\"\n [class.lc-tabs__panel--active]=\"isSelected(i)\"\n role=\"tabpanel\"\n [attr.id]=\"tab.panelId\"\n [attr.aria-labelledby]=\"tab.id\"\n [attr.hidden]=\"!isSelected(i) || null\"\n [tabindex]=\"0\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.template()\"></ng-container>\n </div>\n }\n </div>\n</div>\n"],"mappings":"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"}
|