import type { TemplateResult } from 'lit' import { when } from 'lit/directives/when.js' import { css, html } from 'lit' import { unsafeSVG } from 'lit/directives/unsafe-svg.js' import { property, query, state } from 'lit/decorators.js' import { parseSvg } from '../../utils/svgUtils' import { UsBaseElement } from '../base/UsBaseElement' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import type { TIconsStorageKeys } from './iconsStorage' import type { IIconProps } from './model/IIconProps' import UsIcon_css from './icon.pcss' declare global { interface HTMLElementTagNameMap { 'us-icon': UsIcon } } @customElementIfNotExist('us-icon') export class UsIcon extends UsBaseElement implements IIconProps { static override shadowRootOptions: ShadowRootInit = { mode: 'open', delegatesFocus: false } static override styles = [ UsBaseElement.styles, css([UsIcon_css] as TemplateStringsArray | any), ] @property({ type: String, attribute: 'name' }) iconName!: TIconsStorageKeys @property({ type: String }) color = 'currentColor' @property({ type: String }) size: IIconProps['size'] = 'md' @property({ type: String, attribute: 'title' }) iconTitle = '' @property({ type: String }) ariaLabel = '' @property({ type: String, attribute: 'data-testid', reflect: true }) dataTestId = 'icon' @state() inlineSvgContent?: string = '' @state() viewBox = '0 0 32 32' @state() iconSize = 24 @query('.us-icon') icon!: SVGElement willUpdate(changedProperties: Map) { super.willUpdate(changedProperties) if (changedProperties.has('iconName')) this._loadSvg() if (changedProperties.has('size')) this.iconSize = this.calculatedIconSize() } updated(changedProperties: Map) { if (changedProperties.has('viewBox')) this.icon?.setAttribute('viewBox', this.viewBox) } render(): TemplateResult { const { inlineSvgContent, iconName, iconSize, color } = this return html` ${when(this.title, () => html`${this.getTitle()}`)} ${unsafeSVG(inlineSvgContent)} ` } calculatedIconSize(): number { switch (this.size) { case 'lg': return 48 case 'md': return 24 case 'sm': return 16 case 'xs': return 12 default: return 24 } } async _loadSvg() { const svg = await parseSvg(this.iconName) const content = svg?.content const viewBox = svg?.viewBox this.inlineSvgContent = content if (viewBox?.length) this.viewBox = viewBox.join(' ') } getTitle(): string { return this.iconTitle ? this.iconTitle : `${this.iconName} icon` } getAriaLabel(): string { return this.ariaLabel ? this.ariaLabel : `${this.iconName} icon` } }