@truenas/ui-components 0.4.6 → 0.4.8
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, 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, 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';
|
|
@@ -6214,6 +6214,15 @@ class TnCardComponent {
|
|
|
6214
6214
|
elevation = input('medium', ...(ngDevMode ? [{ debugName: "elevation" }] : []));
|
|
6215
6215
|
padding = input('medium', ...(ngDevMode ? [{ debugName: "padding" }] : []));
|
|
6216
6216
|
padContent = input(true, ...(ngDevMode ? [{ debugName: "padContent" }] : []));
|
|
6217
|
+
/**
|
|
6218
|
+
* Whether the card stretches to fill its container's height.
|
|
6219
|
+
*
|
|
6220
|
+
* Defaults to `true`, which is what an equal-height dashboard grid wants: every card in a row
|
|
6221
|
+
* matches the tallest. Set it to `false` for a card that should size to its content instead —
|
|
6222
|
+
* typically a full-page card wrapping a table, which otherwise leaves a large empty area below
|
|
6223
|
+
* the content when the page is taller than the table.
|
|
6224
|
+
*/
|
|
6225
|
+
fillHeight = input(true, ...(ngDevMode ? [{ debugName: "fillHeight" }] : []));
|
|
6217
6226
|
bordered = input(false, ...(ngDevMode ? [{ debugName: "bordered" }] : []));
|
|
6218
6227
|
background = input(true, ...(ngDevMode ? [{ debugName: "background" }] : []));
|
|
6219
6228
|
// Header elements (top-right) - Always render in header
|
|
@@ -6258,7 +6267,11 @@ class TnCardComponent {
|
|
|
6258
6267
|
const contentPaddingClass = this.padContent() ? `tn-card--content-padding-${this.padding()}` : 'tn-card--content-padding-none';
|
|
6259
6268
|
const borderedClass = this.bordered() ? 'tn-card--bordered' : '';
|
|
6260
6269
|
const backgroundClass = this.background() ? 'tn-card--background' : '';
|
|
6261
|
-
|
|
6270
|
+
// Note: fill-height has no class here — the opt-out is a host modifier
|
|
6271
|
+
// (`tn-card-host--content-height`) that releases the host and this element together.
|
|
6272
|
+
return [
|
|
6273
|
+
'tn-card', elevationClass, paddingClass, contentPaddingClass, borderedClass, backgroundClass,
|
|
6274
|
+
].filter(Boolean);
|
|
6262
6275
|
}, ...(ngDevMode ? [{ debugName: "classes" }] : []));
|
|
6263
6276
|
hasHeader = computed(() => {
|
|
6264
6277
|
return !!(this.projectedHeader() || this.title() || this.headerStatus()
|
|
@@ -6292,7 +6305,7 @@ class TnCardComponent {
|
|
|
6292
6305
|
return type ? `tn-card__status--${type}` : 'tn-card__status--neutral';
|
|
6293
6306
|
}
|
|
6294
6307
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6295
|
-
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 }, 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 } }, 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 (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 (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: [".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"] }] });
|
|
6308
|
+
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 (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 (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"] }] });
|
|
6296
6309
|
}
|
|
6297
6310
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnCardComponent, decorators: [{
|
|
6298
6311
|
type: Component,
|
|
@@ -6307,8 +6320,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
6307
6320
|
TnMenuTriggerDirective,
|
|
6308
6321
|
TnTestIdDirective,
|
|
6309
6322
|
TnTooltipDirective,
|
|
6310
|
-
],
|
|
6311
|
-
|
|
6323
|
+
], host: {
|
|
6324
|
+
'[class.tn-card-host--content-height]': '!fillHeight()',
|
|
6325
|
+
}, 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 (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 (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"] }]
|
|
6326
|
+
}], 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 }] }] } });
|
|
6312
6327
|
|
|
6313
6328
|
class TnExpansionPanelComponent {
|
|
6314
6329
|
title = input(undefined, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
@@ -6835,10 +6850,19 @@ class TnCheckboxHarness extends ComponentHarness {
|
|
|
6835
6850
|
}
|
|
6836
6851
|
}
|
|
6837
6852
|
|
|
6853
|
+
/** DI token under which `tn-radio-group` exposes its {@link TnRadioGroupApi} to child radios. */
|
|
6854
|
+
const TN_RADIO_GROUP = new InjectionToken('TN_RADIO_GROUP');
|
|
6855
|
+
|
|
6838
6856
|
class TnRadioComponent {
|
|
6839
6857
|
radioEl = viewChild.required('radioEl');
|
|
6840
6858
|
label = input('Radio', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
6841
6859
|
value = input('', ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
6860
|
+
/**
|
|
6861
|
+
* Native `name`, which is what binds radios into one keyboard-navigable set. Inside a
|
|
6862
|
+
* `tn-radio-group` it takes precedence over the group's own name — which means setting it there
|
|
6863
|
+
* quietly excludes this option from the group's arrow-key navigation and lets it be checked
|
|
6864
|
+
* alongside a sibling. Only set it on a standalone radio.
|
|
6865
|
+
*/
|
|
6842
6866
|
name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
6843
6867
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
6844
6868
|
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
@@ -6846,12 +6870,35 @@ class TnRadioComponent {
|
|
|
6846
6870
|
/** Test-id base, falling back to the bound control name when `testId` is unset. */
|
|
6847
6871
|
resolvedTestId = controlTestId(this.testId);
|
|
6848
6872
|
error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
6873
|
+
/**
|
|
6874
|
+
* Emits the picked value — only while standalone. Inside a `tn-radio-group` the group's own
|
|
6875
|
+
* `change` is the single public event, so bind it there instead.
|
|
6876
|
+
*/
|
|
6849
6877
|
change = output();
|
|
6850
6878
|
id = `tn-radio-${Math.random().toString(36).substr(2, 9)}`;
|
|
6851
|
-
|
|
6879
|
+
/**
|
|
6880
|
+
* The enclosing `tn-radio-group`, when there is one. Present for options rendered from the
|
|
6881
|
+
* group's `options` input as well as for projected ones — element injectors chain through the
|
|
6882
|
+
* template an element is *declared* in, which is the group either way. With groups nested in
|
|
6883
|
+
* projected content it resolves to the nearest one, so an option always derives its state from
|
|
6884
|
+
* the group it visually belongs to.
|
|
6885
|
+
*/
|
|
6886
|
+
group = inject(TN_RADIO_GROUP, { optional: true });
|
|
6887
|
+
/**
|
|
6888
|
+
* Checked state while standalone. Ignored inside a group, where {@link checked} derives from the
|
|
6889
|
+
* group's value instead — the state that used to go stale, because Angular suppresses the
|
|
6890
|
+
* model→view write on whichever accessor originated a change.
|
|
6891
|
+
*/
|
|
6892
|
+
standaloneChecked = signal(false, ...(ngDevMode ? [{ debugName: "standaloneChecked" }] : []));
|
|
6893
|
+
/** Whether this option renders as selected. */
|
|
6894
|
+
checked = computed(() => (this.group ? this.group.isSelected(this.value()) : this.standaloneChecked()), ...(ngDevMode ? [{ debugName: "checked" }] : []));
|
|
6895
|
+
/** Native `name`: the group's (binding its options into one keyboard set) unless overridden. */
|
|
6896
|
+
resolvedName = computed(() => this.name() ?? this.group?.resolvedName(), ...(ngDevMode ? [{ debugName: "resolvedName" }] : []));
|
|
6852
6897
|
// CVA disabled state management
|
|
6853
6898
|
formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : []));
|
|
6854
|
-
isDisabled = computed(() => this.disabled() || this.formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
6899
|
+
isDisabled = computed(() => this.disabled() || this.formDisabled() || (this.group?.isDisabled() ?? false), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
6900
|
+
/** Own `required` input, or the enclosing group's. */
|
|
6901
|
+
isRequired = computed(() => this.required() || (this.group?.isRequired() ?? false), ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
|
|
6855
6902
|
focusMonitor = inject(FocusMonitor);
|
|
6856
6903
|
onChange = (_) => { };
|
|
6857
6904
|
onTouched = () => { };
|
|
@@ -6872,7 +6919,7 @@ class TnRadioComponent {
|
|
|
6872
6919
|
}
|
|
6873
6920
|
// ControlValueAccessor implementation
|
|
6874
6921
|
writeValue(value) {
|
|
6875
|
-
this.
|
|
6922
|
+
this.standaloneChecked.set(value !== null && value !== undefined && value === this.value());
|
|
6876
6923
|
}
|
|
6877
6924
|
registerOnChange(fn) {
|
|
6878
6925
|
this.onChange = fn;
|
|
@@ -6890,13 +6937,31 @@ class TnRadioComponent {
|
|
|
6890
6937
|
// public event, so the native event stops here.
|
|
6891
6938
|
event.stopPropagation();
|
|
6892
6939
|
const target = event.target;
|
|
6893
|
-
|
|
6894
|
-
|
|
6940
|
+
if (!target.checked) {
|
|
6941
|
+
return;
|
|
6942
|
+
}
|
|
6943
|
+
if (this.group) {
|
|
6944
|
+
// The group reconciles every option's DOM afterwards — this one and the sibling the
|
|
6945
|
+
// browser unchecked. No `change` emit either: inside a group the group's own output is the
|
|
6946
|
+
// single public event, and emitting here too would fire a projected radio's (change)
|
|
6947
|
+
// binding twice per pick.
|
|
6948
|
+
this.group.select(this.value());
|
|
6949
|
+
}
|
|
6950
|
+
else {
|
|
6951
|
+
this.standaloneChecked.set(true);
|
|
6895
6952
|
this.onChange(this.value());
|
|
6896
6953
|
this.onTouched();
|
|
6897
6954
|
this.change.emit(this.value());
|
|
6898
6955
|
}
|
|
6899
6956
|
}
|
|
6957
|
+
/**
|
|
6958
|
+
* Writes the resolved {@link checked} state straight to the input, past Angular's binding diff.
|
|
6959
|
+
* Called by the enclosing group after a pick: the browser has already flipped two inputs by
|
|
6960
|
+
* then, and `[checked]` only rewrites an input whose bound value actually changed.
|
|
6961
|
+
*/
|
|
6962
|
+
syncNativeChecked() {
|
|
6963
|
+
this.radioEl().nativeElement.checked = this.checked();
|
|
6964
|
+
}
|
|
6900
6965
|
classes = computed(() => {
|
|
6901
6966
|
const classes = ['tn-radio'];
|
|
6902
6967
|
if (this.isDisabled()) {
|
|
@@ -6914,7 +6979,7 @@ class TnRadioComponent {
|
|
|
6914
6979
|
useExisting: forwardRef(() => TnRadioComponent),
|
|
6915
6980
|
multi: true
|
|
6916
6981
|
}
|
|
6917
|
-
], viewQueries: [{ propertyName: "radioEl", first: true, predicate: ["radioEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [ngClass]=\"classes()\">\n <label class=\"tn-radio__label\" tnTestIdType=\"radio\" [for]=\"id\" [tnTestId]=\"resolvedTestId()\">\n <input\n #radioEl\n type=\"radio\"\n class=\"tn-radio__input\"\n [id]=\"id\"\n [name]=\"
|
|
6982
|
+
], viewQueries: [{ propertyName: "radioEl", first: true, predicate: ["radioEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [ngClass]=\"classes()\">\n <label class=\"tn-radio__label\" tnTestIdType=\"radio\" [for]=\"id\" [tnTestId]=\"resolvedTestId()\">\n <input\n #radioEl\n type=\"radio\"\n class=\"tn-radio__input\"\n [id]=\"id\"\n [name]=\"resolvedName()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"isRequired()\"\n [attr.aria-describedby]=\"error() ? id + '-error' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n (change)=\"onRadioChange($event)\"\n />\n <span class=\"tn-radio__checkmark\"></span>\n <span class=\"tn-radio__text\" [innerHTML]=\"label() | tnLabelMarkup\"></span>\n </label>\n\n @if (error()) {\n <div\n class=\"tn-radio__error\"\n role=\"alert\"\n aria-live=\"polite\"\n [id]=\"id + '-error'\"\n >\n {{ error() }}\n </div>\n }\n</div>", styles: [".tn-radio{display:inline-block;margin-bottom:.5rem}.tn-radio__label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;gap:.5rem}.tn-radio__label:hover .tn-radio__checkmark{border-color:var(--tn-primary, #007cba)}.tn-radio__input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.tn-radio__input:focus+.tn-radio__checkmark{outline:2px solid var(--tn-primary, #007cba);outline-offset:2px}.tn-radio__input:checked+.tn-radio__checkmark{border-color:var(--tn-primary, #007cba);background-color:var(--tn-primary, #007cba)}.tn-radio__input:checked+.tn-radio__checkmark:after{display:block}.tn-radio__input:disabled+.tn-radio__checkmark{border-color:var(--tn-lines, #e5e7eb);background-color:var(--tn-alt-bg1, #f8f9fa);cursor:not-allowed}.tn-radio__checkmark{position:relative;height:18px;width:18px;border:2px solid var(--tn-lines, #e5e7eb);border-radius:50%;background-color:transparent;transition:all .2s ease;flex-shrink:0}.tn-radio__checkmark:after{content:\"\";position:absolute;display:none;top:50%;left:50%;width:8px;height:8px;border-radius:50%;background-color:#fff;transform:translate(-50%,-50%)}.tn-radio__text ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-radio__text{color:var(--tn-fg1, #000000);font-size:14px;line-height:1.4}.tn-radio__error{margin-top:.25rem;font-size:12px;color:var(--tn-red, #dc3545)}.tn-radio--disabled .tn-radio__label{cursor:not-allowed;opacity:.6}.tn-radio--disabled .tn-radio__text{color:var(--tn-fg2, #6c757d)}.tn-radio--error .tn-radio__checkmark{border-color:var(--tn-red, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "pipe", type: LabelMarkupPipe, name: "tnLabelMarkup" }] });
|
|
6918
6983
|
}
|
|
6919
6984
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnRadioComponent, decorators: [{
|
|
6920
6985
|
type: Component,
|
|
@@ -6924,7 +6989,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
6924
6989
|
useExisting: forwardRef(() => TnRadioComponent),
|
|
6925
6990
|
multi: true
|
|
6926
6991
|
}
|
|
6927
|
-
], template: "<div [ngClass]=\"classes()\">\n <label class=\"tn-radio__label\" tnTestIdType=\"radio\" [for]=\"id\" [tnTestId]=\"resolvedTestId()\">\n <input\n #radioEl\n type=\"radio\"\n class=\"tn-radio__input\"\n [id]=\"id\"\n [name]=\"
|
|
6992
|
+
], template: "<div [ngClass]=\"classes()\">\n <label class=\"tn-radio__label\" tnTestIdType=\"radio\" [for]=\"id\" [tnTestId]=\"resolvedTestId()\">\n <input\n #radioEl\n type=\"radio\"\n class=\"tn-radio__input\"\n [id]=\"id\"\n [name]=\"resolvedName()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"isRequired()\"\n [attr.aria-describedby]=\"error() ? id + '-error' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n (change)=\"onRadioChange($event)\"\n />\n <span class=\"tn-radio__checkmark\"></span>\n <span class=\"tn-radio__text\" [innerHTML]=\"label() | tnLabelMarkup\"></span>\n </label>\n\n @if (error()) {\n <div\n class=\"tn-radio__error\"\n role=\"alert\"\n aria-live=\"polite\"\n [id]=\"id + '-error'\"\n >\n {{ error() }}\n </div>\n }\n</div>", styles: [".tn-radio{display:inline-block;margin-bottom:.5rem}.tn-radio__label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;gap:.5rem}.tn-radio__label:hover .tn-radio__checkmark{border-color:var(--tn-primary, #007cba)}.tn-radio__input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.tn-radio__input:focus+.tn-radio__checkmark{outline:2px solid var(--tn-primary, #007cba);outline-offset:2px}.tn-radio__input:checked+.tn-radio__checkmark{border-color:var(--tn-primary, #007cba);background-color:var(--tn-primary, #007cba)}.tn-radio__input:checked+.tn-radio__checkmark:after{display:block}.tn-radio__input:disabled+.tn-radio__checkmark{border-color:var(--tn-lines, #e5e7eb);background-color:var(--tn-alt-bg1, #f8f9fa);cursor:not-allowed}.tn-radio__checkmark{position:relative;height:18px;width:18px;border:2px solid var(--tn-lines, #e5e7eb);border-radius:50%;background-color:transparent;transition:all .2s ease;flex-shrink:0}.tn-radio__checkmark:after{content:\"\";position:absolute;display:none;top:50%;left:50%;width:8px;height:8px;border-radius:50%;background-color:#fff;transform:translate(-50%,-50%)}.tn-radio__text ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-radio__text{color:var(--tn-fg1, #000000);font-size:14px;line-height:1.4}.tn-radio__error{margin-top:.25rem;font-size:12px;color:var(--tn-red, #dc3545)}.tn-radio--disabled .tn-radio__label{cursor:not-allowed;opacity:.6}.tn-radio--disabled .tn-radio__text{color:var(--tn-fg2, #6c757d)}.tn-radio--error .tn-radio__checkmark{border-color:var(--tn-red, #dc3545)}\n"] }]
|
|
6928
6993
|
}], propDecorators: { radioEl: [{ type: i0.ViewChild, args: ['radioEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], change: [{ type: i0.Output, args: ["change"] }] } });
|
|
6929
6994
|
|
|
6930
6995
|
/**
|
|
@@ -7054,6 +7119,374 @@ class TnRadioHarness extends ComponentHarness {
|
|
|
7054
7119
|
}
|
|
7055
7120
|
}
|
|
7056
7121
|
|
|
7122
|
+
/** Feeds the auto-generated native `name` — see {@link TnRadioGroupComponent.resolvedName}. */
|
|
7123
|
+
let nextGroupId = 0;
|
|
7124
|
+
/**
|
|
7125
|
+
* A `role="radiogroup"` wrapper that owns the selection for a set of `tn-radio`s.
|
|
7126
|
+
*
|
|
7127
|
+
* `tn-radio` on its own is a per-option `ControlValueAccessor`, which makes a hand-rolled group
|
|
7128
|
+
* (one `[formControl]` bound to every option) go stale: Angular suppresses the model→view write on
|
|
7129
|
+
* the accessor that originated a change, so the previously checked option keeps rendering as
|
|
7130
|
+
* checked. The group fixes that at the source — it is the single accessor, and each child derives
|
|
7131
|
+
* its checked state from the group's value, so there is nothing to fall out of step.
|
|
7132
|
+
*
|
|
7133
|
+
* Options come from either the `options` input or projected `<tn-radio>` children; both resolve the
|
|
7134
|
+
* group through DI, so the two forms behave identically and can be mixed.
|
|
7135
|
+
*
|
|
7136
|
+
* ```html
|
|
7137
|
+
* <tn-form-field label="Encryption">
|
|
7138
|
+
* <tn-radio-group formControlName="encryption" [options]="encryptionOptions" />
|
|
7139
|
+
* </tn-form-field>
|
|
7140
|
+
*
|
|
7141
|
+
* <tn-radio-group [formControl]="control" ariaLabel="Encryption">
|
|
7142
|
+
* <tn-radio label="None" value="none" />
|
|
7143
|
+
* <tn-radio label="Passphrase" value="passphrase" />
|
|
7144
|
+
* </tn-radio-group>
|
|
7145
|
+
* ```
|
|
7146
|
+
*
|
|
7147
|
+
* Inside a `tn-form-field` the group is a normal control: the field names it via
|
|
7148
|
+
* `aria-labelledby`, infers the required indicator from its validators, and renders its validation
|
|
7149
|
+
* message — none of which a bare set of radios gets.
|
|
7150
|
+
*/
|
|
7151
|
+
class TnRadioGroupComponent {
|
|
7152
|
+
/**
|
|
7153
|
+
* Options to render. Leave unset to project `<tn-radio>` children instead — the group drives
|
|
7154
|
+
* both the same way.
|
|
7155
|
+
*
|
|
7156
|
+
* Options are tracked by `value`, so object values must be referentially stable — rebuilding
|
|
7157
|
+
* them as fresh literals on every change detection pass re-creates the whole list. Same
|
|
7158
|
+
* requirement the `compareWith` case implies; hold the objects in a field.
|
|
7159
|
+
*/
|
|
7160
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
7161
|
+
/**
|
|
7162
|
+
* Lays the options out in a wrapping row instead of stacking them. Opt-in: stacked is the right
|
|
7163
|
+
* default for anything longer than a two-option yes/no, the only shape a row reads well at.
|
|
7164
|
+
*/
|
|
7165
|
+
inline = input(false, ...(ngDevMode ? [{ debugName: "inline" }] : []));
|
|
7166
|
+
/**
|
|
7167
|
+
* Native `name` shared by the options, which is what makes the browser treat them as one group
|
|
7168
|
+
* for arrow-key navigation. Defaults to a per-instance generated name; set it only when
|
|
7169
|
+
* something outside the group depends on the value. The scope of a native radio `name` is the
|
|
7170
|
+
* whole form (or document), so two groups sharing an explicit name fuse into one — prefer the
|
|
7171
|
+
* default when the same call site can render more than once.
|
|
7172
|
+
*
|
|
7173
|
+
* It is the group's name that binds the set, so a projected `<tn-radio name="…">` overrides it
|
|
7174
|
+
* for itself and drops out: still rendered inside the group, but outside its arrow-key
|
|
7175
|
+
* navigation and independently checkable. Leave `name` off projected children.
|
|
7176
|
+
*/
|
|
7177
|
+
name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
7178
|
+
/**
|
|
7179
|
+
* Explicit accessible name for the group. Inside a `tn-form-field` with a label this is
|
|
7180
|
+
* unnecessary — the field names the group automatically — but a group with neither is announced
|
|
7181
|
+
* as an unlabeled radiogroup.
|
|
7182
|
+
*/
|
|
7183
|
+
ariaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
7184
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
7185
|
+
/** Propagated to each option's native `required`, so an unpicked group blocks native submit. */
|
|
7186
|
+
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
7187
|
+
/**
|
|
7188
|
+
* Test-id base for the group and, scoped by the option label, for every option rendered from
|
|
7189
|
+
* `options`: base `encryption` yields `radio-group-encryption` on the group and
|
|
7190
|
+
* `radio-encryption-passphrase` on its options. Falls back to the bound control name.
|
|
7191
|
+
*
|
|
7192
|
+
* Projected `<tn-radio>` children keep their own test-id resolution — the group does not reach
|
|
7193
|
+
* into content it did not render — so give each one a `testId` of its own.
|
|
7194
|
+
*/
|
|
7195
|
+
testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
7196
|
+
/**
|
|
7197
|
+
* Custom comparator for matching option values against the selected value. Provide it when
|
|
7198
|
+
* option values are objects — the default is identity, so a structurally-equal-but-distinct
|
|
7199
|
+
* object would leave the group rendering nothing as checked.
|
|
7200
|
+
*
|
|
7201
|
+
* Called once per option on every check, including while nothing is selected — the selected
|
|
7202
|
+
* value is `null` then, so the comparator must tolerate `null` on either side rather than
|
|
7203
|
+
* dereferencing straight into a property.
|
|
7204
|
+
*
|
|
7205
|
+
* @example
|
|
7206
|
+
* ```ts
|
|
7207
|
+
* compareWith = (a, b) => a?.id === b?.id;
|
|
7208
|
+
* ```
|
|
7209
|
+
*/
|
|
7210
|
+
compareWith = input(...(ngDevMode ? [undefined, { debugName: "compareWith" }] : []));
|
|
7211
|
+
/** Emits the newly selected value on every user pick (not on programmatic writes). */
|
|
7212
|
+
change = output();
|
|
7213
|
+
/**
|
|
7214
|
+
* ARIA wiring from an enclosing `tn-form-field` (label, error/hint, invalid, required).
|
|
7215
|
+
* All-null standalone, or when `ariaLabel` overrides the field's label.
|
|
7216
|
+
*/
|
|
7217
|
+
fieldAria = injectTnFormFieldAria(this.ariaLabel);
|
|
7218
|
+
/** Test-id base, falling back to the bound control name when `testId` is unset. */
|
|
7219
|
+
resolvedTestId = controlTestId(this.testId);
|
|
7220
|
+
/** Options rendered from `options` (view) and projected ones (content) — see {@link syncNativeChecked}. */
|
|
7221
|
+
renderedOptions = viewChildren(TnRadioComponent, ...(ngDevMode ? [{ debugName: "renderedOptions" }] : []));
|
|
7222
|
+
/**
|
|
7223
|
+
* `descendants: true` so a projected option wrapped in markup is still found. This over-matches
|
|
7224
|
+
* a `tn-radio-group` nested in the projected content, picking up its options too — harmless,
|
|
7225
|
+
* because {@link syncNativeChecked} has each option write its *own* resolved checked state, and
|
|
7226
|
+
* that derives from the option's nearest group rather than from this one.
|
|
7227
|
+
*/
|
|
7228
|
+
projectedOptions = contentChildren(TnRadioComponent, { ...(ngDevMode ? { debugName: "projectedOptions" } : {}), descendants: true });
|
|
7229
|
+
uid = `tn-radio-group-${nextGroupId++}`;
|
|
7230
|
+
selectedValue = signal(null, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
|
|
7231
|
+
formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : []));
|
|
7232
|
+
/** Group name handed to every child radio; generated per instance unless `name` is set. */
|
|
7233
|
+
resolvedName = computed(() => this.name() ?? this.uid, ...(ngDevMode ? [{ debugName: "resolvedName" }] : []));
|
|
7234
|
+
/**
|
|
7235
|
+
* Combined disabled state (own input plus a disabled bound control), read by child radios and
|
|
7236
|
+
* announced on the group root. The native `disabled` on each option is what blocks interaction;
|
|
7237
|
+
* `aria-disabled` on the radiogroup is what tells a screen reader landing on the container
|
|
7238
|
+
* about it, and gives styling an `[aria-disabled]` hook for the group as a whole.
|
|
7239
|
+
*/
|
|
7240
|
+
isDisabled = computed(() => this.disabled() || this.formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
7241
|
+
/**
|
|
7242
|
+
* Required state read by child radios, which render it as the native attribute. Deliberately
|
|
7243
|
+
* *not* widened to an enclosing `tn-form-field`'s inferred required state: native constraint
|
|
7244
|
+
* validation on a radio would block submission with a browser popup, which is not what attaching
|
|
7245
|
+
* `Validators.required` to a control asks for. The inferred state still reaches assistive tech —
|
|
7246
|
+
* see the group's `aria-required` binding.
|
|
7247
|
+
*/
|
|
7248
|
+
isRequired = computed(() => this.required(), ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
|
|
7249
|
+
/** Whether the group announces itself as required: its own input, or the field's inferred state. */
|
|
7250
|
+
ariaRequired = computed(() => this.isRequired() || this.fieldAria.required() === true, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
|
|
7251
|
+
onChange = () => { };
|
|
7252
|
+
onTouched = () => { };
|
|
7253
|
+
// ── TnRadioGroupApi (consumed by child `tn-radio`s via DI) ──
|
|
7254
|
+
isSelected(value) {
|
|
7255
|
+
const selected = this.selectedValue();
|
|
7256
|
+
const comparator = this.compareWith();
|
|
7257
|
+
if (comparator) {
|
|
7258
|
+
return comparator(selected, value);
|
|
7259
|
+
}
|
|
7260
|
+
return selected === value;
|
|
7261
|
+
}
|
|
7262
|
+
select(value) {
|
|
7263
|
+
// Only the whole-group disable is checked here. A single `option.disabled` (or a disabled
|
|
7264
|
+
// projected child) is enforced by the DOM instead: a disabled native radio fires no change,
|
|
7265
|
+
// and a click on its label is ignored, so the call never reaches this method.
|
|
7266
|
+
if (this.isDisabled()) {
|
|
7267
|
+
return;
|
|
7268
|
+
}
|
|
7269
|
+
this.selectedValue.set(value);
|
|
7270
|
+
this.onChange(value);
|
|
7271
|
+
this.onTouched();
|
|
7272
|
+
this.change.emit(value);
|
|
7273
|
+
this.syncNativeChecked();
|
|
7274
|
+
}
|
|
7275
|
+
// ── ControlValueAccessor ──
|
|
7276
|
+
writeValue(value) {
|
|
7277
|
+
this.selectedValue.set(value);
|
|
7278
|
+
}
|
|
7279
|
+
registerOnChange(fn) {
|
|
7280
|
+
this.onChange = fn;
|
|
7281
|
+
}
|
|
7282
|
+
registerOnTouched(fn) {
|
|
7283
|
+
this.onTouched = fn;
|
|
7284
|
+
}
|
|
7285
|
+
setDisabledState(isDisabled) {
|
|
7286
|
+
this.formDisabled.set(isDisabled);
|
|
7287
|
+
}
|
|
7288
|
+
/**
|
|
7289
|
+
* Marks the control touched when focus leaves the group. `focusout` also fires while moving
|
|
7290
|
+
* between the group's own options, which is harmless — the user has interacted either way — and
|
|
7291
|
+
* it is the only blur signal available, since a radio's blur does not bubble.
|
|
7292
|
+
*/
|
|
7293
|
+
onFocusOut() {
|
|
7294
|
+
this.onTouched();
|
|
7295
|
+
}
|
|
7296
|
+
/**
|
|
7297
|
+
* Reconciles every option's native `checked` with the group's value. A pick reaches the DOM
|
|
7298
|
+
* before Angular: the browser checks the clicked input and unchecks its sibling on its own, and
|
|
7299
|
+
* `[checked]` then only rewrites an input whose bound value actually changed. When the value
|
|
7300
|
+
* lands somewhere the bindings don't account for — a consumer reverting the pick, a comparator
|
|
7301
|
+
* that matches neither option — one or both inputs are left showing the browser's guess.
|
|
7302
|
+
*/
|
|
7303
|
+
syncNativeChecked() {
|
|
7304
|
+
for (const option of [...this.renderedOptions(), ...this.projectedOptions()]) {
|
|
7305
|
+
option.syncNativeChecked();
|
|
7306
|
+
}
|
|
7307
|
+
}
|
|
7308
|
+
/** Per-option test-id segments: the group's base scoped by the option label. */
|
|
7309
|
+
optionTestId(option) {
|
|
7310
|
+
return scopeTestId(this.resolvedTestId(), option.label);
|
|
7311
|
+
}
|
|
7312
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7313
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnRadioGroupComponent, isStandalone: true, selector: "tn-radio-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "tn-radio-group-host" }, providers: [
|
|
7314
|
+
{
|
|
7315
|
+
provide: NG_VALUE_ACCESSOR,
|
|
7316
|
+
useExisting: forwardRef(() => TnRadioGroupComponent),
|
|
7317
|
+
multi: true
|
|
7318
|
+
},
|
|
7319
|
+
// Consumed by child `tn-radio`s — both the ones rendered from `options` (view children) and
|
|
7320
|
+
// projected ones (whose element injector chains through this host, since DI follows the
|
|
7321
|
+
// template they are declared in).
|
|
7322
|
+
{
|
|
7323
|
+
provide: TN_RADIO_GROUP,
|
|
7324
|
+
useExisting: forwardRef(() => TnRadioGroupComponent)
|
|
7325
|
+
}
|
|
7326
|
+
], queries: [{ propertyName: "projectedOptions", predicate: TnRadioComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "renderedOptions", predicate: TnRadioComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"tn-radio-group\"\n role=\"radiogroup\"\n tnTestIdType=\"radio-group\"\n [class.tn-radio-group--inline]=\"inline()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"fieldAria.labelledby()\"\n [attr.aria-describedby]=\"fieldAria.describedby()\"\n [attr.aria-invalid]=\"fieldAria.invalid()\"\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\n [attr.aria-required]=\"ariaRequired() ? true : null\"\n [attr.aria-orientation]=\"inline() ? 'horizontal' : 'vertical'\"\n [tnTestId]=\"resolvedTestId()\"\n (focusout)=\"onFocusOut()\"\n>\n @for (option of options(); track option.value) {\n <tn-radio\n [label]=\"option.label\"\n [value]=\"option.value\"\n [disabled]=\"!!option.disabled\"\n [testId]=\"optionTestId(option)\"\n />\n }\n <ng-content />\n</div>\n", styles: [".tn-radio-group-host{display:block}.tn-radio-group{display:flex;flex-direction:column;gap:var(--tn-radio-group-gap, 8px)}.tn-radio-group--inline{align-items:center;flex-direction:row;flex-wrap:wrap;column-gap:var(--tn-radio-group-inline-gap, 16px)}.tn-radio-group>tn-radio{margin:0}.tn-radio-group .tn-radio{margin-bottom:0}\n"], dependencies: [{ kind: "component", type: TnRadioComponent, selector: "tn-radio", inputs: ["label", "value", "name", "disabled", "required", "testId", "error"], outputs: ["change"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7327
|
+
}
|
|
7328
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnRadioGroupComponent, decorators: [{
|
|
7329
|
+
type: Component,
|
|
7330
|
+
args: [{ selector: 'tn-radio-group', standalone: true, imports: [TnRadioComponent, TnTestIdDirective], providers: [
|
|
7331
|
+
{
|
|
7332
|
+
provide: NG_VALUE_ACCESSOR,
|
|
7333
|
+
useExisting: forwardRef(() => TnRadioGroupComponent),
|
|
7334
|
+
multi: true
|
|
7335
|
+
},
|
|
7336
|
+
// Consumed by child `tn-radio`s — both the ones rendered from `options` (view children) and
|
|
7337
|
+
// projected ones (whose element injector chains through this host, since DI follows the
|
|
7338
|
+
// template they are declared in).
|
|
7339
|
+
{
|
|
7340
|
+
provide: TN_RADIO_GROUP,
|
|
7341
|
+
useExisting: forwardRef(() => TnRadioGroupComponent)
|
|
7342
|
+
}
|
|
7343
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
7344
|
+
'class': 'tn-radio-group-host'
|
|
7345
|
+
}, template: "<div\n class=\"tn-radio-group\"\n role=\"radiogroup\"\n tnTestIdType=\"radio-group\"\n [class.tn-radio-group--inline]=\"inline()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"fieldAria.labelledby()\"\n [attr.aria-describedby]=\"fieldAria.describedby()\"\n [attr.aria-invalid]=\"fieldAria.invalid()\"\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\n [attr.aria-required]=\"ariaRequired() ? true : null\"\n [attr.aria-orientation]=\"inline() ? 'horizontal' : 'vertical'\"\n [tnTestId]=\"resolvedTestId()\"\n (focusout)=\"onFocusOut()\"\n>\n @for (option of options(); track option.value) {\n <tn-radio\n [label]=\"option.label\"\n [value]=\"option.value\"\n [disabled]=\"!!option.disabled\"\n [testId]=\"optionTestId(option)\"\n />\n }\n <ng-content />\n</div>\n", styles: [".tn-radio-group-host{display:block}.tn-radio-group{display:flex;flex-direction:column;gap:var(--tn-radio-group-gap, 8px)}.tn-radio-group--inline{align-items:center;flex-direction:row;flex-wrap:wrap;column-gap:var(--tn-radio-group-inline-gap, 16px)}.tn-radio-group>tn-radio{margin:0}.tn-radio-group .tn-radio{margin-bottom:0}\n"] }]
|
|
7346
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], change: [{ type: i0.Output, args: ["change"] }], renderedOptions: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => TnRadioComponent), { isSignal: true }] }], projectedOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnRadioComponent), { ...{ descendants: true }, isSignal: true }] }] } });
|
|
7347
|
+
|
|
7348
|
+
/**
|
|
7349
|
+
* Harness for interacting with `tn-radio-group` in tests.
|
|
7350
|
+
* Drives the group as a whole — picking an option by label, reading the current selection —
|
|
7351
|
+
* rather than reaching for each `tn-radio` individually.
|
|
7352
|
+
*
|
|
7353
|
+
* @example
|
|
7354
|
+
* ```typescript
|
|
7355
|
+
* const group = await loader.getHarness(TnRadioGroupHarness.with({ testId: 'encryption' }));
|
|
7356
|
+
* await group.select('Passphrase');
|
|
7357
|
+
* expect(await group.getCheckedLabel()).toBe('Passphrase');
|
|
7358
|
+
* ```
|
|
7359
|
+
*/
|
|
7360
|
+
class TnRadioGroupHarness extends ComponentHarness {
|
|
7361
|
+
/** The selector for the host element of a `TnRadioGroupComponent` instance. */
|
|
7362
|
+
static hostSelector = 'tn-radio-group';
|
|
7363
|
+
_root = this.locatorFor('.tn-radio-group');
|
|
7364
|
+
_radios = this.locatorForAll(TnRadioHarness);
|
|
7365
|
+
/**
|
|
7366
|
+
* Gets a `HarnessPredicate` that can be used to search for a radio group with specific
|
|
7367
|
+
* attributes.
|
|
7368
|
+
*
|
|
7369
|
+
* @param options Options for filtering which radio group instances are considered a match.
|
|
7370
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
7371
|
+
*
|
|
7372
|
+
* @example
|
|
7373
|
+
* ```typescript
|
|
7374
|
+
* // Find by accessible name
|
|
7375
|
+
* const group = await loader.getHarness(TnRadioGroupHarness.with({ ariaLabel: 'Encryption' }));
|
|
7376
|
+
*
|
|
7377
|
+
* // Find by testId
|
|
7378
|
+
* const group = await loader.getHarness(TnRadioGroupHarness.with({ testId: 'radio-group-encryption' }));
|
|
7379
|
+
* ```
|
|
7380
|
+
*/
|
|
7381
|
+
static with(options = {}) {
|
|
7382
|
+
return new HarnessPredicate(TnRadioGroupHarness, options)
|
|
7383
|
+
.addOption('ariaLabel', options.ariaLabel, (harness, ariaLabel) => HarnessPredicate.stringMatches(harness.getAriaLabel(), ariaLabel))
|
|
7384
|
+
.addOption('testId', options.testId, async (harness, testId) => {
|
|
7385
|
+
return (await harness.getTestId()) === testId;
|
|
7386
|
+
});
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* Gets the harnesses for the group's options, in DOM order.
|
|
7390
|
+
*
|
|
7391
|
+
* @example
|
|
7392
|
+
* ```typescript
|
|
7393
|
+
* const options = await group.getOptions();
|
|
7394
|
+
* expect(options).toHaveLength(2);
|
|
7395
|
+
* ```
|
|
7396
|
+
*/
|
|
7397
|
+
async getOptions() {
|
|
7398
|
+
return this._radios();
|
|
7399
|
+
}
|
|
7400
|
+
/**
|
|
7401
|
+
* Gets the label text of every option, in DOM order.
|
|
7402
|
+
*
|
|
7403
|
+
* @example
|
|
7404
|
+
* ```typescript
|
|
7405
|
+
* expect(await group.getOptionLabels()).toEqual(['None', 'Passphrase']);
|
|
7406
|
+
* ```
|
|
7407
|
+
*/
|
|
7408
|
+
async getOptionLabels() {
|
|
7409
|
+
const radios = await this._radios();
|
|
7410
|
+
return Promise.all(radios.map((radio) => radio.getLabelText()));
|
|
7411
|
+
}
|
|
7412
|
+
/**
|
|
7413
|
+
* Gets the label of the currently checked option, or null when nothing is selected.
|
|
7414
|
+
*
|
|
7415
|
+
* @example
|
|
7416
|
+
* ```typescript
|
|
7417
|
+
* expect(await group.getCheckedLabel()).toBe('Passphrase');
|
|
7418
|
+
* ```
|
|
7419
|
+
*/
|
|
7420
|
+
async getCheckedLabel() {
|
|
7421
|
+
const radios = await this._radios();
|
|
7422
|
+
for (const radio of radios) {
|
|
7423
|
+
if (await radio.isChecked()) {
|
|
7424
|
+
return radio.getLabelText();
|
|
7425
|
+
}
|
|
7426
|
+
}
|
|
7427
|
+
return null;
|
|
7428
|
+
}
|
|
7429
|
+
/**
|
|
7430
|
+
* Selects the option with the given label. Throws when the group has no such option, so a
|
|
7431
|
+
* renamed or missing option fails loudly instead of silently leaving the selection unchanged.
|
|
7432
|
+
*
|
|
7433
|
+
* @param label Label text of the option to select.
|
|
7434
|
+
*
|
|
7435
|
+
* @example
|
|
7436
|
+
* ```typescript
|
|
7437
|
+
* await group.select('Passphrase');
|
|
7438
|
+
* ```
|
|
7439
|
+
*/
|
|
7440
|
+
async select(label) {
|
|
7441
|
+
const radio = await this.locatorForOptional(TnRadioHarness.with({ label }))();
|
|
7442
|
+
if (!radio) {
|
|
7443
|
+
const available = (await this.getOptionLabels()).join(', ');
|
|
7444
|
+
throw new Error(`No option labelled "${label}" in this tn-radio-group (available: ${available}).`);
|
|
7445
|
+
}
|
|
7446
|
+
await radio.check();
|
|
7447
|
+
}
|
|
7448
|
+
/**
|
|
7449
|
+
* Checks whether every option is disabled — the state a disabled group or a disabled bound
|
|
7450
|
+
* form control produces. Use {@link getOptions} to inspect a single per-option `disabled`.
|
|
7451
|
+
*
|
|
7452
|
+
* @example
|
|
7453
|
+
* ```typescript
|
|
7454
|
+
* expect(await group.isDisabled()).toBe(true);
|
|
7455
|
+
* ```
|
|
7456
|
+
*/
|
|
7457
|
+
async isDisabled() {
|
|
7458
|
+
const radios = await this._radios();
|
|
7459
|
+
if (!radios.length) {
|
|
7460
|
+
return false;
|
|
7461
|
+
}
|
|
7462
|
+
const states = await Promise.all(radios.map((radio) => radio.isDisabled()));
|
|
7463
|
+
return states.every(Boolean);
|
|
7464
|
+
}
|
|
7465
|
+
/**
|
|
7466
|
+
* Gets the group's accessible name as written by the `ariaLabel` input, or null when the name
|
|
7467
|
+
* comes from an enclosing `tn-form-field` (see {@link getAriaLabelledBy}).
|
|
7468
|
+
*/
|
|
7469
|
+
async getAriaLabel() {
|
|
7470
|
+
return (await this._root()).getAttribute('aria-label');
|
|
7471
|
+
}
|
|
7472
|
+
/** Gets the id the group's accessible name is delegated to, or null. */
|
|
7473
|
+
async getAriaLabelledBy() {
|
|
7474
|
+
return (await this._root()).getAttribute('aria-labelledby');
|
|
7475
|
+
}
|
|
7476
|
+
/**
|
|
7477
|
+
* Gets the test ID attribute value of the group root.
|
|
7478
|
+
*
|
|
7479
|
+
* @example
|
|
7480
|
+
* ```typescript
|
|
7481
|
+
* expect(await group.getTestId()).toBe('radio-group-encryption');
|
|
7482
|
+
* ```
|
|
7483
|
+
*/
|
|
7484
|
+
async getTestId() {
|
|
7485
|
+
const root = await this._root();
|
|
7486
|
+
return (await root.getAttribute('data-testid')) ?? (await root.getAttribute('data-test'));
|
|
7487
|
+
}
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7057
7490
|
/**
|
|
7058
7491
|
* Harness for interacting with `tn-slide-toggle` in tests.
|
|
7059
7492
|
*
|
|
@@ -14554,11 +14987,11 @@ class TnMonthViewComponent {
|
|
|
14554
14987
|
return null;
|
|
14555
14988
|
}
|
|
14556
14989
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnMonthViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14557
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnMonthViewComponent, isStandalone: true, selector: "tn-month-view", inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, markedDates: { classPropertyName: "markedDates", publicName: "markedDates", isSignal: true, isRequired: false, transformFunction: null }, rangeMode: { classPropertyName: "rangeMode", publicName: "rangeMode", isSignal: true, isRequired: false, transformFunction: null }, selectedRange: { classPropertyName: "selectedRange", publicName: "selectedRange", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", activeDateChange: "activeDateChange" }, ngImport: i0, template: "<table role=\"grid\" class=\"tn-calendar-table\" [attr.aria-label]=\"gridLabel()\">\n <!-- Table header with day names -->\n <thead class=\"tn-calendar-table-header\">\n <tr>\n @for (day of weekdays(); track day.long) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n <span aria-hidden=\"true\">{{ day.short }}</span>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table body with calendar cells -->\n <tbody class=\"tn-calendar-body\">\n <!-- Calendar rows -->\n @for (row of calendarRows(); track trackByRow($index); let rowIndex = $index) {\n <tr role=\"row\">\n @for (cell of row; track trackByDate($index, cell); let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"tn-calendar-body-cell-container\"\n [attr.data-tn-row]=\"rowIndex\"\n [attr.data-tn-col]=\"colIndex\">\n @if (cell.value > 0) {\n <button\n type=\"button\"\n class=\"tn-calendar-body-cell\"\n [attr.data-tn-date]=\"cell.isoDate\"\n [class.tn-calendar-body-selected]=\"cell.selected\"\n [class.tn-calendar-body-today]=\"cell.today\"\n [class.tn-calendar-body-marked]=\"cell.marked\"\n [class.tn-calendar-body-active]=\"cell.value === activeDay()\"\n [class.tn-calendar-body-range-start]=\"cell.rangeStart\"\n [class.tn-calendar-body-range-end]=\"cell.rangeEnd\"\n [class.tn-calendar-body-in-range]=\"cell.inRange\"\n [disabled]=\"!cell.enabled\"\n [attr.tabindex]=\"cell.value === activeDay() ? 0 : -1\"\n [attr.aria-label]=\"cell.ariaLabel\"\n [attr.aria-pressed]=\"cell.inSelection\"\n [attr.aria-current]=\"cell.today ? 'date' : null\"\n (click)=\"onCellClicked(cell)\"\n (keydown)=\"onKeydown($event)\">\n <span class=\"tn-calendar-body-cell-content\"\n [class.tn-calendar-body-fill-primary]=\"cell.fill === 'primary'\"\n [class.tn-calendar-body-fill-marked]=\"cell.fill === 'marked'\"\n [class.tn-calendar-body-today-outline]=\"cell.todayOutline\">\n {{ cell.label }}\n </span>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n</table>\n", styles: [":host{display:block;--calendar-cell-size: 48px;--calendar-header-height: 40px;--calendar-cell-font-size: 1rem;--calendar-header-font-size: 14px;--calendar-indicator-size: var(--calendar-cell-size);--calendar-radius: 4px}.tn-calendar-table{width:100%;min-width:min(100%,7 * var(--calendar-cell-size));table-layout:fixed;border-spacing:0;border-collapse:separate}.tn-calendar-table-header th{text-align:center;height:var(--calendar-header-height);padding:8px 0;font-size:var(--calendar-header-font-size);font-weight:500;color:var(--tn-fg2, #666)}.tn-calendar-table-header th:first-child{border-start-start-radius:var(--calendar-radius);border-end-start-radius:var(--calendar-radius)}.tn-calendar-table-header th:last-child{border-start-end-radius:var(--calendar-radius);border-end-end-radius:var(--calendar-radius)}.tn-calendar-table-header-divider{height:1px;border:0}.tn-calendar-body tr{border:0}.tn-calendar-body-cell-container{position:relative;border:0;outline:0;height:var(--calendar-cell-size);width:14.2857142857%}.tn-calendar-body-cell{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:0;background:transparent;border:0;outline:0;cursor:pointer;color:var(--tn-fg1, #333)}.tn-calendar-body-cell:focus-visible{outline:none}.tn-calendar-body-cell:focus-visible .tn-calendar-body-cell-content{outline:2px solid var(--tn-primary, #007bff);outline-offset:-2px}.tn-calendar-body-cell:disabled{color:var(--tn-fg2, #666);opacity:.5;cursor:default}.tn-calendar-body-cell.tn-calendar-body-in-range:before,.tn-calendar-body-cell.tn-calendar-body-range-start:before,.tn-calendar-body-cell.tn-calendar-body-range-end:before{content:\"\";position:absolute;top:50%;transform:translateY(-50%);height:var(--calendar-indicator-size);background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell.tn-calendar-body-in-range:before{inset-inline:0}.tn-calendar-body-cell.tn-calendar-body-range-start:before{inset-inline-start:50%;inset-inline-end:0}.tn-calendar-body-cell.tn-calendar-body-range-end:before{inset-inline-start:0;inset-inline-end:50%}.tn-calendar-body-cell.tn-calendar-body-range-start.tn-calendar-body-range-end:before{content:none}.tn-calendar-body-cell-content{position:relative;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--calendar-indicator-size);height:var(--calendar-indicator-size);max-width:100%;border-radius:var(--calendar-radius);font-size:var(--calendar-cell-font-size);font-weight:400;transition:background-color .2s cubic-bezier(.25,.8,.25,1)}.tn-calendar-body-cell:not(:disabled):hover .tn-calendar-body-cell-content:not(.tn-calendar-body-fill-primary){background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell-content.tn-calendar-body-fill-marked{background:var(--tn-alt-bg1, #f0f0f0)}.tn-calendar-body-cell-content.tn-calendar-body-fill-primary{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #fff)}.tn-calendar-body-cell-content.tn-calendar-body-today-outline{border:1px solid var(--tn-primary, #007bff);color:var(--tn-primary, #007bff)}.cdk-visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n"] });
|
|
14990
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnMonthViewComponent, isStandalone: true, selector: "tn-month-view", inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, markedDates: { classPropertyName: "markedDates", publicName: "markedDates", isSignal: true, isRequired: false, transformFunction: null }, rangeMode: { classPropertyName: "rangeMode", publicName: "rangeMode", isSignal: true, isRequired: false, transformFunction: null }, selectedRange: { classPropertyName: "selectedRange", publicName: "selectedRange", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", activeDateChange: "activeDateChange" }, ngImport: i0, template: "<table role=\"grid\" class=\"tn-calendar-table\" [attr.aria-label]=\"gridLabel()\">\n <!-- Table header with day names -->\n <thead class=\"tn-calendar-table-header\">\n <tr>\n @for (day of weekdays(); track day.long) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n <span aria-hidden=\"true\">{{ day.short }}</span>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table body with calendar cells -->\n <tbody class=\"tn-calendar-body\">\n <!-- Calendar rows -->\n @for (row of calendarRows(); track trackByRow($index); let rowIndex = $index) {\n <tr role=\"row\">\n @for (cell of row; track trackByDate($index, cell); let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"tn-calendar-body-cell-container\"\n [attr.data-tn-row]=\"rowIndex\"\n [attr.data-tn-col]=\"colIndex\">\n @if (cell.value > 0) {\n <button\n type=\"button\"\n class=\"tn-calendar-body-cell\"\n [attr.data-tn-date]=\"cell.isoDate\"\n [class.tn-calendar-body-selected]=\"cell.selected\"\n [class.tn-calendar-body-today]=\"cell.today\"\n [class.tn-calendar-body-marked]=\"cell.marked\"\n [class.tn-calendar-body-active]=\"cell.value === activeDay()\"\n [class.tn-calendar-body-range-start]=\"cell.rangeStart\"\n [class.tn-calendar-body-range-end]=\"cell.rangeEnd\"\n [class.tn-calendar-body-in-range]=\"cell.inRange\"\n [disabled]=\"!cell.enabled\"\n [attr.tabindex]=\"cell.value === activeDay() ? 0 : -1\"\n [attr.aria-label]=\"cell.ariaLabel\"\n [attr.aria-pressed]=\"cell.inSelection\"\n [attr.aria-current]=\"cell.today ? 'date' : null\"\n (click)=\"onCellClicked(cell)\"\n (keydown)=\"onKeydown($event)\">\n <span class=\"tn-calendar-body-cell-content\"\n [class.tn-calendar-body-fill-primary]=\"cell.fill === 'primary'\"\n [class.tn-calendar-body-fill-marked]=\"cell.fill === 'marked'\"\n [class.tn-calendar-body-today-outline]=\"cell.todayOutline\">\n {{ cell.label }}\n </span>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n</table>\n", styles: [":host{display:block;--calendar-cell-size: 48px;--calendar-header-height: 40px;--calendar-cell-font-size: 1rem;--calendar-header-font-size: 14px;--calendar-indicator-size: var(--calendar-cell-size);--calendar-radius: 4px}.tn-calendar-table{width:100%;min-width:min(100%,7 * var(--calendar-cell-size));table-layout:fixed;border-spacing:0;border-collapse:separate}.tn-calendar-table-header th{text-align:center;width:14.2857142857%;height:var(--calendar-header-height);padding:8px 0;font-size:var(--calendar-header-font-size);font-weight:500;color:var(--tn-fg2, #666)}.tn-calendar-table-header th:first-child{border-start-start-radius:var(--calendar-radius);border-end-start-radius:var(--calendar-radius)}.tn-calendar-table-header th:last-child{border-start-end-radius:var(--calendar-radius);border-end-end-radius:var(--calendar-radius)}.tn-calendar-table-header-divider{height:1px;border:0}.tn-calendar-body tr{border:0}.tn-calendar-body-cell-container{position:relative;border:0;outline:0;height:var(--calendar-cell-size);width:14.2857142857%}.tn-calendar-body-cell{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:0;background:transparent;border:0;outline:0;cursor:pointer;color:var(--tn-fg1, #333)}.tn-calendar-body-cell:focus-visible{outline:none}.tn-calendar-body-cell:focus-visible .tn-calendar-body-cell-content{outline:2px solid var(--tn-primary, #007bff);outline-offset:-2px}.tn-calendar-body-cell:disabled{color:var(--tn-fg2, #666);opacity:.5;cursor:default}.tn-calendar-body-cell.tn-calendar-body-in-range:before,.tn-calendar-body-cell.tn-calendar-body-range-start:before,.tn-calendar-body-cell.tn-calendar-body-range-end:before{content:\"\";position:absolute;top:50%;transform:translateY(-50%);height:var(--calendar-indicator-size);background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell.tn-calendar-body-in-range:before{inset-inline:0}.tn-calendar-body-cell.tn-calendar-body-range-start:before{inset-inline-start:50%;inset-inline-end:0}.tn-calendar-body-cell.tn-calendar-body-range-end:before{inset-inline-start:0;inset-inline-end:50%}.tn-calendar-body-cell.tn-calendar-body-range-start.tn-calendar-body-range-end:before{content:none}.tn-calendar-body-cell-content{position:relative;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--calendar-indicator-size);height:var(--calendar-indicator-size);max-width:100%;border-radius:var(--calendar-radius);font-size:var(--calendar-cell-font-size);font-weight:400;transition:background-color .2s cubic-bezier(.25,.8,.25,1)}.tn-calendar-body-cell:not(:disabled):hover .tn-calendar-body-cell-content:not(.tn-calendar-body-fill-primary){background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell-content.tn-calendar-body-fill-marked{background:var(--tn-alt-bg1, #f0f0f0)}.tn-calendar-body-cell-content.tn-calendar-body-fill-primary{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #fff)}.tn-calendar-body-cell-content.tn-calendar-body-today-outline{border:1px solid var(--tn-primary, #007bff);color:var(--tn-primary, #007bff)}.cdk-visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n"] });
|
|
14558
14991
|
}
|
|
14559
14992
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnMonthViewComponent, decorators: [{
|
|
14560
14993
|
type: Component,
|
|
14561
|
-
args: [{ selector: 'tn-month-view', standalone: true, imports: [], template: "<table role=\"grid\" class=\"tn-calendar-table\" [attr.aria-label]=\"gridLabel()\">\n <!-- Table header with day names -->\n <thead class=\"tn-calendar-table-header\">\n <tr>\n @for (day of weekdays(); track day.long) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n <span aria-hidden=\"true\">{{ day.short }}</span>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table body with calendar cells -->\n <tbody class=\"tn-calendar-body\">\n <!-- Calendar rows -->\n @for (row of calendarRows(); track trackByRow($index); let rowIndex = $index) {\n <tr role=\"row\">\n @for (cell of row; track trackByDate($index, cell); let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"tn-calendar-body-cell-container\"\n [attr.data-tn-row]=\"rowIndex\"\n [attr.data-tn-col]=\"colIndex\">\n @if (cell.value > 0) {\n <button\n type=\"button\"\n class=\"tn-calendar-body-cell\"\n [attr.data-tn-date]=\"cell.isoDate\"\n [class.tn-calendar-body-selected]=\"cell.selected\"\n [class.tn-calendar-body-today]=\"cell.today\"\n [class.tn-calendar-body-marked]=\"cell.marked\"\n [class.tn-calendar-body-active]=\"cell.value === activeDay()\"\n [class.tn-calendar-body-range-start]=\"cell.rangeStart\"\n [class.tn-calendar-body-range-end]=\"cell.rangeEnd\"\n [class.tn-calendar-body-in-range]=\"cell.inRange\"\n [disabled]=\"!cell.enabled\"\n [attr.tabindex]=\"cell.value === activeDay() ? 0 : -1\"\n [attr.aria-label]=\"cell.ariaLabel\"\n [attr.aria-pressed]=\"cell.inSelection\"\n [attr.aria-current]=\"cell.today ? 'date' : null\"\n (click)=\"onCellClicked(cell)\"\n (keydown)=\"onKeydown($event)\">\n <span class=\"tn-calendar-body-cell-content\"\n [class.tn-calendar-body-fill-primary]=\"cell.fill === 'primary'\"\n [class.tn-calendar-body-fill-marked]=\"cell.fill === 'marked'\"\n [class.tn-calendar-body-today-outline]=\"cell.todayOutline\">\n {{ cell.label }}\n </span>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n</table>\n", styles: [":host{display:block;--calendar-cell-size: 48px;--calendar-header-height: 40px;--calendar-cell-font-size: 1rem;--calendar-header-font-size: 14px;--calendar-indicator-size: var(--calendar-cell-size);--calendar-radius: 4px}.tn-calendar-table{width:100%;min-width:min(100%,7 * var(--calendar-cell-size));table-layout:fixed;border-spacing:0;border-collapse:separate}.tn-calendar-table-header th{text-align:center;height:var(--calendar-header-height);padding:8px 0;font-size:var(--calendar-header-font-size);font-weight:500;color:var(--tn-fg2, #666)}.tn-calendar-table-header th:first-child{border-start-start-radius:var(--calendar-radius);border-end-start-radius:var(--calendar-radius)}.tn-calendar-table-header th:last-child{border-start-end-radius:var(--calendar-radius);border-end-end-radius:var(--calendar-radius)}.tn-calendar-table-header-divider{height:1px;border:0}.tn-calendar-body tr{border:0}.tn-calendar-body-cell-container{position:relative;border:0;outline:0;height:var(--calendar-cell-size);width:14.2857142857%}.tn-calendar-body-cell{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:0;background:transparent;border:0;outline:0;cursor:pointer;color:var(--tn-fg1, #333)}.tn-calendar-body-cell:focus-visible{outline:none}.tn-calendar-body-cell:focus-visible .tn-calendar-body-cell-content{outline:2px solid var(--tn-primary, #007bff);outline-offset:-2px}.tn-calendar-body-cell:disabled{color:var(--tn-fg2, #666);opacity:.5;cursor:default}.tn-calendar-body-cell.tn-calendar-body-in-range:before,.tn-calendar-body-cell.tn-calendar-body-range-start:before,.tn-calendar-body-cell.tn-calendar-body-range-end:before{content:\"\";position:absolute;top:50%;transform:translateY(-50%);height:var(--calendar-indicator-size);background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell.tn-calendar-body-in-range:before{inset-inline:0}.tn-calendar-body-cell.tn-calendar-body-range-start:before{inset-inline-start:50%;inset-inline-end:0}.tn-calendar-body-cell.tn-calendar-body-range-end:before{inset-inline-start:0;inset-inline-end:50%}.tn-calendar-body-cell.tn-calendar-body-range-start.tn-calendar-body-range-end:before{content:none}.tn-calendar-body-cell-content{position:relative;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--calendar-indicator-size);height:var(--calendar-indicator-size);max-width:100%;border-radius:var(--calendar-radius);font-size:var(--calendar-cell-font-size);font-weight:400;transition:background-color .2s cubic-bezier(.25,.8,.25,1)}.tn-calendar-body-cell:not(:disabled):hover .tn-calendar-body-cell-content:not(.tn-calendar-body-fill-primary){background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell-content.tn-calendar-body-fill-marked{background:var(--tn-alt-bg1, #f0f0f0)}.tn-calendar-body-cell-content.tn-calendar-body-fill-primary{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #fff)}.tn-calendar-body-cell-content.tn-calendar-body-today-outline{border:1px solid var(--tn-primary, #007bff);color:var(--tn-primary, #007bff)}.cdk-visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n"] }]
|
|
14994
|
+
args: [{ selector: 'tn-month-view', standalone: true, imports: [], template: "<table role=\"grid\" class=\"tn-calendar-table\" [attr.aria-label]=\"gridLabel()\">\n <!-- Table header with day names -->\n <thead class=\"tn-calendar-table-header\">\n <tr>\n @for (day of weekdays(); track day.long) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n <span aria-hidden=\"true\">{{ day.short }}</span>\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table body with calendar cells -->\n <tbody class=\"tn-calendar-body\">\n <!-- Calendar rows -->\n @for (row of calendarRows(); track trackByRow($index); let rowIndex = $index) {\n <tr role=\"row\">\n @for (cell of row; track trackByDate($index, cell); let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"tn-calendar-body-cell-container\"\n [attr.data-tn-row]=\"rowIndex\"\n [attr.data-tn-col]=\"colIndex\">\n @if (cell.value > 0) {\n <button\n type=\"button\"\n class=\"tn-calendar-body-cell\"\n [attr.data-tn-date]=\"cell.isoDate\"\n [class.tn-calendar-body-selected]=\"cell.selected\"\n [class.tn-calendar-body-today]=\"cell.today\"\n [class.tn-calendar-body-marked]=\"cell.marked\"\n [class.tn-calendar-body-active]=\"cell.value === activeDay()\"\n [class.tn-calendar-body-range-start]=\"cell.rangeStart\"\n [class.tn-calendar-body-range-end]=\"cell.rangeEnd\"\n [class.tn-calendar-body-in-range]=\"cell.inRange\"\n [disabled]=\"!cell.enabled\"\n [attr.tabindex]=\"cell.value === activeDay() ? 0 : -1\"\n [attr.aria-label]=\"cell.ariaLabel\"\n [attr.aria-pressed]=\"cell.inSelection\"\n [attr.aria-current]=\"cell.today ? 'date' : null\"\n (click)=\"onCellClicked(cell)\"\n (keydown)=\"onKeydown($event)\">\n <span class=\"tn-calendar-body-cell-content\"\n [class.tn-calendar-body-fill-primary]=\"cell.fill === 'primary'\"\n [class.tn-calendar-body-fill-marked]=\"cell.fill === 'marked'\"\n [class.tn-calendar-body-today-outline]=\"cell.todayOutline\">\n {{ cell.label }}\n </span>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n</table>\n", styles: [":host{display:block;--calendar-cell-size: 48px;--calendar-header-height: 40px;--calendar-cell-font-size: 1rem;--calendar-header-font-size: 14px;--calendar-indicator-size: var(--calendar-cell-size);--calendar-radius: 4px}.tn-calendar-table{width:100%;min-width:min(100%,7 * var(--calendar-cell-size));table-layout:fixed;border-spacing:0;border-collapse:separate}.tn-calendar-table-header th{text-align:center;width:14.2857142857%;height:var(--calendar-header-height);padding:8px 0;font-size:var(--calendar-header-font-size);font-weight:500;color:var(--tn-fg2, #666)}.tn-calendar-table-header th:first-child{border-start-start-radius:var(--calendar-radius);border-end-start-radius:var(--calendar-radius)}.tn-calendar-table-header th:last-child{border-start-end-radius:var(--calendar-radius);border-end-end-radius:var(--calendar-radius)}.tn-calendar-table-header-divider{height:1px;border:0}.tn-calendar-body tr{border:0}.tn-calendar-body-cell-container{position:relative;border:0;outline:0;height:var(--calendar-cell-size);width:14.2857142857%}.tn-calendar-body-cell{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:0;background:transparent;border:0;outline:0;cursor:pointer;color:var(--tn-fg1, #333)}.tn-calendar-body-cell:focus-visible{outline:none}.tn-calendar-body-cell:focus-visible .tn-calendar-body-cell-content{outline:2px solid var(--tn-primary, #007bff);outline-offset:-2px}.tn-calendar-body-cell:disabled{color:var(--tn-fg2, #666);opacity:.5;cursor:default}.tn-calendar-body-cell.tn-calendar-body-in-range:before,.tn-calendar-body-cell.tn-calendar-body-range-start:before,.tn-calendar-body-cell.tn-calendar-body-range-end:before{content:\"\";position:absolute;top:50%;transform:translateY(-50%);height:var(--calendar-indicator-size);background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell.tn-calendar-body-in-range:before{inset-inline:0}.tn-calendar-body-cell.tn-calendar-body-range-start:before{inset-inline-start:50%;inset-inline-end:0}.tn-calendar-body-cell.tn-calendar-body-range-end:before{inset-inline-start:0;inset-inline-end:50%}.tn-calendar-body-cell.tn-calendar-body-range-start.tn-calendar-body-range-end:before{content:none}.tn-calendar-body-cell-content{position:relative;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--calendar-indicator-size);height:var(--calendar-indicator-size);max-width:100%;border-radius:var(--calendar-radius);font-size:var(--calendar-cell-font-size);font-weight:400;transition:background-color .2s cubic-bezier(.25,.8,.25,1)}.tn-calendar-body-cell:not(:disabled):hover .tn-calendar-body-cell-content:not(.tn-calendar-body-fill-primary){background:var(--tn-alt-bg2, #e8f4fd)}.tn-calendar-body-cell-content.tn-calendar-body-fill-marked{background:var(--tn-alt-bg1, #f0f0f0)}.tn-calendar-body-cell-content.tn-calendar-body-fill-primary{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #fff)}.tn-calendar-body-cell-content.tn-calendar-body-today-outline{border:1px solid var(--tn-primary, #007bff);color:var(--tn-primary, #007bff)}.cdk-visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n"] }]
|
|
14562
14995
|
}], propDecorators: { activeDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeDate", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], markedDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "markedDates", required: false }] }], rangeMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeMode", required: false }] }], selectedRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedRange", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }] } });
|
|
14563
14996
|
|
|
14564
14997
|
class TnMultiYearViewComponent {
|
|
@@ -20669,5 +21102,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
20669
21102
|
* Generated bundle index. Do not edit.
|
|
20670
21103
|
*/
|
|
20671
21104
|
|
|
20672
|
-
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, THEME_MAP, THEME_STORAGE_KEY, TN_CALENDAR_INTL, TN_CALENDAR_INTL_DEFAULTS, TN_FORM_FIELD_CONTEXT, TN_FORM_FIELD_ERRORS, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarCellHarness, TnCalendarComponent, TnCalendarHarness, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerHarness, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnFormSectionComponent, TnFormSectionHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeDataSource, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnStepperHarness, TnStepperNextDirective, TnStepperPreviousDirective, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeHarness, TnTreeNodeComponent, TnTreeNodeHarness, TnTreeNodeOutletDirective, TnTreeVirtualScrollNodeOutletDirective, TnTreeVirtualScrollViewComponent, TnTreeVirtualScrollViewHarness, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, allowsCurrentDirectorySelection, composeTestId, controlTestId, createFlatTreeControl, createLucideLibrary, createNestedTreeControl, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, defaultTreeItemSize, formatSize, getSelectableTypes, injectTnCalendarIntl, injectTnFormFieldAria, isPathWithinRoot, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, normalizeRootPath, optionTestId, parseLabelMarkup, parseSize, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
|
|
21105
|
+
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, THEME_MAP, THEME_STORAGE_KEY, TN_CALENDAR_INTL, TN_CALENDAR_INTL_DEFAULTS, TN_FORM_FIELD_CONTEXT, TN_FORM_FIELD_ERRORS, TN_RADIO_GROUP, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarCellHarness, TnCalendarComponent, TnCalendarHarness, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerHarness, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnFormSectionComponent, TnFormSectionHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeDataSource, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioGroupComponent, TnRadioGroupHarness, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnStepperHarness, TnStepperNextDirective, TnStepperPreviousDirective, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeHarness, TnTreeNodeComponent, TnTreeNodeHarness, TnTreeNodeOutletDirective, TnTreeVirtualScrollNodeOutletDirective, TnTreeVirtualScrollViewComponent, TnTreeVirtualScrollViewHarness, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, allowsCurrentDirectorySelection, composeTestId, controlTestId, createFlatTreeControl, createLucideLibrary, createNestedTreeControl, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, defaultTreeItemSize, formatSize, getSelectableTypes, injectTnCalendarIntl, injectTnFormFieldAria, isPathWithinRoot, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, normalizeRootPath, optionTestId, parseLabelMarkup, parseSize, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
|
|
20673
21106
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|