- Replace incorrect semantic token names with correct ones: • $semantic-border-width-thin → $semantic-border-width-1 • $semantic-color-border-default → $semantic-color-border-primary • $semantic-spacing-content-* → $semantic-spacing-component-* • $semantic-typography-body-* → $semantic-typography-font-size-* • $semantic-typography-caption-* → $semantic-typography-font-size-* • $semantic-motion-easing-standard → $semantic-easing-standard • $semantic-color-surface-tertiary → $semantic-color-surface-secondary • Various hover color tokens → base color tokens - Fix typography map usage errors: • Replace heading map tokens with individual size tokens • $semantic-typography-heading-h* → $semantic-typography-heading-h*-size - Update affected components: • tooltip, divider, progress-circle, range-slider components • Related demo components and SCSS files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
311 lines
11 KiB
TypeScript
311 lines
11 KiB
TypeScript
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/image-container.component';
|
|
import { BadgeComponent } from '../../../../../ui-essentials/src/lib/components/data-display/badge/badge.component';
|
|
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);
|
|
}
|
|
}
|
|
} |