@truenas/ui-components 0.1.13 → 0.1.15
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.
|
@@ -1119,6 +1119,15 @@ class TnInputComponent {
|
|
|
1119
1119
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
1120
1120
|
multiline = input(false, ...(ngDevMode ? [{ debugName: "multiline" }] : []));
|
|
1121
1121
|
rows = input(3, ...(ngDevMode ? [{ debugName: "rows" }] : []));
|
|
1122
|
+
// Icon inputs
|
|
1123
|
+
prefixIcon = input(undefined, ...(ngDevMode ? [{ debugName: "prefixIcon" }] : []));
|
|
1124
|
+
prefixIconLibrary = input(undefined, ...(ngDevMode ? [{ debugName: "prefixIconLibrary" }] : []));
|
|
1125
|
+
suffixIcon = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIcon" }] : []));
|
|
1126
|
+
suffixIconLibrary = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIconLibrary" }] : []));
|
|
1127
|
+
suffixIconAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIconAriaLabel" }] : []));
|
|
1128
|
+
onSuffixAction = output();
|
|
1129
|
+
hasPrefixIcon = computed(() => !!this.prefixIcon(), ...(ngDevMode ? [{ debugName: "hasPrefixIcon" }] : []));
|
|
1130
|
+
hasSuffixIcon = computed(() => !!this.suffixIcon(), ...(ngDevMode ? [{ debugName: "hasSuffixIcon" }] : []));
|
|
1122
1131
|
id = 'tn-input';
|
|
1123
1132
|
value = '';
|
|
1124
1133
|
// CVA disabled state management
|
|
@@ -1153,24 +1162,425 @@ class TnInputComponent {
|
|
|
1153
1162
|
this.onTouched();
|
|
1154
1163
|
}
|
|
1155
1164
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1156
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnInputComponent, isStandalone: true, selector: "tn-input", inputs: { inputType: { classPropertyName: "inputType", publicName: "inputType", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiline: { classPropertyName: "multiline", publicName: "multiline", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1165
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnInputComponent, isStandalone: true, selector: "tn-input", inputs: { inputType: { classPropertyName: "inputType", publicName: "inputType", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiline: { classPropertyName: "multiline", publicName: "multiline", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, prefixIcon: { classPropertyName: "prefixIcon", publicName: "prefixIcon", isSignal: true, isRequired: false, transformFunction: null }, prefixIconLibrary: { classPropertyName: "prefixIconLibrary", publicName: "prefixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIcon: { classPropertyName: "suffixIcon", publicName: "suffixIcon", isSignal: true, isRequired: false, transformFunction: null }, suffixIconLibrary: { classPropertyName: "suffixIconLibrary", publicName: "suffixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIconAriaLabel: { classPropertyName: "suffixIconAriaLabel", publicName: "suffixIconAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSuffixAction: "onSuffixAction" }, providers: [
|
|
1157
1166
|
{
|
|
1158
1167
|
provide: NG_VALUE_ACCESSOR,
|
|
1159
1168
|
useExisting: forwardRef(() => TnInputComponent),
|
|
1160
1169
|
multi: true
|
|
1161
1170
|
}
|
|
1162
|
-
], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div
|
|
1171
|
+
], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!multiline()) {\n <input\n #inputEl\n class=\"tn-input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"inputType()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.data-testid]=\"testId()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (multiline()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.placeholder]=\"placeholder()\"\n [attr.data-testid]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: A11yModule }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "fullSize", "customSize"] }] });
|
|
1163
1172
|
}
|
|
1164
1173
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnInputComponent, decorators: [{
|
|
1165
1174
|
type: Component,
|
|
1166
|
-
args: [{ selector: 'tn-input', standalone: true, imports: [FormsModule, A11yModule], providers: [
|
|
1175
|
+
args: [{ selector: 'tn-input', standalone: true, imports: [FormsModule, A11yModule, TnIconComponent], providers: [
|
|
1167
1176
|
{
|
|
1168
1177
|
provide: NG_VALUE_ACCESSOR,
|
|
1169
1178
|
useExisting: forwardRef(() => TnInputComponent),
|
|
1170
1179
|
multi: true
|
|
1171
1180
|
}
|
|
1172
|
-
], template: "<div
|
|
1173
|
-
}], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], inputType: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputType", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }] } });
|
|
1181
|
+
], template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!multiline()) {\n <input\n #inputEl\n class=\"tn-input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"inputType()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.data-testid]=\"testId()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (multiline()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.placeholder]=\"placeholder()\"\n [attr.data-testid]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"] }]
|
|
1182
|
+
}], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], inputType: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputType", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], prefixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIcon", required: false }] }], prefixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIconLibrary", required: false }] }], suffixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIcon", required: false }] }], suffixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconLibrary", required: false }] }], suffixIconAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconAriaLabel", required: false }] }], onSuffixAction: [{ type: i0.Output, args: ["onSuffixAction"] }] } });
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* Harness for interacting with tn-icon in tests.
|
|
1186
|
+
* Provides filtering by icon name and library for existence checks, as well as click interaction.
|
|
1187
|
+
*
|
|
1188
|
+
* @example
|
|
1189
|
+
* ```typescript
|
|
1190
|
+
* // Check for existence
|
|
1191
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1192
|
+
*
|
|
1193
|
+
* // Find icon by name
|
|
1194
|
+
* const folderIcon = await loader.getHarness(
|
|
1195
|
+
* TnIconHarness.with({ name: 'folder' })
|
|
1196
|
+
* );
|
|
1197
|
+
*
|
|
1198
|
+
* // Find icon by name and library
|
|
1199
|
+
* const mdiIcon = await loader.getHarness(
|
|
1200
|
+
* TnIconHarness.with({ name: 'account-circle', library: 'mdi' })
|
|
1201
|
+
* );
|
|
1202
|
+
*
|
|
1203
|
+
* // Check if icon exists
|
|
1204
|
+
* const hasIcon = await loader.hasHarness(
|
|
1205
|
+
* TnIconHarness.with({ name: 'check' })
|
|
1206
|
+
* );
|
|
1207
|
+
*
|
|
1208
|
+
* // Click an icon
|
|
1209
|
+
* const closeIcon = await loader.getHarness(
|
|
1210
|
+
* TnIconHarness.with({ name: 'close' })
|
|
1211
|
+
* );
|
|
1212
|
+
* await closeIcon.click();
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
class TnIconHarness extends ComponentHarness {
|
|
1216
|
+
/**
|
|
1217
|
+
* The selector for the host element of a `TnIconComponent` instance.
|
|
1218
|
+
*/
|
|
1219
|
+
static hostSelector = 'tn-icon';
|
|
1220
|
+
/**
|
|
1221
|
+
* Gets a `HarnessPredicate` that can be used to search for an icon
|
|
1222
|
+
* with specific attributes.
|
|
1223
|
+
*
|
|
1224
|
+
* @param options Options for filtering which icon instances are considered a match.
|
|
1225
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
1226
|
+
*
|
|
1227
|
+
* @example
|
|
1228
|
+
* ```typescript
|
|
1229
|
+
* // Find icon by name
|
|
1230
|
+
* const icon = await loader.getHarness(
|
|
1231
|
+
* TnIconHarness.with({ name: 'home' })
|
|
1232
|
+
* );
|
|
1233
|
+
*
|
|
1234
|
+
* // Find icon by library
|
|
1235
|
+
* const customIcon = await loader.getHarness(
|
|
1236
|
+
* TnIconHarness.with({ library: 'custom' })
|
|
1237
|
+
* );
|
|
1238
|
+
*
|
|
1239
|
+
* // Find icon with specific size
|
|
1240
|
+
* const largeIcon = await loader.getHarness(
|
|
1241
|
+
* TnIconHarness.with({ size: 'lg' })
|
|
1242
|
+
* );
|
|
1243
|
+
* ```
|
|
1244
|
+
*/
|
|
1245
|
+
static with(options = {}) {
|
|
1246
|
+
return new HarnessPredicate(TnIconHarness, options)
|
|
1247
|
+
.addOption('name', options.name, async (harness, name) => {
|
|
1248
|
+
return (await harness.getName()) === name;
|
|
1249
|
+
})
|
|
1250
|
+
.addOption('library', options.library, async (harness, library) => {
|
|
1251
|
+
return (await harness.getLibrary()) === library;
|
|
1252
|
+
})
|
|
1253
|
+
.addOption('size', options.size, async (harness, size) => {
|
|
1254
|
+
return (await harness.getSize()) === size;
|
|
1255
|
+
})
|
|
1256
|
+
.addOption('fullSize', options.fullSize, async (harness, fullSize) => {
|
|
1257
|
+
return (await harness.isFullSize()) === fullSize;
|
|
1258
|
+
})
|
|
1259
|
+
.addOption('customSize', options.customSize, async (harness, customSize) => {
|
|
1260
|
+
return (await harness.getCustomSize()) === customSize;
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Gets the icon name.
|
|
1265
|
+
*
|
|
1266
|
+
* @returns Promise resolving to the icon name.
|
|
1267
|
+
*
|
|
1268
|
+
* @example
|
|
1269
|
+
* ```typescript
|
|
1270
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1271
|
+
* const name = await icon.getName();
|
|
1272
|
+
* expect(name).toBe('folder');
|
|
1273
|
+
* ```
|
|
1274
|
+
*/
|
|
1275
|
+
async getName() {
|
|
1276
|
+
const host = await this.host();
|
|
1277
|
+
return host.getAttribute('name');
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Gets the icon library.
|
|
1281
|
+
*
|
|
1282
|
+
* @returns Promise resolving to the icon library.
|
|
1283
|
+
*
|
|
1284
|
+
* @example
|
|
1285
|
+
* ```typescript
|
|
1286
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1287
|
+
* const library = await icon.getLibrary();
|
|
1288
|
+
* expect(library).toBe('mdi');
|
|
1289
|
+
* ```
|
|
1290
|
+
*/
|
|
1291
|
+
async getLibrary() {
|
|
1292
|
+
const host = await this.host();
|
|
1293
|
+
return host.getAttribute('library');
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Gets the icon size.
|
|
1297
|
+
*
|
|
1298
|
+
* @returns Promise resolving to the icon size.
|
|
1299
|
+
*
|
|
1300
|
+
* @example
|
|
1301
|
+
* ```typescript
|
|
1302
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1303
|
+
* const size = await icon.getSize();
|
|
1304
|
+
* expect(size).toBe('lg');
|
|
1305
|
+
* ```
|
|
1306
|
+
*/
|
|
1307
|
+
async getSize() {
|
|
1308
|
+
const host = await this.host();
|
|
1309
|
+
return host.getAttribute('size');
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Gets the icon color.
|
|
1313
|
+
*
|
|
1314
|
+
* @returns Promise resolving to the icon color.
|
|
1315
|
+
*
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```typescript
|
|
1318
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1319
|
+
* const color = await icon.getColor();
|
|
1320
|
+
* expect(color).toBe('primary');
|
|
1321
|
+
* ```
|
|
1322
|
+
*/
|
|
1323
|
+
async getColor() {
|
|
1324
|
+
const host = await this.host();
|
|
1325
|
+
return host.getAttribute('color');
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Checks if the icon is in full-size mode.
|
|
1329
|
+
*
|
|
1330
|
+
* @returns Promise resolving to true if the icon is full-size, false otherwise.
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* ```typescript
|
|
1334
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1335
|
+
* const isFullSize = await icon.isFullSize();
|
|
1336
|
+
* expect(isFullSize).toBe(true);
|
|
1337
|
+
* ```
|
|
1338
|
+
*/
|
|
1339
|
+
async isFullSize() {
|
|
1340
|
+
const host = await this.host();
|
|
1341
|
+
const fullSizeAttr = await host.getAttribute('full-size');
|
|
1342
|
+
return fullSizeAttr === 'true';
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Gets the icon custom size.
|
|
1346
|
+
*
|
|
1347
|
+
* @returns Promise resolving to the custom size value, or null if not set.
|
|
1348
|
+
*
|
|
1349
|
+
* @example
|
|
1350
|
+
* ```typescript
|
|
1351
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1352
|
+
* const customSize = await icon.getCustomSize();
|
|
1353
|
+
* expect(customSize).toBe('64px');
|
|
1354
|
+
* ```
|
|
1355
|
+
*/
|
|
1356
|
+
async getCustomSize() {
|
|
1357
|
+
const host = await this.host();
|
|
1358
|
+
return host.getAttribute('custom-size');
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Checks if the icon is using a custom size.
|
|
1362
|
+
*
|
|
1363
|
+
* @returns Promise resolving to true if a custom size is set, false otherwise.
|
|
1364
|
+
*
|
|
1365
|
+
* @example
|
|
1366
|
+
* ```typescript
|
|
1367
|
+
* const icon = await loader.getHarness(TnIconHarness);
|
|
1368
|
+
* const hasCustomSize = await icon.hasCustomSize();
|
|
1369
|
+
* expect(hasCustomSize).toBe(true);
|
|
1370
|
+
* ```
|
|
1371
|
+
*/
|
|
1372
|
+
async hasCustomSize() {
|
|
1373
|
+
const customSize = await this.getCustomSize();
|
|
1374
|
+
return customSize !== null;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Clicks the icon.
|
|
1378
|
+
*
|
|
1379
|
+
* @returns Promise that resolves when the click action is complete.
|
|
1380
|
+
*
|
|
1381
|
+
* @example
|
|
1382
|
+
* ```typescript
|
|
1383
|
+
* const icon = await loader.getHarness(TnIconHarness.with({ name: 'close' }));
|
|
1384
|
+
* await icon.click();
|
|
1385
|
+
* ```
|
|
1386
|
+
*/
|
|
1387
|
+
async click() {
|
|
1388
|
+
const host = await this.host();
|
|
1389
|
+
return host.click();
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Harness for interacting with tn-input in tests.
|
|
1395
|
+
* Provides methods for querying state, setting values, and interacting with prefix/suffix icons.
|
|
1396
|
+
*
|
|
1397
|
+
* @example
|
|
1398
|
+
* ```typescript
|
|
1399
|
+
* // Get the input harness
|
|
1400
|
+
* const input = await loader.getHarness(TnInputHarness);
|
|
1401
|
+
*
|
|
1402
|
+
* // Set and read a value
|
|
1403
|
+
* await input.setValue('hello');
|
|
1404
|
+
* expect(await input.getValue()).toBe('hello');
|
|
1405
|
+
*
|
|
1406
|
+
* // Check for prefix icon
|
|
1407
|
+
* expect(await input.hasPrefixIcon()).toBe(true);
|
|
1408
|
+
*
|
|
1409
|
+
* // Click suffix action
|
|
1410
|
+
* await input.clickSuffixAction();
|
|
1411
|
+
* ```
|
|
1412
|
+
*/
|
|
1413
|
+
class TnInputHarness extends ComponentHarness {
|
|
1414
|
+
/**
|
|
1415
|
+
* The selector for the host element of a `TnInputComponent` instance.
|
|
1416
|
+
*/
|
|
1417
|
+
static hostSelector = 'tn-input';
|
|
1418
|
+
inputEl = this.locatorFor('input.tn-input');
|
|
1419
|
+
textareaEl = this.locatorFor('textarea.tn-input');
|
|
1420
|
+
prefixIconEl = this.locatorForOptional('tn-icon.tn-input__prefix-icon');
|
|
1421
|
+
suffixButton = this.locatorForOptional('.tn-input__suffix-action');
|
|
1422
|
+
suffixIconEl = this.locatorForOptional(TnIconHarness.with({ ancestor: '.tn-input__suffix-action' }));
|
|
1423
|
+
/**
|
|
1424
|
+
* Gets a `HarnessPredicate` that can be used to search for an input
|
|
1425
|
+
* with specific attributes.
|
|
1426
|
+
*
|
|
1427
|
+
* @param options Options for filtering which input instances are considered a match.
|
|
1428
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
1429
|
+
*/
|
|
1430
|
+
static with(options = {}) {
|
|
1431
|
+
return new HarnessPredicate(TnInputHarness, options)
|
|
1432
|
+
.addOption('placeholder', options.placeholder, async (harness, placeholder) => {
|
|
1433
|
+
return (await harness.getPlaceholder()) === placeholder;
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Gets the current value of the input.
|
|
1438
|
+
*
|
|
1439
|
+
* @returns Promise resolving to the input value.
|
|
1440
|
+
*/
|
|
1441
|
+
async getValue() {
|
|
1442
|
+
if (await this.isMultiline()) {
|
|
1443
|
+
const textarea = await this.textareaEl();
|
|
1444
|
+
return (await textarea.getProperty('value')) ?? '';
|
|
1445
|
+
}
|
|
1446
|
+
const input = await this.inputEl();
|
|
1447
|
+
return (await input.getProperty('value')) ?? '';
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Sets the value of the input by clearing and typing.
|
|
1451
|
+
*
|
|
1452
|
+
* @param value The value to set.
|
|
1453
|
+
*/
|
|
1454
|
+
async setValue(value) {
|
|
1455
|
+
if (await this.isMultiline()) {
|
|
1456
|
+
const textarea = await this.textareaEl();
|
|
1457
|
+
await textarea.clear();
|
|
1458
|
+
await textarea.sendKeys(value);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const input = await this.inputEl();
|
|
1462
|
+
await input.clear();
|
|
1463
|
+
await input.sendKeys(value);
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Gets the placeholder text.
|
|
1467
|
+
*
|
|
1468
|
+
* @returns Promise resolving to the placeholder string.
|
|
1469
|
+
*/
|
|
1470
|
+
async getPlaceholder() {
|
|
1471
|
+
if (await this.isMultiline()) {
|
|
1472
|
+
const textarea = await this.textareaEl();
|
|
1473
|
+
return textarea.getAttribute('placeholder');
|
|
1474
|
+
}
|
|
1475
|
+
const input = await this.inputEl();
|
|
1476
|
+
return input.getAttribute('placeholder');
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Checks whether the input is disabled.
|
|
1480
|
+
*
|
|
1481
|
+
* @returns Promise resolving to true if the input is disabled.
|
|
1482
|
+
*/
|
|
1483
|
+
async isDisabled() {
|
|
1484
|
+
if (await this.isMultiline()) {
|
|
1485
|
+
const textarea = await this.textareaEl();
|
|
1486
|
+
return (await textarea.getProperty('disabled')) ?? false;
|
|
1487
|
+
}
|
|
1488
|
+
const input = await this.inputEl();
|
|
1489
|
+
return (await input.getProperty('disabled')) ?? false;
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Checks whether the input renders as a textarea.
|
|
1493
|
+
*
|
|
1494
|
+
* @returns Promise resolving to true if multiline.
|
|
1495
|
+
*/
|
|
1496
|
+
async isMultiline() {
|
|
1497
|
+
const textareas = await this.locatorForAll('textarea.tn-input')();
|
|
1498
|
+
return textareas.length > 0;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Checks whether a prefix icon is present.
|
|
1502
|
+
*
|
|
1503
|
+
* @returns Promise resolving to true if a prefix icon exists.
|
|
1504
|
+
*/
|
|
1505
|
+
async hasPrefixIcon() {
|
|
1506
|
+
const icon = await this.prefixIconEl();
|
|
1507
|
+
return icon !== null;
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Gets the prefix icon harness for further inspection.
|
|
1511
|
+
*
|
|
1512
|
+
* @returns Promise resolving to the prefix TnIconHarness, or null if not present.
|
|
1513
|
+
*/
|
|
1514
|
+
async getPrefixIcon() {
|
|
1515
|
+
const el = await this.prefixIconEl();
|
|
1516
|
+
if (!el) {
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
const allIcons = await this.locatorForAll(TnIconHarness)();
|
|
1520
|
+
for (const icon of allIcons) {
|
|
1521
|
+
const host = await icon.host();
|
|
1522
|
+
if (await host.hasClass('tn-input__prefix-icon')) {
|
|
1523
|
+
return icon;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Checks whether a suffix action button is present.
|
|
1530
|
+
*
|
|
1531
|
+
* @returns Promise resolving to true if a suffix action exists.
|
|
1532
|
+
*/
|
|
1533
|
+
async hasSuffixAction() {
|
|
1534
|
+
const button = await this.suffixButton();
|
|
1535
|
+
return button !== null;
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Gets the suffix icon harness for further inspection.
|
|
1539
|
+
*
|
|
1540
|
+
* @returns Promise resolving to the suffix TnIconHarness, or null if not present.
|
|
1541
|
+
*/
|
|
1542
|
+
async getSuffixIcon() {
|
|
1543
|
+
return this.suffixIconEl();
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Clicks the suffix action button.
|
|
1547
|
+
*
|
|
1548
|
+
* @returns Promise that resolves when the click action is complete.
|
|
1549
|
+
*/
|
|
1550
|
+
async clickSuffixAction() {
|
|
1551
|
+
const button = await this.suffixButton();
|
|
1552
|
+
if (!button) {
|
|
1553
|
+
throw new Error('No suffix action button found on this input.');
|
|
1554
|
+
}
|
|
1555
|
+
return button.click();
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Focuses the input element.
|
|
1559
|
+
*
|
|
1560
|
+
* @returns Promise that resolves when the input is focused.
|
|
1561
|
+
*/
|
|
1562
|
+
async focus() {
|
|
1563
|
+
if (await this.isMultiline()) {
|
|
1564
|
+
const textarea = await this.textareaEl();
|
|
1565
|
+
return textarea.focus();
|
|
1566
|
+
}
|
|
1567
|
+
const input = await this.inputEl();
|
|
1568
|
+
return input.focus();
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Blurs the input element.
|
|
1572
|
+
*
|
|
1573
|
+
* @returns Promise that resolves when the input is blurred.
|
|
1574
|
+
*/
|
|
1575
|
+
async blur() {
|
|
1576
|
+
if (await this.isMultiline()) {
|
|
1577
|
+
const textarea = await this.textareaEl();
|
|
1578
|
+
return textarea.blur();
|
|
1579
|
+
}
|
|
1580
|
+
const input = await this.inputEl();
|
|
1581
|
+
return input.blur();
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1174
1584
|
|
|
1175
1585
|
class TnInputDirective {
|
|
1176
1586
|
constructor() { }
|
|
@@ -1896,15 +2306,15 @@ class TnTabComponent {
|
|
|
1896
2306
|
// Internal properties set by parent TnTabsComponent (public signals for parent control)
|
|
1897
2307
|
index = signal(0, ...(ngDevMode ? [{ debugName: "index" }] : []));
|
|
1898
2308
|
isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
|
|
1899
|
-
tabsComponent;
|
|
2309
|
+
tabsComponent;
|
|
1900
2310
|
elementRef = inject((ElementRef));
|
|
1901
2311
|
hasIconContent = signal(false, ...(ngDevMode ? [{ debugName: "hasIconContent" }] : []));
|
|
1902
2312
|
ngAfterContentInit() {
|
|
1903
2313
|
this.hasIconContent.set(!!this.iconContent());
|
|
1904
2314
|
}
|
|
1905
2315
|
onClick() {
|
|
1906
|
-
if (!this.disabled()) {
|
|
1907
|
-
this.
|
|
2316
|
+
if (!this.disabled() && this.tabsComponent) {
|
|
2317
|
+
this.tabsComponent.selectTab(this.index());
|
|
1908
2318
|
}
|
|
1909
2319
|
}
|
|
1910
2320
|
onKeydown(event) {
|
|
@@ -2017,10 +2427,6 @@ class TnTabsComponent {
|
|
|
2017
2427
|
}
|
|
2018
2428
|
this.cdr.detectChanges();
|
|
2019
2429
|
});
|
|
2020
|
-
}
|
|
2021
|
-
ngAfterContentInit() {
|
|
2022
|
-
this.initializeTabs();
|
|
2023
|
-
this.selectTab(this.internalSelectedIndex());
|
|
2024
2430
|
// Listen for tab changes
|
|
2025
2431
|
effect(() => {
|
|
2026
2432
|
// Track tabs signal to react to changes
|
|
@@ -2031,6 +2437,10 @@ class TnTabsComponent {
|
|
|
2031
2437
|
}
|
|
2032
2438
|
});
|
|
2033
2439
|
}
|
|
2440
|
+
ngAfterContentInit() {
|
|
2441
|
+
this.initializeTabs();
|
|
2442
|
+
this.selectTab(this.internalSelectedIndex());
|
|
2443
|
+
}
|
|
2034
2444
|
ngAfterViewInit() {
|
|
2035
2445
|
// Wait for next tick to ensure DOM is fully rendered
|
|
2036
2446
|
setTimeout(() => {
|
|
@@ -2056,12 +2466,6 @@ class TnTabsComponent {
|
|
|
2056
2466
|
if (tab.elementRef) {
|
|
2057
2467
|
this.focusMonitor.monitor(tab.elementRef);
|
|
2058
2468
|
}
|
|
2059
|
-
// Set up click handlers
|
|
2060
|
-
tab.selected.subscribe(() => {
|
|
2061
|
-
if (!tab.disabled()) {
|
|
2062
|
-
this.selectTab(index);
|
|
2063
|
-
}
|
|
2064
|
-
});
|
|
2065
2469
|
});
|
|
2066
2470
|
this.panels().forEach((panel, index) => {
|
|
2067
2471
|
panel.index.set(index);
|
|
@@ -2221,40 +2625,443 @@ class TnTabsComponent {
|
|
|
2221
2625
|
}
|
|
2222
2626
|
return 0;
|
|
2223
2627
|
}
|
|
2224
|
-
getLastEnabledTabIndex() {
|
|
2225
|
-
const tabs = this.tabs();
|
|
2226
|
-
for (let i = tabs.length - 1; i >= 0; i--) {
|
|
2227
|
-
if (!tabs[i]?.disabled()) {
|
|
2228
|
-
return i;
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
return tabs.length - 1;
|
|
2628
|
+
getLastEnabledTabIndex() {
|
|
2629
|
+
const tabs = this.tabs();
|
|
2630
|
+
for (let i = tabs.length - 1; i >= 0; i--) {
|
|
2631
|
+
if (!tabs[i]?.disabled()) {
|
|
2632
|
+
return i;
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
return tabs.length - 1;
|
|
2636
|
+
}
|
|
2637
|
+
focusTab(index) {
|
|
2638
|
+
const tab = this.tabs()[index];
|
|
2639
|
+
if (tab && tab.elementRef) {
|
|
2640
|
+
tab.elementRef.nativeElement.focus();
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
classes = computed(() => {
|
|
2644
|
+
const classes = ['tn-tabs'];
|
|
2645
|
+
if (this.orientation() === 'vertical') {
|
|
2646
|
+
classes.push('tn-tabs--vertical');
|
|
2647
|
+
}
|
|
2648
|
+
else {
|
|
2649
|
+
classes.push('tn-tabs--horizontal');
|
|
2650
|
+
}
|
|
2651
|
+
// Add highlight position class
|
|
2652
|
+
classes.push(`tn-tabs--highlight-${this.highlightPosition()}`);
|
|
2653
|
+
return classes.join(' ');
|
|
2654
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : []));
|
|
2655
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2656
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnTabsComponent, isStandalone: true, selector: "tn-tabs", inputs: { selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, highlightPosition: { classPropertyName: "highlightPosition", publicName: "highlightPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndexChange: "selectedIndexChange", tabChange: "tabChange" }, queries: [{ propertyName: "tabs", predicate: TnTabComponent, isSignal: true }, { propertyName: "panels", predicate: TnTabPanelComponent, isSignal: true }], viewQueries: [{ propertyName: "tabHeader", first: true, predicate: ["tabHeader"], descendants: true, isSignal: true }], ngImport: i0, template: "<div role=\"tablist\" [ngClass]=\"classes()\" [attr.aria-orientation]=\"orientation()\">\n <div #tabHeader class=\"tn-tabs__header\">\n <ng-content select=\"tn-tab\" />\n @if (highlightBarVisible()) {\n <div class=\"tn-tabs__highlight-bar\"\n [style.left.px]=\"highlightBarLeft()\"\n [style.width.px]=\"highlightBarWidth()\"\n [style.top.px]=\"highlightBarTop()\"\n [style.height.px]=\"highlightBarHeight()\">\n </div>\n }\n </div>\n\n <div class=\"tn-tabs__content\">\n <ng-content select=\"tn-tab-panel\" />\n </div>\n</div>", styles: [".tn-tabs{display:flex;flex-direction:column;width:100%;height:100%;min-width:0;font-family:var(--tn-font-family-body, \"Inter\", sans-serif)}.tn-tabs--disabled{opacity:.6;pointer-events:none}.tn-tabs--vertical{flex-direction:row}.tn-tabs--vertical .tn-tabs__header{flex-direction:column;border-bottom:none;border-right:1px solid var(--tn-lines, #e0e0e0);min-width:240px;width:auto}.tn-tabs--vertical .tn-tabs__content{flex:1;min-width:0}.tn-tabs--vertical .tn-tabs__highlight-bar{bottom:auto;height:auto}.tn-tabs--vertical .tn-tab{justify-content:flex-start;text-align:left;width:100%}.tn-tabs--vertical .tn-tab:hover:not(.tn-tab--disabled){background-color:var(--tn-alt-bg1, #f5f5f5);color:var(--tn-fg1, #333)}.tn-tabs__header{display:flex;background-color:var(--tn-bg1, #fff);position:relative;border-bottom:1px solid var(--tn-lines, #e0e0e0)}.tn-tabs__highlight-bar{position:absolute;bottom:-1px;height:2px;background-color:var(--tn-primary, #0095d5);transition:left .3s ease,width .3s ease,top .3s ease,height .3s ease;z-index:1}.tn-tabs__content{flex:1;position:relative;background-color:var(--tn-bg1, #fff);min-height:0;width:100%;overflow:hidden}@media(max-width:768px){.tn-tabs__header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.tn-tabs__header::-webkit-scrollbar{display:none}.tn-tabs--vertical .tn-tabs__header{overflow-y:auto;overflow-x:visible;max-height:300px}}@media(prefers-reduced-motion:reduce){.tn-tabs__highlight-bar{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: A11yModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2657
|
+
}
|
|
2658
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTabsComponent, decorators: [{
|
|
2659
|
+
type: Component,
|
|
2660
|
+
args: [{ selector: 'tn-tabs', standalone: true, imports: [CommonModule, A11yModule, TnTabComponent, TnTabPanelComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div role=\"tablist\" [ngClass]=\"classes()\" [attr.aria-orientation]=\"orientation()\">\n <div #tabHeader class=\"tn-tabs__header\">\n <ng-content select=\"tn-tab\" />\n @if (highlightBarVisible()) {\n <div class=\"tn-tabs__highlight-bar\"\n [style.left.px]=\"highlightBarLeft()\"\n [style.width.px]=\"highlightBarWidth()\"\n [style.top.px]=\"highlightBarTop()\"\n [style.height.px]=\"highlightBarHeight()\">\n </div>\n }\n </div>\n\n <div class=\"tn-tabs__content\">\n <ng-content select=\"tn-tab-panel\" />\n </div>\n</div>", styles: [".tn-tabs{display:flex;flex-direction:column;width:100%;height:100%;min-width:0;font-family:var(--tn-font-family-body, \"Inter\", sans-serif)}.tn-tabs--disabled{opacity:.6;pointer-events:none}.tn-tabs--vertical{flex-direction:row}.tn-tabs--vertical .tn-tabs__header{flex-direction:column;border-bottom:none;border-right:1px solid var(--tn-lines, #e0e0e0);min-width:240px;width:auto}.tn-tabs--vertical .tn-tabs__content{flex:1;min-width:0}.tn-tabs--vertical .tn-tabs__highlight-bar{bottom:auto;height:auto}.tn-tabs--vertical .tn-tab{justify-content:flex-start;text-align:left;width:100%}.tn-tabs--vertical .tn-tab:hover:not(.tn-tab--disabled){background-color:var(--tn-alt-bg1, #f5f5f5);color:var(--tn-fg1, #333)}.tn-tabs__header{display:flex;background-color:var(--tn-bg1, #fff);position:relative;border-bottom:1px solid var(--tn-lines, #e0e0e0)}.tn-tabs__highlight-bar{position:absolute;bottom:-1px;height:2px;background-color:var(--tn-primary, #0095d5);transition:left .3s ease,width .3s ease,top .3s ease,height .3s ease;z-index:1}.tn-tabs__content{flex:1;position:relative;background-color:var(--tn-bg1, #fff);min-height:0;width:100%;overflow:hidden}@media(max-width:768px){.tn-tabs__header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.tn-tabs__header::-webkit-scrollbar{display:none}.tn-tabs--vertical .tn-tabs__header{overflow-y:auto;overflow-x:visible;max-height:300px}}@media(prefers-reduced-motion:reduce){.tn-tabs__highlight-bar{transition:none}}\n"] }]
|
|
2661
|
+
}], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnTabComponent), { isSignal: true }] }], panels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnTabPanelComponent), { isSignal: true }] }], tabHeader: [{ type: i0.ViewChild, args: ['tabHeader', { isSignal: true }] }], selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], highlightPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightPosition", required: false }] }], selectedIndexChange: [{ type: i0.Output, args: ["selectedIndexChange"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }] } });
|
|
2662
|
+
|
|
2663
|
+
/**
|
|
2664
|
+
* Harness for interacting with tn-tab in tests.
|
|
2665
|
+
* Provides methods for querying individual tab state and simulating selection.
|
|
2666
|
+
*
|
|
2667
|
+
* @example
|
|
2668
|
+
* ```typescript
|
|
2669
|
+
* // Find a tab by label
|
|
2670
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: 'Settings' }));
|
|
2671
|
+
*
|
|
2672
|
+
* // Check if active
|
|
2673
|
+
* expect(await tab.isSelected()).toBe(false);
|
|
2674
|
+
*
|
|
2675
|
+
* // Select it
|
|
2676
|
+
* await tab.select();
|
|
2677
|
+
* expect(await tab.isSelected()).toBe(true);
|
|
2678
|
+
* ```
|
|
2679
|
+
*/
|
|
2680
|
+
class TnTabHarness extends ComponentHarness {
|
|
2681
|
+
/**
|
|
2682
|
+
* The selector for the host element of a `TnTabComponent` instance.
|
|
2683
|
+
*/
|
|
2684
|
+
static hostSelector = 'tn-tab';
|
|
2685
|
+
_button = this.locatorFor('button[role="tab"]');
|
|
2686
|
+
/**
|
|
2687
|
+
* Gets a `HarnessPredicate` that can be used to search for a tab
|
|
2688
|
+
* with specific attributes.
|
|
2689
|
+
*
|
|
2690
|
+
* @param options Options for filtering which tab instances are considered a match.
|
|
2691
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
2692
|
+
*
|
|
2693
|
+
* @example
|
|
2694
|
+
* ```typescript
|
|
2695
|
+
* // Find tab by exact label
|
|
2696
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: 'Overview' }));
|
|
2697
|
+
*
|
|
2698
|
+
* // Find tab by regex
|
|
2699
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: /settings/i }));
|
|
2700
|
+
* ```
|
|
2701
|
+
*/
|
|
2702
|
+
static with(options = {}) {
|
|
2703
|
+
return new HarnessPredicate(TnTabHarness, options)
|
|
2704
|
+
.addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* Gets the tab's label text.
|
|
2708
|
+
*
|
|
2709
|
+
* @returns Promise resolving to the tab's label string.
|
|
2710
|
+
*
|
|
2711
|
+
* @example
|
|
2712
|
+
* ```typescript
|
|
2713
|
+
* const tab = await loader.getHarness(TnTabHarness);
|
|
2714
|
+
* expect(await tab.getLabel()).toBe('Overview');
|
|
2715
|
+
* ```
|
|
2716
|
+
*/
|
|
2717
|
+
async getLabel() {
|
|
2718
|
+
const labelEl = await this.locatorFor('.tn-tab__label')();
|
|
2719
|
+
return (await labelEl.text()).trim();
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Checks whether the tab is currently selected (active).
|
|
2723
|
+
*
|
|
2724
|
+
* @returns Promise resolving to true if the tab is selected.
|
|
2725
|
+
*
|
|
2726
|
+
* @example
|
|
2727
|
+
* ```typescript
|
|
2728
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: 'Overview' }));
|
|
2729
|
+
* expect(await tab.isSelected()).toBe(true);
|
|
2730
|
+
* ```
|
|
2731
|
+
*/
|
|
2732
|
+
async isSelected() {
|
|
2733
|
+
const button = await this._button();
|
|
2734
|
+
return (await button.getAttribute('aria-selected')) === 'true';
|
|
2735
|
+
}
|
|
2736
|
+
/**
|
|
2737
|
+
* Checks whether the tab is disabled.
|
|
2738
|
+
*
|
|
2739
|
+
* @returns Promise resolving to true if the tab is disabled.
|
|
2740
|
+
*
|
|
2741
|
+
* @example
|
|
2742
|
+
* ```typescript
|
|
2743
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: 'Disabled Tab' }));
|
|
2744
|
+
* expect(await tab.isDisabled()).toBe(true);
|
|
2745
|
+
* ```
|
|
2746
|
+
*/
|
|
2747
|
+
async isDisabled() {
|
|
2748
|
+
const button = await this._button();
|
|
2749
|
+
return (await button.getProperty('disabled')) ?? false;
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Selects the tab by clicking it.
|
|
2753
|
+
*
|
|
2754
|
+
* @returns Promise that resolves when the tab has been clicked.
|
|
2755
|
+
*
|
|
2756
|
+
* @example
|
|
2757
|
+
* ```typescript
|
|
2758
|
+
* const tab = await loader.getHarness(TnTabHarness.with({ label: 'Settings' }));
|
|
2759
|
+
* await tab.select();
|
|
2760
|
+
* ```
|
|
2761
|
+
*/
|
|
2762
|
+
async select() {
|
|
2763
|
+
const button = await this._button();
|
|
2764
|
+
await button.click();
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Gets the tab's testId attribute value.
|
|
2768
|
+
*
|
|
2769
|
+
* @returns Promise resolving to the data-testid value, or null if not set.
|
|
2770
|
+
*
|
|
2771
|
+
* @example
|
|
2772
|
+
* ```typescript
|
|
2773
|
+
* const tab = await loader.getHarness(TnTabHarness);
|
|
2774
|
+
* expect(await tab.getTestId()).toBe('my-tab');
|
|
2775
|
+
* ```
|
|
2776
|
+
*/
|
|
2777
|
+
async getTestId() {
|
|
2778
|
+
const button = await this._button();
|
|
2779
|
+
return button.getAttribute('data-testid');
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
/**
|
|
2784
|
+
* Harness for interacting with tn-tab-panel in tests.
|
|
2785
|
+
* Provides methods for querying panel visibility and content.
|
|
2786
|
+
*
|
|
2787
|
+
* @example
|
|
2788
|
+
* ```typescript
|
|
2789
|
+
* // Get all panels
|
|
2790
|
+
* const panels = await loader.getAllHarnesses(TnTabPanelHarness);
|
|
2791
|
+
*
|
|
2792
|
+
* // Check which panel is active
|
|
2793
|
+
* for (const panel of panels) {
|
|
2794
|
+
* if (await panel.isActive()) {
|
|
2795
|
+
* const text = await panel.getTextContent();
|
|
2796
|
+
* expect(text).toContain('Overview');
|
|
2797
|
+
* }
|
|
2798
|
+
* }
|
|
2799
|
+
* ```
|
|
2800
|
+
*/
|
|
2801
|
+
class TnTabPanelHarness extends ComponentHarness {
|
|
2802
|
+
/**
|
|
2803
|
+
* The selector for the host element of a `TnTabPanelComponent` instance.
|
|
2804
|
+
*/
|
|
2805
|
+
static hostSelector = 'tn-tab-panel';
|
|
2806
|
+
_panel = this.locatorFor('[role="tabpanel"]');
|
|
2807
|
+
/**
|
|
2808
|
+
* Gets a `HarnessPredicate` that can be used to search for a tab panel
|
|
2809
|
+
* with specific attributes.
|
|
2810
|
+
*
|
|
2811
|
+
* @param options Options for filtering which panel instances are considered a match.
|
|
2812
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
2813
|
+
*
|
|
2814
|
+
* @example
|
|
2815
|
+
* ```typescript
|
|
2816
|
+
* const panel = await loader.getHarness(
|
|
2817
|
+
* TnTabPanelHarness.with({ textContains: 'Overview' })
|
|
2818
|
+
* );
|
|
2819
|
+
* ```
|
|
2820
|
+
*/
|
|
2821
|
+
static with(options = {}) {
|
|
2822
|
+
return new HarnessPredicate(TnTabPanelHarness, options)
|
|
2823
|
+
.addOption('textContains', options.textContains, (harness, text) => HarnessPredicate.stringMatches(harness.getTextContent(), text));
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Checks whether the panel is currently active (visible).
|
|
2827
|
+
*
|
|
2828
|
+
* @returns Promise resolving to true if the panel is active.
|
|
2829
|
+
*
|
|
2830
|
+
* @example
|
|
2831
|
+
* ```typescript
|
|
2832
|
+
* const panel = await loader.getHarness(TnTabPanelHarness);
|
|
2833
|
+
* expect(await panel.isActive()).toBe(true);
|
|
2834
|
+
* ```
|
|
2835
|
+
*/
|
|
2836
|
+
async isActive() {
|
|
2837
|
+
const panel = await this._panel();
|
|
2838
|
+
return (await panel.getAttribute('aria-hidden')) === 'false';
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* Gets the text content of the panel.
|
|
2842
|
+
*
|
|
2843
|
+
* @returns Promise resolving to the panel's text content.
|
|
2844
|
+
*
|
|
2845
|
+
* @example
|
|
2846
|
+
* ```typescript
|
|
2847
|
+
* const panel = await loader.getHarness(TnTabPanelHarness);
|
|
2848
|
+
* expect(await panel.getTextContent()).toContain('Overview');
|
|
2849
|
+
* ```
|
|
2850
|
+
*/
|
|
2851
|
+
async getTextContent() {
|
|
2852
|
+
const panel = await this._panel();
|
|
2853
|
+
return (await panel.text()).trim();
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Gets the panel's testId attribute value.
|
|
2857
|
+
*
|
|
2858
|
+
* @returns Promise resolving to the data-testid value, or null if not set.
|
|
2859
|
+
*
|
|
2860
|
+
* @example
|
|
2861
|
+
* ```typescript
|
|
2862
|
+
* const panel = await loader.getHarness(TnTabPanelHarness);
|
|
2863
|
+
* expect(await panel.getTestId()).toBe('my-panel');
|
|
2864
|
+
* ```
|
|
2865
|
+
*/
|
|
2866
|
+
async getTestId() {
|
|
2867
|
+
const panel = await this._panel();
|
|
2868
|
+
return panel.getAttribute('data-testid');
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
/**
|
|
2873
|
+
* Harness for interacting with tn-tabs in tests.
|
|
2874
|
+
* Provides methods for querying tab state, selecting tabs, and inspecting panels.
|
|
2875
|
+
*
|
|
2876
|
+
* @example
|
|
2877
|
+
* ```typescript
|
|
2878
|
+
* // Get the tabs harness
|
|
2879
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
2880
|
+
*
|
|
2881
|
+
* // Select a tab by label
|
|
2882
|
+
* await tabs.selectTab({ label: 'Settings' });
|
|
2883
|
+
*
|
|
2884
|
+
* // Get the selected tab label
|
|
2885
|
+
* const selected = await tabs.getSelectedTab();
|
|
2886
|
+
* expect(await selected.getLabel()).toBe('Settings');
|
|
2887
|
+
*
|
|
2888
|
+
* // Get all tab labels
|
|
2889
|
+
* const labels = await tabs.getTabLabels();
|
|
2890
|
+
* expect(labels).toEqual(['Overview', 'Details', 'Settings']);
|
|
2891
|
+
* ```
|
|
2892
|
+
*/
|
|
2893
|
+
class TnTabsHarness extends ComponentHarness {
|
|
2894
|
+
/**
|
|
2895
|
+
* The selector for the host element of a `TnTabsComponent` instance.
|
|
2896
|
+
*/
|
|
2897
|
+
static hostSelector = 'tn-tabs';
|
|
2898
|
+
/**
|
|
2899
|
+
* Gets a `HarnessPredicate` that can be used to search for tabs
|
|
2900
|
+
* with specific attributes.
|
|
2901
|
+
*
|
|
2902
|
+
* @param options Options for filtering which tabs instances are considered a match.
|
|
2903
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
2904
|
+
*
|
|
2905
|
+
* @example
|
|
2906
|
+
* ```typescript
|
|
2907
|
+
* // Find by orientation
|
|
2908
|
+
* const tabs = await loader.getHarness(TnTabsHarness.with({ orientation: 'vertical' }));
|
|
2909
|
+
*
|
|
2910
|
+
* // Find the tab group containing a "Settings" tab
|
|
2911
|
+
* const tabs = await loader.getHarness(TnTabsHarness.with({ hasTab: 'Settings' }));
|
|
2912
|
+
*
|
|
2913
|
+
* // Find by regex
|
|
2914
|
+
* const tabs = await loader.getHarness(TnTabsHarness.with({ hasTab: /settings/i }));
|
|
2915
|
+
* ```
|
|
2916
|
+
*/
|
|
2917
|
+
static with(options = {}) {
|
|
2918
|
+
return new HarnessPredicate(TnTabsHarness, options)
|
|
2919
|
+
.addOption('orientation', options.orientation, async (harness, orientation) => {
|
|
2920
|
+
return (await harness.getOrientation()) === orientation;
|
|
2921
|
+
})
|
|
2922
|
+
.addOption('hasTab', options.hasTab, async (harness, label) => {
|
|
2923
|
+
const labels = await harness.getTabLabels();
|
|
2924
|
+
if (label instanceof RegExp) {
|
|
2925
|
+
return labels.some(l => label.test(l));
|
|
2926
|
+
}
|
|
2927
|
+
return labels.includes(label);
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* Gets all tab harnesses within this tab group.
|
|
2932
|
+
*
|
|
2933
|
+
* @returns Promise resolving to an array of `TnTabHarness` instances.
|
|
2934
|
+
*
|
|
2935
|
+
* @example
|
|
2936
|
+
* ```typescript
|
|
2937
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
2938
|
+
* const allTabs = await tabs.getTabs();
|
|
2939
|
+
* expect(allTabs.length).toBe(3);
|
|
2940
|
+
* ```
|
|
2941
|
+
*/
|
|
2942
|
+
async getTabs() {
|
|
2943
|
+
return this.locatorForAll(TnTabHarness)();
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Gets a specific tab by filter criteria.
|
|
2947
|
+
*
|
|
2948
|
+
* @param filter Optional filter to match a specific tab.
|
|
2949
|
+
* @returns Promise resolving to the matching `TnTabHarness`.
|
|
2950
|
+
*
|
|
2951
|
+
* @example
|
|
2952
|
+
* ```typescript
|
|
2953
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
2954
|
+
* const settingsTab = await tabs.getTab({ label: 'Settings' });
|
|
2955
|
+
* ```
|
|
2956
|
+
*/
|
|
2957
|
+
async getTab(filter = {}) {
|
|
2958
|
+
return this.locatorFor(TnTabHarness.with(filter))();
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Gets all panel harnesses within this tab group.
|
|
2962
|
+
*
|
|
2963
|
+
* @returns Promise resolving to an array of `TnTabPanelHarness` instances.
|
|
2964
|
+
*
|
|
2965
|
+
* @example
|
|
2966
|
+
* ```typescript
|
|
2967
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
2968
|
+
* const panels = await tabs.getPanels();
|
|
2969
|
+
* expect(panels.length).toBe(3);
|
|
2970
|
+
* ```
|
|
2971
|
+
*/
|
|
2972
|
+
async getPanels() {
|
|
2973
|
+
return this.locatorForAll(TnTabPanelHarness)();
|
|
2974
|
+
}
|
|
2975
|
+
/**
|
|
2976
|
+
* Gets all tab labels as strings.
|
|
2977
|
+
*
|
|
2978
|
+
* @returns Promise resolving to an array of tab label strings.
|
|
2979
|
+
*
|
|
2980
|
+
* @example
|
|
2981
|
+
* ```typescript
|
|
2982
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
2983
|
+
* const labels = await tabs.getTabLabels();
|
|
2984
|
+
* expect(labels).toEqual(['Overview', 'Details', 'Settings']);
|
|
2985
|
+
* ```
|
|
2986
|
+
*/
|
|
2987
|
+
async getTabLabels() {
|
|
2988
|
+
const tabs = await this.getTabs();
|
|
2989
|
+
return Promise.all(tabs.map(tab => tab.getLabel()));
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* Gets the currently selected (active) tab.
|
|
2993
|
+
*
|
|
2994
|
+
* @returns Promise resolving to the active `TnTabHarness`.
|
|
2995
|
+
* @throws If no active tab is found.
|
|
2996
|
+
*
|
|
2997
|
+
* @example
|
|
2998
|
+
* ```typescript
|
|
2999
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
3000
|
+
* const selected = await tabs.getSelectedTab();
|
|
3001
|
+
* expect(await selected.getLabel()).toBe('Overview');
|
|
3002
|
+
* ```
|
|
3003
|
+
*/
|
|
3004
|
+
async getSelectedTab() {
|
|
3005
|
+
const tabs = await this.getTabs();
|
|
3006
|
+
for (const tab of tabs) {
|
|
3007
|
+
if (await tab.isSelected()) {
|
|
3008
|
+
return tab;
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
throw new Error('No selected tab found');
|
|
3012
|
+
}
|
|
3013
|
+
/**
|
|
3014
|
+
* Selects a tab by filter criteria. Clicks the matching tab button.
|
|
3015
|
+
*
|
|
3016
|
+
* @param filter Filter to identify which tab to select.
|
|
3017
|
+
* @returns Promise that resolves when the tab has been selected.
|
|
3018
|
+
*
|
|
3019
|
+
* @example
|
|
3020
|
+
* ```typescript
|
|
3021
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
3022
|
+
*
|
|
3023
|
+
* // Select by label
|
|
3024
|
+
* await tabs.selectTab({ label: 'Settings' });
|
|
3025
|
+
*
|
|
3026
|
+
* // Select by regex
|
|
3027
|
+
* await tabs.selectTab({ label: /detail/i });
|
|
3028
|
+
* ```
|
|
3029
|
+
*/
|
|
3030
|
+
async selectTab(filter) {
|
|
3031
|
+
const tab = await this.getTab(filter);
|
|
3032
|
+
await tab.select();
|
|
3033
|
+
}
|
|
3034
|
+
/**
|
|
3035
|
+
* Gets the orientation of the tab group.
|
|
3036
|
+
*
|
|
3037
|
+
* @returns Promise resolving to 'horizontal' or 'vertical'.
|
|
3038
|
+
*
|
|
3039
|
+
* @example
|
|
3040
|
+
* ```typescript
|
|
3041
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
3042
|
+
* expect(await tabs.getOrientation()).toBe('horizontal');
|
|
3043
|
+
* ```
|
|
3044
|
+
*/
|
|
3045
|
+
async getOrientation() {
|
|
3046
|
+
const tablist = await this.locatorFor('[role="tablist"]')();
|
|
3047
|
+
return (await tablist.getAttribute('aria-orientation')) ?? 'horizontal';
|
|
2232
3048
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
3049
|
+
/**
|
|
3050
|
+
* Gets the number of tabs.
|
|
3051
|
+
*
|
|
3052
|
+
* @returns Promise resolving to the tab count.
|
|
3053
|
+
*
|
|
3054
|
+
* @example
|
|
3055
|
+
* ```typescript
|
|
3056
|
+
* const tabs = await loader.getHarness(TnTabsHarness);
|
|
3057
|
+
* expect(await tabs.getTabCount()).toBe(3);
|
|
3058
|
+
* ```
|
|
3059
|
+
*/
|
|
3060
|
+
async getTabCount() {
|
|
3061
|
+
const tabs = await this.getTabs();
|
|
3062
|
+
return tabs.length;
|
|
2238
3063
|
}
|
|
2239
|
-
classes = computed(() => {
|
|
2240
|
-
const classes = ['tn-tabs'];
|
|
2241
|
-
if (this.orientation() === 'vertical') {
|
|
2242
|
-
classes.push('tn-tabs--vertical');
|
|
2243
|
-
}
|
|
2244
|
-
else {
|
|
2245
|
-
classes.push('tn-tabs--horizontal');
|
|
2246
|
-
}
|
|
2247
|
-
// Add highlight position class
|
|
2248
|
-
classes.push(`tn-tabs--highlight-${this.highlightPosition()}`);
|
|
2249
|
-
return classes.join(' ');
|
|
2250
|
-
}, ...(ngDevMode ? [{ debugName: "classes" }] : []));
|
|
2251
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2252
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnTabsComponent, isStandalone: true, selector: "tn-tabs", inputs: { selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, highlightPosition: { classPropertyName: "highlightPosition", publicName: "highlightPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndexChange: "selectedIndexChange", tabChange: "tabChange" }, queries: [{ propertyName: "tabs", predicate: TnTabComponent, isSignal: true }, { propertyName: "panels", predicate: TnTabPanelComponent, isSignal: true }], viewQueries: [{ propertyName: "tabHeader", first: true, predicate: ["tabHeader"], descendants: true, isSignal: true }], ngImport: i0, template: "<div role=\"tablist\" [ngClass]=\"classes()\" [attr.aria-orientation]=\"orientation()\">\n <div #tabHeader class=\"tn-tabs__header\">\n <ng-content select=\"tn-tab\" />\n @if (highlightBarVisible()) {\n <div class=\"tn-tabs__highlight-bar\"\n [style.left.px]=\"highlightBarLeft()\"\n [style.width.px]=\"highlightBarWidth()\"\n [style.top.px]=\"highlightBarTop()\"\n [style.height.px]=\"highlightBarHeight()\">\n </div>\n }\n </div>\n\n <div class=\"tn-tabs__content\">\n <ng-content select=\"tn-tab-panel\" />\n </div>\n</div>", styles: [".tn-tabs{display:flex;flex-direction:column;width:100%;height:100%;min-width:0;font-family:var(--tn-font-family-body, \"Inter\", sans-serif)}.tn-tabs--disabled{opacity:.6;pointer-events:none}.tn-tabs--vertical{flex-direction:row}.tn-tabs--vertical .tn-tabs__header{flex-direction:column;border-bottom:none;border-right:1px solid var(--tn-lines, #e0e0e0);min-width:240px;width:auto}.tn-tabs--vertical .tn-tabs__content{flex:1;min-width:0}.tn-tabs--vertical .tn-tabs__highlight-bar{bottom:auto;height:auto}.tn-tabs--vertical .tn-tab{justify-content:flex-start;text-align:left;width:100%}.tn-tabs--vertical .tn-tab:hover:not(.tn-tab--disabled){background-color:var(--tn-alt-bg1, #f5f5f5);color:var(--tn-fg1, #333)}.tn-tabs__header{display:flex;background-color:var(--tn-bg1, #fff);position:relative;border-bottom:1px solid var(--tn-lines, #e0e0e0)}.tn-tabs__highlight-bar{position:absolute;bottom:-1px;height:2px;background-color:var(--tn-primary, #0095d5);transition:left .3s ease,width .3s ease,top .3s ease,height .3s ease;z-index:1}.tn-tabs__content{flex:1;position:relative;background-color:var(--tn-bg1, #fff);min-height:0;width:100%;overflow:hidden}@media(max-width:768px){.tn-tabs__header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.tn-tabs__header::-webkit-scrollbar{display:none}.tn-tabs--vertical .tn-tabs__header{overflow-y:auto;overflow-x:visible;max-height:300px}}@media(prefers-reduced-motion:reduce){.tn-tabs__highlight-bar{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: A11yModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2253
3064
|
}
|
|
2254
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTabsComponent, decorators: [{
|
|
2255
|
-
type: Component,
|
|
2256
|
-
args: [{ selector: 'tn-tabs', standalone: true, imports: [CommonModule, A11yModule, TnTabComponent, TnTabPanelComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div role=\"tablist\" [ngClass]=\"classes()\" [attr.aria-orientation]=\"orientation()\">\n <div #tabHeader class=\"tn-tabs__header\">\n <ng-content select=\"tn-tab\" />\n @if (highlightBarVisible()) {\n <div class=\"tn-tabs__highlight-bar\"\n [style.left.px]=\"highlightBarLeft()\"\n [style.width.px]=\"highlightBarWidth()\"\n [style.top.px]=\"highlightBarTop()\"\n [style.height.px]=\"highlightBarHeight()\">\n </div>\n }\n </div>\n\n <div class=\"tn-tabs__content\">\n <ng-content select=\"tn-tab-panel\" />\n </div>\n</div>", styles: [".tn-tabs{display:flex;flex-direction:column;width:100%;height:100%;min-width:0;font-family:var(--tn-font-family-body, \"Inter\", sans-serif)}.tn-tabs--disabled{opacity:.6;pointer-events:none}.tn-tabs--vertical{flex-direction:row}.tn-tabs--vertical .tn-tabs__header{flex-direction:column;border-bottom:none;border-right:1px solid var(--tn-lines, #e0e0e0);min-width:240px;width:auto}.tn-tabs--vertical .tn-tabs__content{flex:1;min-width:0}.tn-tabs--vertical .tn-tabs__highlight-bar{bottom:auto;height:auto}.tn-tabs--vertical .tn-tab{justify-content:flex-start;text-align:left;width:100%}.tn-tabs--vertical .tn-tab:hover:not(.tn-tab--disabled){background-color:var(--tn-alt-bg1, #f5f5f5);color:var(--tn-fg1, #333)}.tn-tabs__header{display:flex;background-color:var(--tn-bg1, #fff);position:relative;border-bottom:1px solid var(--tn-lines, #e0e0e0)}.tn-tabs__highlight-bar{position:absolute;bottom:-1px;height:2px;background-color:var(--tn-primary, #0095d5);transition:left .3s ease,width .3s ease,top .3s ease,height .3s ease;z-index:1}.tn-tabs__content{flex:1;position:relative;background-color:var(--tn-bg1, #fff);min-height:0;width:100%;overflow:hidden}@media(max-width:768px){.tn-tabs__header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.tn-tabs__header::-webkit-scrollbar{display:none}.tn-tabs--vertical .tn-tabs__header{overflow-y:auto;overflow-x:visible;max-height:300px}}@media(prefers-reduced-motion:reduce){.tn-tabs__highlight-bar{transition:none}}\n"] }]
|
|
2257
|
-
}], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnTabComponent), { isSignal: true }] }], panels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnTabPanelComponent), { isSignal: true }] }], tabHeader: [{ type: i0.ViewChild, args: ['tabHeader', { isSignal: true }] }], selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], highlightPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightPosition", required: false }] }], selectedIndexChange: [{ type: i0.Output, args: ["selectedIndexChange"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }] } });
|
|
2258
3065
|
|
|
2259
3066
|
class TnKeyboardShortcutComponent {
|
|
2260
3067
|
shortcut = input('', ...(ngDevMode ? [{ debugName: "shortcut" }] : []));
|
|
@@ -2762,215 +3569,6 @@ class TnSelectHarness extends ComponentHarness {
|
|
|
2762
3569
|
}
|
|
2763
3570
|
}
|
|
2764
3571
|
|
|
2765
|
-
/**
|
|
2766
|
-
* Harness for interacting with tn-icon in tests.
|
|
2767
|
-
* Provides filtering by icon name and library for existence checks, as well as click interaction.
|
|
2768
|
-
*
|
|
2769
|
-
* @example
|
|
2770
|
-
* ```typescript
|
|
2771
|
-
* // Check for existence
|
|
2772
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2773
|
-
*
|
|
2774
|
-
* // Find icon by name
|
|
2775
|
-
* const folderIcon = await loader.getHarness(
|
|
2776
|
-
* TnIconHarness.with({ name: 'folder' })
|
|
2777
|
-
* );
|
|
2778
|
-
*
|
|
2779
|
-
* // Find icon by name and library
|
|
2780
|
-
* const mdiIcon = await loader.getHarness(
|
|
2781
|
-
* TnIconHarness.with({ name: 'account-circle', library: 'mdi' })
|
|
2782
|
-
* );
|
|
2783
|
-
*
|
|
2784
|
-
* // Check if icon exists
|
|
2785
|
-
* const hasIcon = await loader.hasHarness(
|
|
2786
|
-
* TnIconHarness.with({ name: 'check' })
|
|
2787
|
-
* );
|
|
2788
|
-
*
|
|
2789
|
-
* // Click an icon
|
|
2790
|
-
* const closeIcon = await loader.getHarness(
|
|
2791
|
-
* TnIconHarness.with({ name: 'close' })
|
|
2792
|
-
* );
|
|
2793
|
-
* await closeIcon.click();
|
|
2794
|
-
* ```
|
|
2795
|
-
*/
|
|
2796
|
-
class TnIconHarness extends ComponentHarness {
|
|
2797
|
-
/**
|
|
2798
|
-
* The selector for the host element of a `TnIconComponent` instance.
|
|
2799
|
-
*/
|
|
2800
|
-
static hostSelector = 'tn-icon';
|
|
2801
|
-
/**
|
|
2802
|
-
* Gets a `HarnessPredicate` that can be used to search for an icon
|
|
2803
|
-
* with specific attributes.
|
|
2804
|
-
*
|
|
2805
|
-
* @param options Options for filtering which icon instances are considered a match.
|
|
2806
|
-
* @returns A `HarnessPredicate` configured with the given options.
|
|
2807
|
-
*
|
|
2808
|
-
* @example
|
|
2809
|
-
* ```typescript
|
|
2810
|
-
* // Find icon by name
|
|
2811
|
-
* const icon = await loader.getHarness(
|
|
2812
|
-
* TnIconHarness.with({ name: 'home' })
|
|
2813
|
-
* );
|
|
2814
|
-
*
|
|
2815
|
-
* // Find icon by library
|
|
2816
|
-
* const customIcon = await loader.getHarness(
|
|
2817
|
-
* TnIconHarness.with({ library: 'custom' })
|
|
2818
|
-
* );
|
|
2819
|
-
*
|
|
2820
|
-
* // Find icon with specific size
|
|
2821
|
-
* const largeIcon = await loader.getHarness(
|
|
2822
|
-
* TnIconHarness.with({ size: 'lg' })
|
|
2823
|
-
* );
|
|
2824
|
-
* ```
|
|
2825
|
-
*/
|
|
2826
|
-
static with(options = {}) {
|
|
2827
|
-
return new HarnessPredicate(TnIconHarness, options)
|
|
2828
|
-
.addOption('name', options.name, async (harness, name) => {
|
|
2829
|
-
return (await harness.getName()) === name;
|
|
2830
|
-
})
|
|
2831
|
-
.addOption('library', options.library, async (harness, library) => {
|
|
2832
|
-
return (await harness.getLibrary()) === library;
|
|
2833
|
-
})
|
|
2834
|
-
.addOption('size', options.size, async (harness, size) => {
|
|
2835
|
-
return (await harness.getSize()) === size;
|
|
2836
|
-
})
|
|
2837
|
-
.addOption('fullSize', options.fullSize, async (harness, fullSize) => {
|
|
2838
|
-
return (await harness.isFullSize()) === fullSize;
|
|
2839
|
-
})
|
|
2840
|
-
.addOption('customSize', options.customSize, async (harness, customSize) => {
|
|
2841
|
-
return (await harness.getCustomSize()) === customSize;
|
|
2842
|
-
});
|
|
2843
|
-
}
|
|
2844
|
-
/**
|
|
2845
|
-
* Gets the icon name.
|
|
2846
|
-
*
|
|
2847
|
-
* @returns Promise resolving to the icon name.
|
|
2848
|
-
*
|
|
2849
|
-
* @example
|
|
2850
|
-
* ```typescript
|
|
2851
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2852
|
-
* const name = await icon.getName();
|
|
2853
|
-
* expect(name).toBe('folder');
|
|
2854
|
-
* ```
|
|
2855
|
-
*/
|
|
2856
|
-
async getName() {
|
|
2857
|
-
const host = await this.host();
|
|
2858
|
-
return host.getAttribute('name');
|
|
2859
|
-
}
|
|
2860
|
-
/**
|
|
2861
|
-
* Gets the icon library.
|
|
2862
|
-
*
|
|
2863
|
-
* @returns Promise resolving to the icon library.
|
|
2864
|
-
*
|
|
2865
|
-
* @example
|
|
2866
|
-
* ```typescript
|
|
2867
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2868
|
-
* const library = await icon.getLibrary();
|
|
2869
|
-
* expect(library).toBe('mdi');
|
|
2870
|
-
* ```
|
|
2871
|
-
*/
|
|
2872
|
-
async getLibrary() {
|
|
2873
|
-
const host = await this.host();
|
|
2874
|
-
return host.getAttribute('library');
|
|
2875
|
-
}
|
|
2876
|
-
/**
|
|
2877
|
-
* Gets the icon size.
|
|
2878
|
-
*
|
|
2879
|
-
* @returns Promise resolving to the icon size.
|
|
2880
|
-
*
|
|
2881
|
-
* @example
|
|
2882
|
-
* ```typescript
|
|
2883
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2884
|
-
* const size = await icon.getSize();
|
|
2885
|
-
* expect(size).toBe('lg');
|
|
2886
|
-
* ```
|
|
2887
|
-
*/
|
|
2888
|
-
async getSize() {
|
|
2889
|
-
const host = await this.host();
|
|
2890
|
-
return host.getAttribute('size');
|
|
2891
|
-
}
|
|
2892
|
-
/**
|
|
2893
|
-
* Gets the icon color.
|
|
2894
|
-
*
|
|
2895
|
-
* @returns Promise resolving to the icon color.
|
|
2896
|
-
*
|
|
2897
|
-
* @example
|
|
2898
|
-
* ```typescript
|
|
2899
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2900
|
-
* const color = await icon.getColor();
|
|
2901
|
-
* expect(color).toBe('primary');
|
|
2902
|
-
* ```
|
|
2903
|
-
*/
|
|
2904
|
-
async getColor() {
|
|
2905
|
-
const host = await this.host();
|
|
2906
|
-
return host.getAttribute('color');
|
|
2907
|
-
}
|
|
2908
|
-
/**
|
|
2909
|
-
* Checks if the icon is in full-size mode.
|
|
2910
|
-
*
|
|
2911
|
-
* @returns Promise resolving to true if the icon is full-size, false otherwise.
|
|
2912
|
-
*
|
|
2913
|
-
* @example
|
|
2914
|
-
* ```typescript
|
|
2915
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2916
|
-
* const isFullSize = await icon.isFullSize();
|
|
2917
|
-
* expect(isFullSize).toBe(true);
|
|
2918
|
-
* ```
|
|
2919
|
-
*/
|
|
2920
|
-
async isFullSize() {
|
|
2921
|
-
const host = await this.host();
|
|
2922
|
-
const fullSizeAttr = await host.getAttribute('full-size');
|
|
2923
|
-
return fullSizeAttr === 'true';
|
|
2924
|
-
}
|
|
2925
|
-
/**
|
|
2926
|
-
* Gets the icon custom size.
|
|
2927
|
-
*
|
|
2928
|
-
* @returns Promise resolving to the custom size value, or null if not set.
|
|
2929
|
-
*
|
|
2930
|
-
* @example
|
|
2931
|
-
* ```typescript
|
|
2932
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2933
|
-
* const customSize = await icon.getCustomSize();
|
|
2934
|
-
* expect(customSize).toBe('64px');
|
|
2935
|
-
* ```
|
|
2936
|
-
*/
|
|
2937
|
-
async getCustomSize() {
|
|
2938
|
-
const host = await this.host();
|
|
2939
|
-
return host.getAttribute('custom-size');
|
|
2940
|
-
}
|
|
2941
|
-
/**
|
|
2942
|
-
* Checks if the icon is using a custom size.
|
|
2943
|
-
*
|
|
2944
|
-
* @returns Promise resolving to true if a custom size is set, false otherwise.
|
|
2945
|
-
*
|
|
2946
|
-
* @example
|
|
2947
|
-
* ```typescript
|
|
2948
|
-
* const icon = await loader.getHarness(TnIconHarness);
|
|
2949
|
-
* const hasCustomSize = await icon.hasCustomSize();
|
|
2950
|
-
* expect(hasCustomSize).toBe(true);
|
|
2951
|
-
* ```
|
|
2952
|
-
*/
|
|
2953
|
-
async hasCustomSize() {
|
|
2954
|
-
const customSize = await this.getCustomSize();
|
|
2955
|
-
return customSize !== null;
|
|
2956
|
-
}
|
|
2957
|
-
/**
|
|
2958
|
-
* Clicks the icon.
|
|
2959
|
-
*
|
|
2960
|
-
* @returns Promise that resolves when the click action is complete.
|
|
2961
|
-
*
|
|
2962
|
-
* @example
|
|
2963
|
-
* ```typescript
|
|
2964
|
-
* const icon = await loader.getHarness(TnIconHarness.with({ name: 'close' }));
|
|
2965
|
-
* await icon.click();
|
|
2966
|
-
* ```
|
|
2967
|
-
*/
|
|
2968
|
-
async click() {
|
|
2969
|
-
const host = await this.host();
|
|
2970
|
-
return host.click();
|
|
2971
|
-
}
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
3572
|
/**
|
|
2975
3573
|
* Marks an icon name for inclusion in the TrueNAS UI sprite generation.
|
|
2976
3574
|
*
|
|
@@ -8711,5 +9309,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8711
9309
|
* Generated bundle index. Do not edit.
|
|
8712
9310
|
*/
|
|
8713
9311
|
|
|
8714
|
-
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, TnDialogShellComponent, TnDividerComponent, TnDividerDirective, TnExpansionPanelComponent, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuComponent, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSlideToggleComponent, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabPanelComponent, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTheme, TnThemeService, TnTimeInputComponent, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
9312
|
+
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, TnDialogShellComponent, TnDividerComponent, TnDividerDirective, 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, TnMenuComponent, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, 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 };
|
|
8715
9313
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|