🎯 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>
136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
|
import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
|
|
import {
|
|
faCheckCircle,
|
|
faExclamationTriangle,
|
|
faExclamationCircle,
|
|
faInfoCircle,
|
|
faTimes
|
|
} from '@fortawesome/free-solid-svg-icons';
|
|
|
|
type AlertSize = 'sm' | 'md' | 'lg';
|
|
type AlertVariant = 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
|
|
|
@Component({
|
|
selector: 'ui-alert',
|
|
standalone: true,
|
|
imports: [CommonModule, FontAwesomeModule],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
encapsulation: ViewEncapsulation.None,
|
|
template: `
|
|
<div
|
|
class="ui-alert"
|
|
[class.ui-alert--sm]="size === 'sm'"
|
|
[class.ui-alert--md]="size === 'md'"
|
|
[class.ui-alert--lg]="size === 'lg'"
|
|
[class.ui-alert--primary]="variant === 'primary'"
|
|
[class.ui-alert--success]="variant === 'success'"
|
|
[class.ui-alert--warning]="variant === 'warning'"
|
|
[class.ui-alert--danger]="variant === 'danger'"
|
|
[class.ui-alert--info]="variant === 'info'"
|
|
[class.ui-alert--dismissible]="dismissible"
|
|
[attr.role]="role"
|
|
[attr.aria-live]="ariaLive"
|
|
[attr.aria-labelledby]="title ? 'alert-title-' + alertId : null"
|
|
[attr.aria-describedby]="'alert-content-' + alertId">
|
|
|
|
@if (showIcon && alertIcon) {
|
|
<fa-icon
|
|
class="ui-alert__icon"
|
|
[icon]="alertIcon"
|
|
[attr.aria-hidden]="true">
|
|
</fa-icon>
|
|
}
|
|
|
|
<div class="ui-alert__content">
|
|
@if (title) {
|
|
<h4
|
|
class="ui-alert__title"
|
|
[class.ui-alert__title--bold]="boldTitle"
|
|
[id]="'alert-title-' + alertId">
|
|
{{ title }}
|
|
</h4>
|
|
}
|
|
|
|
<div
|
|
class="ui-alert__message"
|
|
[id]="'alert-content-' + alertId">
|
|
<ng-content></ng-content>
|
|
</div>
|
|
|
|
@if (actions && actions.length > 0) {
|
|
<div class="ui-alert__actions">
|
|
<ng-content select="[slot=actions]"></ng-content>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@if (dismissible) {
|
|
<button
|
|
type="button"
|
|
class="ui-alert__dismiss"
|
|
[attr.aria-label]="dismissLabel"
|
|
(click)="handleDismiss()"
|
|
(keydown)="handleDismissKeydown($event)">
|
|
<fa-icon [icon]="faTimes" [attr.aria-hidden]="true"></fa-icon>
|
|
</button>
|
|
}
|
|
</div>
|
|
`,
|
|
styleUrl: './alert.component.scss'
|
|
})
|
|
export class AlertComponent {
|
|
@Input() size: AlertSize = 'md';
|
|
@Input() variant: AlertVariant = 'primary';
|
|
@Input() title?: string;
|
|
@Input() boldTitle = false;
|
|
@Input() showIcon = true;
|
|
@Input() dismissible = false;
|
|
@Input() dismissLabel = 'Dismiss alert';
|
|
@Input() role = 'alert';
|
|
@Input() ariaLive: 'polite' | 'assertive' | 'off' = 'polite';
|
|
@Input() actions: any[] = [];
|
|
|
|
@Output() dismissed = new EventEmitter<void>();
|
|
|
|
// Icons
|
|
readonly faCheckCircle = faCheckCircle;
|
|
readonly faExclamationTriangle = faExclamationTriangle;
|
|
readonly faExclamationCircle = faExclamationCircle;
|
|
readonly faInfoCircle = faInfoCircle;
|
|
readonly faTimes = faTimes;
|
|
|
|
// Generate unique ID for accessibility
|
|
readonly alertId = Math.random().toString(36).substr(2, 9);
|
|
|
|
get alertIcon(): IconDefinition | null {
|
|
if (!this.showIcon) return null;
|
|
|
|
switch (this.variant) {
|
|
case 'success':
|
|
return this.faCheckCircle;
|
|
case 'warning':
|
|
return this.faExclamationTriangle;
|
|
case 'danger':
|
|
return this.faExclamationCircle;
|
|
case 'info':
|
|
return this.faInfoCircle;
|
|
case 'primary':
|
|
default:
|
|
return this.faInfoCircle;
|
|
}
|
|
}
|
|
|
|
handleDismiss(): void {
|
|
this.dismissed.emit();
|
|
}
|
|
|
|
handleDismissKeydown(event: KeyboardEvent): void {
|
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
event.preventDefault();
|
|
this.handleDismiss();
|
|
}
|
|
}
|
|
} |