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,588 @@
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppbarComponent } from '../../../../../ui-essentials/src/lib/components/navigation/appbar';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import {
faBars,
faSearch,
faUser,
faHeart,
faBell,
faCog,
faShoppingCart,
faHome,
faArrowLeft,
faPlus,
faShare,
faEllipsisV,
faSun,
faMoon
} from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'ui-appbar-demo',
standalone: true,
imports: [
CommonModule,
AppbarComponent,
FontAwesomeModule
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="padding: 2rem;">
<h2>Appbar Component Showcase</h2>
<!-- Appbar Variants -->
<section style="margin-bottom: 3rem;">
<h3>Appbar Variants</h3>
<div style="margin-bottom: 2rem;">
<h4>Compact</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="compact"
[slots]="{title: true, leftIcon: true, rightIcon: true}"
[elevated]="false">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">Compact Appbar</span>
<fa-icon [icon]="faSearch" slot="right-icon"></fa-icon>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Standard</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightAvatar: true}"
[elevated]="false">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">Standard Appbar</span>
<div slot="right-avatar" style="width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-weight: 500;">
JS
</div>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Large</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="large"
[slots]="{title: true, leftIcon: true, rightMenu: true}"
[elevated]="false">
<fa-icon [icon]="faArrowLeft" slot="left-icon"></fa-icon>
<span slot="title">Large Appbar with More Space</span>
<div slot="right-menu" style="display: flex; gap: 1rem;">
<fa-icon [icon]="faHeart" style="cursor: pointer;"></fa-icon>
<fa-icon [icon]="faShare" style="cursor: pointer;"></fa-icon>
<fa-icon [icon]="faEllipsisV" style="cursor: pointer;"></fa-icon>
</div>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Prominent</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="prominent"
[slots]="{title: true, leftLogo: true, rightIcon: true}"
[elevated]="false">
<div slot="left-logo" style="display: flex; align-items: center; gap: 0.5rem; font-weight: 600; color: #007bff;">
<div style="width: 24px; height: 24px; background: linear-gradient(135deg, #007bff, #0056b3); border-radius: 4px;"></div>
SkyUI
</div>
<span slot="title">Prominent Design System</span>
<fa-icon [icon]="faCog" slot="right-icon"></fa-icon>
</ui-appbar>
</div>
</div>
</section>
<!-- Elevation and Position -->
<section style="margin-bottom: 3rem;">
<h3>Elevation & Position</h3>
<div style="margin-bottom: 2rem;">
<h4>Elevated</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightIcon: true}"
[elevated]="true">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">Elevated Appbar</span>
<fa-icon [icon]="faBell" slot="right-icon"></fa-icon>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Not Elevated</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightIcon: true}"
[elevated]="false">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">Flat Appbar</span>
<fa-icon [icon]="faBell" slot="right-icon"></fa-icon>
</ui-appbar>
</div>
</div>
</section>
<!-- Slot Configurations -->
<section style="margin-bottom: 3rem;">
<h3>Slot Configurations</h3>
<div style="margin-bottom: 2rem;">
<h4>Left Icon + Title + Right Avatar</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightAvatar: true}">
<fa-icon [icon]="faHome" slot="left-icon"></fa-icon>
<span slot="title">Home</span>
<img
slot="right-avatar"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face"
style="width: 32px; height: 32px; border-radius: 50%; object-fit: cover;"
alt="User avatar"
(error)="handleImageError($event)">
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Left Logo + Title + Right Menu</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftLogo: true, rightMenu: true}">
<div slot="left-logo" style="display: flex; align-items: center; gap: 0.5rem; font-weight: 600;">
<div style="width: 20px; height: 20px; background: linear-gradient(45deg, #ff6b6b, #ee5a24); border-radius: 50%;"></div>
App
</div>
<span slot="title">Dashboard</span>
<div slot="right-menu" style="display: flex; gap: 1rem; align-items: center;">
<fa-icon [icon]="faShoppingCart" style="cursor: pointer;" (click)="handleAction('cart')"></fa-icon>
<fa-icon [icon]="faBell" style="cursor: pointer;" (click)="handleAction('notifications')"></fa-icon>
<fa-icon [icon]="faUser" style="cursor: pointer;" (click)="handleAction('profile')"></fa-icon>
</div>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Left Avatar + Title + Right Icon</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftAvatar: true, rightIcon: true}">
<div slot="left-avatar" style="width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); display: flex; align-items: center; justify-content: center; color: #333; font-weight: 500; font-size: 12px;">
UI
</div>
<span slot="title">Profile Settings</span>
<button
slot="right-icon"
style="background: none; border: none; padding: 8px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center;"
(click)="toggleTheme()">
<fa-icon [icon]="isDarkMode() ? faSun : faMoon"></fa-icon>
</button>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Multiple Right Elements</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightIcon: true, rightMenu: true}">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">Multi-Action Bar</span>
<fa-icon [icon]="faPlus" slot="right-icon" style="cursor: pointer;" (click)="handleAction('add')"></fa-icon>
<div slot="right-menu" style="display: flex; gap: 0.5rem;">
<fa-icon [icon]="faSearch" style="cursor: pointer;" (click)="handleAction('search')"></fa-icon>
<fa-icon [icon]="faEllipsisV" style="cursor: pointer;" (click)="handleAction('more')"></fa-icon>
</div>
</ui-appbar>
</div>
</div>
</section>
<!-- Real-world Examples -->
<section style="margin-bottom: 3rem;">
<h3>Real-world Examples</h3>
<div style="margin-bottom: 2rem;">
<h4>E-commerce App</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftIcon: true, rightMenu: true}"
[elevated]="true">
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
<span slot="title">ShopMart</span>
<div slot="right-menu" style="display: flex; gap: 1rem; align-items: center;">
<div style="position: relative;">
<fa-icon [icon]="faShoppingCart" style="cursor: pointer;" (click)="handleAction('cart')"></fa-icon>
<span style="position: absolute; top: -8px; right: -8px; background: #ff4757; color: white; border-radius: 50%; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 600;">3</span>
</div>
<fa-icon [icon]="faSearch" style="cursor: pointer;" (click)="handleAction('search')"></fa-icon>
</div>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Social Media App</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="standard"
[slots]="{title: true, leftLogo: true, rightMenu: true}"
[elevated]="false">
<div slot="left-logo" style="font-weight: 700; font-size: 1.25rem; background: linear-gradient(45deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">
SocialHub
</div>
<span slot="title">Feed</span>
<div slot="right-menu" style="display: flex; gap: 1rem; align-items: center;">
<div style="position: relative;">
<fa-icon [icon]="faBell" style="cursor: pointer;" (click)="handleAction('notifications')"></fa-icon>
<span style="position: absolute; top: -6px; right: -6px; background: #ff3742; width: 8px; height: 8px; border-radius: 50%;"></span>
</div>
<fa-icon [icon]="faUser" style="cursor: pointer;" (click)="handleAction('profile')"></fa-icon>
</div>
</ui-appbar>
</div>
</div>
<div style="margin-bottom: 2rem;">
<h4>Settings Page</h4>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
variant="large"
[slots]="{title: true, leftIcon: true, rightIcon: true}"
[elevated]="false">
<fa-icon [icon]="faArrowLeft" slot="left-icon" style="cursor: pointer;" (click)="handleAction('back')"></fa-icon>
<span slot="title">Account Settings</span>
<fa-icon [icon]="faCog" slot="right-icon" style="cursor: pointer;" (click)="handleAction('settings')"></fa-icon>
</ui-appbar>
</div>
</div>
</section>
<!-- Interactive Demo -->
<section style="margin-bottom: 3rem;">
<h3>Interactive Demo</h3>
<p style="margin-bottom: 1rem; color: #6c757d;">
Customize the appbar below by toggling different options:
</p>
<div style="display: flex; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap;">
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().elevated"
(change)="updateDemoConfig('elevated', $event)">
Elevated
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().slots.leftIcon"
(change)="updateDemoSlot('leftIcon', $event)">
Left Icon
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().slots.leftLogo"
(change)="updateDemoSlot('leftLogo', $event)">
Left Logo
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().slots.rightIcon"
(change)="updateDemoSlot('rightIcon', $event)">
Right Icon
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().slots.rightAvatar"
(change)="updateDemoSlot('rightAvatar', $event)">
Right Avatar
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input
type="checkbox"
[checked]="demoConfig().slots.rightMenu"
(change)="updateDemoSlot('rightMenu', $event)">
Right Menu
</label>
</div>
<div style="margin-bottom: 1rem;">
<label style="display: flex; align-items: center; gap: 0.5rem;">
Variant:
<select
[value]="demoConfig().variant"
(change)="updateDemoVariant($event)"
style="padding: 0.25rem 0.5rem; border: 1px solid #ced4da; border-radius: 4px;">
<option value="compact">Compact</option>
<option value="standard">Standard</option>
<option value="large">Large</option>
<option value="prominent">Prominent</option>
</select>
</label>
</div>
<div style="border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
<ui-appbar
[variant]="demoConfig().variant"
[slots]="demoConfig().slots"
[elevated]="demoConfig().elevated">
@if (demoConfig().slots.leftIcon) {
<fa-icon [icon]="faBars" slot="left-icon"></fa-icon>
}
@if (demoConfig().slots.leftLogo) {
<div slot="left-logo" style="display: flex; align-items: center; gap: 0.5rem; font-weight: 600; color: #007bff;">
<div style="width: 20px; height: 20px; background: linear-gradient(135deg, #007bff, #0056b3); border-radius: 4px;"></div>
Demo
</div>
}
<span slot="title">{{ demoConfig().title }}</span>
@if (demoConfig().slots.rightIcon) {
<fa-icon [icon]="faSearch" slot="right-icon"></fa-icon>
}
@if (demoConfig().slots.rightAvatar) {
<div slot="right-avatar" style="width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-weight: 500; font-size: 12px;">
DU
</div>
}
@if (demoConfig().slots.rightMenu) {
<div slot="right-menu" style="display: flex; gap: 1rem;">
<fa-icon [icon]="faHeart" style="cursor: pointer;"></fa-icon>
<fa-icon [icon]="faCog" style="cursor: pointer;"></fa-icon>
</div>
}
</ui-appbar>
</div>
@if (lastAction()) {
<div style="padding: 1rem; background: #e3f2fd; border-radius: 4px;">
<strong>Last action:</strong> {{ lastAction() }}
</div>
}
</section>
<!-- Usage Examples -->
<section style="margin-bottom: 3rem;">
<h3>Code Examples</h3>
<div style="background: #f8f9fa; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #007bff;">
<h4>Basic Appbar:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-appbar
variant="standard"
[slots]="&#123;title: true, leftIcon: true, rightIcon: true&#125;"
[elevated]="false"&gt;
&lt;fa-icon [icon]="faBars" slot="left-icon"&gt;&lt;/fa-icon&gt;
&lt;span slot="title"&gt;My App&lt;/span&gt;
&lt;fa-icon [icon]="faSearch" slot="right-icon"&gt;&lt;/fa-icon&gt;
&lt;/ui-appbar&gt;</code></pre>
<h4>Appbar with Logo and Avatar:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-appbar
variant="standard"
[slots]="&#123;title: true, leftLogo: true, rightAvatar: true&#125;"
[elevated]="true"&gt;
&lt;div slot="left-logo" style="font-weight: 600;"&gt;
MyBrand
&lt;/div&gt;
&lt;span slot="title"&gt;Dashboard&lt;/span&gt;
&lt;img slot="right-avatar" src="avatar.jpg"
style="width: 32px; height: 32px; border-radius: 50%;"&gt;
&lt;/ui-appbar&gt;</code></pre>
<h4>Appbar with Multiple Actions:</h4>
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code>&lt;ui-appbar
variant="standard"
[slots]="&#123;title: true, leftIcon: true, rightMenu: true&#125;"
[elevated]="false"&gt;
&lt;fa-icon [icon]="faArrowLeft" slot="left-icon"&gt;&lt;/fa-icon&gt;
&lt;span slot="title"&gt;Settings&lt;/span&gt;
&lt;div slot="right-menu" style="display: flex; gap: 1rem;"&gt;
&lt;fa-icon [icon]="faSearch"&gt;&lt;/fa-icon&gt;
&lt;fa-icon [icon]="faEllipsisV"&gt;&lt;/fa-icon&gt;
&lt;/div&gt;
&lt;/ui-appbar&gt;</code></pre>
</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;
}
fa-icon {
transition: color 0.2s ease;
}
fa-icon:hover {
color: #007bff;
}
label {
font-size: 0.875rem;
user-select: none;
}
input[type="checkbox"] {
margin: 0;
}
select {
font-size: 0.875rem;
}
`]
})
export class AppbarDemoComponent {
// State signals
isDarkMode = signal(false);
lastAction = signal('');
// Demo configuration
demoConfig = signal({
variant: 'standard' as any,
elevated: false,
title: 'Interactive Demo',
slots: {
leftIcon: true,
leftLogo: false,
leftAvatar: false,
title: true,
rightIcon: true,
rightLogo: false,
rightAvatar: false,
rightMenu: false
}
});
// Font Awesome icons
faBars = faBars;
faSearch = faSearch;
faUser = faUser;
faHeart = faHeart;
faBell = faBell;
faCog = faCog;
faShoppingCart = faShoppingCart;
faHome = faHome;
faArrowLeft = faArrowLeft;
faPlus = faPlus;
faShare = faShare;
faEllipsisV = faEllipsisV;
faSun = faSun;
faMoon = faMoon;
handleAction(action: string): void {
this.lastAction.set(`${action} clicked at ${new Date().toLocaleTimeString()}`);
console.log(`Appbar action: ${action}`);
}
toggleTheme(): void {
this.isDarkMode.update(current => !current);
this.handleAction(`Theme switched to ${this.isDarkMode() ? 'dark' : 'light'} mode`);
}
handleImageError(event: Event): void {
const target = event.target as HTMLImageElement;
target.style.display = 'none';
const fallback = document.createElement('div');
fallback.style.width = '32px';
fallback.style.height = '32px';
fallback.style.borderRadius = '50%';
fallback.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
fallback.style.display = 'flex';
fallback.style.alignItems = 'center';
fallback.style.justifyContent = 'center';
fallback.style.color = 'white';
fallback.style.fontWeight = '500';
fallback.style.fontSize = '12px';
fallback.textContent = 'U';
target.parentNode?.insertBefore(fallback, target);
}
updateDemoConfig(key: string, event: Event): void {
const target = event.target as HTMLInputElement;
const value = target.checked;
this.demoConfig.update(config => ({
...config,
[key]: value
}));
}
updateDemoSlot(slotName: string, event: Event): void {
const target = event.target as HTMLInputElement;
const checked = target.checked;
this.demoConfig.update(config => ({
...config,
slots: {
...config.slots,
[slotName]: checked
}
}));
}
updateDemoVariant(event: Event): void {
const target = event.target as HTMLSelectElement;
const variant = target.value as any;
this.demoConfig.update(config => ({
...config,
variant,
title: variant.charAt(0).toUpperCase() + variant.slice(1) + ' Demo'
}));
}
}