@truenas/ui-components 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Overlay, OverlayPositionBuilder, OverlayModule } from '@angular/cdk/overlay';
|
|
2
2
|
import { TemplatePortal, ComponentPortal, PortalModule } from '@angular/cdk/portal';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { InjectionToken, inject, computed, input, ViewEncapsulation, ChangeDetectionStrategy, Component, Injector, Renderer2, ElementRef, effect, Directive, ViewContainerRef, output, viewChild, signal, untracked, isDevMode, forwardRef, model, afterNextRender, Injectable, contentChildren, Pipe, HostListener, linkedSignal, contentChild, TemplateRef, viewChildren, ChangeDetectorRef, DestroyRef, isSignal, IterableDiffers, booleanAttribute, LOCALE_ID, ApplicationRef, EnvironmentInjector, createComponent, PLATFORM_ID } from '@angular/core';
|
|
4
|
+
import { InjectionToken, inject, computed, input, ViewEncapsulation, ChangeDetectionStrategy, Component, Injector, Renderer2, ElementRef, effect, Directive, ViewContainerRef, output, viewChild, signal, untracked, isDevMode, forwardRef, model, afterNextRender, Injectable, contentChildren, Pipe, NgZone, HostListener, linkedSignal, contentChild, TemplateRef, viewChildren, ChangeDetectorRef, DestroyRef, isSignal, IterableDiffers, booleanAttribute, LOCALE_ID, ApplicationRef, EnvironmentInjector, createComponent, PLATFORM_ID } from '@angular/core';
|
|
5
5
|
import * as i1$3 from '@angular/forms';
|
|
6
6
|
import { NgControl, NG_VALUE_ACCESSOR, FormsModule, Validators } from '@angular/forms';
|
|
7
7
|
import { ComponentHarness, HarnessPredicate, TestKey, parallel } from '@angular/cdk/testing';
|
|
@@ -2854,6 +2854,7 @@ class TnTooltipDirective {
|
|
|
2854
2854
|
_hideTimeout = null;
|
|
2855
2855
|
_isTooltipVisible = false;
|
|
2856
2856
|
_positionSub = null;
|
|
2857
|
+
_focusSub = null;
|
|
2857
2858
|
// Unique ID for the overlay tooltip element
|
|
2858
2859
|
_tooltipId = `tn-tooltip-${Math.random().toString(36).substr(2, 9)}`;
|
|
2859
2860
|
_overlay = inject(Overlay);
|
|
@@ -2861,6 +2862,8 @@ class TnTooltipDirective {
|
|
|
2861
2862
|
_viewContainerRef = inject(ViewContainerRef);
|
|
2862
2863
|
_overlayPositionBuilder = inject(OverlayPositionBuilder);
|
|
2863
2864
|
_ariaDescriber = inject(AriaDescriber);
|
|
2865
|
+
_focusMonitor = inject(FocusMonitor);
|
|
2866
|
+
_ngZone = inject(NgZone);
|
|
2864
2867
|
_viewInitialized = false;
|
|
2865
2868
|
_describedTarget = null;
|
|
2866
2869
|
_describedMessage = '';
|
|
@@ -2898,6 +2901,23 @@ class TnTooltipDirective {
|
|
|
2898
2901
|
this._innerObserver = new MutationObserver(() => this._syncAriaDescription());
|
|
2899
2902
|
this._innerObserver.observe(host, { childList: true, subtree: true });
|
|
2900
2903
|
}
|
|
2904
|
+
// Focus opens the tooltip only when the focus actually came from the keyboard. A plain
|
|
2905
|
+
// `focus`/`focusin` listener also fires for programmatic `.focus()` — e.g.
|
|
2906
|
+
// TnMenuTriggerDirective restoring focus to its trigger after the menu closes — which
|
|
2907
|
+
// popped a tooltip back up next to a button the pointer was nowhere near, and left it
|
|
2908
|
+
// there until the user clicked or tabbed away. Routing through FocusMonitor (same
|
|
2909
|
+
// approach as MatTooltip) also skips mouse/touch focus, where the hover handlers already
|
|
2910
|
+
// cover the interaction, and it watches descendants — so a wrapper host (e.g.
|
|
2911
|
+
// `<tn-button>`) still reacts when its inner control takes focus.
|
|
2912
|
+
this._focusSub = this._focusMonitor.monitor(this._elementRef, true).subscribe((origin) => {
|
|
2913
|
+
// FocusMonitor emits outside the Angular zone, so re-enter before touching the overlay.
|
|
2914
|
+
if (!origin) {
|
|
2915
|
+
this._ngZone.run(() => this.hide(this.hideDelay()));
|
|
2916
|
+
}
|
|
2917
|
+
else if (origin === 'keyboard') {
|
|
2918
|
+
this._ngZone.run(() => this.show(this.showDelay()));
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2901
2921
|
}
|
|
2902
2922
|
_syncAriaDescription() {
|
|
2903
2923
|
const host = this._elementRef.nativeElement;
|
|
@@ -2945,6 +2965,8 @@ class TnTooltipDirective {
|
|
|
2945
2965
|
this._clearTimeouts();
|
|
2946
2966
|
this.hide(0);
|
|
2947
2967
|
this._positionSub?.unsubscribe();
|
|
2968
|
+
this._focusSub?.unsubscribe();
|
|
2969
|
+
this._focusMonitor.stopMonitoring(this._elementRef);
|
|
2948
2970
|
this._innerObserver?.disconnect();
|
|
2949
2971
|
this._removeAriaDescription();
|
|
2950
2972
|
if (this._overlayRef) {
|
|
@@ -2960,24 +2982,6 @@ class TnTooltipDirective {
|
|
|
2960
2982
|
_onMouseLeave() {
|
|
2961
2983
|
this.hide(this.hideDelay());
|
|
2962
2984
|
}
|
|
2963
|
-
// focusin/focusout (not focus/blur): focus does not bubble, so on a wrapper host
|
|
2964
|
-
// (e.g. `<tn-button>`) a focus listener never fires when the inner control is
|
|
2965
|
-
// focused via keyboard. focusin/focusout bubble and cover both shapes.
|
|
2966
|
-
_onFocusIn() {
|
|
2967
|
-
if (!this.disabled() && this.message()) {
|
|
2968
|
-
this.show(this.showDelay());
|
|
2969
|
-
}
|
|
2970
|
-
}
|
|
2971
|
-
_onFocusOut(event) {
|
|
2972
|
-
// focusout bubbles for every focus move inside the host too — only hide when
|
|
2973
|
-
// focus actually leaves the host, or a wrapper-internal move would tear down
|
|
2974
|
-
// the visible tooltip via the armed hide timeout.
|
|
2975
|
-
const next = event.relatedTarget;
|
|
2976
|
-
if (next && this._elementRef.nativeElement.contains(next)) {
|
|
2977
|
-
return;
|
|
2978
|
-
}
|
|
2979
|
-
this.hide(this.hideDelay());
|
|
2980
|
-
}
|
|
2981
2985
|
_onKeydown(event) {
|
|
2982
2986
|
if (event.key === 'Escape' && this._isTooltipVisible) {
|
|
2983
2987
|
this.hide(0);
|
|
@@ -3115,7 +3119,7 @@ class TnTooltipDirective {
|
|
|
3115
3119
|
}
|
|
3116
3120
|
}
|
|
3117
3121
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3118
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: TnTooltipDirective, isStandalone: true, selector: "[tnTooltip]", inputs: { message: { classPropertyName: "message", publicName: "tnTooltip", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "tnTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "tnTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, showDelay: { classPropertyName: "showDelay", publicName: "tnTooltipShowDelay", isSignal: true, isRequired: false, transformFunction: null }, hideDelay: { classPropertyName: "hideDelay", publicName: "tnTooltipHideDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipClass: { classPropertyName: "tooltipClass", publicName: "tnTooltipClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "_onMouseEnter()", "mouseleave": "_onMouseLeave()", "
|
|
3122
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: TnTooltipDirective, isStandalone: true, selector: "[tnTooltip]", inputs: { message: { classPropertyName: "message", publicName: "tnTooltip", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "tnTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "tnTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, showDelay: { classPropertyName: "showDelay", publicName: "tnTooltipShowDelay", isSignal: true, isRequired: false, transformFunction: null }, hideDelay: { classPropertyName: "hideDelay", publicName: "tnTooltipHideDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipClass: { classPropertyName: "tooltipClass", publicName: "tnTooltipClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "_onMouseEnter()", "mouseleave": "_onMouseLeave()", "keydown": "_onKeydown($event)" } }, ngImport: i0 });
|
|
3119
3123
|
}
|
|
3120
3124
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTooltipDirective, decorators: [{
|
|
3121
3125
|
type: Directive,
|
|
@@ -3129,12 +3133,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
3129
3133
|
}], _onMouseLeave: [{
|
|
3130
3134
|
type: HostListener,
|
|
3131
3135
|
args: ['mouseleave']
|
|
3132
|
-
}], _onFocusIn: [{
|
|
3133
|
-
type: HostListener,
|
|
3134
|
-
args: ['focusin']
|
|
3135
|
-
}], _onFocusOut: [{
|
|
3136
|
-
type: HostListener,
|
|
3137
|
-
args: ['focusout', ['$event']]
|
|
3138
3136
|
}], _onKeydown: [{
|
|
3139
3137
|
type: HostListener,
|
|
3140
3138
|
args: ['keydown', ['$event']]
|
|
@@ -6373,6 +6371,13 @@ class TnCardComponent {
|
|
|
6373
6371
|
titleQueryParams = input(undefined, ...(ngDevMode ? [{ debugName: "titleQueryParams" }] : []));
|
|
6374
6372
|
/** Help/hover text shown on the title via the tooltip directive. */
|
|
6375
6373
|
titleTooltip = input(undefined, ...(ngDevMode ? [{ debugName: "titleTooltip" }] : []));
|
|
6374
|
+
/**
|
|
6375
|
+
* Accessible name for the help button that carries `titleTooltip`. Same reason as
|
|
6376
|
+
* `headerMenuTriggerAriaLabel`: the library cannot translate its own `'More information'`
|
|
6377
|
+
* default, so a consumer with an i18n layer passes an already-translated string here.
|
|
6378
|
+
*/
|
|
6379
|
+
titleTooltipAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "titleTooltipAriaLabel" }] : []));
|
|
6380
|
+
resolvedTitleTooltipAriaLabel = computed(() => this.titleTooltipAriaLabel() ?? 'More information', ...(ngDevMode ? [{ debugName: "resolvedTitleTooltipAriaLabel" }] : []));
|
|
6376
6381
|
elevation = input('medium', ...(ngDevMode ? [{ debugName: "elevation" }] : []));
|
|
6377
6382
|
padding = input('medium', ...(ngDevMode ? [{ debugName: "padding" }] : []));
|
|
6378
6383
|
padContent = input(true, ...(ngDevMode ? [{ debugName: "padContent" }] : []));
|
|
@@ -6397,6 +6402,28 @@ class TnCardComponent {
|
|
|
6397
6402
|
* (default `data-testid`).
|
|
6398
6403
|
*/
|
|
6399
6404
|
headerMenuTriggerTestId = input(undefined, ...(ngDevMode ? [{ debugName: "headerMenuTriggerTestId" }] : []));
|
|
6405
|
+
/**
|
|
6406
|
+
* Accessible name for the kebab-menu trigger rendered when `headerMenu` is set.
|
|
6407
|
+
*
|
|
6408
|
+
* The library cannot translate its own default, so a consumer with an i18n layer must be able to
|
|
6409
|
+
* supply an already-translated string here — otherwise the trigger's only accessible name is the
|
|
6410
|
+
* English `'Card menu'` fallback. Also used as the trigger's tooltip when
|
|
6411
|
+
* `headerMenuTriggerTooltip` is not set.
|
|
6412
|
+
*/
|
|
6413
|
+
headerMenuTriggerAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "headerMenuTriggerAriaLabel" }] : []));
|
|
6414
|
+
/**
|
|
6415
|
+
* Hover tooltip for the kebab-menu trigger. Defaults to `headerMenuTriggerAriaLabel`, so setting
|
|
6416
|
+
* a single translated string covers both the accessible name and the visible hint.
|
|
6417
|
+
*/
|
|
6418
|
+
headerMenuTriggerTooltip = input(undefined, ...(ngDevMode ? [{ debugName: "headerMenuTriggerTooltip" }] : []));
|
|
6419
|
+
resolvedHeaderMenuAriaLabel = computed(() => this.headerMenuTriggerAriaLabel() ?? 'Card menu', ...(ngDevMode ? [{ debugName: "resolvedHeaderMenuAriaLabel" }] : []));
|
|
6420
|
+
/**
|
|
6421
|
+
* The trigger has no tooltip unless the consumer asks for one — either explicitly, or by naming
|
|
6422
|
+
* the trigger and letting that one string drive both. Falling back to the built-in `'Card menu'`
|
|
6423
|
+
* default instead would put a new hover hint on every existing `[headerMenu]` consumer, one that
|
|
6424
|
+
* only repeats the button's accessible name.
|
|
6425
|
+
*/
|
|
6426
|
+
resolvedHeaderMenuTooltip = computed(() => this.headerMenuTriggerTooltip() ?? this.headerMenuTriggerAriaLabel(), ...(ngDevMode ? [{ debugName: "resolvedHeaderMenuTooltip" }] : []));
|
|
6400
6427
|
// Footer elements (bottom-right) - Always render in footer
|
|
6401
6428
|
primaryAction = input(undefined, ...(ngDevMode ? [{ debugName: "primaryAction" }] : []));
|
|
6402
6429
|
secondaryAction = input(undefined, ...(ngDevMode ? [{ debugName: "secondaryAction" }] : []));
|
|
@@ -6467,7 +6494,7 @@ class TnCardComponent {
|
|
|
6467
6494
|
return type ? `tn-card__status--${type}` : 'tn-card__status--neutral';
|
|
6468
6495
|
}
|
|
6469
6496
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6470
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnCardComponent, isStandalone: true, selector: "tn-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, titleLink: { classPropertyName: "titleLink", publicName: "titleLink", isSignal: true, isRequired: false, transformFunction: null }, titleRouterLink: { classPropertyName: "titleRouterLink", publicName: "titleRouterLink", isSignal: true, isRequired: false, transformFunction: null }, titleQueryParams: { classPropertyName: "titleQueryParams", publicName: "titleQueryParams", isSignal: true, isRequired: false, transformFunction: null }, titleTooltip: { classPropertyName: "titleTooltip", publicName: "titleTooltip", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, padContent: { classPropertyName: "padContent", publicName: "padContent", isSignal: true, isRequired: false, transformFunction: null }, fillHeight: { classPropertyName: "fillHeight", publicName: "fillHeight", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null }, headerStatus: { classPropertyName: "headerStatus", publicName: "headerStatus", isSignal: true, isRequired: false, transformFunction: null }, headerControl: { classPropertyName: "headerControl", publicName: "headerControl", isSignal: true, isRequired: false, transformFunction: null }, headerMenu: { classPropertyName: "headerMenu", publicName: "headerMenu", isSignal: true, isRequired: false, transformFunction: null }, headerMenuTriggerTestId: { classPropertyName: "headerMenuTriggerTestId", publicName: "headerMenuTriggerTestId", isSignal: true, isRequired: false, transformFunction: null }, primaryAction: { classPropertyName: "primaryAction", publicName: "primaryAction", isSignal: true, isRequired: false, transformFunction: null }, secondaryAction: { classPropertyName: "secondaryAction", publicName: "secondaryAction", isSignal: true, isRequired: false, transformFunction: null }, footerLink: { classPropertyName: "footerLink", publicName: "footerLink", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.tn-card-host--content-height": "!fillHeight()" } }, queries: [{ propertyName: "projectedHeader", first: true, predicate: TnCardHeaderDirective, descendants: true, isSignal: true }, { propertyName: "headerActions", first: true, predicate: TnCardHeaderActionsDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "footerActions", first: true, predicate: TnCardFooterActionsDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<div [ngClass]=\"classes()\">\n <!-- Header section -->\n @if (hasHeader()) {\n <div class=\"tn-card__header\">\n <div class=\"tn-card__header-left\">\n <ng-content select=\"[tnCardHeader]\" />\n @if (!projectedHeader() && title()) {\n <h3 class=\"tn-card__title\">\n @if (isTitleRouterLink()) {\n <a\n class=\"tn-card__title-link\"\n [routerLink]=\"titleRouterLink()\"\n [queryParams]=\"titleQueryParams()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </a>\n } @else if (titleLink()) {\n <span\n class=\"tn-card__title-link\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"onTitleClick()\"\n (keydown.enter)=\"onTitleClick()\"\n (keydown.space)=\"onTitleClick(); $event.preventDefault()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </span>\n } @else {\n {{ title() }}\n }\n\n @if (titleTooltip(); as tooltip) {\n <button\n type=\"button\"\n class=\"tn-card__title-tooltip\"\n aria-label=\"More information\"\n [tnTooltip]=\"tooltip\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </button>\n }\n </h3>\n }\n </div>\n\n @if (hasHeaderRight()) {\n <div class=\"tn-card__header-right\">\n <!-- Header Status -->\n @if (headerStatus(); as status) {\n <div\n class=\"tn-card__status\"\n [ngClass]=\"getStatusClass(status?.type)\"\n [tnTestId]=\"status.testId\">\n {{ status.label }}\n </div>\n }\n\n <!-- Header Control (Slide Toggle) -->\n @if (headerControl(); as control) {\n <div class=\"tn-card__control\">\n <tn-slide-toggle\n [label]=\"control.label\"\n [checked]=\"control.checked\"\n [disabled]=\"control.disabled || false\"\n [testId]=\"control.testId\"\n (change)=\"onControlChange($event)\" />\n </div>\n }\n\n <!-- Projected header actions (escape hatch for custom/permission-gated controls) -->\n @if (headerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n\n <!-- Header Menu -->\n @if (headerMenu(); as menu) {\n @if (menu.length) {\n <div class=\"tn-card__menu\">\n <tn-icon-button\n name=\"dots-vertical\"\n library=\"mdi\"\n size=\"md\"\n ariaLabel=\"Card menu\"\n [testId]=\"headerMenuTriggerTestId()\"\n [tnMenuTriggerFor]=\"cardMenu\" />\n <tn-menu\n #cardMenu\n [items]=\"menu\"\n (menuItemClick)=\"onHeaderMenuItemClick($event)\" />\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Content section -->\n <div class=\"tn-card__content\">\n <ng-content />\n </div>\n\n <!-- Footer section -->\n @if (hasFooter()) {\n <div class=\"tn-card__footer\">\n <div class=\"tn-card__footer-left\">\n @if (footerLink(); as link) {\n <button\n type=\"button\"\n class=\"tn-card__footer-link\"\n tnTestIdType=\"link\"\n [tnTestId]=\"link.testId\"\n (click)=\"link.handler()\">\n {{ link.label }}\n </button>\n }\n </div>\n\n <div class=\"tn-card__footer-right\">\n @if (secondaryAction(); as action) {\n <tn-button\n variant=\"outline\"\n color=\"default\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n @if (primaryAction(); as action) {\n <tn-button\n variant=\"filled\"\n color=\"primary\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n <!-- Projected footer actions (escape hatch for custom/permission-gated controls) -->\n @if (footerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n </div>\n </div>\n }\n</div>", styles: [":host{display:block}:host(.tn-card-host--content-height){height:auto}:host(.tn-card-host--content-height) .tn-card{height:auto}.tn-card{height:100%;display:flex;flex-direction:column;border-radius:8px;transition:box-shadow .3s ease;overflow:hidden}.tn-card--elevation-none{box-shadow:none}.tn-card--elevation-low{box-shadow:0 1px 3px #0000001a}.tn-card--elevation-medium{box-shadow:0 4px 6px #0000001a}.tn-card--elevation-high{box-shadow:0 10px 15px #0000001a}.tn-card--bordered{border:1px solid var(--tn-lines, #e5e7eb)}.tn-card--background{background-color:var(--tn-bg2, #ffffff)}.tn-card--padding-small .tn-card__header{padding:12px 16px}.tn-card--padding-medium .tn-card__header{padding:16px 24px}.tn-card--padding-large .tn-card__header{padding:24px 32px}.tn-card--content-padding-none .tn-card__content{padding:0}.tn-card--content-padding-small .tn-card__content{padding:16px}.tn-card--content-padding-medium .tn-card__content{padding:24px}.tn-card--content-padding-large .tn-card__content{padding:32px}.tn-card__content{flex:1;min-height:0;font-size:14px}.tn-card__header{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--tn-lines, #e5e7eb)}.tn-card__header-left{flex:1;min-width:0}.tn-card__header-right{display:flex;align-items:center;gap:12px;flex-shrink:0}.tn-card__title{display:inline-flex;align-items:center;gap:8px;margin:0;font-size:1.125rem;font-weight:600;color:var(--tn-fg1, #1f2937);line-height:1.5}.tn-card__title-link{display:inline-flex;align-items:center;gap:4px;color:inherit;cursor:pointer;text-decoration:none;transition:color .2s ease}.tn-card__title-link:hover{color:var(--tn-primary, #2563eb)}.tn-card__title-link-icon{flex-shrink:0}.tn-card__title-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-card__title-tooltip:hover,.tn-card__title-tooltip:focus-visible{color:var(--tn-fg1, #1f2937)}.tn-card__status{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.tn-card__status--success{background-color:#10b9811a;color:var(--tn-success, #10b981)}.tn-card__status--warning{background-color:#f59e0b1a;color:var(--tn-warning, #f59e0b)}.tn-card__status--error{background-color:#ef44441a;color:var(--tn-error, #ef4444)}.tn-card__status--info{background-color:#3b82f61a;color:var(--tn-info, #3b82f6)}.tn-card__status--neutral{background-color:#6b72801a;color:var(--tn-fg2, #6b7280)}.tn-card__control,.tn-card__menu{display:flex;align-items:center}.tn-card__footer{display:flex;align-items:center;justify-content:space-between;gap:16px;border-top:1px solid var(--tn-lines, #e5e7eb);padding:16px 24px}.tn-card--padding-small .tn-card__footer{padding:12px 16px}.tn-card--padding-large .tn-card__footer{padding:24px 32px}.tn-card__footer-left{flex:1;min-width:0}.tn-card__footer-right{display:flex;align-items:center;gap:8px;flex-shrink:0}.tn-card__footer-link{border:none;background:transparent;color:var(--tn-primary, #2563eb);font-size:1rem;font-weight:600;cursor:pointer;padding:0;text-decoration:none;transition:color .2s ease}.tn-card__footer-link:hover{color:var(--tn-primary-dark, #1d4ed8);text-decoration:underline}.tn-card__footer-link:focus{outline:2px solid var(--tn-primary, #2563eb);outline-offset:2px;border-radius:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: TnButtonComponent, selector: "tn-button", inputs: ["primary", "color", "variant", "backgroundColor", "label", "icon", "iconPosition", "disabled", "fullWidth", "type", "testId", "href", "routerLink", "queryParams", "fragment", "target", "rel", "ariaLabel"], outputs: ["onClick"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "component", type: TnIconButtonComponent, selector: "tn-icon-button", inputs: ["disabled", "dense", "ariaLabel", "ariaExpanded", "testId", "name", "size", "color", "tooltip", "tooltipPosition", "library", "iconClass"], outputs: ["onClick"] }, { kind: "component", type: TnSlideToggleComponent, selector: "tn-slide-toggle", inputs: ["labelPosition", "label", "fullWidth", "disabled", "required", "color", "testId", "ariaLabel", "ariaLabelledby", "checked"], outputs: ["change", "toggleChange"] }, { kind: "component", type: TnMenuComponent, selector: "tn-menu", inputs: ["items", "contextMenu", "testId"], outputs: ["menuItemClick", "menuOpen", "menuClose"] }, { kind: "directive", type: TnMenuTriggerDirective, selector: "[tnMenuTriggerFor]", inputs: ["tnMenuTriggerFor", "tnMenuPosition"], exportAs: ["tnMenuTrigger"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
|
|
6497
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnCardComponent, isStandalone: true, selector: "tn-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, titleLink: { classPropertyName: "titleLink", publicName: "titleLink", isSignal: true, isRequired: false, transformFunction: null }, titleRouterLink: { classPropertyName: "titleRouterLink", publicName: "titleRouterLink", isSignal: true, isRequired: false, transformFunction: null }, titleQueryParams: { classPropertyName: "titleQueryParams", publicName: "titleQueryParams", isSignal: true, isRequired: false, transformFunction: null }, titleTooltip: { classPropertyName: "titleTooltip", publicName: "titleTooltip", isSignal: true, isRequired: false, transformFunction: null }, titleTooltipAriaLabel: { classPropertyName: "titleTooltipAriaLabel", publicName: "titleTooltipAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, padContent: { classPropertyName: "padContent", publicName: "padContent", isSignal: true, isRequired: false, transformFunction: null }, fillHeight: { classPropertyName: "fillHeight", publicName: "fillHeight", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null }, headerStatus: { classPropertyName: "headerStatus", publicName: "headerStatus", isSignal: true, isRequired: false, transformFunction: null }, headerControl: { classPropertyName: "headerControl", publicName: "headerControl", isSignal: true, isRequired: false, transformFunction: null }, headerMenu: { classPropertyName: "headerMenu", publicName: "headerMenu", isSignal: true, isRequired: false, transformFunction: null }, headerMenuTriggerTestId: { classPropertyName: "headerMenuTriggerTestId", publicName: "headerMenuTriggerTestId", isSignal: true, isRequired: false, transformFunction: null }, headerMenuTriggerAriaLabel: { classPropertyName: "headerMenuTriggerAriaLabel", publicName: "headerMenuTriggerAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, headerMenuTriggerTooltip: { classPropertyName: "headerMenuTriggerTooltip", publicName: "headerMenuTriggerTooltip", isSignal: true, isRequired: false, transformFunction: null }, primaryAction: { classPropertyName: "primaryAction", publicName: "primaryAction", isSignal: true, isRequired: false, transformFunction: null }, secondaryAction: { classPropertyName: "secondaryAction", publicName: "secondaryAction", isSignal: true, isRequired: false, transformFunction: null }, footerLink: { classPropertyName: "footerLink", publicName: "footerLink", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.tn-card-host--content-height": "!fillHeight()" } }, queries: [{ propertyName: "projectedHeader", first: true, predicate: TnCardHeaderDirective, descendants: true, isSignal: true }, { propertyName: "headerActions", first: true, predicate: TnCardHeaderActionsDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "footerActions", first: true, predicate: TnCardFooterActionsDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<div [ngClass]=\"classes()\">\n <!-- Header section -->\n @if (hasHeader()) {\n <div class=\"tn-card__header\">\n <div class=\"tn-card__header-left\">\n <ng-content select=\"[tnCardHeader]\" />\n @if (!projectedHeader() && title()) {\n <h3 class=\"tn-card__title\">\n @if (isTitleRouterLink()) {\n <a\n class=\"tn-card__title-link\"\n [routerLink]=\"titleRouterLink()\"\n [queryParams]=\"titleQueryParams()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </a>\n } @else if (titleLink()) {\n <span\n class=\"tn-card__title-link\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"onTitleClick()\"\n (keydown.enter)=\"onTitleClick()\"\n (keydown.space)=\"onTitleClick(); $event.preventDefault()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </span>\n } @else {\n {{ title() }}\n }\n\n @if (titleTooltip(); as tooltip) {\n <button\n type=\"button\"\n class=\"tn-card__title-tooltip\"\n [attr.aria-label]=\"resolvedTitleTooltipAriaLabel()\"\n [tnTooltip]=\"tooltip\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </button>\n }\n </h3>\n }\n </div>\n\n @if (hasHeaderRight()) {\n <div class=\"tn-card__header-right\">\n <!-- Header Status -->\n @if (headerStatus(); as status) {\n <div\n class=\"tn-card__status\"\n [ngClass]=\"getStatusClass(status?.type)\"\n [tnTestId]=\"status.testId\">\n {{ status.label }}\n </div>\n }\n\n <!-- Header Control (Slide Toggle) -->\n @if (headerControl(); as control) {\n <div class=\"tn-card__control\">\n <tn-slide-toggle\n [label]=\"control.label\"\n [checked]=\"control.checked\"\n [disabled]=\"control.disabled || false\"\n [testId]=\"control.testId\"\n (change)=\"onControlChange($event)\" />\n </div>\n }\n\n <!-- Projected header actions (escape hatch for custom/permission-gated controls) -->\n @if (headerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n\n <!-- Header Menu -->\n @if (headerMenu(); as menu) {\n @if (menu.length) {\n <div class=\"tn-card__menu\">\n <tn-icon-button\n name=\"dots-vertical\"\n library=\"mdi\"\n size=\"md\"\n [ariaLabel]=\"resolvedHeaderMenuAriaLabel()\"\n [tooltip]=\"resolvedHeaderMenuTooltip()\"\n [testId]=\"headerMenuTriggerTestId()\"\n [tnMenuTriggerFor]=\"cardMenu\" />\n <tn-menu\n #cardMenu\n [items]=\"menu\"\n (menuItemClick)=\"onHeaderMenuItemClick($event)\" />\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Content section -->\n <div class=\"tn-card__content\">\n <ng-content />\n </div>\n\n <!-- Footer section -->\n @if (hasFooter()) {\n <div class=\"tn-card__footer\">\n <div class=\"tn-card__footer-left\">\n @if (footerLink(); as link) {\n <button\n type=\"button\"\n class=\"tn-card__footer-link\"\n tnTestIdType=\"link\"\n [tnTestId]=\"link.testId\"\n (click)=\"link.handler()\">\n {{ link.label }}\n </button>\n }\n </div>\n\n <div class=\"tn-card__footer-right\">\n @if (secondaryAction(); as action) {\n <tn-button\n variant=\"outline\"\n color=\"default\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n @if (primaryAction(); as action) {\n <tn-button\n variant=\"filled\"\n color=\"primary\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n <!-- Projected footer actions (escape hatch for custom/permission-gated controls) -->\n @if (footerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n </div>\n </div>\n }\n</div>", styles: [":host{display:block}:host(.tn-card-host--content-height){height:auto}:host(.tn-card-host--content-height) .tn-card{height:auto}.tn-card{height:100%;display:flex;flex-direction:column;border-radius:8px;transition:box-shadow .3s ease;overflow:hidden}.tn-card--elevation-none{box-shadow:none}.tn-card--elevation-low{box-shadow:0 1px 3px #0000001a}.tn-card--elevation-medium{box-shadow:0 4px 6px #0000001a}.tn-card--elevation-high{box-shadow:0 10px 15px #0000001a}.tn-card--bordered{border:1px solid var(--tn-lines, #e5e7eb)}.tn-card--background{background-color:var(--tn-bg2, #ffffff)}.tn-card--padding-small .tn-card__header{padding:12px 16px}.tn-card--padding-medium .tn-card__header{padding:16px 24px}.tn-card--padding-large .tn-card__header{padding:24px 32px}.tn-card--content-padding-none .tn-card__content{padding:0}.tn-card--content-padding-small .tn-card__content{padding:16px}.tn-card--content-padding-medium .tn-card__content{padding:24px}.tn-card--content-padding-large .tn-card__content{padding:32px}.tn-card__content{flex:1;min-height:0;font-size:14px}.tn-card__header{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--tn-lines, #e5e7eb)}.tn-card__header-left{flex:1;min-width:0}.tn-card__header-right{display:flex;align-items:center;gap:12px;flex-shrink:0}.tn-card__title{display:inline-flex;align-items:center;gap:8px;margin:0;font-size:1.125rem;font-weight:600;color:var(--tn-fg1, #1f2937);line-height:1.5}.tn-card__title-link{display:inline-flex;align-items:center;gap:4px;color:inherit;cursor:pointer;text-decoration:none;transition:color .2s ease}.tn-card__title-link:hover{color:var(--tn-primary, #2563eb)}.tn-card__title-link-icon{flex-shrink:0}.tn-card__title-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-card__title-tooltip:hover,.tn-card__title-tooltip:focus-visible{color:var(--tn-fg1, #1f2937)}.tn-card__status{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.tn-card__status--success{background-color:#10b9811a;color:var(--tn-success, #10b981)}.tn-card__status--warning{background-color:#f59e0b1a;color:var(--tn-warning, #f59e0b)}.tn-card__status--error{background-color:#ef44441a;color:var(--tn-error, #ef4444)}.tn-card__status--info{background-color:#3b82f61a;color:var(--tn-info, #3b82f6)}.tn-card__status--neutral{background-color:#6b72801a;color:var(--tn-fg2, #6b7280)}.tn-card__control,.tn-card__menu{display:flex;align-items:center}.tn-card__footer{display:flex;align-items:center;justify-content:space-between;gap:16px;border-top:1px solid var(--tn-lines, #e5e7eb);padding:16px 24px}.tn-card--padding-small .tn-card__footer{padding:12px 16px}.tn-card--padding-large .tn-card__footer{padding:24px 32px}.tn-card__footer-left{flex:1;min-width:0}.tn-card__footer-right{display:flex;align-items:center;gap:8px;flex-shrink:0}.tn-card__footer-link{border:none;background:transparent;color:var(--tn-primary, #2563eb);font-size:1rem;font-weight:600;cursor:pointer;padding:0;text-decoration:none;transition:color .2s ease}.tn-card__footer-link:hover{color:var(--tn-primary-dark, #1d4ed8);text-decoration:underline}.tn-card__footer-link:focus{outline:2px solid var(--tn-primary, #2563eb);outline-offset:2px;border-radius:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: TnButtonComponent, selector: "tn-button", inputs: ["primary", "color", "variant", "backgroundColor", "label", "icon", "iconPosition", "disabled", "fullWidth", "type", "testId", "href", "routerLink", "queryParams", "fragment", "target", "rel", "ariaLabel"], outputs: ["onClick"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "component", type: TnIconButtonComponent, selector: "tn-icon-button", inputs: ["disabled", "dense", "ariaLabel", "ariaExpanded", "testId", "name", "size", "color", "tooltip", "tooltipPosition", "library", "iconClass"], outputs: ["onClick"] }, { kind: "component", type: TnSlideToggleComponent, selector: "tn-slide-toggle", inputs: ["labelPosition", "label", "fullWidth", "disabled", "required", "color", "testId", "ariaLabel", "ariaLabelledby", "checked"], outputs: ["change", "toggleChange"] }, { kind: "component", type: TnMenuComponent, selector: "tn-menu", inputs: ["items", "contextMenu", "testId"], outputs: ["menuItemClick", "menuOpen", "menuClose"] }, { kind: "directive", type: TnMenuTriggerDirective, selector: "[tnMenuTriggerFor]", inputs: ["tnMenuTriggerFor", "tnMenuPosition"], exportAs: ["tnMenuTrigger"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
|
|
6471
6498
|
}
|
|
6472
6499
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnCardComponent, decorators: [{
|
|
6473
6500
|
type: Component,
|
|
@@ -6484,8 +6511,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
6484
6511
|
TnTooltipDirective,
|
|
6485
6512
|
], host: {
|
|
6486
6513
|
'[class.tn-card-host--content-height]': '!fillHeight()',
|
|
6487
|
-
}, template: "<div [ngClass]=\"classes()\">\n <!-- Header section -->\n @if (hasHeader()) {\n <div class=\"tn-card__header\">\n <div class=\"tn-card__header-left\">\n <ng-content select=\"[tnCardHeader]\" />\n @if (!projectedHeader() && title()) {\n <h3 class=\"tn-card__title\">\n @if (isTitleRouterLink()) {\n <a\n class=\"tn-card__title-link\"\n [routerLink]=\"titleRouterLink()\"\n [queryParams]=\"titleQueryParams()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </a>\n } @else if (titleLink()) {\n <span\n class=\"tn-card__title-link\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"onTitleClick()\"\n (keydown.enter)=\"onTitleClick()\"\n (keydown.space)=\"onTitleClick(); $event.preventDefault()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </span>\n } @else {\n {{ title() }}\n }\n\n @if (titleTooltip(); as tooltip) {\n <button\n type=\"button\"\n class=\"tn-card__title-tooltip\"\n aria-label=\"
|
|
6488
|
-
}], ctorParameters: () => [], propDecorators: { projectedHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardHeaderDirective), { isSignal: true }] }], headerActions: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardHeaderActionsDirective), { ...{ read: TemplateRef }, isSignal: true }] }], footerActions: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardFooterActionsDirective), { ...{ read: TemplateRef }, isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], titleLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleLink", required: false }] }], titleRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleRouterLink", required: false }] }], titleQueryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleQueryParams", required: false }] }], titleTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleTooltip", required: false }] }], elevation: [{ type: i0.Input, args: [{ isSignal: true, alias: "elevation", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], padContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "padContent", required: false }] }], fillHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillHeight", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], background: [{ type: i0.Input, args: [{ isSignal: true, alias: "background", required: false }] }], headerStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerStatus", required: false }] }], headerControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerControl", required: false }] }], headerMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenu", required: false }] }], headerMenuTriggerTestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenuTriggerTestId", required: false }] }], primaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryAction", required: false }] }], secondaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryAction", required: false }] }], footerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerLink", required: false }] }] } });
|
|
6514
|
+
}, template: "<div [ngClass]=\"classes()\">\n <!-- Header section -->\n @if (hasHeader()) {\n <div class=\"tn-card__header\">\n <div class=\"tn-card__header-left\">\n <ng-content select=\"[tnCardHeader]\" />\n @if (!projectedHeader() && title()) {\n <h3 class=\"tn-card__title\">\n @if (isTitleRouterLink()) {\n <a\n class=\"tn-card__title-link\"\n [routerLink]=\"titleRouterLink()\"\n [queryParams]=\"titleQueryParams()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </a>\n } @else if (titleLink()) {\n <span\n class=\"tn-card__title-link\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"onTitleClick()\"\n (keydown.enter)=\"onTitleClick()\"\n (keydown.space)=\"onTitleClick(); $event.preventDefault()\">\n {{ title() }}\n <tn-icon class=\"tn-card__title-link-icon\" name=\"open-in-new\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </span>\n } @else {\n {{ title() }}\n }\n\n @if (titleTooltip(); as tooltip) {\n <button\n type=\"button\"\n class=\"tn-card__title-tooltip\"\n [attr.aria-label]=\"resolvedTitleTooltipAriaLabel()\"\n [tnTooltip]=\"tooltip\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" [attr.aria-hidden]=\"true\" />\n </button>\n }\n </h3>\n }\n </div>\n\n @if (hasHeaderRight()) {\n <div class=\"tn-card__header-right\">\n <!-- Header Status -->\n @if (headerStatus(); as status) {\n <div\n class=\"tn-card__status\"\n [ngClass]=\"getStatusClass(status?.type)\"\n [tnTestId]=\"status.testId\">\n {{ status.label }}\n </div>\n }\n\n <!-- Header Control (Slide Toggle) -->\n @if (headerControl(); as control) {\n <div class=\"tn-card__control\">\n <tn-slide-toggle\n [label]=\"control.label\"\n [checked]=\"control.checked\"\n [disabled]=\"control.disabled || false\"\n [testId]=\"control.testId\"\n (change)=\"onControlChange($event)\" />\n </div>\n }\n\n <!-- Projected header actions (escape hatch for custom/permission-gated controls) -->\n @if (headerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n\n <!-- Header Menu -->\n @if (headerMenu(); as menu) {\n @if (menu.length) {\n <div class=\"tn-card__menu\">\n <tn-icon-button\n name=\"dots-vertical\"\n library=\"mdi\"\n size=\"md\"\n [ariaLabel]=\"resolvedHeaderMenuAriaLabel()\"\n [tooltip]=\"resolvedHeaderMenuTooltip()\"\n [testId]=\"headerMenuTriggerTestId()\"\n [tnMenuTriggerFor]=\"cardMenu\" />\n <tn-menu\n #cardMenu\n [items]=\"menu\"\n (menuItemClick)=\"onHeaderMenuItemClick($event)\" />\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Content section -->\n <div class=\"tn-card__content\">\n <ng-content />\n </div>\n\n <!-- Footer section -->\n @if (hasFooter()) {\n <div class=\"tn-card__footer\">\n <div class=\"tn-card__footer-left\">\n @if (footerLink(); as link) {\n <button\n type=\"button\"\n class=\"tn-card__footer-link\"\n tnTestIdType=\"link\"\n [tnTestId]=\"link.testId\"\n (click)=\"link.handler()\">\n {{ link.label }}\n </button>\n }\n </div>\n\n <div class=\"tn-card__footer-right\">\n @if (secondaryAction(); as action) {\n <tn-button\n variant=\"outline\"\n color=\"default\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n @if (primaryAction(); as action) {\n <tn-button\n variant=\"filled\"\n color=\"primary\"\n [label]=\"action.label\"\n [disabled]=\"action.disabled || false\"\n [testId]=\"action.testId\"\n [tnTooltip]=\"action.tooltip || ''\"\n (click)=\"action.handler()\" />\n }\n\n <!-- Projected footer actions (escape hatch for custom/permission-gated controls) -->\n @if (footerActions(); as actions) {\n <ng-container [ngTemplateOutlet]=\"actions\" />\n }\n </div>\n </div>\n }\n</div>", styles: [":host{display:block}:host(.tn-card-host--content-height){height:auto}:host(.tn-card-host--content-height) .tn-card{height:auto}.tn-card{height:100%;display:flex;flex-direction:column;border-radius:8px;transition:box-shadow .3s ease;overflow:hidden}.tn-card--elevation-none{box-shadow:none}.tn-card--elevation-low{box-shadow:0 1px 3px #0000001a}.tn-card--elevation-medium{box-shadow:0 4px 6px #0000001a}.tn-card--elevation-high{box-shadow:0 10px 15px #0000001a}.tn-card--bordered{border:1px solid var(--tn-lines, #e5e7eb)}.tn-card--background{background-color:var(--tn-bg2, #ffffff)}.tn-card--padding-small .tn-card__header{padding:12px 16px}.tn-card--padding-medium .tn-card__header{padding:16px 24px}.tn-card--padding-large .tn-card__header{padding:24px 32px}.tn-card--content-padding-none .tn-card__content{padding:0}.tn-card--content-padding-small .tn-card__content{padding:16px}.tn-card--content-padding-medium .tn-card__content{padding:24px}.tn-card--content-padding-large .tn-card__content{padding:32px}.tn-card__content{flex:1;min-height:0;font-size:14px}.tn-card__header{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--tn-lines, #e5e7eb)}.tn-card__header-left{flex:1;min-width:0}.tn-card__header-right{display:flex;align-items:center;gap:12px;flex-shrink:0}.tn-card__title{display:inline-flex;align-items:center;gap:8px;margin:0;font-size:1.125rem;font-weight:600;color:var(--tn-fg1, #1f2937);line-height:1.5}.tn-card__title-link{display:inline-flex;align-items:center;gap:4px;color:inherit;cursor:pointer;text-decoration:none;transition:color .2s ease}.tn-card__title-link:hover{color:var(--tn-primary, #2563eb)}.tn-card__title-link-icon{flex-shrink:0}.tn-card__title-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-card__title-tooltip:hover,.tn-card__title-tooltip:focus-visible{color:var(--tn-fg1, #1f2937)}.tn-card__status{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.tn-card__status--success{background-color:#10b9811a;color:var(--tn-success, #10b981)}.tn-card__status--warning{background-color:#f59e0b1a;color:var(--tn-warning, #f59e0b)}.tn-card__status--error{background-color:#ef44441a;color:var(--tn-error, #ef4444)}.tn-card__status--info{background-color:#3b82f61a;color:var(--tn-info, #3b82f6)}.tn-card__status--neutral{background-color:#6b72801a;color:var(--tn-fg2, #6b7280)}.tn-card__control,.tn-card__menu{display:flex;align-items:center}.tn-card__footer{display:flex;align-items:center;justify-content:space-between;gap:16px;border-top:1px solid var(--tn-lines, #e5e7eb);padding:16px 24px}.tn-card--padding-small .tn-card__footer{padding:12px 16px}.tn-card--padding-large .tn-card__footer{padding:24px 32px}.tn-card__footer-left{flex:1;min-width:0}.tn-card__footer-right{display:flex;align-items:center;gap:8px;flex-shrink:0}.tn-card__footer-link{border:none;background:transparent;color:var(--tn-primary, #2563eb);font-size:1rem;font-weight:600;cursor:pointer;padding:0;text-decoration:none;transition:color .2s ease}.tn-card__footer-link:hover{color:var(--tn-primary-dark, #1d4ed8);text-decoration:underline}.tn-card__footer-link:focus{outline:2px solid var(--tn-primary, #2563eb);outline-offset:2px;border-radius:2px}\n"] }]
|
|
6515
|
+
}], ctorParameters: () => [], propDecorators: { projectedHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardHeaderDirective), { isSignal: true }] }], headerActions: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardHeaderActionsDirective), { ...{ read: TemplateRef }, isSignal: true }] }], footerActions: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TnCardFooterActionsDirective), { ...{ read: TemplateRef }, isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], titleLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleLink", required: false }] }], titleRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleRouterLink", required: false }] }], titleQueryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleQueryParams", required: false }] }], titleTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleTooltip", required: false }] }], titleTooltipAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleTooltipAriaLabel", required: false }] }], elevation: [{ type: i0.Input, args: [{ isSignal: true, alias: "elevation", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], padContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "padContent", required: false }] }], fillHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillHeight", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], background: [{ type: i0.Input, args: [{ isSignal: true, alias: "background", required: false }] }], headerStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerStatus", required: false }] }], headerControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerControl", required: false }] }], headerMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenu", required: false }] }], headerMenuTriggerTestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenuTriggerTestId", required: false }] }], headerMenuTriggerAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenuTriggerAriaLabel", required: false }] }], headerMenuTriggerTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerMenuTriggerTooltip", required: false }] }], primaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryAction", required: false }] }], secondaryAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryAction", required: false }] }], footerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerLink", required: false }] }] } });
|
|
6489
6516
|
|
|
6490
6517
|
class TnExpansionPanelComponent {
|
|
6491
6518
|
title = input(undefined, ...(ngDevMode ? [{ debugName: "title" }] : []));
|