Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion renderers/angular/src/lib/catalog/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
import { DynamicComponent } from '../rendering/dynamic-component';
import * as Primitives from '@a2ui/web_core/types/primitives';
import { toSnakeCase } from '@a2ui/web_core/styles/icons';

@Component({
selector: 'a2ui-icon',
Expand Down Expand Up @@ -45,5 +46,8 @@ import * as Primitives from '@a2ui/web_core/types/primitives';
})
export class Icon extends DynamicComponent {
readonly name = input.required<Primitives.StringValue | null>();
protected readonly resolvedName = computed(() => this.resolvePrimitive(this.name()));
protected readonly resolvedName = computed(() => {
const name = this.resolvePrimitive(this.name());
return name ? toSnakeCase(name) : name;
});
}
4 changes: 2 additions & 2 deletions renderers/lit/src/0.8/ui/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as Primitives from "@a2ui/web_core/types/primitives";
import { classMap } from "lit/directives/class-map.js";
import { styleMap } from "lit/directives/style-map.js";
import { structuralStyles } from "./styles.js";
import { toSnakeCase } from "@a2ui/web_core/styles/icons";

@customElement("a2ui-icon")
export class Icon extends Root {
Expand Down Expand Up @@ -68,8 +69,7 @@ export class Icon extends Root {
}

const render = (url: string) => {
url = url.replace(/([A-Z])/gm, "_$1").toLocaleLowerCase();
return html`<span class="g-icon">${url}</span>`;
return html`<span class="g-icon">${toSnakeCase(url)}</span>`;
};

if (this.name && typeof this.name === "object") {
Expand Down
10 changes: 1 addition & 9 deletions renderers/react/src/components/content/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,7 @@ import type * as Types from '@a2ui/web_core/types/types';
import type {A2UIComponentProps} from '../../types';
import {useA2UIComponent} from '../../hooks/useA2UIComponent';
import {classMapToString, stylesToObject} from '../../lib/utils';

/**
* Convert camelCase to snake_case for Material Symbols font.
* e.g., "shoppingCart" -> "shopping_cart"
* This matches the Lit renderer's approach.
*/
function toSnakeCase(str: string): string {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
import {toSnakeCase} from '@a2ui/web_core/styles/icons';

/**
* Icon component - renders an icon using Material Symbols Outlined font.
Expand Down
15 changes: 15 additions & 0 deletions renderers/web_core/src/v0_8/styles/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
* limitations under the License.
*/

/**
* Converts a camelCase icon name to snake_case for use with Material Symbols.
*
* The Material Symbols font uses snake_case names (e.g., "shopping_cart"),
* but A2UI icon names may be provided in camelCase (e.g., "shoppingCart").
* This utility normalizes them so all renderers behave consistently.
*
* @example
* toSnakeCase('shoppingCart') // -> 'shopping_cart'
* toSnakeCase('home') // -> 'home'
*/
export function toSnakeCase(str: string): string {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}

/**
* CSS classes for Google Symbols.
*
Expand Down