@truenas/ui-components 0.1.47 → 0.1.49

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truenas/ui-components",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public"
@@ -1,77 +1,182 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
1
3
  import path from 'path';
2
- import { findForwardingComponentMappings } from './find-icons-in-forwarding-components';
4
+ import { discoverForwardingMappings } from './find-icons-in-forwarding-components';
5
+ import type { ForwardingComponentMapping } from './find-icons-in-forwarding-components';
3
6
 
4
- const FIXTURES_DIR = path.resolve(__dirname, '../__fixtures__/forwarding-components');
7
+ function writeManifest(dir: string, mappings: ForwardingComponentMapping[]): void {
8
+ fs.mkdirSync(dir, { recursive: true });
9
+ fs.writeFileSync(
10
+ path.join(dir, 'forwarding-mappings.json'),
11
+ JSON.stringify(mappings),
12
+ );
13
+ }
5
14
 
6
- describe('findForwardingComponentMappings', () => {
7
- it('should discover components implementing TnIconForwardingComponent', () => {
8
- const mappings = findForwardingComponentMappings([FIXTURES_DIR]);
9
- const selectors = mappings.map(m => m.selector).sort();
10
- expect(selectors).toEqual(['tn-chip', 'tn-empty', 'tn-input']);
15
+ describe('discoverForwardingMappings', () => {
16
+ let tempDir: string;
17
+
18
+ beforeEach(() => {
19
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fwd-mappings-'));
11
20
  });
12
21
 
13
- it('should not discover components that do not implement the interface', () => {
14
- const mappings = findForwardingComponentMappings([FIXTURES_DIR]);
15
- const selectors = mappings.map(m => m.selector);
16
- expect(selectors).not.toContain('tn-button');
22
+ afterEach(() => {
23
+ fs.rmSync(tempDir, { recursive: true, force: true });
17
24
  });
18
25
 
19
- it('should extract single icon slot with library from tn-empty', () => {
20
- const mappings = findForwardingComponentMappings([FIXTURES_DIR]);
21
- const empty = mappings.find(m => m.selector === 'tn-empty');
26
+ describe('library manifest loading', () => {
27
+ it('should load mappings from the installed library manifest', () => {
28
+ const manifestDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
29
+ writeManifest(manifestDir, [
30
+ {
31
+ selector: 'tn-empty',
32
+ iconSlots: [{ iconAttribute: 'icon', libraryAttribute: 'iconLibrary', defaultLibrary: 'mdi' }],
33
+ },
34
+ {
35
+ selector: 'tn-chip',
36
+ iconSlots: [{ iconAttribute: 'icon' }],
37
+ },
38
+ ]);
39
+
40
+ const mappings = discoverForwardingMappings([], tempDir);
41
+ expect(mappings).toHaveLength(2);
22
42
 
23
- expect(empty).toBeDefined();
24
- expect(empty!.iconSlots).toHaveLength(1);
25
- expect(empty!.iconSlots[0]).toEqual({
26
- iconAttribute: 'icon',
27
- libraryAttribute: 'iconLibrary',
28
- defaultLibrary: 'mdi',
43
+ const selectors = mappings.map(m => m.selector).sort();
44
+ expect(selectors).toEqual(['tn-chip', 'tn-empty']);
29
45
  });
30
- });
31
46
 
32
- it('should extract multiple icon slots from tn-input', () => {
33
- const mappings = findForwardingComponentMappings([FIXTURES_DIR]);
34
- const input = mappings.find(m => m.selector === 'tn-input');
47
+ it('should extract icon slot details from manifest', () => {
48
+ const manifestDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
49
+ writeManifest(manifestDir, [
50
+ {
51
+ selector: 'tn-empty',
52
+ iconSlots: [{ iconAttribute: 'icon', libraryAttribute: 'iconLibrary', defaultLibrary: 'mdi' }],
53
+ },
54
+ ]);
35
55
 
36
- expect(input).toBeDefined();
37
- expect(input!.iconSlots).toHaveLength(2);
56
+ const mappings = discoverForwardingMappings([], tempDir);
57
+ expect(mappings[0].iconSlots[0]).toEqual({
58
+ iconAttribute: 'icon',
59
+ libraryAttribute: 'iconLibrary',
60
+ defaultLibrary: 'mdi',
61
+ });
62
+ });
38
63
 
39
- const attrNames = input!.iconSlots.map(s => s.iconAttribute).sort();
40
- expect(attrNames).toEqual(['prefixIcon', 'suffixIcon']);
64
+ it('should return empty array when library is not installed', () => {
65
+ const mappings = discoverForwardingMappings([], tempDir);
66
+ expect(mappings).toEqual([]);
67
+ });
41
68
 
42
- const prefix = input!.iconSlots.find(s => s.iconAttribute === 'prefixIcon');
43
- expect(prefix!.libraryAttribute).toBe('prefixIconLibrary');
69
+ it('should handle corrupt library manifest gracefully', () => {
70
+ const manifestDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
71
+ fs.mkdirSync(manifestDir, { recursive: true });
72
+ fs.writeFileSync(path.join(manifestDir, 'forwarding-mappings.json'), 'not valid json');
44
73
 
45
- const suffix = input!.iconSlots.find(s => s.iconAttribute === 'suffixIcon');
46
- expect(suffix!.libraryAttribute).toBe('suffixIconLibrary');
47
- });
74
+ const mappings = discoverForwardingMappings([], tempDir);
75
+ expect(mappings).toEqual([]);
76
+ });
48
77
 
49
- it('should handle component with no library attribute (tn-chip)', () => {
50
- const mappings = findForwardingComponentMappings([FIXTURES_DIR]);
51
- const chip = mappings.find(m => m.selector === 'tn-chip');
78
+ it('should handle manifest with non-array content gracefully', () => {
79
+ const manifestDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
80
+ fs.mkdirSync(manifestDir, { recursive: true });
81
+ fs.writeFileSync(path.join(manifestDir, 'forwarding-mappings.json'), '{"not": "an array"}');
52
82
 
53
- expect(chip).toBeDefined();
54
- expect(chip!.iconSlots).toHaveLength(1);
55
- expect(chip!.iconSlots[0].iconAttribute).toBe('icon');
56
- expect(chip!.iconSlots[0].libraryAttribute).toBeUndefined();
57
- expect(chip!.iconSlots[0].defaultLibrary).toBeUndefined();
83
+ const mappings = discoverForwardingMappings([], tempDir);
84
+ expect(mappings).toEqual([]);
85
+ });
58
86
  });
59
87
 
60
- it('should return empty array for directory with no forwarding components', () => {
61
- const mappings = findForwardingComponentMappings([path.resolve(__dirname, '../__fixtures__/custom-icons')]);
62
- expect(mappings).toEqual([]);
63
- });
88
+ describe('consumer manifest loading', () => {
89
+ it('should load mappings from a consumer source directory', () => {
90
+ const consumerSrc = path.join(tempDir, 'src/app');
91
+ writeManifest(consumerSrc, [
92
+ {
93
+ selector: 'app-status',
94
+ iconSlots: [{ iconAttribute: 'statusIcon', libraryAttribute: 'statusIconLibrary' }],
95
+ },
96
+ ]);
97
+
98
+ const mappings = discoverForwardingMappings([consumerSrc], tempDir);
99
+ expect(mappings).toHaveLength(1);
100
+ expect(mappings[0].selector).toBe('app-status');
101
+ });
102
+
103
+ it('should ignore source dirs without a manifest', () => {
104
+ const emptySrc = path.join(tempDir, 'src/app');
105
+ fs.mkdirSync(emptySrc, { recursive: true });
106
+
107
+ const mappings = discoverForwardingMappings([emptySrc], tempDir);
108
+ expect(mappings).toEqual([]);
109
+ });
64
110
 
65
- it('should return empty array for non-existent directory', () => {
66
- const mappings = findForwardingComponentMappings(['/non/existent/path']);
67
- expect(mappings).toEqual([]);
111
+ it('should handle non-existent source dirs', () => {
112
+ const mappings = discoverForwardingMappings(['/non/existent/path'], tempDir);
113
+ expect(mappings).toEqual([]);
114
+ });
68
115
  });
69
116
 
70
- it('should deduplicate results from overlapping search paths', () => {
71
- // Pass the same path twice duplicates should be removed by selector
72
- const mappings = findForwardingComponentMappings([FIXTURES_DIR, FIXTURES_DIR]);
73
- expect(mappings).toHaveLength(3);
74
- const selectors = mappings.map(m => m.selector).sort();
75
- expect(selectors).toEqual(['tn-chip', 'tn-empty', 'tn-input']);
117
+ describe('merging library and consumer manifests', () => {
118
+ it('should merge mappings from library and consumer', () => {
119
+ // Library provides tn-empty
120
+ const libraryDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
121
+ writeManifest(libraryDir, [
122
+ {
123
+ selector: 'tn-empty',
124
+ iconSlots: [{ iconAttribute: 'icon', libraryAttribute: 'iconLibrary', defaultLibrary: 'mdi' }],
125
+ },
126
+ ]);
127
+
128
+ // Consumer provides app-status
129
+ const consumerSrc = path.join(tempDir, 'src/app');
130
+ writeManifest(consumerSrc, [
131
+ {
132
+ selector: 'app-status',
133
+ iconSlots: [{ iconAttribute: 'statusIcon' }],
134
+ },
135
+ ]);
136
+
137
+ const mappings = discoverForwardingMappings([consumerSrc], tempDir);
138
+ const selectors = mappings.map(m => m.selector).sort();
139
+ expect(selectors).toEqual(['app-status', 'tn-empty']);
140
+ });
141
+
142
+ it('should allow consumer to override a library mapping by selector', () => {
143
+ // Library defines tn-empty with defaultLibrary: 'mdi'
144
+ const libraryDir = path.join(tempDir, 'node_modules/@truenas/ui-components/assets/tn-icons');
145
+ writeManifest(libraryDir, [
146
+ {
147
+ selector: 'tn-empty',
148
+ iconSlots: [{ iconAttribute: 'icon', libraryAttribute: 'iconLibrary', defaultLibrary: 'mdi' }],
149
+ },
150
+ ]);
151
+
152
+ // Consumer overrides tn-empty with a different default
153
+ const consumerSrc = path.join(tempDir, 'src/app');
154
+ writeManifest(consumerSrc, [
155
+ {
156
+ selector: 'tn-empty',
157
+ iconSlots: [{ iconAttribute: 'icon', libraryAttribute: 'iconLibrary', defaultLibrary: 'material' }],
158
+ },
159
+ ]);
160
+
161
+ const mappings = discoverForwardingMappings([consumerSrc], tempDir);
162
+ expect(mappings).toHaveLength(1);
163
+ expect(mappings[0].iconSlots[0].defaultLibrary).toBe('material');
164
+ });
165
+
166
+ it('should merge manifests from multiple consumer source dirs', () => {
167
+ const srcApp = path.join(tempDir, 'src/app');
168
+ writeManifest(srcApp, [
169
+ { selector: 'app-foo', iconSlots: [{ iconAttribute: 'icon' }] },
170
+ ]);
171
+
172
+ const srcLib = path.join(tempDir, 'src/lib');
173
+ writeManifest(srcLib, [
174
+ { selector: 'app-bar', iconSlots: [{ iconAttribute: 'barIcon' }] },
175
+ ]);
176
+
177
+ const mappings = discoverForwardingMappings([srcApp, srcLib], tempDir);
178
+ const selectors = mappings.map(m => m.selector).sort();
179
+ expect(selectors).toEqual(['app-bar', 'app-foo']);
180
+ });
76
181
  });
77
182
  });
@@ -1,8 +1,5 @@
1
- import { spawnSync } from 'node:child_process';
2
1
  import fs from 'fs';
3
- import path from 'path';
4
2
  import { resolve } from 'path';
5
- import * as cheerio from 'cheerio';
6
3
 
7
4
  export interface IconSlotMapping {
8
5
  iconAttribute: string;
@@ -16,23 +13,33 @@ export interface ForwardingComponentMapping {
16
13
  }
17
14
 
18
15
  /**
19
- * Discovers components implementing TnIconForwardingComponent and derives
20
- * their icon attribute mappings by reading their templates.
16
+ * Load forwarding component mappings from JSON manifest files.
21
17
  *
22
- * Steps:
23
- * 1. Grep for `implements TnIconForwardingComponent` (or `implements.*TnIconForwardingComponent`)
24
- * 2. Extract the @Component selector and templateUrl from the same file
25
- * 3. Read the component template and find <tn-icon> [name] / [library] bindings
26
- * 4. Map binding expressions back to input names (strip signal call syntax)
27
- * 5. Extract default library values from input declarations in the TS file
18
+ * Mappings tell the sprite scanner which component attributes forward icon
19
+ * values to `<tn-icon>`. For example, `<tn-empty icon="inbox">` forwards
20
+ * its `icon` attribute to an internal `<tn-icon [name]>`.
21
+ *
22
+ * Sources (merged in order, later entries override by selector):
23
+ * 1. The library's published manifest at
24
+ * `node_modules/@truenas/ui-components/assets/tn-icons/forwarding-mappings.json`
25
+ * 2. Any `forwarding-mappings.json` files found in the consumer's source dirs
28
26
  */
29
- export function findForwardingComponentMappings(searchPaths: string[]): ForwardingComponentMapping[] {
27
+ export function discoverForwardingMappings(srcDirs: string[], projectRoot: string): ForwardingComponentMapping[] {
30
28
  const bySelector = new Map<string, ForwardingComponentMapping>();
31
- const componentFiles = findForwardingComponentFiles(searchPaths);
32
29
 
33
- for (const filePath of componentFiles) {
34
- const mapping = extractMappingFromComponent(filePath);
35
- if (mapping && !bySelector.has(mapping.selector)) {
30
+ // Load library manifest (published with the npm package)
31
+ const libraryMappings = loadManifest(resolve(
32
+ projectRoot,
33
+ 'node_modules/@truenas/ui-components/assets/tn-icons/forwarding-mappings.json',
34
+ ));
35
+ for (const mapping of libraryMappings) {
36
+ bySelector.set(mapping.selector, mapping);
37
+ }
38
+
39
+ // Load consumer manifests from source dirs
40
+ for (const srcDir of srcDirs) {
41
+ const consumerManifest = resolve(srcDir, 'forwarding-mappings.json');
42
+ for (const mapping of loadManifest(consumerManifest)) {
36
43
  bySelector.set(mapping.selector, mapping);
37
44
  }
38
45
  }
@@ -41,169 +48,21 @@ export function findForwardingComponentMappings(searchPaths: string[]): Forwardi
41
48
  }
42
49
 
43
50
  /**
44
- * Discover icon-forwarding component mappings from consumer source dirs
45
- * and the installed library (if present in node_modules).
51
+ * Read and parse a forwarding-mappings.json file.
52
+ * Returns an empty array if the file is missing or malformed.
46
53
  */
47
- export function discoverForwardingMappings(srcDirs: string[], projectRoot: string): ForwardingComponentMapping[] {
48
- const searchPaths = [...srcDirs];
49
-
50
- const libSrcPath = resolve(projectRoot, 'node_modules/@truenas/ui-components/src/lib');
51
- if (fs.existsSync(libSrcPath)) {
52
- searchPaths.push(libSrcPath);
54
+ function loadManifest(filePath: string): ForwardingComponentMapping[] {
55
+ if (!fs.existsSync(filePath)) {
56
+ return [];
53
57
  }
54
58
 
55
- return findForwardingComponentMappings(searchPaths);
56
- }
57
-
58
- /**
59
- * Grep for .ts files containing `implements TnIconForwardingComponent`
60
- * (handles multi-interface: `implements TnIconForwardingComponent, AfterViewInit`)
61
- */
62
- function findForwardingComponentFiles(searchPaths: string[]): string[] {
63
- const files: string[] = [];
64
-
65
- for (const searchPath of searchPaths) {
66
- if (!fs.existsSync(searchPath)) {
67
- continue;
68
- }
69
-
70
- const result = spawnSync(
71
- 'grep',
72
- ['-rl', 'implements.*TnIconForwardingComponent', '--include=*.ts', searchPath],
73
- { encoding: 'utf-8' },
74
- );
75
-
76
- if (result.status === 1 && !result.stderr) {
77
- // grep returns exit code 1 when no matches found
78
- continue;
79
- }
80
- if (result.status !== 0 && result.status !== 1) {
81
- throw new Error(`grep failed: ${result.stderr}`);
59
+ try {
60
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
61
+ if (!Array.isArray(data)) {
62
+ return [];
82
63
  }
83
-
84
- result.stdout
85
- .split('\n')
86
- .filter(Boolean)
87
- .forEach((file) => files.push(file));
64
+ return data;
65
+ } catch {
66
+ return [];
88
67
  }
89
-
90
- return files;
91
- }
92
-
93
- /**
94
- * Extract the forwarding component mapping from a single component .ts file.
95
- *
96
- * Reads the TS file to get the selector and templateUrl, then reads the
97
- * template to find <tn-icon> bindings and maps them back to input names.
98
- */
99
- function extractMappingFromComponent(tsFilePath: string): ForwardingComponentMapping | null {
100
- const tsContent = fs.readFileSync(tsFilePath, 'utf-8');
101
-
102
- // Extract selector from @Component({ selector: '...' })
103
- const selectorMatch = /selector:\s*['"]([^'"]+)['"]/.exec(tsContent);
104
- if (!selectorMatch) {
105
- return null;
106
- }
107
- const selector = selectorMatch[1];
108
-
109
- // Extract templateUrl from @Component({ templateUrl: '...' })
110
- const templateUrlMatch = /templateUrl:\s*['"]([^'"]+)['"]/.exec(tsContent);
111
- if (!templateUrlMatch) {
112
- return null;
113
- }
114
-
115
- // Resolve template path relative to the TS file
116
- const templatePath = path.resolve(path.dirname(tsFilePath), templateUrlMatch[1]);
117
- if (!fs.existsSync(templatePath)) {
118
- return null;
119
- }
120
-
121
- const templateContent = fs.readFileSync(templatePath, 'utf-8');
122
- const iconSlots = extractIconSlotsFromTemplate(templateContent, tsContent);
123
-
124
- if (iconSlots.length === 0) {
125
- return null;
126
- }
127
-
128
- return { selector, iconSlots };
129
- }
130
-
131
- /**
132
- * Find <tn-icon> elements in the template and extract the input names
133
- * from their [name] and [library] bindings.
134
- *
135
- * For example:
136
- * <tn-icon [name]="icon()!" [library]="iconLibrary()">
137
- * yields: { iconAttribute: 'icon', libraryAttribute: 'iconLibrary' }
138
- *
139
- * <tn-icon [name]="prefixIcon()!">
140
- * yields: { iconAttribute: 'prefixIcon' }
141
- */
142
- function extractIconSlotsFromTemplate(templateContent: string, tsContent: string): IconSlotMapping[] {
143
- const $ = cheerio.load(templateContent);
144
- const slots: IconSlotMapping[] = [];
145
-
146
- $('tn-icon').each((_, el) => {
147
- const boundName = $(el).attr('[name]');
148
- if (!boundName) {
149
- return;
150
- }
151
-
152
- // Extract the signal/input name from the binding expression
153
- // Handles: "icon()", "icon()!", "prefixIcon()!", etc.
154
- const inputName = extractInputName(boundName);
155
- if (!inputName) {
156
- return;
157
- }
158
-
159
- const slot: IconSlotMapping = { iconAttribute: inputName };
160
-
161
- // Check for [library] binding on the same <tn-icon>
162
- const boundLibrary = $(el).attr('[library]');
163
- if (boundLibrary) {
164
- const libraryInputName = extractInputName(boundLibrary);
165
- if (libraryInputName) {
166
- slot.libraryAttribute = libraryInputName;
167
-
168
- // Try to extract the default value from the input declaration in the TS
169
- const defaultLibrary = extractInputDefault(tsContent, libraryInputName);
170
- if (defaultLibrary) {
171
- slot.defaultLibrary = defaultLibrary;
172
- }
173
- }
174
- }
175
-
176
- slots.push(slot);
177
- });
178
-
179
- return slots;
180
- }
181
-
182
- /**
183
- * Extract a simple input/signal name from a template binding expression.
184
- *
185
- * Matches patterns like:
186
- * "icon()" -> "icon"
187
- * "icon()!" -> "icon"
188
- * "prefixIcon()!" -> "prefixIcon"
189
- *
190
- * Returns null for complex expressions (ternaries, method calls with args, etc.)
191
- * since those represent computed values, not direct input forwarding.
192
- */
193
- function extractInputName(expression: string): string | null {
194
- const match = /^\s*(\w+)\(\)!?\s*$/.exec(expression);
195
- return match ? match[1] : null;
196
- }
197
-
198
- /**
199
- * Extract the default value from an Angular input declaration.
200
- *
201
- * Matches patterns like:
202
- * iconLibrary = input<IconLibraryType>('mdi') -> "mdi"
203
- * iconLibrary = input('mdi') -> "mdi"
204
- */
205
- function extractInputDefault(tsContent: string, inputName: string): string | null {
206
- const regex = new RegExp(`${inputName}\\s*=\\s*input[^(]*\\(\\s*['"]([^'"]+)['"]`);
207
- const match = regex.exec(tsContent);
208
- return match ? match[1] : null;
209
68
  }
@@ -483,11 +483,31 @@ textarea.tn-input-directive {
483
483
  border-radius: 8px;
484
484
  background: var(--tn-bg1, #ffffff);
485
485
  color: var(--tn-fg1, #000000);
486
+ overflow: hidden; /* enforce max-height boundary */
486
487
  box-shadow: 0 11px 15px -7px rgba(0, 0, 0, 0.2),
487
488
  0 24px 38px 3px rgba(0, 0, 0, 0.14),
488
489
  0 9px 46px 8px rgba(0, 0, 0, 0.12);
489
490
  }
490
491
 
492
+ /*
493
+ * Propagate the flex / height constraint from .tn-dialog-panel through
494
+ * the two CDK-generated wrapper elements so tn-dialog-shell can size
495
+ * itself correctly and .tn-dialog__content can scroll.
496
+ */
497
+ .tn-dialog-panel > .cdk-dialog-container {
498
+ display: flex;
499
+ flex-direction: column;
500
+ flex: 1 1 auto;
501
+ min-height: 0;
502
+ }
503
+
504
+ .tn-dialog-panel > .cdk-dialog-container > :first-child {
505
+ display: flex;
506
+ flex-direction: column;
507
+ flex: 1 1 auto;
508
+ min-height: 0;
509
+ }
510
+
491
511
  /* Shell component wrapper */
492
512
  tn-dialog-shell {
493
513
  display: flex;
@@ -591,15 +611,6 @@ tn-dialog-shell {
591
611
  background: rgba(0, 0, 0, 0.5);
592
612
  }
593
613
 
594
- /* Container positioning */
595
- .cdk-dialog-container {
596
- display: flex;
597
- align-items: center;
598
- justify-content: center;
599
- min-height: 100vh;
600
- box-sizing: border-box;
601
- }
602
-
603
614
  /* Fullscreen dialog support */
604
615
  .tn-dialog--fullscreen .cdk-dialog-container {
605
616
  position: fixed;
@@ -839,38 +839,7 @@ declare enum InputType {
839
839
  PlainText = "text"
840
840
  }
841
841
 
842
- /**
843
- * Marker interface for components that forward icon inputs to `<tn-icon>`.
844
- *
845
- * When a component implements this interface, the sprite generation scanner
846
- * will read its template to discover which inputs map to `<tn-icon>` `[name]`
847
- * and `[library]` bindings, then scan consumer templates for those attributes.
848
- *
849
- * This means icons passed as template attributes to these components are
850
- * automatically included in the sprite — no `tnIconMarker()` needed.
851
- *
852
- * @example
853
- * ```typescript
854
- * @Component({
855
- * selector: 'tn-empty',
856
- * templateUrl: './empty.component.html',
857
- * })
858
- * export class TnEmptyComponent implements TnIconForwardingComponent {
859
- * icon = input<string>();
860
- * iconLibrary = input<IconLibraryType>('mdi');
861
- * }
862
- * ```
863
- *
864
- * The scanner will detect that `tn-empty` forwards `icon` to `<tn-icon [name]>`,
865
- * so `<tn-empty icon="inbox">` in consumer templates will automatically include
866
- * `mdi-inbox` in the sprite.
867
- *
868
- * @public
869
- */
870
- interface TnIconForwardingComponent {
871
- }
872
-
873
- declare class TnInputComponent implements TnIconForwardingComponent, AfterViewInit, ControlValueAccessor {
842
+ declare class TnInputComponent implements AfterViewInit, ControlValueAccessor {
874
843
  inputEl: _angular_core.Signal<ElementRef<HTMLInputElement | HTMLTextAreaElement>>;
875
844
  inputType: _angular_core.InputSignal<InputType>;
876
845
  placeholder: _angular_core.InputSignal<string>;
@@ -1212,7 +1181,7 @@ declare class TnInputDirective {
1212
1181
  }
1213
1182
 
1214
1183
  type ChipColor = 'primary' | 'secondary' | 'accent';
1215
- declare class TnChipComponent implements TnIconForwardingComponent, AfterViewInit, OnDestroy {
1184
+ declare class TnChipComponent implements AfterViewInit, OnDestroy {
1216
1185
  chipEl: _angular_core.Signal<ElementRef<HTMLElement>>;
1217
1186
  label: _angular_core.InputSignal<string>;
1218
1187
  icon: _angular_core.InputSignal<string | undefined>;
@@ -5552,7 +5521,7 @@ declare class TnFilePickerPopupComponent implements OnInit, AfterViewInit, After
5552
5521
  }
5553
5522
 
5554
5523
  type TnEmptySize = 'default' | 'compact';
5555
- declare class TnEmptyComponent implements TnIconForwardingComponent {
5524
+ declare class TnEmptyComponent {
5556
5525
  title: _angular_core.InputSignal<string>;
5557
5526
  description: _angular_core.InputSignal<string | undefined>;
5558
5527
  icon: _angular_core.InputSignal<string | undefined>;
@@ -6100,4 +6069,4 @@ declare const TN_THEME_DEFINITIONS: readonly TnThemeDefinition[];
6100
6069
  declare const THEME_MAP: Map<TnTheme, TnThemeDefinition>;
6101
6070
 
6102
6071
  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, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
6103
- export type { AutocompleteHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CheckboxHarnessFilters, ChipColor, CreateFolderEvent, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FilePickerCallbacks, FilePickerError, FilePickerMode, FileSystemItem, FormFieldHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, PathSegment, PlatformType, ProgressBarMode, RadioHarnessFilters, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnBannerType, TnButtonToggleType, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnConfirmDialogData, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFlatTreeNode, TnIconForwardingComponent, TnMenuItem, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataSource, TnTableHarnessFilters, TnThemeDefinition, TnToastCall, TnToastConfig, TooltipPosition, YearCell };
6072
+ export type { AutocompleteHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CheckboxHarnessFilters, ChipColor, CreateFolderEvent, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FilePickerCallbacks, FilePickerError, FilePickerMode, FileSystemItem, FormFieldHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, PathSegment, PlatformType, ProgressBarMode, RadioHarnessFilters, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnBannerType, TnButtonToggleType, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnConfirmDialogData, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFlatTreeNode, TnMenuItem, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataSource, TnTableHarnessFilters, TnThemeDefinition, TnToastCall, TnToastConfig, TooltipPosition, YearCell };
@@ -1,7 +0,0 @@
1
- @if (prefixIcon()) {
2
- <tn-icon [name]="prefixIcon()!" [library]="prefixIconLibrary()" />
3
- }
4
- <input class="tn-input" />
5
- @if (suffixIcon()) {
6
- <tn-icon [name]="suffixIcon()!" [library]="suffixIconLibrary()" />
7
- }
@@ -1,15 +0,0 @@
1
- import { Component, input } from '@angular/core';
2
- import type { TnIconForwardingComponent } from '../icon/icon-forwarding';
3
- import type { IconLibraryType } from '../icon/icon.component';
4
-
5
- @Component({
6
- selector: 'tn-input',
7
- standalone: true,
8
- templateUrl: './multi-icon.component.html',
9
- })
10
- export class TnInputComponent implements TnIconForwardingComponent, AfterViewInit {
11
- prefixIcon = input<string | undefined>(undefined);
12
- prefixIconLibrary = input<IconLibraryType | undefined>(undefined);
13
- suffixIcon = input<string | undefined>(undefined);
14
- suffixIconLibrary = input<IconLibraryType | undefined>(undefined);
15
- }
@@ -1,3 +0,0 @@
1
- @if (icon()) {
2
- <tn-icon [name]="icon()!" />
3
- }
@@ -1,11 +0,0 @@
1
- import { Component, input } from '@angular/core';
2
- import type { TnIconForwardingComponent } from '../icon/icon-forwarding';
3
-
4
- @Component({
5
- selector: 'tn-chip',
6
- standalone: true,
7
- templateUrl: './no-library.component.html',
8
- })
9
- export class TnChipComponent implements TnIconForwardingComponent {
10
- icon = input<string | undefined>(undefined);
11
- }
@@ -1 +0,0 @@
1
- <button class="tn-button">{{ label() }}</button>