- 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>
620 lines
20 KiB
TypeScript
620 lines
20 KiB
TypeScript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { ListItemComponent } from '../../../../../ui-essentials/src/lib/components/data-display/list/list-item.component';
|
|
import { ListContainerComponent } from '../../../../../ui-essentials/src/lib/components/data-display/list/list-container.component';
|
|
import { ListItemData } from '../../../../../ui-essentials/src/lib/components/data-display/list/list-item.component';
|
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
|
import {
|
|
faInbox,
|
|
faStar,
|
|
faPaperPlane,
|
|
faEdit,
|
|
faArchive,
|
|
faTrash,
|
|
faPhone,
|
|
faComment,
|
|
faPlay,
|
|
faDownload,
|
|
faCheck,
|
|
faChevronRight,
|
|
faPlus,
|
|
faMinus
|
|
} from '@fortawesome/free-solid-svg-icons';
|
|
|
|
@Component({
|
|
selector: 'ui-list-demo',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
ListItemComponent,
|
|
ListContainerComponent,
|
|
FontAwesomeModule
|
|
],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
template: `
|
|
<div style="padding: 2rem;">
|
|
<h2>List Component Showcase</h2>
|
|
|
|
<!-- Basic Lists with Size Variants -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h3>Basic Lists - Size Variants</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin-bottom: 2rem;">
|
|
<div>
|
|
<h4>Small (sm)</h4>
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="xs"
|
|
[rounded]="true"
|
|
ariaLabel="Small navigation list">
|
|
@for (item of navigationItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="sm"
|
|
lines="one"
|
|
variant="text"
|
|
[divider]="true"
|
|
>
|
|
<fa-icon [icon]="getIconForItem(item.primary)" slot="trailing"></fa-icon>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
|
|
<div>
|
|
<h4>Medium (md)</h4>
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="xs"
|
|
[rounded]="true"
|
|
ariaLabel="Medium navigation list">
|
|
@for (item of navigationItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="md"
|
|
lines="one"
|
|
variant="text"
|
|
[divider]="true"
|
|
>
|
|
<fa-icon [icon]="getIconForItem(item.primary)" slot="trailing"></fa-icon>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
|
|
<div>
|
|
<h4>Large (lg)</h4>
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="xs"
|
|
[rounded]="true"
|
|
ariaLabel="Large navigation list">
|
|
@for (item of navigationItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="lg"
|
|
lines="one"
|
|
variant="text"
|
|
[divider]="true"
|
|
>
|
|
<fa-icon [icon]="getIconForItem(item.primary)" slot="trailing"></fa-icon>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Multi-line Lists -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h3>Multi-line Lists</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 2rem;">
|
|
<div>
|
|
<h4>Two Lines</h4>
|
|
<ui-list-container
|
|
elevation="md"
|
|
spacing="sm"
|
|
[rounded]="true"
|
|
ariaLabel="Two line task list">
|
|
@for (item of twoLineItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="md"
|
|
lines="two"
|
|
variant="text"
|
|
>
|
|
<button
|
|
slot="trailing"
|
|
style="background: none; border: none; padding: 8px; cursor: pointer; color: #6c757d;"
|
|
(click)="handleAction('complete', item.primary)"
|
|
[attr.aria-label]="'Complete ' + item.primary">
|
|
<fa-icon [icon]="faCheck"></fa-icon>
|
|
</button>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
|
|
<div>
|
|
<h4>Three Lines</h4>
|
|
<ui-list-container
|
|
elevation="md"
|
|
spacing="sm"
|
|
[rounded]="true"
|
|
ariaLabel="Three line task list">
|
|
@for (item of threeLineItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="lg"
|
|
lines="three"
|
|
variant="text"
|
|
>
|
|
<div slot="trailing" style="display: flex; gap: 0.5rem;">
|
|
<button
|
|
style="background: none; border: none; padding: 4px; cursor: pointer; color: #28a745;"
|
|
(click)="handleAction('approve', item.primary)"
|
|
[attr.aria-label]="'Approve ' + item.primary">
|
|
<fa-icon [icon]="faCheck"></fa-icon>
|
|
</button>
|
|
</div>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Avatar Lists -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h3>Lists with Avatars</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 2rem;">
|
|
<div>
|
|
<h4>Contacts - Avatar + One Line</h4>
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="xs"
|
|
[rounded]="true"
|
|
ariaLabel="Contact list">
|
|
@for (item of contactItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="md"
|
|
lines="one"
|
|
variant="avatar"
|
|
>
|
|
<div slot="trailing" style="display: flex; gap: 0.75rem;">
|
|
<button
|
|
style="background: none; border: none; padding: 4px; cursor: pointer; color: #007bff;"
|
|
(click)="handleAction('call', item.primary)"
|
|
[attr.aria-label]="'Call ' + item.primary">
|
|
<fa-icon [icon]="faPhone"></fa-icon>
|
|
</button>
|
|
<button
|
|
style="background: none; border: none; padding: 4px; cursor: pointer; color: #28a745;"
|
|
(click)="handleAction('message', item.primary)"
|
|
[attr.aria-label]="'Message ' + item.primary">
|
|
<fa-icon [icon]="faComment"></fa-icon>
|
|
</button>
|
|
</div>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
|
|
<div>
|
|
<h4>Messages - Avatar + Two Lines</h4>
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="xs"
|
|
[rounded]="true"
|
|
ariaLabel="Message list">
|
|
@for (item of messageItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="lg"
|
|
lines="two"
|
|
variant="avatar"
|
|
>
|
|
<span
|
|
slot="trailing"
|
|
style="font-size: 0.75rem; color: #6c757d;">
|
|
2h
|
|
</span>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Media Lists -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h3>Lists with Media</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); gap: 2rem;">
|
|
<div>
|
|
<h4>Music - Media + Two Lines</h4>
|
|
<ui-list-container
|
|
elevation="md"
|
|
spacing="sm"
|
|
[rounded]="true"
|
|
ariaLabel="Music playlist">
|
|
@for (item of musicItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="lg"
|
|
lines="two"
|
|
variant="media"
|
|
>
|
|
<div slot="trailing" style="display: flex; gap: 0.75rem; align-items: center;">
|
|
<span style="font-size: 0.875rem; color: #6c757d;">3:42</span>
|
|
<button
|
|
style="background: none; border: none; padding: 8px; cursor: pointer; color: #007bff;"
|
|
(click)="handleAction('play', item.primary)"
|
|
[attr.aria-label]="'Play ' + item.primary">
|
|
<fa-icon [icon]="faPlay"></fa-icon>
|
|
</button>
|
|
</div>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
|
|
<div>
|
|
<h4>Videos - Media + Three Lines</h4>
|
|
<ui-list-container
|
|
elevation="md"
|
|
spacing="sm"
|
|
[rounded]="true"
|
|
ariaLabel="Video library">
|
|
@for (item of videoItems; track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="lg"
|
|
lines="three"
|
|
variant="media"
|
|
>
|
|
<div slot="trailing" style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
|
<div style="display: flex; align-items: center; gap: 0.25rem;">
|
|
<fa-icon [icon]="faStar" style="color: #ffc107; font-size: 0.75rem;"></fa-icon>
|
|
<span style="font-size: 0.75rem; color: #6c757d;">4.8</span>
|
|
</div>
|
|
<button
|
|
style="background: none; border: none; padding: 4px; cursor: pointer; color: #007bff;"
|
|
(click)="handleAction('download', item.primary)"
|
|
[attr.aria-label]="'Download ' + item.primary">
|
|
<fa-icon [icon]="faDownload"></fa-icon>
|
|
</button>
|
|
</div>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Interactive Demo -->
|
|
<section style="margin-bottom: 3rem;">
|
|
<h3>Interactive Demo</h3>
|
|
<p style="margin-bottom: 1rem; color: #6c757d;">
|
|
Click items to interact with them:
|
|
</p>
|
|
|
|
<div style="max-width: 600px;">
|
|
<ui-list-container
|
|
elevation="sm"
|
|
spacing="sm"
|
|
[rounded]="true"
|
|
ariaLabel="Interactive demo list">
|
|
@for (item of interactiveItems(); track item.primary) {
|
|
<ui-list-item
|
|
[data]="item"
|
|
size="md"
|
|
lines="two"
|
|
variant="avatar"
|
|
style="cursor: pointer;"
|
|
(click)="toggleInteractiveItem(item.primary)"
|
|
>
|
|
<div slot="trailing" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
@if (item.selected) {
|
|
<span style="background: #28a745; color: white; padding: 4px 8px; border-radius: 12px; font-size: 0.75rem;">
|
|
Enabled
|
|
</span>
|
|
} @else if (item.disabled) {
|
|
<span style="background: #dc3545; color: white; padding: 4px 8px; border-radius: 12px; font-size: 0.75rem;">
|
|
Disabled
|
|
</span>
|
|
} @else {
|
|
<span style="background: #6c757d; color: white; padding: 4px 8px; border-radius: 12px; font-size: 0.75rem;">
|
|
Click to enable
|
|
</span>
|
|
}
|
|
</div>
|
|
</ui-list-item>
|
|
}
|
|
</ui-list-container>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Code 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 List with Icons:</h4>
|
|
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto; font-size: 0.875rem;"><code><ui-list-container elevation="sm" spacing="xs" [rounded]="true">
|
|
<ui-list-item
|
|
[data]="{primary: 'Inbox'}"
|
|
size="md"
|
|
lines="one"
|
|
variant="text">
|
|
<fa-icon [icon]="faInbox" slot="trailing"></fa-icon>
|
|
</ui-list-item>
|
|
</ui-list-container></code></pre>
|
|
|
|
<h4>List with Avatar and Actions:</h4>
|
|
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto; font-size: 0.875rem;"><code><ui-list-container elevation="md" spacing="sm" [rounded]="true">
|
|
<ui-list-item
|
|
[data]="{
|
|
primary: 'John Doe',
|
|
secondary: 'john@example.com',
|
|
avatarSrc: 'avatar.jpg'
|
|
}"
|
|
size="lg"
|
|
lines="two"
|
|
variant="avatar">
|
|
<button slot="trailing" (click)="call()">
|
|
<fa-icon [icon]="faPhone"></fa-icon>
|
|
</button>
|
|
</ui-list-item>
|
|
</ui-list-container></code></pre>
|
|
</div>
|
|
</section>
|
|
|
|
@if (lastAction()) {
|
|
<div style="position: fixed; bottom: 20px; right: 20px; background: #007bff; color: white; padding: 1rem; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); max-width: 300px;">
|
|
<strong>Action:</strong> {{ lastAction() }}
|
|
</div>
|
|
}
|
|
</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;
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
h4 {
|
|
color: hsl(287, 12%, 35%);
|
|
font-size: 1.125rem;
|
|
margin-bottom: 0.75rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
section {
|
|
border: 1px solid hsl(289, 14%, 90%);
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
background: hsl(286, 20%, 99%);
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
pre {
|
|
font-size: 0.875rem;
|
|
line-height: 1.5;
|
|
margin: 0.5rem 0;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
code {
|
|
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Courier New', monospace;
|
|
color: #d63384;
|
|
}
|
|
|
|
button {
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
button:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
button:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
fa-icon {
|
|
transition: color 0.2s ease;
|
|
}
|
|
`]
|
|
})
|
|
export class ListDemoComponent {
|
|
// State signals
|
|
lastAction = signal('');
|
|
|
|
// Demo data
|
|
navigationItems: ListItemData[] = [
|
|
{ primary: 'Inbox' },
|
|
{ primary: 'Starred' },
|
|
{ primary: 'Sent' },
|
|
{ primary: 'Drafts' },
|
|
{ primary: 'Archive' },
|
|
{ primary: 'Trash' }
|
|
];
|
|
|
|
twoLineItems: ListItemData[] = [
|
|
{
|
|
primary: 'Review Design System Updates',
|
|
secondary: 'Check new component specifications and design tokens'
|
|
},
|
|
{
|
|
primary: 'Team Standup Meeting',
|
|
secondary: 'Daily sync with development and design teams'
|
|
},
|
|
{
|
|
primary: 'Code Review - Authentication',
|
|
secondary: 'Review pull request #247 for OAuth integration'
|
|
}
|
|
];
|
|
|
|
threeLineItems: ListItemData[] = [
|
|
{
|
|
primary: 'Angular 19 Migration',
|
|
secondary: 'Upgrade project to latest Angular version with control flow',
|
|
tertiary: 'Estimated completion: Next Sprint • Priority: High'
|
|
},
|
|
{
|
|
primary: 'Design System Documentation',
|
|
secondary: 'Create comprehensive documentation for all components',
|
|
tertiary: 'Status: In Progress • Assigned: Design Team'
|
|
}
|
|
];
|
|
|
|
contactItems: ListItemData[] = [
|
|
{
|
|
primary: 'Sarah Wilson',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=sarah',
|
|
avatarAlt: 'Sarah Wilson avatar'
|
|
},
|
|
{
|
|
primary: 'Alex Chen',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=alex',
|
|
avatarAlt: 'Alex Chen avatar'
|
|
},
|
|
{
|
|
primary: 'Mike Rodriguez',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=mike',
|
|
avatarAlt: 'Mike Rodriguez avatar'
|
|
}
|
|
];
|
|
|
|
messageItems: ListItemData[] = [
|
|
{
|
|
primary: 'John Doe',
|
|
secondary: 'Hey! How\'s the new component library coming along?',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=john',
|
|
avatarAlt: 'John Doe avatar'
|
|
},
|
|
{
|
|
primary: 'Jane Smith',
|
|
secondary: 'The design tokens look great. Ready for review when you are.',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=jane',
|
|
avatarAlt: 'Jane Smith avatar'
|
|
}
|
|
];
|
|
|
|
musicItems: ListItemData[] = [
|
|
{
|
|
primary: 'Bohemian Rhapsody',
|
|
secondary: 'Queen • A Night at the Opera',
|
|
mediaSrc: 'https://picsum.photos/80/80?random=1',
|
|
mediaAlt: 'Album cover'
|
|
},
|
|
{
|
|
primary: 'Hotel California',
|
|
secondary: 'Eagles • Hotel California',
|
|
mediaSrc: 'https://picsum.photos/80/80?random=2',
|
|
mediaAlt: 'Album cover'
|
|
}
|
|
];
|
|
|
|
videoItems: ListItemData[] = [
|
|
{
|
|
primary: 'Angular Fundamentals',
|
|
secondary: 'Complete guide to modern Angular development',
|
|
tertiary: 'Duration: 2h 30m • Updated: Today • 1.2M views',
|
|
mediaSrc: 'https://picsum.photos/80/80?random=5',
|
|
mediaAlt: 'Video thumbnail'
|
|
}
|
|
];
|
|
|
|
interactiveItems = signal<ListItemData[]>([
|
|
{
|
|
primary: 'Email Notifications',
|
|
secondary: 'Receive emails about important updates',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=email',
|
|
selected: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
primary: 'SMS Alerts',
|
|
secondary: 'Get text messages for urgent notifications',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=sms',
|
|
selected: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
primary: 'Marketing Communications',
|
|
secondary: 'Promotional offers and product updates',
|
|
avatarSrc: 'https://api.dicebear.com/7.x/avataaars/svg?seed=marketing',
|
|
selected: false,
|
|
disabled: true
|
|
}
|
|
]);
|
|
|
|
// Font Awesome icons
|
|
faInbox = faInbox;
|
|
faStar = faStar;
|
|
faPaperPlane = faPaperPlane;
|
|
faEdit = faEdit;
|
|
faArchive = faArchive;
|
|
faTrash = faTrash;
|
|
faPhone = faPhone;
|
|
faComment = faComment;
|
|
faPlay = faPlay;
|
|
faDownload = faDownload;
|
|
faCheck = faCheck;
|
|
faChevronRight = faChevronRight;
|
|
faPlus = faPlus;
|
|
faMinus = faMinus;
|
|
|
|
// Helper methods
|
|
getIconForItem(itemName: string): any {
|
|
const iconMap: { [key: string]: any } = {
|
|
'Inbox': faInbox,
|
|
'Starred': faStar,
|
|
'Sent': faPaperPlane,
|
|
'Drafts': faEdit,
|
|
'Archive': faArchive,
|
|
'Trash': faTrash
|
|
};
|
|
return iconMap[itemName] || faInbox;
|
|
}
|
|
|
|
// Event handlers
|
|
handleAction(action: string, item?: string): void {
|
|
const message = item
|
|
? `${action} action on "${item}" at ${new Date().toLocaleTimeString()}`
|
|
: `${action} action at ${new Date().toLocaleTimeString()}`;
|
|
|
|
this.lastAction.set(message);
|
|
console.log(`List action: ${action}`, item);
|
|
|
|
// Clear the message after 3 seconds
|
|
setTimeout(() => this.lastAction.set(''), 3000);
|
|
}
|
|
|
|
toggleInteractiveItem(itemName: string): void {
|
|
this.interactiveItems.update(items =>
|
|
items.map(item => {
|
|
if (item.primary === itemName && !item.disabled) {
|
|
return { ...item, selected: !item.selected };
|
|
}
|
|
return item;
|
|
})
|
|
);
|
|
|
|
this.handleAction('toggle', itemName);
|
|
}
|
|
} |