@truenas/ui-components 0.1.72 → 0.1.73
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,8 +1,8 @@
|
|
|
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, Renderer2, ElementRef, input, effect, Directive, ViewContainerRef, output, viewChild, computed, signal, forwardRef, Component, model, afterNextRender, ChangeDetectionStrategy, Injectable, isDevMode, ViewEncapsulation, contentChildren, HostListener, contentChild, ChangeDetectorRef,
|
|
5
|
-
import * as
|
|
4
|
+
import { InjectionToken, inject, Renderer2, ElementRef, input, effect, Directive, ViewContainerRef, output, viewChild, computed, signal, forwardRef, Component, model, afterNextRender, ChangeDetectionStrategy, Injectable, isDevMode, ViewEncapsulation, contentChildren, HostListener, contentChild, ChangeDetectorRef, DestroyRef, TemplateRef, isSignal, untracked, IterableDiffers, Pipe, ApplicationRef, EnvironmentInjector, createComponent, PLATFORM_ID } from '@angular/core';
|
|
5
|
+
import * as i1$3 from '@angular/forms';
|
|
6
6
|
import { NG_VALUE_ACCESSOR, FormsModule, NgControl } from '@angular/forms';
|
|
7
7
|
import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';
|
|
8
8
|
import * as i1 from '@angular/cdk/a11y';
|
|
@@ -19,13 +19,13 @@ import { CdkMenu, CdkMenuItem, CdkMenuTrigger } from '@angular/cdk/menu';
|
|
|
19
19
|
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
20
20
|
import { SPACE, ENTER, END, HOME, DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW } from '@angular/cdk/keycodes';
|
|
21
21
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
|
22
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
22
23
|
import { SelectionModel, DataSource } from '@angular/cdk/collections';
|
|
23
|
-
import * as i2
|
|
24
|
+
import * as i2 from '@angular/cdk/tree';
|
|
24
25
|
import { CdkTree, CdkTreeModule, CdkTreeNode, CDK_TREE_NODE_OUTLET_NODE, CdkTreeNodeOutlet, CdkNestedTreeNode } from '@angular/cdk/tree';
|
|
25
26
|
export { FlatTreeControl } from '@angular/cdk/tree';
|
|
26
27
|
import { map } from 'rxjs/operators';
|
|
27
28
|
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
28
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
29
29
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -140,6 +140,25 @@ const TN_TEST_ATTR = new InjectionToken('TN_TEST_ATTR', {
|
|
|
140
140
|
factory: () => 'data-testid',
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Apply a composed test-id string to `element`'s `attrName`, removing the
|
|
145
|
+
* attribute entirely when `composed` is empty (avoids `attr=""`).
|
|
146
|
+
*
|
|
147
|
+
* Shared by {@link TnTestIdDirective} and by components that must write the
|
|
148
|
+
* attribute imperatively because they also read their base back — notably
|
|
149
|
+
* `tn-table-pager`, where injecting a host directive to read its input signal
|
|
150
|
+
* is unreliable in the AOT-linked package build. Centralizing the set/remove
|
|
151
|
+
* branch keeps those write semantics in one place so they can't drift.
|
|
152
|
+
*/
|
|
153
|
+
function writeTestId(renderer, element, attrName, composed) {
|
|
154
|
+
if (composed) {
|
|
155
|
+
renderer.setAttribute(element, attrName, composed);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
renderer.removeAttribute(element, attrName);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
/**
|
|
144
163
|
* Writes a composed `testId` value to whichever attribute name is configured via
|
|
145
164
|
* {@link TN_TEST_ATTR} (default `data-testid`).
|
|
@@ -178,13 +197,7 @@ class TnTestIdDirective {
|
|
|
178
197
|
constructor() {
|
|
179
198
|
effect(() => {
|
|
180
199
|
const composed = composeTestId(this.tnTestIdType(), this.testId());
|
|
181
|
-
|
|
182
|
-
if (composed) {
|
|
183
|
-
this.renderer.setAttribute(element, this.attrName, composed);
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
this.renderer.removeAttribute(element, this.attrName);
|
|
187
|
-
}
|
|
200
|
+
writeTestId(this.renderer, this.host.nativeElement, this.attrName, composed);
|
|
188
201
|
});
|
|
189
202
|
}
|
|
190
203
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTestIdDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -5895,6 +5908,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
5895
5908
|
args: [{ selector: 'tn-keyboard-shortcut', standalone: true, imports: [], template: "<span class=\"tn-keyboard-shortcut\" [attr.aria-label]=\"'Keyboard shortcut: ' + displayShortcut()\">\n @for (key of shortcutKeys(); track $index; let last = $last) {\n <kbd class=\"tn-key\">{{ key }}</kbd>\n @if (!last) {\n <span class=\"tn-key-separator\">{{ separator() || '+' }}</span>\n }\n }\n</span>", styles: [".tn-keyboard-shortcut{display:inline-flex;align-items:center;gap:2px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:12px;line-height:1}.tn-key{display:inline-block;padding:2px 6px;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ddd);border-radius:3px;font-family:inherit;font-size:inherit;font-weight:500;color:var(--tn-fg2, #666666);text-align:center;min-width:16px;line-height:1.2;box-shadow:0 1px 2px #0000001a}.tn-key-separator{color:var(--tn-fg2, #666666);font-size:10px;font-weight:400;margin:0 1px}\n"] }]
|
|
5896
5909
|
}], propDecorators: { shortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcut", required: false }] }], platform: [{ type: i0.Input, args: [{ isSignal: true, alias: "platform", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }] } });
|
|
5897
5910
|
|
|
5911
|
+
/**
|
|
5912
|
+
* Injection token for an app-wide {@link TnFormFieldErrorResolver}.
|
|
5913
|
+
*
|
|
5914
|
+
* Because the library ships no localized strings, this is the recommended hook
|
|
5915
|
+
* for wiring a translation service so every `tn-form-field` resolves messages
|
|
5916
|
+
* consistently. Per-field `errorMessages` still take precedence over it.
|
|
5917
|
+
*
|
|
5918
|
+
* @example
|
|
5919
|
+
* ```ts
|
|
5920
|
+
* providers: [
|
|
5921
|
+
* {
|
|
5922
|
+
* provide: TN_FORM_FIELD_ERRORS,
|
|
5923
|
+
* useFactory: (translate: TranslateService): TnFormFieldErrorResolver =>
|
|
5924
|
+
* (key, value) => translate.instant(`errors.${key}`, value as object),
|
|
5925
|
+
* deps: [TranslateService],
|
|
5926
|
+
* },
|
|
5927
|
+
* ];
|
|
5928
|
+
* ```
|
|
5929
|
+
*/
|
|
5930
|
+
const TN_FORM_FIELD_ERRORS = new InjectionToken('TN_FORM_FIELD_ERRORS');
|
|
5931
|
+
/**
|
|
5932
|
+
* Built-in fallback messages for Angular's standard validators. Used only when
|
|
5933
|
+
* neither a per-field `errorMessages` entry nor a {@link TN_FORM_FIELD_ERRORS}
|
|
5934
|
+
* resolver supplies a message. English-only by design — override the others for
|
|
5935
|
+
* localization.
|
|
5936
|
+
*
|
|
5937
|
+
* @param errorKey The active validation error key.
|
|
5938
|
+
* @param errorValue The detail Angular stored for that key (e.g.
|
|
5939
|
+
* `{ requiredLength: 8 }` for `minlength`). Tolerates malformed
|
|
5940
|
+
* shapes so a bad validator can't crash rendering.
|
|
5941
|
+
* @internal
|
|
5942
|
+
*/
|
|
5943
|
+
function defaultErrorMessage(errorKey, errorValue) {
|
|
5944
|
+
const detail = (errorValue ?? {});
|
|
5945
|
+
switch (errorKey) {
|
|
5946
|
+
case 'required':
|
|
5947
|
+
return 'This field is required';
|
|
5948
|
+
case 'email':
|
|
5949
|
+
return 'Please enter a valid email address';
|
|
5950
|
+
case 'minlength':
|
|
5951
|
+
return detail['requiredLength'] == null
|
|
5952
|
+
? 'Value is too short'
|
|
5953
|
+
: `Minimum length is ${detail['requiredLength']}`;
|
|
5954
|
+
case 'maxlength':
|
|
5955
|
+
return detail['requiredLength'] == null
|
|
5956
|
+
? 'Value is too long'
|
|
5957
|
+
: `Maximum length is ${detail['requiredLength']}`;
|
|
5958
|
+
case 'pattern':
|
|
5959
|
+
return 'Please enter a valid format';
|
|
5960
|
+
case 'min':
|
|
5961
|
+
return detail['min'] == null
|
|
5962
|
+
? 'Value is too small'
|
|
5963
|
+
: `Minimum value is ${detail['min']}`;
|
|
5964
|
+
case 'max':
|
|
5965
|
+
return detail['max'] == null
|
|
5966
|
+
? 'Value is too large'
|
|
5967
|
+
: `Maximum value is ${detail['max']}`;
|
|
5968
|
+
default:
|
|
5969
|
+
return null;
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
/**
|
|
5973
|
+
* Order in which built-in validator errors are surfaced when a control reports
|
|
5974
|
+
* more than one at once.
|
|
5975
|
+
*/
|
|
5976
|
+
const BUILT_IN_ERROR_PRIORITY = [
|
|
5977
|
+
'required', 'email', 'minlength', 'maxlength', 'pattern', 'min', 'max',
|
|
5978
|
+
];
|
|
5979
|
+
/**
|
|
5980
|
+
* Picks which error to display when a control has more than one. Built-in keys
|
|
5981
|
+
* are preferred in {@link BUILT_IN_ERROR_PRIORITY} order; any remaining custom
|
|
5982
|
+
* key falls back to insertion order.
|
|
5983
|
+
*
|
|
5984
|
+
* @internal
|
|
5985
|
+
*/
|
|
5986
|
+
function activeErrorKey(errors) {
|
|
5987
|
+
for (const key of BUILT_IN_ERROR_PRIORITY) {
|
|
5988
|
+
if (errors[key] != null) {
|
|
5989
|
+
return key;
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5992
|
+
return Object.keys(errors)[0] ?? null;
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5898
5995
|
class TnFormFieldComponent {
|
|
5899
5996
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
5900
5997
|
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
@@ -5905,62 +6002,123 @@ class TnFormFieldComponent {
|
|
|
5905
6002
|
tooltip = input('', ...(ngDevMode ? [{ debugName: "tooltip" }] : []));
|
|
5906
6003
|
/** Placement of the tooltip relative to its help icon. */
|
|
5907
6004
|
tooltipPosition = input('above', ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : []));
|
|
6005
|
+
/**
|
|
6006
|
+
* Per-field overrides for validation messages, keyed by error key. Values may
|
|
6007
|
+
* be a string or a function that receives the error's detail value. Takes
|
|
6008
|
+
* precedence over the app-wide {@link TN_FORM_FIELD_ERRORS} resolver and the
|
|
6009
|
+
* built-in defaults.
|
|
6010
|
+
*/
|
|
6011
|
+
errorMessages = input({}, ...(ngDevMode ? [{ debugName: "errorMessages" }] : []));
|
|
5908
6012
|
control = contentChild(NgControl, ...(ngDevMode ? [{ debugName: "control" }] : []));
|
|
5909
|
-
|
|
5910
|
-
|
|
6013
|
+
destroyRef = inject(DestroyRef);
|
|
6014
|
+
/**
|
|
6015
|
+
* App-wide message resolver, captured once at construction. Unlike the
|
|
6016
|
+
* `errorMessages` input it is not reactive — swapping the provided function at
|
|
6017
|
+
* runtime will not be picked up by an already-created field.
|
|
6018
|
+
*/
|
|
6019
|
+
errorResolver = inject(TN_FORM_FIELD_ERRORS, { optional: true });
|
|
6020
|
+
/**
|
|
6021
|
+
* Snapshot of the relevant control state. Updated from the control's status
|
|
6022
|
+
* stream because `NgControl` itself is not signal-based; downstream `computed`s
|
|
6023
|
+
* read this so the derived state stays reactive.
|
|
6024
|
+
*/
|
|
6025
|
+
controlState = signal({
|
|
6026
|
+
invalid: false,
|
|
6027
|
+
interacted: false,
|
|
6028
|
+
errors: null,
|
|
6029
|
+
}, ...(ngDevMode ? [{ debugName: "controlState" }] : []));
|
|
6030
|
+
hasError = computed(() => {
|
|
6031
|
+
const state = this.controlState();
|
|
6032
|
+
return state.invalid && state.interacted;
|
|
6033
|
+
}, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
|
|
6034
|
+
errorMessage = computed(() => {
|
|
6035
|
+
const { errors } = this.controlState();
|
|
6036
|
+
return errors ? this.resolveErrorMessage(errors) : '';
|
|
6037
|
+
}, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
|
|
5911
6038
|
ngAfterContentInit() {
|
|
5912
6039
|
const control = this.control();
|
|
5913
6040
|
if (control) {
|
|
5914
|
-
// Listen for control status changes
|
|
5915
|
-
|
|
5916
|
-
|
|
6041
|
+
// Listen for control status changes.
|
|
6042
|
+
// NOTE: `statusChanges` does not emit on touched/pristine-only transitions
|
|
6043
|
+
// (e.g. `markAsTouched()` / `markAllAsTouched()` on blur or submit), so an
|
|
6044
|
+
// error may not surface until the next value/status change. Reacting to
|
|
6045
|
+
// those via `control.control?.events` is tracked as a follow-up.
|
|
6046
|
+
control.statusChanges
|
|
6047
|
+
?.pipe(takeUntilDestroyed(this.destroyRef))
|
|
6048
|
+
.subscribe(() => {
|
|
6049
|
+
this.syncControlState();
|
|
5917
6050
|
});
|
|
5918
6051
|
// Initial error state check
|
|
5919
|
-
this.
|
|
6052
|
+
this.syncControlState();
|
|
5920
6053
|
}
|
|
5921
6054
|
}
|
|
5922
|
-
|
|
6055
|
+
syncControlState() {
|
|
5923
6056
|
const control = this.control();
|
|
5924
6057
|
if (control) {
|
|
5925
|
-
this.
|
|
5926
|
-
|
|
6058
|
+
this.controlState.set({
|
|
6059
|
+
invalid: !!control.invalid,
|
|
6060
|
+
interacted: !!(control.dirty || control.touched),
|
|
6061
|
+
errors: control.errors ?? null,
|
|
6062
|
+
});
|
|
5927
6063
|
}
|
|
5928
6064
|
}
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
if (errors['email']) {
|
|
5940
|
-
return 'Please enter a valid email address';
|
|
6065
|
+
/**
|
|
6066
|
+
* Resolves a user-facing message for the active error. Reads the
|
|
6067
|
+
* `errorMessages` input (and the injected resolver), so it is reactive: the
|
|
6068
|
+
* displayed message updates when either the control errors or the overrides
|
|
6069
|
+
* change — e.g. a runtime locale switch.
|
|
6070
|
+
*/
|
|
6071
|
+
resolveErrorMessage(errors) {
|
|
6072
|
+
const key = activeErrorKey(errors);
|
|
6073
|
+
if (!key) {
|
|
6074
|
+
return 'Invalid input';
|
|
5941
6075
|
}
|
|
5942
|
-
|
|
5943
|
-
|
|
6076
|
+
const value = errors[key];
|
|
6077
|
+
// 1. Per-field override (string or factory). A throwing factory must not
|
|
6078
|
+
// break change detection, so fall through to the next layer instead.
|
|
6079
|
+
const override = this.errorMessages()[key];
|
|
6080
|
+
if (override != null) {
|
|
6081
|
+
const message = this.runGuarded(() => (typeof override === 'function' ? override(value) : override), `errorMessages["${key}"]`);
|
|
6082
|
+
if (message != null) {
|
|
6083
|
+
return message;
|
|
6084
|
+
}
|
|
5944
6085
|
}
|
|
5945
|
-
|
|
5946
|
-
|
|
6086
|
+
// 2. App-wide resolver (e.g. wired to a translation service).
|
|
6087
|
+
const resolved = this.runGuarded(() => this.errorResolver?.(key, value, this.control()?.control ?? null), 'TN_FORM_FIELD_ERRORS resolver');
|
|
6088
|
+
if (resolved != null) {
|
|
6089
|
+
return resolved;
|
|
5947
6090
|
}
|
|
5948
|
-
|
|
5949
|
-
|
|
6091
|
+
// 3. Built-in default messages for standard validators.
|
|
6092
|
+
const builtIn = defaultErrorMessage(key, value);
|
|
6093
|
+
if (builtIn != null) {
|
|
6094
|
+
return builtIn;
|
|
5950
6095
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
6096
|
+
// 4. A custom validator that returned its own message string.
|
|
6097
|
+
if (typeof value === 'string') {
|
|
6098
|
+
return value;
|
|
5953
6099
|
}
|
|
5954
|
-
|
|
5955
|
-
|
|
6100
|
+
// 5. Last resort: the raw error key.
|
|
6101
|
+
return key;
|
|
6102
|
+
}
|
|
6103
|
+
/**
|
|
6104
|
+
* Runs a caller-supplied message provider, swallowing any throw so a buggy
|
|
6105
|
+
* override or resolver cannot break change detection. Logs in dev mode and
|
|
6106
|
+
* returns null so resolution falls through to the next layer.
|
|
6107
|
+
*/
|
|
6108
|
+
runGuarded(provider, context) {
|
|
6109
|
+
try {
|
|
6110
|
+
// Treat a blank message as "no answer" so it falls through to the next
|
|
6111
|
+
// layer instead of hiding the error — e.g. a translation service that
|
|
6112
|
+
// returns '' for a missing key.
|
|
6113
|
+
const message = provider();
|
|
6114
|
+
return message != null && message.trim() !== '' ? message : null;
|
|
5956
6115
|
}
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
return
|
|
6116
|
+
catch (error) {
|
|
6117
|
+
if (isDevMode()) {
|
|
6118
|
+
console.error(`[tn-form-field] ${context} threw while resolving a validation message`, error);
|
|
6119
|
+
}
|
|
6120
|
+
return null;
|
|
5962
6121
|
}
|
|
5963
|
-
return 'Invalid input';
|
|
5964
6122
|
}
|
|
5965
6123
|
showError = computed(() => {
|
|
5966
6124
|
return this.hasError() && !!this.errorMessage();
|
|
@@ -5972,12 +6130,12 @@ class TnFormFieldComponent {
|
|
|
5972
6130
|
return this.subscriptSizing() === 'fixed' || this.showError() || this.showHint();
|
|
5973
6131
|
}, ...(ngDevMode ? [{ debugName: "showSubscript" }] : []));
|
|
5974
6132
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5975
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", 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 }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-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-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"], dependencies: [{ kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
|
|
6133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", 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 }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-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-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"], dependencies: [{ kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
|
|
5976
6134
|
}
|
|
5977
6135
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, decorators: [{
|
|
5978
6136
|
type: Component,
|
|
5979
6137
|
args: [{ selector: 'tn-form-field', standalone: true, imports: [TnTestIdDirective, TnIconComponent, TnTooltipDirective], template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-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-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"] }]
|
|
5980
|
-
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
|
|
6138
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
|
|
5981
6139
|
|
|
5982
6140
|
/**
|
|
5983
6141
|
* Harness for interacting with `tn-form-field` in tests.
|
|
@@ -8469,12 +8627,22 @@ const TN_TABLE_PAGER_LABELS = new InjectionToken('TN_TABLE_PAGER_LABELS', { prov
|
|
|
8469
8627
|
*/
|
|
8470
8628
|
class TnTablePagerComponent {
|
|
8471
8629
|
destroyRef = inject(DestroyRef);
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8630
|
+
renderer = inject(Renderer2);
|
|
8631
|
+
hostRef = inject(ElementRef);
|
|
8632
|
+
testAttrName = inject(TN_TEST_ATTR);
|
|
8633
|
+
/**
|
|
8634
|
+
* Semantic base applied to the host (via the `tnTestId` host directive) and
|
|
8635
|
+
* used to scope each child control, so multiple pagers on one page don't
|
|
8636
|
+
* collide on `select-page-size` / `button-first-page`. With a base of
|
|
8637
|
+
* `storage` the children become `select-storage-page-size`,
|
|
8638
|
+
* `button-storage-first-page`, etc.; with no base they stay
|
|
8639
|
+
* `select-page-size` / `button-first-page`.
|
|
8640
|
+
*
|
|
8641
|
+
* The page-size dropdown also scopes each option by its value, so individual
|
|
8642
|
+
* sizes are addressable: `option-page-size-10` / `-20` / `-50` / `-100` (or
|
|
8643
|
+
* `option-storage-page-size-10` under a base).
|
|
8644
|
+
*/
|
|
8645
|
+
testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
8478
8646
|
/**
|
|
8479
8647
|
* Build a child control's test-id base by joining the pager's `testId` with
|
|
8480
8648
|
* `suffix` into a single string (kebab-joined). Returning a string keeps it
|
|
@@ -8482,7 +8650,7 @@ class TnTablePagerComponent {
|
|
|
8482
8650
|
* `TnTestIdValue` testId.
|
|
8483
8651
|
*/
|
|
8484
8652
|
childTestId(suffix) {
|
|
8485
|
-
return scopeTestId(this.
|
|
8653
|
+
return scopeTestId(this.testId(), suffix)
|
|
8486
8654
|
.filter((part) => part !== null && part !== undefined && part !== '')
|
|
8487
8655
|
.join('-');
|
|
8488
8656
|
}
|
|
@@ -8588,6 +8756,15 @@ class TnTablePagerComponent {
|
|
|
8588
8756
|
constructor() {
|
|
8589
8757
|
const provided = inject(TN_TABLE_PAGER_LABELS);
|
|
8590
8758
|
this.defaultLabels = isSignal(provided) ? provided : signal(provided).asReadonly();
|
|
8759
|
+
// Write the pager's own test-id to the host. We write it imperatively rather
|
|
8760
|
+
// than applying `TnTestIdDirective` via `hostDirectives`: the pager also needs
|
|
8761
|
+
// to read this base back (see `childTestId`), and injecting a host directive
|
|
8762
|
+
// to read its input signal is unreliable in the AOT-linked package build. The
|
|
8763
|
+
// set/remove semantics are shared with the directive via `writeTestId`.
|
|
8764
|
+
effect(() => {
|
|
8765
|
+
const composed = composeTestId(undefined, this.testId());
|
|
8766
|
+
writeTestId(this.renderer, this.hostRef.nativeElement, this.testAttrName, composed);
|
|
8767
|
+
});
|
|
8591
8768
|
// Re-bind when the dataProvider reference changes (including swap to a
|
|
8592
8769
|
// different instance or clearing back to undefined). `untracked` keeps the
|
|
8593
8770
|
// provider's imperative reads out of the reactive graph so this effect only
|
|
@@ -8702,16 +8879,16 @@ class TnTablePagerComponent {
|
|
|
8702
8879
|
provider.setPagination(pagination);
|
|
8703
8880
|
}
|
|
8704
8881
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTablePagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8705
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnTablePagerComponent, isStandalone: true, selector: "tn-table-pager", inputs: { currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, dataProvider: { classPropertyName: "dataProvider", publicName: "dataProvider", isSignal: true, isRequired: false, transformFunction: null }, itemsPerPageLabel: { classPropertyName: "itemsPerPageLabel", publicName: "itemsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, tablePaginationLabel: { classPropertyName: "tablePaginationLabel", publicName: "tablePaginationLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange", pageChange: "pageChange", pageSizeChange: "pageSizeChange" }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "resolvedTablePaginationLabel()" }, classAttribute: "tn-table-pager" },
|
|
8882
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnTablePagerComponent, isStandalone: true, selector: "tn-table-pager", inputs: { testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, dataProvider: { classPropertyName: "dataProvider", publicName: "dataProvider", isSignal: true, isRequired: false, transformFunction: null }, itemsPerPageLabel: { classPropertyName: "itemsPerPageLabel", publicName: "itemsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, tablePaginationLabel: { classPropertyName: "tablePaginationLabel", publicName: "tablePaginationLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange", pageChange: "pageChange", pageSizeChange: "pageSizeChange" }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "resolvedTablePaginationLabel()" }, classAttribute: "tn-table-pager" }, ngImport: i0, template: "<div class=\"tn-table-pager__page-size\">\n <span class=\"tn-table-pager__label\">{{ resolvedItemsPerPageLabel() }}:</span>\n <tn-select\n class=\"tn-table-pager__size-select\"\n [testId]=\"childTestId('page-size')\"\n [options]=\"pageSizeSelectOptions()\"\n [ariaLabel]=\"resolvedItemsPerPageLabel()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onPageSizeChange($event)\" />\n</div>\n\n<span class=\"tn-table-pager__range\">\n @if (effectiveTotalItems() === 0) {\n 0 {{ resolvedOfLabel() }} 0\n } @else if (lastItemOnPage() > firstItemOnPage()) {\n {{ firstItemOnPage() }} \u2013 {{ lastItemOnPage() }} {{ resolvedOfLabel() }} {{ effectiveTotalItems() }}\n } @else {\n {{ lastItemOnPage() }} {{ resolvedOfLabel() }} {{ effectiveTotalItems() }}\n }\n</span>\n\n<div class=\"tn-table-pager__buttons\">\n <tn-icon-button\n name=\"page-first\"\n library=\"mdi\"\n [testId]=\"childTestId('first-page')\"\n [ariaLabel]=\"resolvedFirstPageLabel()\"\n [disabled]=\"isFirstPageDisabled()\"\n (onClick)=\"goToPage(1)\" />\n <tn-icon-button\n name=\"chevron-left\"\n library=\"mdi\"\n [testId]=\"childTestId('previous-page')\"\n [ariaLabel]=\"resolvedPreviousPageLabel()\"\n [disabled]=\"isFirstPageDisabled()\"\n (onClick)=\"previousPage()\" />\n <tn-icon-button\n name=\"chevron-right\"\n library=\"mdi\"\n [testId]=\"childTestId('next-page')\"\n [ariaLabel]=\"resolvedNextPageLabel()\"\n [disabled]=\"isLastPageDisabled()\"\n (onClick)=\"nextPage()\" />\n <tn-icon-button\n name=\"page-last\"\n library=\"mdi\"\n [testId]=\"childTestId('last-page')\"\n [ariaLabel]=\"resolvedLastPageLabel()\"\n [disabled]=\"isLastPageDisabled()\"\n (onClick)=\"goToPage(totalPages())\" />\n</div>\n", styles: [":host{display:flex;align-items:center;justify-content:flex-end;gap:16px;padding:10px;background-color:var(--tn-bg2);border:1px solid var(--tn-lines);color:var(--tn-fg2)}.tn-table-pager__page-size{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.tn-table-pager__label{white-space:nowrap}.tn-table-pager__size-select{min-width:84px}.tn-table-pager__range{white-space:nowrap}.tn-table-pager__buttons{display:flex;align-items:center;gap:4px}@media(max-width:600px){:host{gap:8px}.tn-table-pager__page-size{gap:4px}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: TnSelectComponent, selector: "tn-select", inputs: ["options", "optionGroups", "placeholder", "ariaLabel", "noOptionsLabel", "disabled", "testId", "multiple", "optionTestIdKey", "compareWith"], outputs: ["selectionChange", "multiSelectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8706
8883
|
}
|
|
8707
8884
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTablePagerComponent, decorators: [{
|
|
8708
8885
|
type: Component,
|
|
8709
|
-
args: [{ selector: 'tn-table-pager', standalone: true, imports: [FormsModule, TnIconButtonComponent, TnSelectComponent
|
|
8886
|
+
args: [{ selector: 'tn-table-pager', standalone: true, imports: [FormsModule, TnIconButtonComponent, TnSelectComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
8710
8887
|
'class': 'tn-table-pager',
|
|
8711
8888
|
'role': 'navigation',
|
|
8712
8889
|
'[attr.aria-label]': 'resolvedTablePaginationLabel()',
|
|
8713
|
-
},
|
|
8714
|
-
}], ctorParameters: () => [], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], dataProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataProvider", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], tablePaginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "tablePaginationLabel", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }] } });
|
|
8890
|
+
}, template: "<div class=\"tn-table-pager__page-size\">\n <span class=\"tn-table-pager__label\">{{ resolvedItemsPerPageLabel() }}:</span>\n <tn-select\n class=\"tn-table-pager__size-select\"\n [testId]=\"childTestId('page-size')\"\n [options]=\"pageSizeSelectOptions()\"\n [ariaLabel]=\"resolvedItemsPerPageLabel()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onPageSizeChange($event)\" />\n</div>\n\n<span class=\"tn-table-pager__range\">\n @if (effectiveTotalItems() === 0) {\n 0 {{ resolvedOfLabel() }} 0\n } @else if (lastItemOnPage() > firstItemOnPage()) {\n {{ firstItemOnPage() }} \u2013 {{ lastItemOnPage() }} {{ resolvedOfLabel() }} {{ effectiveTotalItems() }}\n } @else {\n {{ lastItemOnPage() }} {{ resolvedOfLabel() }} {{ effectiveTotalItems() }}\n }\n</span>\n\n<div class=\"tn-table-pager__buttons\">\n <tn-icon-button\n name=\"page-first\"\n library=\"mdi\"\n [testId]=\"childTestId('first-page')\"\n [ariaLabel]=\"resolvedFirstPageLabel()\"\n [disabled]=\"isFirstPageDisabled()\"\n (onClick)=\"goToPage(1)\" />\n <tn-icon-button\n name=\"chevron-left\"\n library=\"mdi\"\n [testId]=\"childTestId('previous-page')\"\n [ariaLabel]=\"resolvedPreviousPageLabel()\"\n [disabled]=\"isFirstPageDisabled()\"\n (onClick)=\"previousPage()\" />\n <tn-icon-button\n name=\"chevron-right\"\n library=\"mdi\"\n [testId]=\"childTestId('next-page')\"\n [ariaLabel]=\"resolvedNextPageLabel()\"\n [disabled]=\"isLastPageDisabled()\"\n (onClick)=\"nextPage()\" />\n <tn-icon-button\n name=\"page-last\"\n library=\"mdi\"\n [testId]=\"childTestId('last-page')\"\n [ariaLabel]=\"resolvedLastPageLabel()\"\n [disabled]=\"isLastPageDisabled()\"\n (onClick)=\"goToPage(totalPages())\" />\n</div>\n", styles: [":host{display:flex;align-items:center;justify-content:flex-end;gap:16px;padding:10px;background-color:var(--tn-bg2);border:1px solid var(--tn-lines);color:var(--tn-fg2)}.tn-table-pager__page-size{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.tn-table-pager__label{white-space:nowrap}.tn-table-pager__size-select{min-width:84px}.tn-table-pager__range{white-space:nowrap}.tn-table-pager__buttons{display:flex;align-items:center;gap:4px}@media(max-width:600px){:host{gap:8px}.tn-table-pager__page-size{gap:4px}}\n"] }]
|
|
8891
|
+
}], ctorParameters: () => [], propDecorators: { testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], dataProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataProvider", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], tablePaginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "tablePaginationLabel", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }] } });
|
|
8715
8892
|
|
|
8716
8893
|
/**
|
|
8717
8894
|
* Harness for interacting with `tn-table-pager` in tests.
|
|
@@ -8866,7 +9043,7 @@ class TnTreeComponent extends CdkTree {
|
|
|
8866
9043
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8867
9044
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.0", type: TnTreeComponent, isStandalone: true, selector: "tn-tree", host: { attributes: { "role": "tree" }, classAttribute: "tn-tree" }, providers: [
|
|
8868
9045
|
{ provide: CdkTree, useExisting: TnTreeComponent }
|
|
8869
|
-
], exportAs: ["tnTree"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<ng-container cdkTreeNodeOutlet />", styles: [":host{display:block;width:100%}.tn-tree{width:100%;background-color:var(--tn-bg1);border:1px solid var(--tn-lines);border-radius:6px;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2
|
|
9046
|
+
], exportAs: ["tnTree"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<ng-container cdkTreeNodeOutlet />", styles: [":host{display:block;width:100%}.tn-tree{width:100%;background-color:var(--tn-bg1);border:1px solid var(--tn-lines);border-radius:6px;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2.CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
8870
9047
|
}
|
|
8871
9048
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, decorators: [{
|
|
8872
9049
|
type: Component,
|
|
@@ -8897,7 +9074,7 @@ class TnTreeNodeComponent extends CdkTreeNode {
|
|
|
8897
9074
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8898
9075
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnTreeNodeComponent, isStandalone: true, selector: "tn-tree-node", host: { attributes: { "role": "treeitem" }, properties: { "attr.aria-level": "level + 1", "attr.aria-expanded": "isExpandable ? isExpanded : null" }, classAttribute: "tn-tree-node-wrapper" }, providers: [
|
|
8899
9076
|
{ provide: CdkTreeNode, useExisting: TnTreeNodeComponent }
|
|
8900
|
-
], exportAs: ["tnTreeNode"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<div class=\"tn-tree-node\"\n cdkTreeNodeToggle\n role=\"treeitem\"\n [class.tn-tree-node--expandable]=\"isExpandable\"\n [attr.aria-level]=\"level + 1\"\n [attr.aria-expanded]=\"isExpandable ? isExpanded : null\"\n [attr.aria-selected]=\"false\"\n [style.cursor]=\"isExpandable ? 'pointer' : 'default'\">\n \n <div class=\"tn-tree-node__content\">\n <!-- Arrow icon for expandable nodes -->\n @if (isExpandable) {\n <div\n class=\"tn-tree-node__toggle\"\n [class.tn-tree-node__toggle--expanded]=\"isExpanded\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </div>\n }\n\n <!-- Spacer for non-expandable nodes -->\n @if (!isExpandable) {\n <div class=\"tn-tree-node__spacer\"></div>\n }\n \n <!-- Node content -->\n <div class=\"tn-tree-node__text\">\n <ng-content />\n </div>\n </div>\n</div>", styles: [":host{display:block}.tn-tree-node{border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-tree-node:hover{background-color:var(--tn-alt-bg2)}.tn-tree-node:last-child{border-bottom:none}.tn-tree-node--expandable{cursor:pointer}.tn-tree-node--expandable:hover{background-color:var(--tn-alt-bg2)}.tn-tree-node--expandable:active{background-color:var(--tn-alt-bg1)}.tn-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:12px 16px}.tn-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:none;color:var(--tn-fg2);cursor:pointer;border-radius:3px;transition:all .2s ease;flex-shrink:0}.tn-tree-node__toggle:hover{background-color:var(--tn-alt-bg2);color:var(--tn-fg1)}.tn-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:1px}.tn-tree-node__toggle svg{transition:transform .2s ease;transform:rotate(0)}.tn-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-tree-node__text{flex:1;min-width:0;color:var(--tn-fg1)}.tn-tree-node__children{padding-left:24px}.tn-tree-invisible{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2
|
|
9077
|
+
], exportAs: ["tnTreeNode"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<div class=\"tn-tree-node\"\n cdkTreeNodeToggle\n role=\"treeitem\"\n [class.tn-tree-node--expandable]=\"isExpandable\"\n [attr.aria-level]=\"level + 1\"\n [attr.aria-expanded]=\"isExpandable ? isExpanded : null\"\n [attr.aria-selected]=\"false\"\n [style.cursor]=\"isExpandable ? 'pointer' : 'default'\">\n \n <div class=\"tn-tree-node__content\">\n <!-- Arrow icon for expandable nodes -->\n @if (isExpandable) {\n <div\n class=\"tn-tree-node__toggle\"\n [class.tn-tree-node__toggle--expanded]=\"isExpanded\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </div>\n }\n\n <!-- Spacer for non-expandable nodes -->\n @if (!isExpandable) {\n <div class=\"tn-tree-node__spacer\"></div>\n }\n \n <!-- Node content -->\n <div class=\"tn-tree-node__text\">\n <ng-content />\n </div>\n </div>\n</div>", styles: [":host{display:block}.tn-tree-node{border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-tree-node:hover{background-color:var(--tn-alt-bg2)}.tn-tree-node:last-child{border-bottom:none}.tn-tree-node--expandable{cursor:pointer}.tn-tree-node--expandable:hover{background-color:var(--tn-alt-bg2)}.tn-tree-node--expandable:active{background-color:var(--tn-alt-bg1)}.tn-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:12px 16px}.tn-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:none;color:var(--tn-fg2);cursor:pointer;border-radius:3px;transition:all .2s ease;flex-shrink:0}.tn-tree-node__toggle:hover{background-color:var(--tn-alt-bg2);color:var(--tn-fg1)}.tn-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:1px}.tn-tree-node__toggle svg{transition:transform .2s ease;transform:rotate(0)}.tn-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-tree-node__text{flex:1;min-width:0;color:var(--tn-fg1)}.tn-tree-node__children{padding-left:24px}.tn-tree-invisible{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2.CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8901
9078
|
}
|
|
8902
9079
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeComponent, decorators: [{
|
|
8903
9080
|
type: Component,
|
|
@@ -8913,7 +9090,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8913
9090
|
|
|
8914
9091
|
class TnTreeNodeOutletDirective {
|
|
8915
9092
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeOutletDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
8916
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: TnTreeNodeOutletDirective, isStandalone: true, selector: "[tnTreeNodeOutlet]", hostDirectives: [{ directive: i2
|
|
9093
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: TnTreeNodeOutletDirective, isStandalone: true, selector: "[tnTreeNodeOutlet]", hostDirectives: [{ directive: i2.CdkTreeNodeOutlet }], ngImport: i0 });
|
|
8917
9094
|
}
|
|
8918
9095
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeOutletDirective, decorators: [{
|
|
8919
9096
|
type: Directive,
|
|
@@ -8978,7 +9155,7 @@ class TnNestedTreeNodeComponent extends CdkNestedTreeNode {
|
|
|
8978
9155
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnNestedTreeNodeComponent, isStandalone: true, selector: "tn-nested-tree-node", host: { attributes: { "role": "treeitem" }, properties: { "attr.aria-level": "level + 1", "attr.aria-expanded": "isExpandable ? isExpanded : null" }, classAttribute: "tn-nested-tree-node-wrapper" }, providers: [
|
|
8979
9156
|
{ provide: CdkNestedTreeNode, useExisting: TnNestedTreeNodeComponent },
|
|
8980
9157
|
{ provide: CdkTreeNode, useExisting: TnNestedTreeNodeComponent }
|
|
8981
|
-
], exportAs: ["tnNestedTreeNode"], usesInheritance: true, ngImport: i0, template: "<div class=\"tn-nested-tree-node__content\">\n <!-- Toggle button for expandable nodes (provided by component) -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n cdkTreeNodeToggle\n type=\"button\"\n [class.tn-nested-tree-node__toggle--expanded]=\"isExpanded\"\n [attr.aria-label]=\"'Toggle node'\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </button>\n }\n\n <!-- Spacer for non-expandable nodes to maintain alignment -->\n @if (!isExpandable) {\n <div class=\"tn-nested-tree-node__spacer\"></div>\n }\n\n <!-- Consumer content -->\n <ng-content />\n</div>\n\n<!-- Children container -->\n@if (isExpandable) {\n <div class=\"tn-nested-tree-node-container\" role=\"group\" [class.tn-tree-invisible]=\"!isExpanded\">\n <ng-content select=\"[slot=children]\" />\n </div>\n}", styles: [".tn-nested-tree-node-wrapper{display:block;width:100%}.tn-nested-tree-node{display:block;width:100%;font-family:var(--tn-font-family-body);font-size:1rem;line-height:1.4;color:var(--tn-fg1)}.tn-nested-tree-node--expandable .tn-nested-tree-node__content{cursor:pointer}.tn-nested-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:12px 16px;border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-nested-tree-node__content:hover{background-color:var(--tn-alt-bg2)}.tn-nested-tree-node__content:focus-within{background-color:var(--tn-alt-bg2);outline:2px solid var(--tn-primary);outline-offset:-2px}.tn-tree-invisible{display:none}.tn-nested-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;margin-right:8px;padding:0;border:none;background:transparent;border-radius:4px;cursor:pointer;color:var(--tn-fg2);transition:background-color .2s ease,color .2s ease}.tn-nested-tree-node__toggle:hover{background-color:var(--tn-bg3);color:var(--tn-fg1)}.tn-nested-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:2px}.tn-nested-tree-node__toggle svg{transition:transform .2s ease}.tn-nested-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-nested-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-nested-tree-node__text{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--tn-fg1)}div.tn-nested-tree-node-container{padding-left:40px}@media(prefers-reduced-motion:reduce){.tn-nested-tree-node__toggle svg,.tn-nested-tree-node__content,.tn-nested-tree-node__children{transition:none}}@media(prefers-contrast:high){.tn-nested-tree-node__content{border:1px solid transparent}.tn-nested-tree-node__content:hover,.tn-nested-tree-node__content:focus-within{border-color:var(--tn-fg1)}.tn-nested-tree-node__toggle{border:1px solid var(--tn-fg2)}.tn-nested-tree-node__toggle:hover,.tn-nested-tree-node__toggle:focus{border-color:var(--tn-fg1)}}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2
|
|
9158
|
+
], exportAs: ["tnNestedTreeNode"], usesInheritance: true, ngImport: i0, template: "<div class=\"tn-nested-tree-node__content\">\n <!-- Toggle button for expandable nodes (provided by component) -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n cdkTreeNodeToggle\n type=\"button\"\n [class.tn-nested-tree-node__toggle--expanded]=\"isExpanded\"\n [attr.aria-label]=\"'Toggle node'\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </button>\n }\n\n <!-- Spacer for non-expandable nodes to maintain alignment -->\n @if (!isExpandable) {\n <div class=\"tn-nested-tree-node__spacer\"></div>\n }\n\n <!-- Consumer content -->\n <ng-content />\n</div>\n\n<!-- Children container -->\n@if (isExpandable) {\n <div class=\"tn-nested-tree-node-container\" role=\"group\" [class.tn-tree-invisible]=\"!isExpanded\">\n <ng-content select=\"[slot=children]\" />\n </div>\n}", styles: [".tn-nested-tree-node-wrapper{display:block;width:100%}.tn-nested-tree-node{display:block;width:100%;font-family:var(--tn-font-family-body);font-size:1rem;line-height:1.4;color:var(--tn-fg1)}.tn-nested-tree-node--expandable .tn-nested-tree-node__content{cursor:pointer}.tn-nested-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:12px 16px;border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-nested-tree-node__content:hover{background-color:var(--tn-alt-bg2)}.tn-nested-tree-node__content:focus-within{background-color:var(--tn-alt-bg2);outline:2px solid var(--tn-primary);outline-offset:-2px}.tn-tree-invisible{display:none}.tn-nested-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;margin-right:8px;padding:0;border:none;background:transparent;border-radius:4px;cursor:pointer;color:var(--tn-fg2);transition:background-color .2s ease,color .2s ease}.tn-nested-tree-node__toggle:hover{background-color:var(--tn-bg3);color:var(--tn-fg1)}.tn-nested-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:2px}.tn-nested-tree-node__toggle svg{transition:transform .2s ease}.tn-nested-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-nested-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-nested-tree-node__text{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--tn-fg1)}div.tn-nested-tree-node-container{padding-left:40px}@media(prefers-reduced-motion:reduce){.tn-nested-tree-node__toggle svg,.tn-nested-tree-node__content,.tn-nested-tree-node__children{transition:none}}@media(prefers-contrast:high){.tn-nested-tree-node__content{border:1px solid transparent}.tn-nested-tree-node__content:hover,.tn-nested-tree-node__content:focus-within{border-color:var(--tn-fg1)}.tn-nested-tree-node__toggle{border:1px solid var(--tn-fg2)}.tn-nested-tree-node__toggle:hover,.tn-nested-tree-node__toggle:focus{border-color:var(--tn-fg1)}}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2.CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8982
9159
|
}
|
|
8983
9160
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnNestedTreeNodeComponent, decorators: [{
|
|
8984
9161
|
type: Component,
|
|
@@ -11477,7 +11654,7 @@ class TnTimeInputComponent {
|
|
|
11477
11654
|
useExisting: forwardRef(() => TnTimeInputComponent),
|
|
11478
11655
|
multi: true
|
|
11479
11656
|
}
|
|
11480
|
-
], ngImport: i0, template: "<tn-select\n [options]=\"timeSelectOptions()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [testId]=\"testId()\"\n [ngModel]=\"_value\"\n (selectionChange)=\"onSelectionChange($event)\" />\n", styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type:
|
|
11657
|
+
], ngImport: i0, template: "<tn-select\n [options]=\"timeSelectOptions()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [testId]=\"testId()\"\n [ngModel]=\"_value\"\n (selectionChange)=\"onSelectionChange($event)\" />\n", styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TnSelectComponent, selector: "tn-select", inputs: ["options", "optionGroups", "placeholder", "ariaLabel", "noOptionsLabel", "disabled", "testId", "multiple", "optionTestIdKey", "compareWith"], outputs: ["selectionChange", "multiSelectionChange"] }] });
|
|
11481
11658
|
}
|
|
11482
11659
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTimeInputComponent, decorators: [{
|
|
11483
11660
|
type: Component,
|
|
@@ -14978,5 +15155,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
14978
15155
|
* Generated bundle index. Do not edit.
|
|
14979
15156
|
*/
|
|
14980
15157
|
|
|
14981
|
-
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, 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, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, 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, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, 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, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, composeTestId, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, kebabTestSegment, libIconMarker, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker };
|
|
15158
|
+
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, 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, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, 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, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, 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, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, composeTestId, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, kebabTestSegment, libIconMarker, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
|
|
14982
15159
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|