import clsx from 'clsx' import fitty from 'fitty/dist/fitty.min.js' import { Component, Element, Host, h, State, Prop, Watch } from '@stencil/core' import { ThemeFullVariantAvatarType, ToneMinimalVariantType } from '@type/variant' import { avatarVariant } from './meta/variants' import miBaselinePerson from '@icon/mi/baseline/person.svg' /** * @part icon - The selected icon of the avatar * @part wrapper - The wrapper which contains media displayed */ @Component({ tag: 'mds-avatar', styleUrl: 'mds-avatar.css', shadow: true, }) export class MdsAvatar { // BUG: when user switch from initials to other and turn back to initials fitty breaks @Element() private element: HTMLMdsAvatarElement @State() fallback = false @State() loaded = true private observer: ResizeObserver private fittyElements private fittyInitialized = false private textChanged = false /** * Specifies the path to the icon * @see https://magma.maggiolicloud.it/storybook/?path=/story/design-icon--default */ @Prop({ reflect: true }) readonly icon?: string|undefined /** * The user's inizials displayed if there's no image available, initials will override tone and variant senttings to keep user recognizable from others */ @Prop({ mutable:true, reflect: true }) readonly initials?: string /** * The user's inizials displayed if there's no image available, initials will override tone and variant senttings to keep user recognizable from others */ @Prop({ mutable:true, reflect: true }) readonly count?: number /** * Specifies the path to the image */ @Prop({ reflect: true }) readonly src?: string /** * Specifies the color tone of the component */ @Prop({ reflect: true }) readonly tone?: ToneMinimalVariantType /** * Specifies the color variant of the component */ @Prop({ reflect: true, mutable: true }) variant?: ThemeFullVariantAvatarType private variants: ThemeFullVariantAvatarType[] = ['amaranth', 'aqua', 'blue', 'error', 'green', 'info', 'lime', 'orange', 'orchid', 'primary', 'sky', 'success', 'violet', 'warning', 'yellow'] private addFontResize = (): void => { if (this.fittyInitialized) { return } const initialsElement = this.element.shadowRoot?.querySelector('.fit') this.fittyElements = fitty(initialsElement as HTMLElement, { minSize: 10 }) this.observer = new ResizeObserver(entries => { entries.forEach(() => { this.fittyElements.fit() }) }) this.observer.observe(this.element) this.fittyInitialized = true } private removeFontResize = (): void => { if (!this.fittyInitialized) { return } this.fittyInitialized = false this.observer.unobserve(this.element) } private checkText = (value: string): void => { if (value !== '' && value !== undefined) { if (this.fittyInitialized) return if (!this.fittyInitialized) this.addFontResize() return } if (this.fittyInitialized) this.removeFontResize() } private checkInitialsVariant = (): void => { if (this.initials) { let cleanedInitials = this.initials.toLowerCase().replace(/[^a-zA-Z0-9]+/g, '').substring(0, 2) if (cleanedInitials.length === 1) { cleanedInitials = cleanedInitials + cleanedInitials } this.variant = this.variants[(cleanedInitials.substring(0, 1).charCodeAt(0) + cleanedInitials.substring(1, 2).charCodeAt(0)) % avatarVariant.length] } } componentWillLoad (): void { this.checkInitialsVariant() } componentDidLoad (): void { if (this.src !== undefined) { this.loaded = false } if (this.initials) { this.checkText(this.initials) } if (this.count) { this.checkText(this.count.toString()) } } componentDidRender (): void { if (this.textChanged) { // placed here becase @Watch('initials') is fired // BEFORE the element .fit is attached on shDOM if (this.initials) this.checkText(this.initials) if (this.count) this.checkText(this.count.toString()) this.textChanged = false } } @Watch('initials') initialsHandler (): void { this.textChanged = true this.checkInitialsVariant() } @Watch('count') countHandler (): void { this.textChanged = true this.checkInitialsVariant() } @Watch('src') srcHandler (newValue: string): void { if (newValue === undefined) { this.loaded = true } } @Watch('icon') iconHandler (newValue: string): void { if (newValue !== undefined) { this.loaded = true } } render () { return (
{ this.initials && !this.count && !this.fallback && !this.src &&
{ this.initials.substring(0, 2) }
} { this.count && !this.fallback && !this.src &&
+{ this.count }
} { this.src && !this.count && !this.fallback && !this.icon && { this.loaded = true; this.fallback = true } } onMdsImgLoadSuccess={ () => { this.loaded = true } } src={ this.src } /> } { this.icon && !this.initials && !this.count && } { (this.fallback || (!this.icon && !this.initials && !this.count && !this.src)) && }
) } }