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,502 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AvatarComponent } from '../../../../../ui-essentials/src/lib/components/data-display/avatar';
interface Activity {
user: string;
action: string;
time: string;
status?: 'online' | 'offline' | 'away' | 'busy';
}
@Component({
selector: 'ui-avatar-demo',
standalone: true,
imports: [
CommonModule,
AvatarComponent
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="padding: 2rem;">
<h2>Avatar Component Showcase</h2>
<!-- Size Variants -->
<section style="margin-bottom: 3rem;">
<h3>Size Variants</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="xs" name="Extra Small"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">XS (24px)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="sm" name="Small Avatar"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">SM (32px)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="md" name="Medium Avatar"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">MD (40px)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Large Avatar"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">LG (48px)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="xl" name="Extra Large"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">XL (64px)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="xxl" name="XX Large"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">XXL (80px)</p>
</div>
</div>
</section>
<!-- With Images -->
<section style="margin-bottom: 3rem;">
<h3>With Images</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar
size="lg"
imageUrl="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face"
name="John Doe"
altText="John Doe's profile picture">
</ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">John Doe</p>
</div>
<div style="text-align: center;">
<ui-avatar
size="lg"
imageUrl="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=150&h=150&fit=crop&crop=face"
name="Jane Smith"
altText="Jane Smith's profile picture">
</ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Jane Smith</p>
</div>
<div style="text-align: center;">
<ui-avatar
size="lg"
imageUrl="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face"
name="Alex Rodriguez"
altText="Alex Rodriguez's profile picture">
</ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Alex Rodriguez</p>
</div>
<div style="text-align: center;">
<ui-avatar
size="lg"
imageUrl="broken-url"
name="Fallback Test"
altText="This should show initials">
</ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Fallback (Broken URL)</p>
</div>
</div>
</section>
<!-- With Status -->
<section style="margin-bottom: 3rem;">
<h3>Status Indicators</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="lg" name="Online User" status="online"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Online</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Offline User" status="offline"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Offline</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Away User" status="away"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Away</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Busy User" status="busy"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Busy</p>
</div>
</div>
</section>
<!-- With Badges -->
<section style="margin-bottom: 3rem;">
<h3>Notification Badges</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="lg" name="Badge User" badge="3"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">3 notifications</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Badge User" badge="99+"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">99+ notifications</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Badge User" badge="5" status="online"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Online + Badge</p>
</div>
<div style="text-align: center;">
<ui-avatar
size="lg"
name="Premium User"
badge="⭐"
imageUrl="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face">
</ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Premium User</p>
</div>
</div>
</section>
<!-- Shape Variants -->
<section style="margin-bottom: 3rem;">
<h3>Shape Variants</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="lg" name="Circle User" shape="circle"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Circle (Default)</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Square User" shape="square"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Square</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Rounded User" shape="rounded"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Rounded Square</p>
</div>
</div>
</section>
<!-- Color Variants -->
<section style="margin-bottom: 3rem;">
<h3>Color Variants</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="lg" name="Primary User" color="primary"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Primary</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Secondary User" color="secondary"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Secondary</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Success User" color="success"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Success</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Warning User" color="warning"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Warning</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Danger User" color="danger"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Danger</p>
</div>
</div>
</section>
<!-- Activity List Example -->
<section style="margin-bottom: 3rem;">
<h3>Activity List Example</h3>
<div style="background: #f8f9fa; border-radius: 12px; padding: 1.5rem; max-width: 600px;">
<h4 style="margin: 0 0 1rem 0; font-size: 1.125rem;">Recent Activity</h4>
@for (activity of activities; track activity.user) {
<div style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 0; border-bottom: 1px solid #e9ecef;">
<ui-avatar
size="sm"
[name]="activity.user"
[status]="activity.status">
</ui-avatar>
<div style="flex: 1; min-width: 0;">
<div style="font-weight: 500; font-size: 14px; margin-bottom: 2px;">{{ activity.user }}</div>
<div style="color: #666; font-size: 12px; line-height: 1.3;">{{ activity.action }}</div>
</div>
<div style="color: #999; font-size: 11px; white-space: nowrap;">{{ activity.time }}</div>
</div>
}
</div>
</section>
<!-- User List Example -->
<section style="margin-bottom: 3rem;">
<h3>User List Example</h3>
<div style="background: #f8f9fa; border-radius: 12px; padding: 1.5rem; max-width: 800px;">
<div style="display: grid; gap: 1rem;">
@for (user of userProfiles; track user.id) {
<div style="display: flex; align-items: center; gap: 1rem; padding: 1rem; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<ui-avatar
size="lg"
[name]="user.name"
[imageUrl]="user.avatar"
[status]="user.status"
[badge]="user.notifications > 0 ? user.notifications.toString() : ''">
</ui-avatar>
<div style="flex: 1; min-width: 0;">
<h4 style="margin: 0; font-size: 16px; font-weight: 600;">{{ user.name }}</h4>
<p style="margin: 2px 0; color: #666; font-size: 14px;">{{ user.role }}</p>
<p style="margin: 0; color: #999; font-size: 12px;">{{ user.email }}</p>
</div>
<div style="text-align: right;">
<div style="font-size: 12px; color: #999;">Last seen</div>
<div style="font-size: 12px; font-weight: 500;">{{ user.lastSeen }}</div>
</div>
</div>
}
</div>
</div>
</section>
<!-- Loading and Error States -->
<section style="margin-bottom: 3rem;">
<h3>Special States</h3>
<div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<div style="text-align: center;">
<ui-avatar size="lg" [loading]="true"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Loading</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="" altText="Empty name fallback"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Empty Name</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" name="Single" altText="Single character name"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">Single Character</p>
</div>
<div style="text-align: center;">
<ui-avatar size="lg" altText="No name provided"></ui-avatar>
<p style="font-size: 12px; margin: 0.5rem 0;">No Name</p>
</div>
</div>
</section>
<!-- Avatar Group Example -->
<section style="margin-bottom: 3rem;">
<h3>Avatar Group Example</h3>
<div style="display: flex; align-items: center; gap: -0.5rem;">
@for (member of teamMembers; track member.name; let i = $index) {
<ui-avatar
size="md"
[name]="member.name"
[imageUrl]="member.avatar"
[status]="member.status"
[style.z-index]="teamMembers.length - i"
[style.margin-left]="i > 0 ? '-8px' : '0'">
</ui-avatar>
}
<div style="margin-left: 1rem; padding: 0.5rem 1rem; background: #e3f2fd; border-radius: 20px; font-size: 12px; font-weight: 500;">
+{{ teamMembers.length }} members
</div>
</div>
</section>
<!-- Usage Examples -->
<section style="margin-bottom: 3rem;">
<h3>Usage Examples</h3>
<div style="background: #f8f9fa; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #007bff;">
<h4>Basic Avatar:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-avatar
size="md"
name="Current User"
status="online"&gt;
&lt;/ui-avatar&gt;</code></pre>
<h4>Avatar with Image:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-avatar
size="lg"
name="John Doe"
imageUrl="https://example.com/avatar.jpg"
altText="John's profile picture"
status="away"
badge="5"&gt;
&lt;/ui-avatar&gt;</code></pre>
<h4>Custom Styled Avatar:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-avatar
size="xl"
name="Admin User"
color="primary"
shape="rounded"
badge="⭐"
[loading]="false"&gt;
&lt;/ui-avatar&gt;</code></pre>
</div>
</section>
<!-- Interactive Example -->
<section style="margin-bottom: 3rem;">
<h3>Interactive Example</h3>
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem;">
<button
(click)="cycleStatus()"
style="padding: 0.5rem 1rem; border: 1px solid #007bff; background: #007bff; color: white; border-radius: 4px; cursor: pointer;">
Toggle Status
</button>
<button
(click)="toggleBadge()"
style="padding: 0.5rem 1rem; border: 1px solid #28a745; background: #28a745; color: white; border-radius: 4px; cursor: pointer;">
Toggle Badge
</button>
<button
(click)="cycleSize()"
style="padding: 0.5rem 1rem; border: 1px solid #6c757d; background: #6c757d; color: white; border-radius: 4px; cursor: pointer;">
Cycle Size
</button>
</div>
<div style="display: flex; align-items: center; gap: 1rem;">
<ui-avatar
[size]="interactiveSize"
name="Interactive User"
[status]="interactiveStatus"
[badge]="interactiveBadge"
imageUrl="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=150&h=150&fit=crop&crop=face">
</ui-avatar>
<div>
<div><strong>Size:</strong> {{ interactiveSize }}</div>
<div><strong>Status:</strong> {{ interactiveStatus || 'none' }}</div>
<div><strong>Badge:</strong> {{ interactiveBadge || 'none' }}</div>
</div>
</div>
</section>
</div>
`,
styles: [`
h2 {
color: hsl(279, 14%, 11%);
font-size: 2rem;
margin-bottom: 2rem;
border-bottom: 2px solid hsl(258, 100%, 47%);
padding-bottom: 0.5rem;
}
h3 {
color: hsl(279, 14%, 25%);
font-size: 1.5rem;
margin-bottom: 1rem;
}
h4 {
color: hsl(287, 12%, 35%);
font-size: 1.125rem;
margin-bottom: 0.75rem;
}
section {
border: 1px solid hsl(289, 14%, 90%);
border-radius: 8px;
padding: 1.5rem;
background: hsl(286, 20%, 99%);
}
pre {
font-size: 0.875rem;
line-height: 1.5;
margin: 0.5rem 0;
}
code {
font-family: 'JetBrains Mono', monospace;
color: #d63384;
}
`]
})
export class AvatarDemoComponent {
activities: Activity[] = [
{
user: 'Alice Johnson',
action: 'Updated the project documentation and added new API endpoints',
time: '2 min ago',
status: 'online'
},
{
user: 'Bob Smith',
action: 'Completed code review for the authentication module',
time: '15 min ago',
status: 'away'
},
{
user: 'Carol Williams',
action: 'Created new user interface mockups for the dashboard',
time: '1 hour ago',
status: 'online'
},
{
user: 'David Brown',
action: 'Fixed critical bug in the payment processing system',
time: '2 hours ago',
status: 'busy'
},
{
user: 'Eve Davis',
action: 'Deployed version 2.1.0 to production environment',
time: '3 hours ago',
status: 'offline'
}
];
userProfiles = [
{
id: 1,
name: 'Alice Johnson',
role: 'Senior Developer',
email: 'alice@company.com',
status: 'online' as const,
notifications: 3,
lastSeen: '2 min ago',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=150&h=150&fit=crop&crop=face'
},
{
id: 2,
name: 'Bob Smith',
role: 'Product Manager',
email: 'bob@company.com',
status: 'away' as const,
notifications: 0,
lastSeen: '15 min ago',
avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face'
},
{
id: 3,
name: 'Carol Williams',
role: 'UX Designer',
email: 'carol@company.com',
status: 'online' as const,
notifications: 12,
lastSeen: 'Just now',
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face'
}
];
teamMembers = [
{ name: 'Alex Chen', status: 'online' as const, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face' },
{ name: 'Maria Garcia', status: 'away' as const, avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150&h=150&fit=crop&crop=face' },
{ name: 'James Wilson', status: 'busy' as const, avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=150&h=150&fit=crop&crop=face' },
{ name: 'Sarah Kim', status: 'online' as const, avatar: 'https://images.unsplash.com/photo-1534751516642-a1af1ef26a56?w=150&h=150&fit=crop&crop=face' },
];
// Interactive demo properties
interactiveSize: any = 'lg';
interactiveStatus: any = 'online';
interactiveBadge = '3';
private statusCycle: any[] = ['online', 'away', 'busy', 'offline', null];
private statusIndex = 0;
private sizeCycle: any[] = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
private sizeIndex = 3; // Start with 'lg'
cycleStatus(): void {
this.statusIndex = (this.statusIndex + 1) % this.statusCycle.length;
this.interactiveStatus = this.statusCycle[this.statusIndex];
}
toggleBadge(): void {
if (this.interactiveBadge) {
this.interactiveBadge = '';
} else {
this.interactiveBadge = Math.floor(Math.random() * 99 + 1).toString();
}
}
cycleSize(): void {
this.sizeIndex = (this.sizeIndex + 1) % this.sizeCycle.length;
this.interactiveSize = this.sizeCycle[this.sizeIndex];
}
}