@truenas/ui-components 0.1.72 → 0.1.74
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
|
/**
|
|
@@ -52,20 +52,24 @@ function kebabTestSegment(part) {
|
|
|
52
52
|
* Normalizes the base — a single token or an array of segments — into a flat
|
|
53
53
|
* segment array and appends the suffixes, producing a value that is itself a
|
|
54
54
|
* valid {@link TnTestIdValue}. This is the canonical "derive a per-child id from
|
|
55
|
-
* a parent base" operation, e.g. a
|
|
56
|
-
* a
|
|
57
|
-
* `string | array` flattening contract in one place rather than
|
|
58
|
-
* at each call site.
|
|
55
|
+
* a parent base" operation, e.g. a select's per-option id
|
|
56
|
+
* (`[base, option.label]`) or a menu item's id (`[base, item.id]`). Centralizing
|
|
57
|
+
* it keeps the `string | array` flattening contract in one place rather than
|
|
58
|
+
* re-implemented at each call site.
|
|
59
|
+
*
|
|
60
|
+
* Note this is *base-first*: the suffix trails the base. Fixed chrome whose role
|
|
61
|
+
* should lead (e.g. `tn-dialog-shell`'s `button-close-<base>`) prepends the role
|
|
62
|
+
* manually instead of calling this.
|
|
59
63
|
*
|
|
60
64
|
* Falsy segments are preserved here (not filtered) so `composeTestId` can apply
|
|
61
|
-
* its own drop/scoping rules — `scopeTestId(undefined, '
|
|
62
|
-
* `[undefined, '
|
|
63
|
-
* unscoped `
|
|
65
|
+
* its own drop/scoping rules — `scopeTestId(undefined, 'edit')` yields
|
|
66
|
+
* `[undefined, 'edit']`, which `composeTestId('menu-item', …)` renders as the
|
|
67
|
+
* unscoped `menu-item-edit`.
|
|
64
68
|
*
|
|
65
69
|
* @example
|
|
66
70
|
* scopeTestId('actions', 'edit') // ['actions', 'edit']
|
|
67
71
|
* scopeTestId(['menu', 'main'], 'edit') // ['menu', 'main', 'edit']
|
|
68
|
-
* scopeTestId(undefined, '
|
|
72
|
+
* scopeTestId(undefined, 'edit') // [undefined, 'edit']
|
|
69
73
|
*/
|
|
70
74
|
function scopeTestId(base, ...suffix) {
|
|
71
75
|
return [...(Array.isArray(base) ? base : [base]), ...suffix];
|
|
@@ -140,6 +144,25 @@ const TN_TEST_ATTR = new InjectionToken('TN_TEST_ATTR', {
|
|
|
140
144
|
factory: () => 'data-testid',
|
|
141
145
|
});
|
|
142
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Apply a composed test-id string to `element`'s `attrName`, removing the
|
|
149
|
+
* attribute entirely when `composed` is empty (avoids `attr=""`).
|
|
150
|
+
*
|
|
151
|
+
* Shared by {@link TnTestIdDirective} and by components that must write the
|
|
152
|
+
* attribute imperatively because they also read their base back — notably
|
|
153
|
+
* `tn-table-pager`, where injecting a host directive to read its input signal
|
|
154
|
+
* is unreliable in the AOT-linked package build. Centralizing the set/remove
|
|
155
|
+
* branch keeps those write semantics in one place so they can't drift.
|
|
156
|
+
*/
|
|
157
|
+
function writeTestId(renderer, element, attrName, composed) {
|
|
158
|
+
if (composed) {
|
|
159
|
+
renderer.setAttribute(element, attrName, composed);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
renderer.removeAttribute(element, attrName);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
/**
|
|
144
167
|
* Writes a composed `testId` value to whichever attribute name is configured via
|
|
145
168
|
* {@link TN_TEST_ATTR} (default `data-testid`).
|
|
@@ -178,13 +201,7 @@ class TnTestIdDirective {
|
|
|
178
201
|
constructor() {
|
|
179
202
|
effect(() => {
|
|
180
203
|
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
|
-
}
|
|
204
|
+
writeTestId(this.renderer, this.host.nativeElement, this.attrName, composed);
|
|
188
205
|
});
|
|
189
206
|
}
|
|
190
207
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTestIdDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -5895,6 +5912,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
5895
5912
|
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
5913
|
}], 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
5914
|
|
|
5915
|
+
/**
|
|
5916
|
+
* Injection token for an app-wide {@link TnFormFieldErrorResolver}.
|
|
5917
|
+
*
|
|
5918
|
+
* Because the library ships no localized strings, this is the recommended hook
|
|
5919
|
+
* for wiring a translation service so every `tn-form-field` resolves messages
|
|
5920
|
+
* consistently. Per-field `errorMessages` still take precedence over it.
|
|
5921
|
+
*
|
|
5922
|
+
* @example
|
|
5923
|
+
* ```ts
|
|
5924
|
+
* providers: [
|
|
5925
|
+
* {
|
|
5926
|
+
* provide: TN_FORM_FIELD_ERRORS,
|
|
5927
|
+
* useFactory: (translate: TranslateService): TnFormFieldErrorResolver =>
|
|
5928
|
+
* (key, value) => translate.instant(`errors.${key}`, value as object),
|
|
5929
|
+
* deps: [TranslateService],
|
|
5930
|
+
* },
|
|
5931
|
+
* ];
|
|
5932
|
+
* ```
|
|
5933
|
+
*/
|
|
5934
|
+
const TN_FORM_FIELD_ERRORS = new InjectionToken('TN_FORM_FIELD_ERRORS');
|
|
5935
|
+
/**
|
|
5936
|
+
* Built-in fallback messages for Angular's standard validators. Used only when
|
|
5937
|
+
* neither a per-field `errorMessages` entry nor a {@link TN_FORM_FIELD_ERRORS}
|
|
5938
|
+
* resolver supplies a message. English-only by design — override the others for
|
|
5939
|
+
* localization.
|
|
5940
|
+
*
|
|
5941
|
+
* @param errorKey The active validation error key.
|
|
5942
|
+
* @param errorValue The detail Angular stored for that key (e.g.
|
|
5943
|
+
* `{ requiredLength: 8 }` for `minlength`). Tolerates malformed
|
|
5944
|
+
* shapes so a bad validator can't crash rendering.
|
|
5945
|
+
* @internal
|
|
5946
|
+
*/
|
|
5947
|
+
function defaultErrorMessage(errorKey, errorValue) {
|
|
5948
|
+
const detail = (errorValue ?? {});
|
|
5949
|
+
switch (errorKey) {
|
|
5950
|
+
case 'required':
|
|
5951
|
+
return 'This field is required';
|
|
5952
|
+
case 'email':
|
|
5953
|
+
return 'Please enter a valid email address';
|
|
5954
|
+
case 'minlength':
|
|
5955
|
+
return detail['requiredLength'] == null
|
|
5956
|
+
? 'Value is too short'
|
|
5957
|
+
: `Minimum length is ${detail['requiredLength']}`;
|
|
5958
|
+
case 'maxlength':
|
|
5959
|
+
return detail['requiredLength'] == null
|
|
5960
|
+
? 'Value is too long'
|
|
5961
|
+
: `Maximum length is ${detail['requiredLength']}`;
|
|
5962
|
+
case 'pattern':
|
|
5963
|
+
return 'Please enter a valid format';
|
|
5964
|
+
case 'min':
|
|
5965
|
+
return detail['min'] == null
|
|
5966
|
+
? 'Value is too small'
|
|
5967
|
+
: `Minimum value is ${detail['min']}`;
|
|
5968
|
+
case 'max':
|
|
5969
|
+
return detail['max'] == null
|
|
5970
|
+
? 'Value is too large'
|
|
5971
|
+
: `Maximum value is ${detail['max']}`;
|
|
5972
|
+
default:
|
|
5973
|
+
return null;
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Order in which built-in validator errors are surfaced when a control reports
|
|
5978
|
+
* more than one at once.
|
|
5979
|
+
*/
|
|
5980
|
+
const BUILT_IN_ERROR_PRIORITY = [
|
|
5981
|
+
'required', 'email', 'minlength', 'maxlength', 'pattern', 'min', 'max',
|
|
5982
|
+
];
|
|
5983
|
+
/**
|
|
5984
|
+
* Picks which error to display when a control has more than one. Built-in keys
|
|
5985
|
+
* are preferred in {@link BUILT_IN_ERROR_PRIORITY} order; any remaining custom
|
|
5986
|
+
* key falls back to insertion order.
|
|
5987
|
+
*
|
|
5988
|
+
* @internal
|
|
5989
|
+
*/
|
|
5990
|
+
function activeErrorKey(errors) {
|
|
5991
|
+
for (const key of BUILT_IN_ERROR_PRIORITY) {
|
|
5992
|
+
if (errors[key] != null) {
|
|
5993
|
+
return key;
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
return Object.keys(errors)[0] ?? null;
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5898
5999
|
class TnFormFieldComponent {
|
|
5899
6000
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
5900
6001
|
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
@@ -5905,62 +6006,123 @@ class TnFormFieldComponent {
|
|
|
5905
6006
|
tooltip = input('', ...(ngDevMode ? [{ debugName: "tooltip" }] : []));
|
|
5906
6007
|
/** Placement of the tooltip relative to its help icon. */
|
|
5907
6008
|
tooltipPosition = input('above', ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : []));
|
|
6009
|
+
/**
|
|
6010
|
+
* Per-field overrides for validation messages, keyed by error key. Values may
|
|
6011
|
+
* be a string or a function that receives the error's detail value. Takes
|
|
6012
|
+
* precedence over the app-wide {@link TN_FORM_FIELD_ERRORS} resolver and the
|
|
6013
|
+
* built-in defaults.
|
|
6014
|
+
*/
|
|
6015
|
+
errorMessages = input({}, ...(ngDevMode ? [{ debugName: "errorMessages" }] : []));
|
|
5908
6016
|
control = contentChild(NgControl, ...(ngDevMode ? [{ debugName: "control" }] : []));
|
|
5909
|
-
|
|
5910
|
-
|
|
6017
|
+
destroyRef = inject(DestroyRef);
|
|
6018
|
+
/**
|
|
6019
|
+
* App-wide message resolver, captured once at construction. Unlike the
|
|
6020
|
+
* `errorMessages` input it is not reactive — swapping the provided function at
|
|
6021
|
+
* runtime will not be picked up by an already-created field.
|
|
6022
|
+
*/
|
|
6023
|
+
errorResolver = inject(TN_FORM_FIELD_ERRORS, { optional: true });
|
|
6024
|
+
/**
|
|
6025
|
+
* Snapshot of the relevant control state. Updated from the control's status
|
|
6026
|
+
* stream because `NgControl` itself is not signal-based; downstream `computed`s
|
|
6027
|
+
* read this so the derived state stays reactive.
|
|
6028
|
+
*/
|
|
6029
|
+
controlState = signal({
|
|
6030
|
+
invalid: false,
|
|
6031
|
+
interacted: false,
|
|
6032
|
+
errors: null,
|
|
6033
|
+
}, ...(ngDevMode ? [{ debugName: "controlState" }] : []));
|
|
6034
|
+
hasError = computed(() => {
|
|
6035
|
+
const state = this.controlState();
|
|
6036
|
+
return state.invalid && state.interacted;
|
|
6037
|
+
}, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
|
|
6038
|
+
errorMessage = computed(() => {
|
|
6039
|
+
const { errors } = this.controlState();
|
|
6040
|
+
return errors ? this.resolveErrorMessage(errors) : '';
|
|
6041
|
+
}, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
|
|
5911
6042
|
ngAfterContentInit() {
|
|
5912
6043
|
const control = this.control();
|
|
5913
6044
|
if (control) {
|
|
5914
|
-
// Listen for control status changes
|
|
5915
|
-
|
|
5916
|
-
|
|
6045
|
+
// Listen for control status changes.
|
|
6046
|
+
// NOTE: `statusChanges` does not emit on touched/pristine-only transitions
|
|
6047
|
+
// (e.g. `markAsTouched()` / `markAllAsTouched()` on blur or submit), so an
|
|
6048
|
+
// error may not surface until the next value/status change. Reacting to
|
|
6049
|
+
// those via `control.control?.events` is tracked as a follow-up.
|
|
6050
|
+
control.statusChanges
|
|
6051
|
+
?.pipe(takeUntilDestroyed(this.destroyRef))
|
|
6052
|
+
.subscribe(() => {
|
|
6053
|
+
this.syncControlState();
|
|
5917
6054
|
});
|
|
5918
6055
|
// Initial error state check
|
|
5919
|
-
this.
|
|
6056
|
+
this.syncControlState();
|
|
5920
6057
|
}
|
|
5921
6058
|
}
|
|
5922
|
-
|
|
6059
|
+
syncControlState() {
|
|
5923
6060
|
const control = this.control();
|
|
5924
6061
|
if (control) {
|
|
5925
|
-
this.
|
|
5926
|
-
|
|
6062
|
+
this.controlState.set({
|
|
6063
|
+
invalid: !!control.invalid,
|
|
6064
|
+
interacted: !!(control.dirty || control.touched),
|
|
6065
|
+
errors: control.errors ?? null,
|
|
6066
|
+
});
|
|
5927
6067
|
}
|
|
5928
6068
|
}
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
if (errors['email']) {
|
|
5940
|
-
return 'Please enter a valid email address';
|
|
6069
|
+
/**
|
|
6070
|
+
* Resolves a user-facing message for the active error. Reads the
|
|
6071
|
+
* `errorMessages` input (and the injected resolver), so it is reactive: the
|
|
6072
|
+
* displayed message updates when either the control errors or the overrides
|
|
6073
|
+
* change — e.g. a runtime locale switch.
|
|
6074
|
+
*/
|
|
6075
|
+
resolveErrorMessage(errors) {
|
|
6076
|
+
const key = activeErrorKey(errors);
|
|
6077
|
+
if (!key) {
|
|
6078
|
+
return 'Invalid input';
|
|
5941
6079
|
}
|
|
5942
|
-
|
|
5943
|
-
|
|
6080
|
+
const value = errors[key];
|
|
6081
|
+
// 1. Per-field override (string or factory). A throwing factory must not
|
|
6082
|
+
// break change detection, so fall through to the next layer instead.
|
|
6083
|
+
const override = this.errorMessages()[key];
|
|
6084
|
+
if (override != null) {
|
|
6085
|
+
const message = this.runGuarded(() => (typeof override === 'function' ? override(value) : override), `errorMessages["${key}"]`);
|
|
6086
|
+
if (message != null) {
|
|
6087
|
+
return message;
|
|
6088
|
+
}
|
|
5944
6089
|
}
|
|
5945
|
-
|
|
5946
|
-
|
|
6090
|
+
// 2. App-wide resolver (e.g. wired to a translation service).
|
|
6091
|
+
const resolved = this.runGuarded(() => this.errorResolver?.(key, value, this.control()?.control ?? null), 'TN_FORM_FIELD_ERRORS resolver');
|
|
6092
|
+
if (resolved != null) {
|
|
6093
|
+
return resolved;
|
|
5947
6094
|
}
|
|
5948
|
-
|
|
5949
|
-
|
|
6095
|
+
// 3. Built-in default messages for standard validators.
|
|
6096
|
+
const builtIn = defaultErrorMessage(key, value);
|
|
6097
|
+
if (builtIn != null) {
|
|
6098
|
+
return builtIn;
|
|
5950
6099
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
6100
|
+
// 4. A custom validator that returned its own message string.
|
|
6101
|
+
if (typeof value === 'string') {
|
|
6102
|
+
return value;
|
|
5953
6103
|
}
|
|
5954
|
-
|
|
5955
|
-
|
|
6104
|
+
// 5. Last resort: the raw error key.
|
|
6105
|
+
return key;
|
|
6106
|
+
}
|
|
6107
|
+
/**
|
|
6108
|
+
* Runs a caller-supplied message provider, swallowing any throw so a buggy
|
|
6109
|
+
* override or resolver cannot break change detection. Logs in dev mode and
|
|
6110
|
+
* returns null so resolution falls through to the next layer.
|
|
6111
|
+
*/
|
|
6112
|
+
runGuarded(provider, context) {
|
|
6113
|
+
try {
|
|
6114
|
+
// Treat a blank message as "no answer" so it falls through to the next
|
|
6115
|
+
// layer instead of hiding the error — e.g. a translation service that
|
|
6116
|
+
// returns '' for a missing key.
|
|
6117
|
+
const message = provider();
|
|
6118
|
+
return message != null && message.trim() !== '' ? message : null;
|
|
5956
6119
|
}
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
return
|
|
6120
|
+
catch (error) {
|
|
6121
|
+
if (isDevMode()) {
|
|
6122
|
+
console.error(`[tn-form-field] ${context} threw while resolving a validation message`, error);
|
|
6123
|
+
}
|
|
6124
|
+
return null;
|
|
5962
6125
|
}
|
|
5963
|
-
return 'Invalid input';
|
|
5964
6126
|
}
|
|
5965
6127
|
showError = computed(() => {
|
|
5966
6128
|
return this.hasError() && !!this.errorMessage();
|
|
@@ -5972,12 +6134,12 @@ class TnFormFieldComponent {
|
|
|
5972
6134
|
return this.subscriptSizing() === 'fixed' || this.showError() || this.showHint();
|
|
5973
6135
|
}, ...(ngDevMode ? [{ debugName: "showSubscript" }] : []));
|
|
5974
6136
|
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"] }] });
|
|
6137
|
+
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
6138
|
}
|
|
5977
6139
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, decorators: [{
|
|
5978
6140
|
type: Component,
|
|
5979
6141
|
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 }] }] } });
|
|
6142
|
+
}], 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
6143
|
|
|
5982
6144
|
/**
|
|
5983
6145
|
* Harness for interacting with `tn-form-field` in tests.
|
|
@@ -8469,12 +8631,22 @@ const TN_TABLE_PAGER_LABELS = new InjectionToken('TN_TABLE_PAGER_LABELS', { prov
|
|
|
8469
8631
|
*/
|
|
8470
8632
|
class TnTablePagerComponent {
|
|
8471
8633
|
destroyRef = inject(DestroyRef);
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8634
|
+
renderer = inject(Renderer2);
|
|
8635
|
+
hostRef = inject(ElementRef);
|
|
8636
|
+
testAttrName = inject(TN_TEST_ATTR);
|
|
8637
|
+
/**
|
|
8638
|
+
* Semantic base applied to the host (via the `tnTestId` host directive) and
|
|
8639
|
+
* used to scope each child control, so multiple pagers on one page don't
|
|
8640
|
+
* collide on `select-page-size` / `button-first-page`. With a base of
|
|
8641
|
+
* `storage` the children become `select-storage-page-size`,
|
|
8642
|
+
* `button-storage-first-page`, etc.; with no base they stay
|
|
8643
|
+
* `select-page-size` / `button-first-page`.
|
|
8644
|
+
*
|
|
8645
|
+
* The page-size dropdown also scopes each option by its value, so individual
|
|
8646
|
+
* sizes are addressable: `option-page-size-10` / `-20` / `-50` / `-100` (or
|
|
8647
|
+
* `option-storage-page-size-10` under a base).
|
|
8648
|
+
*/
|
|
8649
|
+
testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
8478
8650
|
/**
|
|
8479
8651
|
* Build a child control's test-id base by joining the pager's `testId` with
|
|
8480
8652
|
* `suffix` into a single string (kebab-joined). Returning a string keeps it
|
|
@@ -8482,7 +8654,7 @@ class TnTablePagerComponent {
|
|
|
8482
8654
|
* `TnTestIdValue` testId.
|
|
8483
8655
|
*/
|
|
8484
8656
|
childTestId(suffix) {
|
|
8485
|
-
return scopeTestId(this.
|
|
8657
|
+
return scopeTestId(this.testId(), suffix)
|
|
8486
8658
|
.filter((part) => part !== null && part !== undefined && part !== '')
|
|
8487
8659
|
.join('-');
|
|
8488
8660
|
}
|
|
@@ -8588,6 +8760,15 @@ class TnTablePagerComponent {
|
|
|
8588
8760
|
constructor() {
|
|
8589
8761
|
const provided = inject(TN_TABLE_PAGER_LABELS);
|
|
8590
8762
|
this.defaultLabels = isSignal(provided) ? provided : signal(provided).asReadonly();
|
|
8763
|
+
// Write the pager's own test-id to the host. We write it imperatively rather
|
|
8764
|
+
// than applying `TnTestIdDirective` via `hostDirectives`: the pager also needs
|
|
8765
|
+
// to read this base back (see `childTestId`), and injecting a host directive
|
|
8766
|
+
// to read its input signal is unreliable in the AOT-linked package build. The
|
|
8767
|
+
// set/remove semantics are shared with the directive via `writeTestId`.
|
|
8768
|
+
effect(() => {
|
|
8769
|
+
const composed = composeTestId(undefined, this.testId());
|
|
8770
|
+
writeTestId(this.renderer, this.hostRef.nativeElement, this.testAttrName, composed);
|
|
8771
|
+
});
|
|
8591
8772
|
// Re-bind when the dataProvider reference changes (including swap to a
|
|
8592
8773
|
// different instance or clearing back to undefined). `untracked` keeps the
|
|
8593
8774
|
// provider's imperative reads out of the reactive graph so this effect only
|
|
@@ -8702,16 +8883,16 @@ class TnTablePagerComponent {
|
|
|
8702
8883
|
provider.setPagination(pagination);
|
|
8703
8884
|
}
|
|
8704
8885
|
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" },
|
|
8886
|
+
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
8887
|
}
|
|
8707
8888
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTablePagerComponent, decorators: [{
|
|
8708
8889
|
type: Component,
|
|
8709
|
-
args: [{ selector: 'tn-table-pager', standalone: true, imports: [FormsModule, TnIconButtonComponent, TnSelectComponent
|
|
8890
|
+
args: [{ selector: 'tn-table-pager', standalone: true, imports: [FormsModule, TnIconButtonComponent, TnSelectComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
8710
8891
|
'class': 'tn-table-pager',
|
|
8711
8892
|
'role': 'navigation',
|
|
8712
8893
|
'[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"] }] } });
|
|
8894
|
+
}, 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"] }]
|
|
8895
|
+
}], 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
8896
|
|
|
8716
8897
|
/**
|
|
8717
8898
|
* Harness for interacting with `tn-table-pager` in tests.
|
|
@@ -8866,7 +9047,7 @@ class TnTreeComponent extends CdkTree {
|
|
|
8866
9047
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8867
9048
|
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
9049
|
{ 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
|
|
9050
|
+
], 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
9051
|
}
|
|
8871
9052
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, decorators: [{
|
|
8872
9053
|
type: Component,
|
|
@@ -8897,7 +9078,7 @@ class TnTreeNodeComponent extends CdkTreeNode {
|
|
|
8897
9078
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8898
9079
|
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
9080
|
{ 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
|
|
9081
|
+
], 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
9082
|
}
|
|
8902
9083
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeComponent, decorators: [{
|
|
8903
9084
|
type: Component,
|
|
@@ -8913,7 +9094,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8913
9094
|
|
|
8914
9095
|
class TnTreeNodeOutletDirective {
|
|
8915
9096
|
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
|
|
9097
|
+
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
9098
|
}
|
|
8918
9099
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeNodeOutletDirective, decorators: [{
|
|
8919
9100
|
type: Directive,
|
|
@@ -8978,7 +9159,7 @@ class TnNestedTreeNodeComponent extends CdkNestedTreeNode {
|
|
|
8978
9159
|
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
9160
|
{ provide: CdkNestedTreeNode, useExisting: TnNestedTreeNodeComponent },
|
|
8980
9161
|
{ 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
|
|
9162
|
+
], 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
9163
|
}
|
|
8983
9164
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnNestedTreeNodeComponent, decorators: [{
|
|
8984
9165
|
type: Component,
|
|
@@ -11477,7 +11658,7 @@ class TnTimeInputComponent {
|
|
|
11477
11658
|
useExisting: forwardRef(() => TnTimeInputComponent),
|
|
11478
11659
|
multi: true
|
|
11479
11660
|
}
|
|
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:
|
|
11661
|
+
], 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
11662
|
}
|
|
11482
11663
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTimeInputComponent, decorators: [{
|
|
11483
11664
|
type: Component,
|
|
@@ -12408,14 +12589,32 @@ class TnDialogShellComponent {
|
|
|
12408
12589
|
/**
|
|
12409
12590
|
* Optional semantic base that scopes the shell's chrome buttons. The close
|
|
12410
12591
|
* and fullscreen buttons emit `button-close` / `button-fullscreen` by default,
|
|
12411
|
-
* or `button-<testId
|
|
12412
|
-
* when more than one dialog can be open).
|
|
12592
|
+
* or `button-close-<testId>` / `button-fullscreen-<testId>` when a base is
|
|
12593
|
+
* provided (useful when more than one dialog can be open).
|
|
12413
12594
|
*/
|
|
12414
12595
|
testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
12415
|
-
/**
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12596
|
+
/**
|
|
12597
|
+
* The `testId` base normalized to a flat segment array. Nothing is dropped
|
|
12598
|
+
* here — `composeTestId` (via the `[tnTestId]` directive) filters falsy
|
|
12599
|
+
* segments, so an unset base collapses to the bare role (`button-close`).
|
|
12600
|
+
*/
|
|
12601
|
+
baseSegments = computed(() => {
|
|
12602
|
+
const base = this.testId();
|
|
12603
|
+
return Array.isArray(base) ? base : [base];
|
|
12604
|
+
}, ...(ngDevMode ? [{ debugName: "baseSegments" }] : []));
|
|
12605
|
+
/**
|
|
12606
|
+
* Role-first test-id segments for the close button: `button-close[-<base>]`.
|
|
12607
|
+
*
|
|
12608
|
+
* The close and fullscreen buttons are fixed dialog chrome, not content
|
|
12609
|
+
* children, so the role leads rather than the base (content children are
|
|
12610
|
+
* base-first via `scopeTestId`). This keeps every dialog's close button under
|
|
12611
|
+
* a shared `button-close-*` prefix — it matches webui's established
|
|
12612
|
+
* close-button ids and lets automation target "all close buttons" with one
|
|
12613
|
+
* selector.
|
|
12614
|
+
*/
|
|
12615
|
+
closeTestId = computed(() => ['close', ...this.baseSegments()], ...(ngDevMode ? [{ debugName: "closeTestId" }] : []));
|
|
12616
|
+
/** Role-first test-id segments for the fullscreen button: `button-fullscreen[-<base>]`. */
|
|
12617
|
+
fullscreenTestId = computed(() => ['fullscreen', ...this.baseSegments()], ...(ngDevMode ? [{ debugName: "fullscreenTestId" }] : []));
|
|
12419
12618
|
/** Stable id for the title heading, referenced by the dialog's aria-labelledby. */
|
|
12420
12619
|
titleId = `tn-dialog-title-${nextUniqueId++}`;
|
|
12421
12620
|
isFullscreen = signal(false, ...(ngDevMode ? [{ debugName: "isFullscreen" }] : []));
|
|
@@ -14978,5 +15177,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
14978
15177
|
* Generated bundle index. Do not edit.
|
|
14979
15178
|
*/
|
|
14980
15179
|
|
|
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 };
|
|
15180
|
+
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
15181
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|