This repository has been archived on 2026-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
Files
ui-essentials/projects/demo-ui-essentials/src/app/demos/list-demo/list-demo.component.ts
skyai_dev 6f0ab0cf5f Fix SCSS semantic token variable errors across components
- 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>
2025-09-03 07:50:34 +10:00

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>&lt;ui-list-container elevation="sm" spacing="xs" [rounded]="true"&gt;
&lt;ui-list-item
[data]="&#123;primary: 'Inbox'&#125;"
size="md"
lines="one"
variant="text"&gt;
&lt;fa-icon [icon]="faInbox" slot="trailing"&gt;&lt;/fa-icon&gt;
&lt;/ui-list-item&gt;
&lt;/ui-list-container&gt;</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>&lt;ui-list-container elevation="md" spacing="sm" [rounded]="true"&gt;
&lt;ui-list-item
[data]="&#123;
primary: 'John Doe',
secondary: 'john&#64;example.com',
avatarSrc: 'avatar.jpg'
&#125;"
size="lg"
lines="two"
variant="avatar"&gt;
&lt;button slot="trailing" (click)="call()"&gt;
&lt;fa-icon [icon]="faPhone"&gt;&lt;/fa-icon&gt;
&lt;/button&gt;
&lt;/ui-list-item&gt;
&lt;/ui-list-container&gt;</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);
}
}