Add comprehensive component library and demo application

Added extensive component library with feedback components (empty state, loading spinner, skeleton loader), enhanced form components (autocomplete, date picker, file upload, form field, time picker), navigation components (pagination), and overlay components (backdrop, drawer, modal, overlay container). Updated demo application with comprehensive showcase components and enhanced styling throughout the project. Excluded font files from repository to reduce size.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
skyai_dev
2025-09-03 05:38:09 +10:00
parent c803831f60
commit 5983722793
246 changed files with 52845 additions and 25 deletions

View File

@@ -0,0 +1,311 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ImageContainerComponent, ImageContainerSize, ImageContainerAspectRatio, ImageContainerObjectFit, ImageContainerShape } from '../../../../../ui-essentials/src/lib/components/data-display/image-container';
import { BadgeComponent } from '../../../../../ui-essentials/src/lib/components/data-display/badge';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faRefresh, faHeart, faPlay } from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'ui-image-container-demo',
standalone: true,
imports: [CommonModule, ImageContainerComponent, BadgeComponent, FontAwesomeModule],
template: `
<div class="demo-container">
<h2>Image Container Demo</h2>
<p>A flexible image container with lazy loading, aspect ratios, and various display options.</p>
<!-- Size Variants -->
<section class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
@for (size of sizes; track size) {
<div class="demo-item">
<ui-image-container
[size]="size"
[src]="sampleImages.landscape"
[alt]="'Sample image - ' + size + ' size'">
</ui-image-container>
<span class="demo-label">{{ size }}</span>
</div>
}
</div>
</section>
<!-- Aspect Ratios -->
<section class="demo-section">
<h3>Aspect Ratios</h3>
<div class="demo-grid">
@for (ratio of aspectRatios; track ratio) {
<div class="demo-item">
<ui-image-container
size="md"
[aspectRatio]="ratio"
[src]="sampleImages.landscape"
[alt]="'Sample image - ' + ratio + ' aspect ratio'">
</ui-image-container>
<span class="demo-label">{{ ratio }}</span>
</div>
}
</div>
</section>
<!-- Object Fit Options -->
<section class="demo-section">
<h3>Object Fit</h3>
<div class="demo-row">
@for (fit of objectFits; track fit) {
<div class="demo-item">
<ui-image-container
size="md"
aspectRatio="1/1"
[objectFit]="fit"
[src]="sampleImages.landscape"
[alt]="'Sample image with ' + fit + ' object fit'">
</ui-image-container>
<span class="demo-label">{{ fit }}</span>
</div>
}
</div>
</section>
<!-- Shape Variants -->
<section class="demo-section">
<h3>Shapes</h3>
<div class="demo-row">
@for (shape of shapes; track shape) {
<div class="demo-item">
<ui-image-container
size="md"
aspectRatio="1/1"
[shape]="shape"
[src]="sampleImages.portrait"
[alt]="'Sample image with ' + shape + ' shape'">
</ui-image-container>
<span class="demo-label">{{ shape }}</span>
</div>
}
</div>
</section>
<!-- Loading States -->
<section class="demo-section">
<h3>Loading States</h3>
<div class="demo-row">
<div class="demo-item">
<ui-image-container
size="md"
[loading]="true"
src="placeholder"
alt="Loading image">
</ui-image-container>
<span class="demo-label">Loading</span>
</div>
<div class="demo-item">
<ui-image-container
size="md"
src="invalid-url.jpg"
alt="Failed to load image">
<div slot="error">
<fa-icon [icon]="faRefresh" size="lg"></fa-icon>
<span>Custom Error</span>
<button class="retry-btn" (click)="retryImage($event)">Retry</button>
</div>
</ui-image-container>
<span class="demo-label">Error State</span>
</div>
<div class="demo-item">
<ui-image-container
size="md"
[src]="sampleImages.landscape"
[lazy]="false"
alt="Eager loaded image">
</ui-image-container>
<span class="demo-label">Eager Load</span>
</div>
</div>
</section>
<!-- Interactive Features -->
<section class="demo-section">
<h3>Interactive Features</h3>
<div class="demo-row">
<div class="demo-item">
<ui-image-container
size="lg"
aspectRatio="16/9"
[src]="sampleImages.landscape"
[overlay]="true"
alt="Image with overlay"
(imageClick)="handleImageClick('overlay')">
<div slot="overlay">
<fa-icon [icon]="faPlay" size="2x"></fa-icon>
<span>Play Video</span>
</div>
</ui-image-container>
<span class="demo-label">With Overlay</span>
</div>
<div class="demo-item">
<ui-image-container
size="lg"
aspectRatio="4/3"
[src]="sampleImages.landscape"
caption="Beautiful landscape scene"
alt="Image with caption"
(imageClick)="handleImageClick('caption')">
</ui-image-container>
<span class="demo-label">With Caption</span>
</div>
</div>
</section>
<!-- Advanced Examples -->
<section class="demo-section">
<h3>Advanced Examples</h3>
<div class="demo-row">
<div class="demo-item">
<ui-image-container
size="lg"
aspectRatio="1/1"
shape="circle"
[src]="sampleImages.portrait"
[overlay]="true"
alt="Profile picture with status"
(imageClick)="handleImageClick('profile')">
<div slot="overlay">
<fa-icon [icon]="faHeart" style="color: red;"></fa-icon>
</div>
</ui-image-container>
<span class="demo-label">Profile Avatar</span>
</div>
<div class="demo-item">
<ui-image-container
size="xl"
aspectRatio="16/9"
[src]="sampleImages.landscape"
caption="Hero image with custom styling"
alt="Hero banner image"
class="hero-image">
<div slot="caption">
<h4>Custom Caption Content</h4>
<p>With additional styled elements</p>
<ui-badge variant="primary" size="xs">Featured</ui-badge>
</div>
</ui-image-container>
<span class="demo-label">Hero Banner</span>
</div>
</div>
</section>
<!-- Responsive Gallery -->
<section class="demo-section">
<h3>Responsive Gallery</h3>
<div class="demo-gallery">
@for (image of galleryImages; track image.id; let i = $index) {
<ui-image-container
size="md"
[aspectRatio]="i % 3 === 0 ? '16/9' : '1/1'"
[src]="image.url"
[alt]="image.alt"
[overlay]="true"
(imageClick)="handleGalleryClick(image, i)"
(imageLoaded)="handleImageLoad(image.id)"
(imageError)="handleImageError(image.id)">
<div slot="overlay">
<span>{{ image.title }}</span>
</div>
</ui-image-container>
}
</div>
</section>
<!-- Event Log -->
@if (eventLog.length > 0) {
<section class="demo-section">
<h3>Event Log</h3>
<div class="event-log">
@for (event of eventLog.slice(-5); track event.id) {
<div class="event-item">
<span class="event-time">{{ event.time | date:'HH:mm:ss' }}</span>
<span class="event-type">{{ event.type }}</span>
<span class="event-message">{{ event.message }}</span>
</div>
}
</div>
</section>
}
</div>
`,
styleUrl: './image-container-demo.component.scss'
})
export class ImageContainerDemoComponent {
// Icons
faRefresh = faRefresh;
faHeart = faHeart;
faPlay = faPlay;
// Demo data
sizes: ImageContainerSize[] = ['sm', 'md', 'lg', 'xl'];
aspectRatios: ImageContainerAspectRatio[] = ['1/1', '4/3', '16/9', '3/2', '2/1', '3/4', '9/16'];
objectFits: ImageContainerObjectFit[] = ['contain', 'cover', 'fill', 'scale-down'];
shapes: ImageContainerShape[] = ['square', 'rounded', 'circle'];
// Sample images (using placeholder service)
sampleImages = {
landscape: 'https://picsum.photos/800/600?random=1',
portrait: 'https://picsum.photos/600/800?random=2',
square: 'https://picsum.photos/600/600?random=3'
};
galleryImages = [
{ id: 1, url: 'https://picsum.photos/400/400?random=10', title: 'Nature', alt: 'Beautiful nature scene' },
{ id: 2, url: 'https://picsum.photos/400/300?random=11', title: 'Architecture', alt: 'Modern building' },
{ id: 3, url: 'https://picsum.photos/400/500?random=12', title: 'Portrait', alt: 'Person portrait' },
{ id: 4, url: 'https://picsum.photos/400/400?random=13', title: 'Urban', alt: 'City landscape' },
{ id: 5, url: 'https://picsum.photos/400/300?random=14', title: 'Technology', alt: 'Tech equipment' },
{ id: 6, url: 'https://picsum.photos/400/400?random=15', title: 'Abstract', alt: 'Abstract art' }
];
// Event tracking
eventLog: Array<{id: number, type: string, message: string, time: Date}> = [];
private eventId = 1;
handleImageClick(context: string): void {
this.logEvent('click', `Image clicked in ${context} context`);
}
handleGalleryClick(image: any, index: number): void {
this.logEvent('gallery-click', `Gallery image "${image.title}" clicked (index ${index})`);
}
handleImageLoad(imageId: number): void {
this.logEvent('load', `Image ${imageId} loaded successfully`);
}
handleImageError(imageId: number): void {
this.logEvent('error', `Image ${imageId} failed to load`);
}
retryImage(event: Event): void {
event.stopPropagation();
this.logEvent('retry', 'Retry button clicked for failed image');
// In a real implementation, this would retry loading the image
}
private logEvent(type: string, message: string): void {
this.eventLog.push({
id: this.eventId++,
type,
message,
time: new Date()
});
// Keep only last 20 events
if (this.eventLog.length > 20) {
this.eventLog.splice(0, this.eventLog.length - 20);
}
}
}