@truenas/ui-components 0.1.22 → 0.1.24
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,5 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { inject, ElementRef, input, output, viewChild, signal, computed, effect, forwardRef, Component, ChangeDetectionStrategy, Injectable, ViewEncapsulation, Directive, contentChildren, ViewContainerRef, contentChild, ChangeDetectorRef, HostListener, TemplateRef, IterableDiffers, Pipe, model, afterNextRender, PLATFORM_ID, DestroyRef } from '@angular/core';
|
|
3
|
+
import * as i1$3 from '@angular/forms';
|
|
4
|
+
import { NG_VALUE_ACCESSOR, FormsModule, NgControl } from '@angular/forms';
|
|
5
|
+
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
3
6
|
import * as i1$1 from '@angular/common';
|
|
4
7
|
import { CommonModule, DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
5
8
|
import { mdiCheckCircle, mdiAlertCircle, mdiAlert, mdiInformation, mdiDotsVertical, mdiClose, mdiFolderOpen, mdiLock, mdiLoading, mdiFolderPlus, mdiFolderNetwork, mdiHarddisk, mdiDatabase, mdiFile, mdiFolder } from '@mdi/js';
|
|
@@ -7,11 +10,8 @@ import * as i1 from '@angular/platform-browser';
|
|
|
7
10
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
8
11
|
import { HttpClient } from '@angular/common/http';
|
|
9
12
|
import { firstValueFrom, BehaviorSubject, merge, Subject } from 'rxjs';
|
|
10
|
-
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
11
13
|
import * as i1$4 from '@angular/cdk/a11y';
|
|
12
14
|
import { FocusMonitor, A11yModule } from '@angular/cdk/a11y';
|
|
13
|
-
import * as i1$3 from '@angular/forms';
|
|
14
|
-
import { FormsModule, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
|
|
15
15
|
import { Overlay, OverlayModule, OverlayPositionBuilder } from '@angular/cdk/overlay';
|
|
16
16
|
import { TemplatePortal, PortalModule, ComponentPortal } from '@angular/cdk/portal';
|
|
17
17
|
import { CdkMenu, CdkMenuItem, CdkMenuTrigger } from '@angular/cdk/menu';
|
|
@@ -26,6 +26,435 @@ import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
|
26
26
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
|
27
27
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
28
28
|
|
|
29
|
+
let nextId = 0;
|
|
30
|
+
class TnAutocompleteComponent {
|
|
31
|
+
elementRef = inject(ElementRef);
|
|
32
|
+
/** Unique instance ID for ARIA linkage */
|
|
33
|
+
uid = `tn-autocomplete-${nextId++}`;
|
|
34
|
+
/** All available options */
|
|
35
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
36
|
+
/** Transform a value to its display string */
|
|
37
|
+
displayWith = input((v) => String(v), ...(ngDevMode ? [{ debugName: "displayWith" }] : []));
|
|
38
|
+
/** Placeholder text for the input */
|
|
39
|
+
placeholder = input('Type to search...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
40
|
+
/** Whether the input is disabled */
|
|
41
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
42
|
+
/** Require the user to select from the dropdown — reverts on blur if no match */
|
|
43
|
+
requireSelection = input(false, ...(ngDevMode ? [{ debugName: "requireSelection" }] : []));
|
|
44
|
+
/** Custom filter function. Defaults to case-insensitive includes on displayWith text */
|
|
45
|
+
filterFn = input(undefined, ...(ngDevMode ? [{ debugName: "filterFn" }] : []));
|
|
46
|
+
/** Text shown when no options match the search */
|
|
47
|
+
noResultsText = input('No results found', ...(ngDevMode ? [{ debugName: "noResultsText" }] : []));
|
|
48
|
+
/** Maximum number of options to render */
|
|
49
|
+
maxResults = input(100, ...(ngDevMode ? [{ debugName: "maxResults" }] : []));
|
|
50
|
+
/** Test ID attribute */
|
|
51
|
+
testId = input('', ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
52
|
+
/** Emits when an option is selected */
|
|
53
|
+
optionSelected = output();
|
|
54
|
+
/** Reference to the input element */
|
|
55
|
+
inputEl = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputEl" }] : []));
|
|
56
|
+
/** Current search term typed by the user */
|
|
57
|
+
searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
|
|
58
|
+
/** Whether the dropdown is open */
|
|
59
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
60
|
+
/** Index of the currently highlighted option for keyboard nav */
|
|
61
|
+
highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : []));
|
|
62
|
+
/** The currently selected value */
|
|
63
|
+
selectedValue = signal(null, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
|
|
64
|
+
/** CVA disabled state from the form */
|
|
65
|
+
formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : []));
|
|
66
|
+
/** Combined disabled state */
|
|
67
|
+
isDisabled = computed(() => this.disabled() || this.formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
68
|
+
/** Filtered and capped options */
|
|
69
|
+
filteredOptions = computed(() => {
|
|
70
|
+
const term = this.searchTerm();
|
|
71
|
+
const all = this.options();
|
|
72
|
+
const customFilter = this.filterFn();
|
|
73
|
+
const display = this.displayWith();
|
|
74
|
+
const max = this.maxResults();
|
|
75
|
+
if (!term) {
|
|
76
|
+
return all.slice(0, max);
|
|
77
|
+
}
|
|
78
|
+
const lowerTerm = term.toLowerCase();
|
|
79
|
+
const filtered = customFilter
|
|
80
|
+
? all.filter((opt) => customFilter(opt, term))
|
|
81
|
+
: all.filter((opt) => display(opt).toLowerCase().includes(lowerTerm));
|
|
82
|
+
return filtered.slice(0, max);
|
|
83
|
+
}, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
|
|
84
|
+
/** Whether there are any results to show */
|
|
85
|
+
hasResults = computed(() => this.filteredOptions().length > 0, ...(ngDevMode ? [{ debugName: "hasResults" }] : []));
|
|
86
|
+
onChange = (_value) => { };
|
|
87
|
+
onTouched = () => { };
|
|
88
|
+
constructor() {
|
|
89
|
+
// Click-outside detection
|
|
90
|
+
effect(() => {
|
|
91
|
+
if (this.isOpen()) {
|
|
92
|
+
const listener = (event) => {
|
|
93
|
+
if (!this.elementRef.nativeElement.contains(event.target)) {
|
|
94
|
+
this.close();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
document.addEventListener('click', listener);
|
|
99
|
+
}, 0);
|
|
100
|
+
return () => {
|
|
101
|
+
document.removeEventListener('click', listener);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// ── ControlValueAccessor ──
|
|
108
|
+
writeValue(value) {
|
|
109
|
+
this.selectedValue.set(value);
|
|
110
|
+
if (value !== null && value !== undefined) {
|
|
111
|
+
this.searchTerm.set(this.displayWith()(value));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.searchTerm.set('');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
registerOnChange(fn) {
|
|
118
|
+
this.onChange = fn;
|
|
119
|
+
}
|
|
120
|
+
registerOnTouched(fn) {
|
|
121
|
+
this.onTouched = fn;
|
|
122
|
+
}
|
|
123
|
+
setDisabledState(isDisabled) {
|
|
124
|
+
this.formDisabled.set(isDisabled);
|
|
125
|
+
}
|
|
126
|
+
// ── Event handlers ──
|
|
127
|
+
onInput(event) {
|
|
128
|
+
const value = event.target.value;
|
|
129
|
+
this.searchTerm.set(value);
|
|
130
|
+
this.highlightedIndex.set(-1);
|
|
131
|
+
if (!this.isOpen()) {
|
|
132
|
+
this.isOpen.set(true);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
onFocus() {
|
|
136
|
+
if (!this.isDisabled()) {
|
|
137
|
+
this.isOpen.set(true);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
onBlur() {
|
|
141
|
+
if (this.requireSelection()) {
|
|
142
|
+
const term = this.searchTerm();
|
|
143
|
+
const display = this.displayWith();
|
|
144
|
+
const match = this.options().find((opt) => display(opt).toLowerCase() === term.toLowerCase());
|
|
145
|
+
if (match) {
|
|
146
|
+
this.selectOption(match);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// Revert to last valid selection or clear
|
|
150
|
+
const current = this.selectedValue();
|
|
151
|
+
if (current !== null && current !== undefined) {
|
|
152
|
+
this.searchTerm.set(display(current));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
this.searchTerm.set('');
|
|
156
|
+
this.onChange(null);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.close();
|
|
161
|
+
this.onTouched();
|
|
162
|
+
}
|
|
163
|
+
onOptionClick(option) {
|
|
164
|
+
this.selectOption(option);
|
|
165
|
+
}
|
|
166
|
+
onKeydown(event) {
|
|
167
|
+
const options = this.filteredOptions();
|
|
168
|
+
switch (event.key) {
|
|
169
|
+
case 'ArrowDown':
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
if (!this.isOpen()) {
|
|
172
|
+
this.isOpen.set(true);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.highlightedIndex.update((i) => i < options.length - 1 ? i + 1 : 0);
|
|
176
|
+
this.scrollToHighlighted();
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case 'ArrowUp':
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
if (this.isOpen()) {
|
|
182
|
+
this.highlightedIndex.update((i) => i > 0 ? i - 1 : options.length - 1);
|
|
183
|
+
this.scrollToHighlighted();
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
case 'Enter':
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
const idx = this.highlightedIndex();
|
|
189
|
+
if (this.isOpen() && idx >= 0 && idx < options.length) {
|
|
190
|
+
this.selectOption(options[idx]);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case 'Escape':
|
|
194
|
+
event.preventDefault();
|
|
195
|
+
this.close();
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// ── Internal ──
|
|
200
|
+
selectOption(option) {
|
|
201
|
+
this.selectedValue.set(option);
|
|
202
|
+
this.searchTerm.set(this.displayWith()(option));
|
|
203
|
+
this.onChange(option);
|
|
204
|
+
this.optionSelected.emit(option);
|
|
205
|
+
this.close();
|
|
206
|
+
}
|
|
207
|
+
close() {
|
|
208
|
+
this.isOpen.set(false);
|
|
209
|
+
this.highlightedIndex.set(-1);
|
|
210
|
+
}
|
|
211
|
+
scrollToHighlighted() {
|
|
212
|
+
const idx = this.highlightedIndex();
|
|
213
|
+
const dropdown = this.elementRef.nativeElement.querySelector('.tn-autocomplete__dropdown');
|
|
214
|
+
const options = dropdown?.querySelectorAll('.tn-autocomplete__option');
|
|
215
|
+
if (options?.[idx]?.scrollIntoView) {
|
|
216
|
+
options[idx].scrollIntoView({ block: 'nearest' });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
220
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnAutocompleteComponent, isStandalone: true, selector: "tn-autocomplete", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, requireSelection: { classPropertyName: "requireSelection", publicName: "requireSelection", isSignal: true, isRequired: false, transformFunction: null }, filterFn: { classPropertyName: "filterFn", publicName: "filterFn", isSignal: true, isRequired: false, transformFunction: null }, noResultsText: { classPropertyName: "noResultsText", publicName: "noResultsText", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, providers: [
|
|
221
|
+
{
|
|
222
|
+
provide: NG_VALUE_ACCESSOR,
|
|
223
|
+
useExisting: forwardRef(() => TnAutocompleteComponent),
|
|
224
|
+
multi: true,
|
|
225
|
+
},
|
|
226
|
+
], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-autocomplete\" [attr.data-testid]=\"testId()\">\n <input\n #inputEl\n type=\"text\"\n class=\"tn-autocomplete__input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [class.open]=\"isOpen()\"\n [class.disabled]=\"isDisabled()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"searchTerm()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"isOpen() ? uid + '-dropdown' : null\"\n [attr.aria-activedescendant]=\"highlightedIndex() >= 0 ? uid + '-option-' + highlightedIndex() : null\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\" />\n\n @if (isOpen()) {\n <div\n class=\"tn-autocomplete__dropdown\"\n role=\"listbox\"\n [attr.id]=\"uid + '-dropdown'\">\n\n @if (hasResults()) {\n @for (option of filteredOptions(); track $index) {\n <div\n class=\"tn-autocomplete__option\"\n role=\"option\"\n tabindex=\"-1\"\n [class.highlighted]=\"highlightedIndex() === $index\"\n [attr.id]=\"uid + '-option-' + $index\"\n [attr.aria-selected]=\"highlightedIndex() === $index\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onOptionClick(option)\"\n (keydown.enter)=\"onOptionClick(option)\">\n {{ displayWith()(option) }}\n </div>\n }\n } @else {\n <div class=\"tn-autocomplete__no-results\">\n {{ noResultsText() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-autocomplete{position:relative;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-autocomplete__input{display:block;width:100%;min-height:2.5rem;padding:.5rem .75rem;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;color:var(--tn-fg1, #212529);font-size:.875rem;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-autocomplete__input::placeholder{color:var(--tn-alt-fg1, #999)}.tn-autocomplete__input:hover:not(.disabled){border-color:var(--tn-primary, #007bff)}.tn-autocomplete__input:focus-visible,.tn-autocomplete__input.open{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px color-mix(in srgb,var(--tn-primary, #007bff) 25%,transparent)}.tn-autocomplete__input.disabled{background-color:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-fg2, #6c757d);cursor:not-allowed;opacity:.6}.tn-autocomplete__dropdown{position:absolute;top:100%;left:0;right:0;z-index:1000;margin-top:.25rem;background-color:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;max-height:200px;overflow-y:auto;padding:.25rem 0}.tn-autocomplete__option{display:flex;align-items:center;padding:.5rem .75rem;cursor:pointer;color:var(--tn-fg1, #212529);transition:background-color .15s ease-in-out}.tn-autocomplete__option:hover{background-color:var(--tn-alt-bg2, #f8f9fa)}.tn-autocomplete__option.highlighted{background-color:var(--tn-alt-bg1, #f8f9fa)}.tn-autocomplete__no-results{padding:1rem .75rem;text-align:center;color:var(--tn-fg2, #6c757d);font-style:italic}@media(prefers-reduced-motion:reduce){.tn-autocomplete__input,.tn-autocomplete__option{transition:none}}\n"] });
|
|
227
|
+
}
|
|
228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnAutocompleteComponent, decorators: [{
|
|
229
|
+
type: Component,
|
|
230
|
+
args: [{ selector: 'tn-autocomplete', standalone: true, imports: [], providers: [
|
|
231
|
+
{
|
|
232
|
+
provide: NG_VALUE_ACCESSOR,
|
|
233
|
+
useExisting: forwardRef(() => TnAutocompleteComponent),
|
|
234
|
+
multi: true,
|
|
235
|
+
},
|
|
236
|
+
], template: "<div class=\"tn-autocomplete\" [attr.data-testid]=\"testId()\">\n <input\n #inputEl\n type=\"text\"\n class=\"tn-autocomplete__input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [class.open]=\"isOpen()\"\n [class.disabled]=\"isDisabled()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"searchTerm()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"isOpen() ? uid + '-dropdown' : null\"\n [attr.aria-activedescendant]=\"highlightedIndex() >= 0 ? uid + '-option-' + highlightedIndex() : null\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\" />\n\n @if (isOpen()) {\n <div\n class=\"tn-autocomplete__dropdown\"\n role=\"listbox\"\n [attr.id]=\"uid + '-dropdown'\">\n\n @if (hasResults()) {\n @for (option of filteredOptions(); track $index) {\n <div\n class=\"tn-autocomplete__option\"\n role=\"option\"\n tabindex=\"-1\"\n [class.highlighted]=\"highlightedIndex() === $index\"\n [attr.id]=\"uid + '-option-' + $index\"\n [attr.aria-selected]=\"highlightedIndex() === $index\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onOptionClick(option)\"\n (keydown.enter)=\"onOptionClick(option)\">\n {{ displayWith()(option) }}\n </div>\n }\n } @else {\n <div class=\"tn-autocomplete__no-results\">\n {{ noResultsText() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-autocomplete{position:relative;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-autocomplete__input{display:block;width:100%;min-height:2.5rem;padding:.5rem .75rem;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;color:var(--tn-fg1, #212529);font-size:.875rem;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-autocomplete__input::placeholder{color:var(--tn-alt-fg1, #999)}.tn-autocomplete__input:hover:not(.disabled){border-color:var(--tn-primary, #007bff)}.tn-autocomplete__input:focus-visible,.tn-autocomplete__input.open{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px color-mix(in srgb,var(--tn-primary, #007bff) 25%,transparent)}.tn-autocomplete__input.disabled{background-color:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-fg2, #6c757d);cursor:not-allowed;opacity:.6}.tn-autocomplete__dropdown{position:absolute;top:100%;left:0;right:0;z-index:1000;margin-top:.25rem;background-color:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;max-height:200px;overflow-y:auto;padding:.25rem 0}.tn-autocomplete__option{display:flex;align-items:center;padding:.5rem .75rem;cursor:pointer;color:var(--tn-fg1, #212529);transition:background-color .15s ease-in-out}.tn-autocomplete__option:hover{background-color:var(--tn-alt-bg2, #f8f9fa)}.tn-autocomplete__option.highlighted{background-color:var(--tn-alt-bg1, #f8f9fa)}.tn-autocomplete__no-results{padding:1rem .75rem;text-align:center;color:var(--tn-fg2, #6c757d);font-style:italic}@media(prefers-reduced-motion:reduce){.tn-autocomplete__input,.tn-autocomplete__option{transition:none}}\n"] }]
|
|
237
|
+
}], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], requireSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireSelection", required: false }] }], filterFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFn", required: false }] }], noResultsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsText", required: false }] }], maxResults: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxResults", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }] } });
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Harness for interacting with tn-autocomplete in tests.
|
|
241
|
+
* Provides methods for querying state, typing search text, and selecting options.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* // Find and select an option
|
|
246
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
247
|
+
* await ac.selectOption('United States');
|
|
248
|
+
*
|
|
249
|
+
* // Type to filter, then read options
|
|
250
|
+
* await ac.setInputValue('Can');
|
|
251
|
+
* const options = await ac.getOptions();
|
|
252
|
+
* expect(options).toEqual(['Canada']);
|
|
253
|
+
*
|
|
254
|
+
* // Filter by placeholder
|
|
255
|
+
* const countryAc = await loader.getHarness(
|
|
256
|
+
* TnAutocompleteHarness.with({ placeholder: 'Search countries...' })
|
|
257
|
+
* );
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
class TnAutocompleteHarness extends ComponentHarness {
|
|
261
|
+
/**
|
|
262
|
+
* The selector for the host element of a `TnAutocompleteComponent` instance.
|
|
263
|
+
*/
|
|
264
|
+
static hostSelector = 'tn-autocomplete';
|
|
265
|
+
_input = this.locatorFor('.tn-autocomplete__input');
|
|
266
|
+
/**
|
|
267
|
+
* Gets a `HarnessPredicate` that can be used to search for an autocomplete
|
|
268
|
+
* with specific attributes.
|
|
269
|
+
*
|
|
270
|
+
* @param options Options for filtering which autocomplete instances are considered a match.
|
|
271
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* // Find by exact placeholder
|
|
276
|
+
* const ac = await loader.getHarness(
|
|
277
|
+
* TnAutocompleteHarness.with({ placeholder: 'Search countries...' })
|
|
278
|
+
* );
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
static with(options = {}) {
|
|
282
|
+
return new HarnessPredicate(TnAutocompleteHarness, options)
|
|
283
|
+
.addOption('placeholder', options.placeholder, async (harness, placeholder) => {
|
|
284
|
+
return (await harness.getPlaceholder()) === placeholder;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Gets the current value of the input field.
|
|
289
|
+
*
|
|
290
|
+
* @returns Promise resolving to the input value.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
295
|
+
* await ac.selectOption('Canada');
|
|
296
|
+
* expect(await ac.getInputValue()).toBe('Canada');
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
async getInputValue() {
|
|
300
|
+
const input = await this._input();
|
|
301
|
+
return (await input.getProperty('value')) ?? '';
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Sets the value of the input by clearing and typing.
|
|
305
|
+
* This triggers filtering of the dropdown options.
|
|
306
|
+
*
|
|
307
|
+
* @param value The text to type into the input.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
312
|
+
* await ac.setInputValue('Can');
|
|
313
|
+
* const options = await ac.getOptions();
|
|
314
|
+
* expect(options).toEqual(['Canada']);
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
async setInputValue(value) {
|
|
318
|
+
const input = await this._input();
|
|
319
|
+
await input.clear();
|
|
320
|
+
await input.sendKeys(value);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Gets the placeholder text of the input.
|
|
324
|
+
*
|
|
325
|
+
* @returns Promise resolving to the placeholder string, or null.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
330
|
+
* expect(await ac.getPlaceholder()).toBe('Type to search...');
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
async getPlaceholder() {
|
|
334
|
+
const input = await this._input();
|
|
335
|
+
return input.getAttribute('placeholder');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Checks whether the dropdown is currently open.
|
|
339
|
+
*
|
|
340
|
+
* @returns Promise resolving to true if the dropdown is visible.
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```typescript
|
|
344
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
345
|
+
* expect(await ac.isOpen()).toBe(false);
|
|
346
|
+
* await ac.focus();
|
|
347
|
+
* expect(await ac.isOpen()).toBe(true);
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
async isOpen() {
|
|
351
|
+
const dropdown = await this.locatorForOptional('.tn-autocomplete__dropdown')();
|
|
352
|
+
return dropdown !== null;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Checks whether the autocomplete input is disabled.
|
|
356
|
+
*
|
|
357
|
+
* @returns Promise resolving to true if the input is disabled.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
362
|
+
* expect(await ac.isDisabled()).toBe(false);
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
async isDisabled() {
|
|
366
|
+
const input = await this._input();
|
|
367
|
+
return (await input.getProperty('disabled')) ?? false;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Gets the labels of all visible options in the dropdown.
|
|
371
|
+
* Opens the dropdown via focus if not already open.
|
|
372
|
+
*
|
|
373
|
+
* @returns Promise resolving to an array of option label strings.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
378
|
+
* await ac.setInputValue('C');
|
|
379
|
+
* const options = await ac.getOptions();
|
|
380
|
+
* expect(options).toContain('Canada');
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
async getOptions() {
|
|
384
|
+
const options = await this.locatorForAll('.tn-autocomplete__option')();
|
|
385
|
+
const labels = [];
|
|
386
|
+
for (const option of options) {
|
|
387
|
+
labels.push((await option.text()).trim());
|
|
388
|
+
}
|
|
389
|
+
return labels;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Selects an option by its label text. Opens the dropdown via focus if needed.
|
|
393
|
+
*
|
|
394
|
+
* @param filter The text to match against option labels. Supports string or RegExp.
|
|
395
|
+
* @returns Promise that resolves when the option has been selected.
|
|
396
|
+
* @throws Error if no matching option is found.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
401
|
+
*
|
|
402
|
+
* // Select by exact text
|
|
403
|
+
* await ac.selectOption('Canada');
|
|
404
|
+
*
|
|
405
|
+
* // Select by regex
|
|
406
|
+
* await ac.selectOption(/united/i);
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
async selectOption(filter) {
|
|
410
|
+
const input = await this._input();
|
|
411
|
+
await input.focus();
|
|
412
|
+
const options = await this.locatorForAll('.tn-autocomplete__option')();
|
|
413
|
+
for (const option of options) {
|
|
414
|
+
const text = (await option.text()).trim();
|
|
415
|
+
const matches = filter instanceof RegExp ? filter.test(text) : text === filter;
|
|
416
|
+
if (matches) {
|
|
417
|
+
await option.click();
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
throw new Error(`Could not find autocomplete option matching "${filter}"`);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Focuses the autocomplete input, which opens the dropdown.
|
|
425
|
+
*
|
|
426
|
+
* @returns Promise that resolves when the input is focused.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
431
|
+
* await ac.focus();
|
|
432
|
+
* expect(await ac.isOpen()).toBe(true);
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
async focus() {
|
|
436
|
+
const input = await this._input();
|
|
437
|
+
return input.focus();
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Blurs the autocomplete input, which closes the dropdown.
|
|
441
|
+
*
|
|
442
|
+
* @returns Promise that resolves when the input is blurred.
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```typescript
|
|
446
|
+
* const ac = await loader.getHarness(TnAutocompleteHarness);
|
|
447
|
+
* await ac.focus();
|
|
448
|
+
* await ac.blur();
|
|
449
|
+
* expect(await ac.isOpen()).toBe(false);
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
async blur() {
|
|
453
|
+
const input = await this._input();
|
|
454
|
+
return input.blur();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
29
458
|
var DiskType;
|
|
30
459
|
(function (DiskType) {
|
|
31
460
|
DiskType["Hdd"] = "HDD";
|
|
@@ -9643,6 +10072,10 @@ var TnTheme;
|
|
|
9643
10072
|
* Default theme used when no theme is set
|
|
9644
10073
|
*/
|
|
9645
10074
|
const DEFAULT_THEME = TnTheme.Dark;
|
|
10075
|
+
/**
|
|
10076
|
+
* Light theme used when OS preference is light
|
|
10077
|
+
*/
|
|
10078
|
+
const LIGHT_THEME = TnTheme.Blue;
|
|
9646
10079
|
/**
|
|
9647
10080
|
* localStorage key for storing the current theme name
|
|
9648
10081
|
*/
|
|
@@ -9711,7 +10144,8 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
|
|
|
9711
10144
|
*
|
|
9712
10145
|
* Features:
|
|
9713
10146
|
* - Signal-based reactive theme state
|
|
9714
|
-
* -
|
|
10147
|
+
* - OS color scheme detection (prefers-color-scheme)
|
|
10148
|
+
* - LocalStorage persistence for explicit user choices (key: 'tn-theme')
|
|
9715
10149
|
* - Automatic CSS class application to document root
|
|
9716
10150
|
* - SSR-safe (checks for browser platform)
|
|
9717
10151
|
*
|
|
@@ -9731,9 +10165,12 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
|
|
|
9731
10165
|
* // Get current theme (signal)
|
|
9732
10166
|
* const currentTheme = this.themeService.currentTheme();
|
|
9733
10167
|
*
|
|
9734
|
-
* // Set theme using enum (recommended)
|
|
10168
|
+
* // Set theme using enum (recommended) — persists to localStorage
|
|
9735
10169
|
* this.themeService.setTheme(TnTheme.Blue);
|
|
9736
10170
|
*
|
|
10171
|
+
* // Clear user preference and follow OS theme
|
|
10172
|
+
* this.themeService.clearPreference();
|
|
10173
|
+
*
|
|
9737
10174
|
* // React to theme changes
|
|
9738
10175
|
* effect(() => {
|
|
9739
10176
|
* console.log('Theme changed to:', this.themeService.currentTheme()?.label);
|
|
@@ -9745,10 +10182,28 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
|
|
|
9745
10182
|
class TnThemeService {
|
|
9746
10183
|
platformId = inject(PLATFORM_ID);
|
|
9747
10184
|
isBrowser = isPlatformBrowser(this.platformId);
|
|
10185
|
+
destroyRef = inject(DestroyRef);
|
|
9748
10186
|
/**
|
|
9749
10187
|
* Internal signal holding the current theme enum value
|
|
9750
10188
|
*/
|
|
9751
10189
|
currentThemeSignal = signal(DEFAULT_THEME, ...(ngDevMode ? [{ debugName: "currentThemeSignal" }] : []));
|
|
10190
|
+
/**
|
|
10191
|
+
* Whether the user has explicitly selected a theme (vs OS-detected default).
|
|
10192
|
+
* When true, theme is persisted to localStorage and OS changes are ignored.
|
|
10193
|
+
*/
|
|
10194
|
+
userSelected = signal(false, ...(ngDevMode ? [{ debugName: "userSelected" }] : []));
|
|
10195
|
+
/**
|
|
10196
|
+
* Reference to the prefers-color-scheme media query for cleanup
|
|
10197
|
+
*/
|
|
10198
|
+
colorSchemeQuery = null;
|
|
10199
|
+
/**
|
|
10200
|
+
* Bound listener reference for cleanup
|
|
10201
|
+
*/
|
|
10202
|
+
colorSchemeListener = (event) => {
|
|
10203
|
+
if (!this.userSelected()) {
|
|
10204
|
+
this.currentThemeSignal.set(event.matches ? DEFAULT_THEME : LIGHT_THEME);
|
|
10205
|
+
}
|
|
10206
|
+
};
|
|
9752
10207
|
/**
|
|
9753
10208
|
* Computed signal that returns the full theme definition for the current theme
|
|
9754
10209
|
*/
|
|
@@ -9762,12 +10217,16 @@ class TnThemeService {
|
|
|
9762
10217
|
currentThemeClass = computed(() => {
|
|
9763
10218
|
return this.currentTheme()?.className ?? TnTheme.Dark;
|
|
9764
10219
|
}, ...(ngDevMode ? [{ debugName: "currentThemeClass" }] : []));
|
|
10220
|
+
/**
|
|
10221
|
+
* Whether the current theme is based on OS preference (no explicit user choice)
|
|
10222
|
+
*/
|
|
10223
|
+
isUsingSystemTheme = computed(() => !this.userSelected(), ...(ngDevMode ? [{ debugName: "isUsingSystemTheme" }] : []));
|
|
9765
10224
|
/**
|
|
9766
10225
|
* All available theme definitions in the library (readonly array)
|
|
9767
10226
|
*/
|
|
9768
10227
|
availableThemes = TN_THEME_DEFINITIONS;
|
|
9769
10228
|
constructor() {
|
|
9770
|
-
// Initialize theme from localStorage or
|
|
10229
|
+
// Initialize theme from localStorage or OS preference
|
|
9771
10230
|
this.initializeTheme();
|
|
9772
10231
|
// Effect to apply theme CSS class to document root whenever theme changes
|
|
9773
10232
|
effect(() => {
|
|
@@ -9775,9 +10234,9 @@ class TnThemeService {
|
|
|
9775
10234
|
this.applyThemeToDOM(this.currentThemeClass());
|
|
9776
10235
|
}
|
|
9777
10236
|
});
|
|
9778
|
-
// Effect to persist theme to localStorage
|
|
10237
|
+
// Effect to persist theme to localStorage only for explicit user choices
|
|
9779
10238
|
effect(() => {
|
|
9780
|
-
if (this.isBrowser) {
|
|
10239
|
+
if (this.isBrowser && this.userSelected()) {
|
|
9781
10240
|
const theme = this.currentTheme();
|
|
9782
10241
|
if (theme) {
|
|
9783
10242
|
this.persistThemeToStorage(theme);
|
|
@@ -9787,7 +10246,8 @@ class TnThemeService {
|
|
|
9787
10246
|
}
|
|
9788
10247
|
/**
|
|
9789
10248
|
* Set the current theme.
|
|
9790
|
-
*
|
|
10249
|
+
* Marks this as an explicit user choice, persists to localStorage,
|
|
10250
|
+
* and stops following OS color scheme changes.
|
|
9791
10251
|
*
|
|
9792
10252
|
* @param theme - The theme to set (use TnTheme enum)
|
|
9793
10253
|
* @returns true if theme was found and set, false otherwise
|
|
@@ -9800,6 +10260,7 @@ class TnThemeService {
|
|
|
9800
10260
|
setTheme(theme) {
|
|
9801
10261
|
const themeDefinition = THEME_MAP.get(theme);
|
|
9802
10262
|
if (themeDefinition) {
|
|
10263
|
+
this.userSelected.set(true);
|
|
9803
10264
|
this.currentThemeSignal.set(theme);
|
|
9804
10265
|
return true;
|
|
9805
10266
|
}
|
|
@@ -9813,10 +10274,27 @@ class TnThemeService {
|
|
|
9813
10274
|
return this.currentThemeSignal();
|
|
9814
10275
|
}
|
|
9815
10276
|
/**
|
|
9816
|
-
* Reset theme to default
|
|
10277
|
+
* Reset theme to default by clearing user preference and reverting to OS detection.
|
|
9817
10278
|
*/
|
|
9818
10279
|
resetToDefault() {
|
|
9819
|
-
this.
|
|
10280
|
+
this.clearPreference();
|
|
10281
|
+
}
|
|
10282
|
+
/**
|
|
10283
|
+
* Clear user preference and revert to OS-based theme detection.
|
|
10284
|
+
* Removes the stored theme from localStorage and follows the OS color scheme.
|
|
10285
|
+
*/
|
|
10286
|
+
clearPreference() {
|
|
10287
|
+
if (this.isBrowser) {
|
|
10288
|
+
try {
|
|
10289
|
+
localStorage.removeItem(THEME_STORAGE_KEY);
|
|
10290
|
+
}
|
|
10291
|
+
catch (error) {
|
|
10292
|
+
console.error('[TnThemeService] Error removing from localStorage:', error);
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
this.userSelected.set(false);
|
|
10296
|
+
this.applySystemTheme();
|
|
10297
|
+
this.listenForColorSchemeChanges();
|
|
9820
10298
|
}
|
|
9821
10299
|
/**
|
|
9822
10300
|
* Check if a theme exists
|
|
@@ -9825,7 +10303,7 @@ class TnThemeService {
|
|
|
9825
10303
|
return THEME_MAP.has(theme);
|
|
9826
10304
|
}
|
|
9827
10305
|
/**
|
|
9828
|
-
* Initialize theme from localStorage or
|
|
10306
|
+
* Initialize theme from localStorage or OS color scheme preference
|
|
9829
10307
|
*/
|
|
9830
10308
|
initializeTheme() {
|
|
9831
10309
|
if (!this.isBrowser) {
|
|
@@ -9834,18 +10312,54 @@ class TnThemeService {
|
|
|
9834
10312
|
try {
|
|
9835
10313
|
const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
|
|
9836
10314
|
if (storedTheme && this.hasTheme(storedTheme)) {
|
|
10315
|
+
this.userSelected.set(true);
|
|
9837
10316
|
this.currentThemeSignal.set(storedTheme);
|
|
9838
10317
|
}
|
|
9839
10318
|
else {
|
|
9840
|
-
//
|
|
9841
|
-
this.
|
|
10319
|
+
// No valid stored theme — detect from OS preference
|
|
10320
|
+
this.applySystemTheme();
|
|
10321
|
+
this.listenForColorSchemeChanges();
|
|
9842
10322
|
}
|
|
9843
10323
|
}
|
|
9844
10324
|
catch (error) {
|
|
9845
10325
|
console.error('[TnThemeService] Error reading from localStorage:', error);
|
|
10326
|
+
this.applySystemTheme();
|
|
10327
|
+
this.listenForColorSchemeChanges();
|
|
10328
|
+
}
|
|
10329
|
+
}
|
|
10330
|
+
/**
|
|
10331
|
+
* Detect OS color scheme preference and apply corresponding theme
|
|
10332
|
+
*/
|
|
10333
|
+
applySystemTheme() {
|
|
10334
|
+
if (!this.isBrowser) {
|
|
10335
|
+
return;
|
|
10336
|
+
}
|
|
10337
|
+
try {
|
|
10338
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
10339
|
+
this.currentThemeSignal.set(prefersDark ? DEFAULT_THEME : LIGHT_THEME);
|
|
10340
|
+
}
|
|
10341
|
+
catch {
|
|
9846
10342
|
this.currentThemeSignal.set(DEFAULT_THEME);
|
|
9847
10343
|
}
|
|
9848
10344
|
}
|
|
10345
|
+
/**
|
|
10346
|
+
* Listen for OS color scheme changes and apply them when no user preference is set
|
|
10347
|
+
*/
|
|
10348
|
+
listenForColorSchemeChanges() {
|
|
10349
|
+
if (!this.isBrowser || this.colorSchemeQuery) {
|
|
10350
|
+
return;
|
|
10351
|
+
}
|
|
10352
|
+
try {
|
|
10353
|
+
this.colorSchemeQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
10354
|
+
this.colorSchemeQuery.addEventListener('change', this.colorSchemeListener);
|
|
10355
|
+
this.destroyRef.onDestroy(() => {
|
|
10356
|
+
this.colorSchemeQuery?.removeEventListener('change', this.colorSchemeListener);
|
|
10357
|
+
});
|
|
10358
|
+
}
|
|
10359
|
+
catch {
|
|
10360
|
+
// matchMedia not supported — fall through silently
|
|
10361
|
+
}
|
|
10362
|
+
}
|
|
9849
10363
|
/**
|
|
9850
10364
|
* Apply theme CSS class to document root.
|
|
9851
10365
|
* Removes all other theme classes first to avoid conflicts.
|
|
@@ -9904,5 +10418,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
9904
10418
|
* Generated bundle index. Do not edit.
|
|
9905
10419
|
*/
|
|
9906
10420
|
|
|
9907
|
-
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateRangeInputComponent, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, 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, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
10421
|
+
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateRangeInputComponent, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, 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, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
9908
10422
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|