AAE-40703 Add support for content projection (#11462)

This commit is contained in:
Diogo Bastos
2025-12-15 13:17:36 +00:00
committed by GitHub
parent 0f9969a387
commit 8da24884ec
3 changed files with 60 additions and 5 deletions

View File

@@ -84,6 +84,20 @@ using the `adf:` namespace.
The Icon Alias Mapping feature allows you to provide custom icon value mappings at runtime using the `ICON_ALIAS_MAP_TOKEN` injection token. When an icon value matches a key in the alias map, the component automatically replaces it with the mapped value. This is useful for creating consistent icon conventions across your application without modifying component code. The Icon Alias Mapping feature allows you to provide custom icon value mappings at runtime using the `ICON_ALIAS_MAP_TOKEN` injection token. When an icon value matches a key in the alias map, the component automatically replaces it with the mapped value. This is useful for creating consistent icon conventions across your application without modifying component code.
**How icon name replacement works:**
When you provide an alias map, the component checks if the icon value matches any key in the map. If a match is found, the icon name is replaced with the corresponding mapped value. This happens automatically and transparently:
- Original icon name: `icon-mock`
- Alias map entry: `'icon-mock': 'alias-mock'`
- Result: Icon renders as `alias-mock` instead of `icon-mock`
This allows you to:
- **Rename icons** without changing component code
- **Support legacy icon names** by mapping them to new ones
- **Centralize icon naming conventions** in your application
- **Create icon aliases** for shorter or more descriptive names
**Example alias map:** **Example alias map:**
```ts ```ts
@@ -102,13 +116,36 @@ function getProviders() {
} }
``` ```
**Usage in your template:** ### Template usage
The Icon Component supports two ways to provide the icon value:
**1. Using the `value` input property:**
```html ```html
<adf-icon value="icon-mock"></adf-icon> <adf-icon value="home"></adf-icon>
<adf-icon value="alert"></adf-icon>
<adf-icon value="adf:custom-icon"></adf-icon>
``` ```
The component will replace `icon-mock` with `alias-mock`. If the icon value doesn't match any key in the alias map, the original value is used. **2. Using content projection (slot):**
Similar to Angular Material's `mat-icon`, you can provide the icon value as text content:
```html
<adf-icon>home</adf-icon>
<adf-icon>alert</adf-icon>
<adf-icon>adf:custom-icon</adf-icon>
```
**Priority:** If both are provided, the slot content takes priority over the `value` input property:
```html
<!-- Uses "home" from slot, ignores value input -->
<adf-icon value="alert">home</adf-icon>
```
This approach provides flexibility and familiarity for developers accustomed to Angular Material's icon component API.
## See also ## See also

View File

@@ -1,3 +1,5 @@
<ng-content />
@if (isSvg) { @if (isSvg) {
<mat-icon [color]="color" [svgIcon]="value" aria-hidden="true" /> <mat-icon [color]="color" [svgIcon]="value" aria-hidden="true" />
} @else { } @else {

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Input, ViewEncapsulation, ChangeDetectionStrategy, inject } from '@angular/core'; import { Component, Input, ViewEncapsulation, ChangeDetectionStrategy, inject, ElementRef, AfterContentInit } from '@angular/core';
import { ThemePalette } from '@angular/material/core'; import { ThemePalette } from '@angular/material/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { ICON_ALIAS_MAP_TOKEN } from './icon-alias-map.token'; import { ICON_ALIAS_MAP_TOKEN } from './icon-alias-map.token';
@@ -30,8 +30,9 @@ export const DEFAULT_ICON_VALUE = 'settings';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'adf-icon' } host: { class: 'adf-icon' }
}) })
export class IconComponent { export class IconComponent implements AfterContentInit {
private readonly ALIAS_MAP = inject(ICON_ALIAS_MAP_TOKEN, { optional: true }); private readonly ALIAS_MAP = inject(ICON_ALIAS_MAP_TOKEN, { optional: true });
private readonly elementRef = inject(ElementRef);
private _value = DEFAULT_ICON_VALUE; private _value = DEFAULT_ICON_VALUE;
private _isSvg = false; private _isSvg = false;
@@ -65,6 +66,15 @@ export class IconComponent {
this._isSvg = isSvg; this._isSvg = isSvg;
} }
ngAfterContentInit(): void {
const textNode = this.getTextNode();
if (textNode) {
this.value = textNode.textContent.trim();
textNode.remove();
}
}
private hasMappedAlias(value: string): boolean { private hasMappedAlias(value: string): boolean {
return !!this.ALIAS_MAP?.[value]; return !!this.ALIAS_MAP?.[value];
} }
@@ -72,4 +82,10 @@ export class IconComponent {
private isCustom(value: string): boolean { private isCustom(value: string): boolean {
return value.includes(':'); return value.includes(':');
} }
private getTextNode(): Text | undefined {
return Array.from(this.elementRef.nativeElement.childNodes).find(
(node: Node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim()
) as Text | undefined;
}
} }