🎯 Implementation Complete! This library has been extracted from the monorepo and is ready for Git submodule distribution. Features: - Standardized SCSS imports (no relative paths) - Optimized public-api.ts exports - Independent Angular library structure - Ready for consumer integration as submodule 🚀 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
|
import { faUser } from '@fortawesome/free-solid-svg-icons';
|
|
import { BadgeComponent } from '../badge/badge.component';
|
|
|
|
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
|
|
|
|
@Component({
|
|
selector: 'ui-avatar',
|
|
standalone: true,
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
imports: [CommonModule, FontAwesomeModule, BadgeComponent],
|
|
template: `
|
|
<div class="skyui-avatar"
|
|
[class]="'skyui-avatar--size-' + size"
|
|
[class.skyui-avatar--loading]="loading"
|
|
[attr.aria-label]="ariaLabel || (name ? name + ' avatar' : 'User avatar')">
|
|
|
|
@if (loading) {
|
|
<div class="skyui-avatar__loading">
|
|
<div class="loading-spinner" aria-hidden="true"></div>
|
|
</div>
|
|
} @else {
|
|
@if (imageUrl && !imageError) {
|
|
<img
|
|
[src]="imageUrl"
|
|
[alt]="altText || (name ? name + ' avatar' : 'User avatar')"
|
|
class="skyui-avatar__image"
|
|
(error)="onImageError()"
|
|
(load)="onImageLoad()">
|
|
} @else {
|
|
<div class="skyui-avatar__fallback">
|
|
@if (computedInitials) {
|
|
<span class="skyui-avatar__initials" aria-hidden="true">{{ computedInitials }}</span>
|
|
} @else {
|
|
<fa-icon
|
|
[icon]="faUser"
|
|
class="skyui-avatar__icon"
|
|
aria-hidden="true">
|
|
</fa-icon>
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@if (status) {
|
|
<div class="skyui-avatar__status"
|
|
[class]="'skyui-avatar__status--' + status"
|
|
[attr.aria-label]="statusLabel || status + ' status'">
|
|
</div>
|
|
}
|
|
|
|
@if (badge !== undefined && badge !== null) {
|
|
<ui-badge
|
|
class="skyui-avatar__badge"
|
|
variant="danger"
|
|
size="xs"
|
|
[ariaLabel]="badgeLabel || 'Badge: ' + badge">
|
|
{{ badge }}
|
|
</ui-badge>
|
|
}
|
|
</div>
|
|
`,
|
|
styleUrl: './avatar.component.scss'
|
|
})
|
|
export class AvatarComponent {
|
|
@Input() imageUrl?: string;
|
|
@Input() name?: string;
|
|
@Input() initials?: string;
|
|
@Input() size: AvatarSize = 'md';
|
|
@Input() altText?: string;
|
|
@Input() ariaLabel?: string;
|
|
@Input() loading: boolean = false;
|
|
@Input() status?: 'online' | 'offline' | 'away' | 'busy';
|
|
@Input() statusLabel?: string;
|
|
@Input() badge?: string | number;
|
|
@Input() badgeLabel?: string;
|
|
|
|
faUser = faUser;
|
|
imageError = false;
|
|
|
|
onImageError(): void {
|
|
this.imageError = true;
|
|
}
|
|
|
|
onImageLoad(): void {
|
|
this.imageError = false;
|
|
}
|
|
|
|
// Auto-generate initials from name if not provided
|
|
get computedInitials(): string {
|
|
if (this.initials) {
|
|
return this.initials.slice(0, 2).toUpperCase();
|
|
}
|
|
|
|
if (this.name) {
|
|
const nameParts = this.name.trim().split(/\s+/);
|
|
if (nameParts.length >= 2) {
|
|
return (nameParts[0][0] + nameParts[nameParts.length - 1][0]).toUpperCase();
|
|
} else if (nameParts[0]) {
|
|
return nameParts[0].slice(0, 2).toUpperCase();
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
} |