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/feed-layout-demo/feed-layout-demo.component.ts
skyai_dev 876eb301a0 Restructure layout components architecture
- Move layout components from layouts/ directory to components/layout/
- Reorganize divider component from data-display to layout category
- Add comprehensive layout component collection including aspect-ratio, bento-grid, box, breakpoint-container, center, column, dashboard-shell, feed-layout, flex, grid-container, hstack, list-detail-layout, scroll-container, section, sidebar-layout, stack, supporting-pane-layout, tabs-container, and vstack
- Update all demo components to match new layout structure
- Refactor routing and index exports to reflect reorganized component architecture

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-04 12:28:47 +10:00

359 lines
12 KiB
TypeScript

import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FeedLayoutComponent, FeedItem } from 'ui-essentials';
interface DemoFeedItem extends FeedItem {
title: string;
content: string;
timestamp: Date;
author: string;
likes: number;
type: 'text' | 'image' | 'video';
}
@Component({
selector: 'ui-feed-layout-demo',
standalone: true,
imports: [CommonModule, FormsModule, FeedLayoutComponent],
template: `
<div class="demo-container">
<h2>Feed Layout Demo</h2>
<!-- Size Variants -->
<section class="demo-section">
<h3>Sizes</h3>
<div class="demo-row">
@for (size of sizes; track size) {
<div class="demo-feed-wrapper">
<h4>{{ size | titlecase }}</h4>
<ui-feed-layout
[size]="size"
[loading]="false"
[enableInfiniteScroll]="false"
[enableRefresh]="false"
class="demo-feed demo-feed--preview">
@for (item of sampleItems.slice(0, 2); track item.id) {
<div class="demo-feed-item">
<div class="demo-feed-item__header">
<strong>{{ item.author }}</strong>
<span class="demo-feed-item__timestamp">{{ item.timestamp | date:'short' }}</span>
</div>
<h5 class="demo-feed-item__title">{{ item.title }}</h5>
<p class="demo-feed-item__content">{{ item.content }}</p>
<div class="demo-feed-item__actions">
<button class="demo-feed-item__action">♡ {{ item.likes }}</button>
<button class="demo-feed-item__action">💬 Comment</button>
<button class="demo-feed-item__action">↗ Share</button>
</div>
</div>
}
</ui-feed-layout>
</div>
}
</div>
</section>
<!-- Interactive Feed -->
<section class="demo-section">
<h3>Interactive Feed with Infinite Scroll</h3>
<div class="demo-controls">
<button (click)="resetFeed()" [disabled]="isLoading()">Reset Feed</button>
<button (click)="toggleError()">
{{ hasError() ? 'Clear Error' : 'Simulate Error' }}
</button>
<label>
<input type="checkbox" [(ngModel)]="enableRefreshControl" />
Enable Pull to Refresh
</label>
</div>
<ui-feed-layout
size="md"
[loading]="isLoading()"
[hasError]="hasError()"
[errorMessage]="errorMessage"
[isEmpty]="feedItems().length === 0 && !isLoading()"
[enableInfiniteScroll]="true"
[enableRefresh]="enableRefreshControl"
(loadMore)="loadMoreItems()"
(refresh)="refreshFeed()"
(retry)="retryLoad()"
class="demo-feed demo-feed--interactive"
#interactiveFeed>
@for (item of feedItems(); track item.id) {
<div class="demo-feed-item">
<div class="demo-feed-item__header">
<strong>{{ item.author }}</strong>
<span class="demo-feed-item__badge demo-feed-item__badge--{{item.type}}">
{{ item.type }}
</span>
<span class="demo-feed-item__timestamp">{{ item.timestamp | date:'short' }}</span>
</div>
<h5 class="demo-feed-item__title">{{ item.title }}</h5>
<p class="demo-feed-item__content">{{ item.content }}</p>
<div class="demo-feed-item__actions">
<button
class="demo-feed-item__action"
(click)="toggleLike(item)">
{{ item.likes > 0 ? '❤️' : '♡' }} {{ item.likes }}
</button>
<button class="demo-feed-item__action">💬 Comment</button>
<button class="demo-feed-item__action">↗ Share</button>
</div>
</div>
}
<!-- Empty state slot -->
<div slot="empty" class="demo-empty-state">
<div class="demo-empty-state__icon">📝</div>
<h4>No posts yet</h4>
<p>Be the first to share something!</p>
<button (click)="addSampleContent()" class="demo-empty-state__button">
Add Sample Content
</button>
</div>
</ui-feed-layout>
</section>
<!-- States Demo -->
<section class="demo-section">
<h3>States</h3>
<div class="demo-row">
<div class="demo-feed-wrapper">
<h4>Loading State</h4>
<ui-feed-layout
size="sm"
[loading]="true"
[enableInfiniteScroll]="false"
[enableRefresh]="false"
class="demo-feed demo-feed--preview">
@for (item of sampleItems.slice(0, 1); track item.id) {
<div class="demo-feed-item demo-feed-item--loading">
<div class="demo-feed-item__header">
<strong>{{ item.author }}</strong>
</div>
<h5 class="demo-feed-item__title">{{ item.title }}</h5>
<p class="demo-feed-item__content">{{ item.content }}</p>
</div>
}
</ui-feed-layout>
</div>
<div class="demo-feed-wrapper">
<h4>Error State</h4>
<ui-feed-layout
size="sm"
[loading]="false"
[hasError]="true"
errorMessage="Network connection failed"
[enableInfiniteScroll]="false"
[enableRefresh]="false"
class="demo-feed demo-feed--preview">
</ui-feed-layout>
</div>
<div class="demo-feed-wrapper">
<h4>Empty State</h4>
<ui-feed-layout
size="sm"
[loading]="false"
[isEmpty]="true"
[enableInfiniteScroll]="false"
[enableRefresh]="false"
class="demo-feed demo-feed--preview">
<div slot="empty" class="demo-empty-state demo-empty-state--small">
<div class="demo-empty-state__icon">📭</div>
<p>No content available</p>
</div>
</ui-feed-layout>
</div>
</div>
</section>
<!-- Usage Statistics -->
<section class="demo-section">
<h3>Usage Statistics</h3>
<div class="demo-stats">
<div class="demo-stat">
<span class="demo-stat__label">Total Items:</span>
<span class="demo-stat__value">{{ feedItems().length }}</span>
</div>
<div class="demo-stat">
<span class="demo-stat__label">Load More Calls:</span>
<span class="demo-stat__value">{{ loadMoreCount }}</span>
</div>
<div class="demo-stat">
<span class="demo-stat__label">Refresh Calls:</span>
<span class="demo-stat__value">{{ refreshCount }}</span>
</div>
</div>
</section>
</div>
`,
styleUrl: './feed-layout-demo.component.scss'
})
export class FeedLayoutDemoComponent {
sizes = ['sm', 'md', 'lg'] as const;
protected readonly feedItems = signal<DemoFeedItem[]>([]);
protected readonly isLoading = signal(false);
protected readonly hasError = signal(false);
protected errorMessage = '';
protected enableRefreshControl = true;
protected loadMoreCount = 0;
protected refreshCount = 0;
protected sampleItems: DemoFeedItem[] = [
{
id: '1',
title: 'Getting Started with Angular 19',
content: 'Angular 19 brings exciting new features including improved SSR, better performance, and enhanced developer experience.',
timestamp: new Date(Date.now() - 3600000),
author: 'Sarah Chen',
likes: 42,
type: 'text'
},
{
id: '2',
title: 'Building Responsive Layouts',
content: 'Learn how to create flexible, mobile-first designs that work beautifully across all devices.',
timestamp: new Date(Date.now() - 7200000),
author: 'Mike Rodriguez',
likes: 28,
type: 'image'
},
{
id: '3',
title: 'TypeScript Best Practices',
content: 'Discover advanced TypeScript patterns and techniques to write more maintainable code.',
timestamp: new Date(Date.now() - 10800000),
author: 'Alex Kim',
likes: 35,
type: 'video'
}
];
constructor() {
this.addSampleContent();
}
loadMoreItems(): void {
if (this.isLoading() || this.hasError()) return;
this.loadMoreCount++;
this.isLoading.set(true);
// Simulate API call delay
setTimeout(() => {
const currentItems = this.feedItems();
const nextBatch = this.generateItems(3, currentItems.length);
this.feedItems.set([...currentItems, ...nextBatch]);
this.isLoading.set(false);
}, 1500);
}
refreshFeed(): void {
this.refreshCount++;
this.isLoading.set(true);
this.hasError.set(false);
// Simulate refresh delay
setTimeout(() => {
const newItems = this.generateItems(5, 0);
this.feedItems.set(newItems);
this.isLoading.set(false);
}, 1000);
}
retryLoad(): void {
this.hasError.set(false);
this.loadMoreItems();
}
resetFeed(): void {
this.feedItems.set([]);
this.hasError.set(false);
this.isLoading.set(false);
this.loadMoreCount = 0;
this.refreshCount = 0;
this.addSampleContent();
}
toggleError(): void {
if (this.hasError()) {
this.hasError.set(false);
this.errorMessage = '';
} else {
this.hasError.set(true);
this.errorMessage = 'Failed to load more content. Please check your connection.';
}
}
toggleLike(item: DemoFeedItem): void {
const items = this.feedItems();
const index = items.findIndex(i => i.id === item.id);
if (index !== -1) {
const updatedItems = [...items];
updatedItems[index] = { ...item, likes: item.likes > 0 ? 0 : 1 };
this.feedItems.set(updatedItems);
}
}
addSampleContent(): void {
const items = this.generateItems(5, 0);
this.feedItems.set(items);
}
private generateItems(count: number, startId: number): DemoFeedItem[] {
const contentTemplates = [
{
title: 'Web Development Trends',
content: 'Exploring the latest trends in modern web development and what they mean for developers.',
author: 'Jane Doe',
type: 'text' as const
},
{
title: 'UI/UX Design Principles',
content: 'Understanding the fundamental principles that make great user interfaces and experiences.',
author: 'John Smith',
type: 'image' as const
},
{
title: 'Performance Optimization',
content: 'Tips and techniques for optimizing web application performance and user experience.',
author: 'Emma Wilson',
type: 'video' as const
},
{
title: 'Accessibility Matters',
content: 'Why web accessibility is crucial and how to implement it in your projects.',
author: 'David Brown',
type: 'text' as const
},
{
title: 'Mobile-First Design',
content: 'Best practices for designing mobile-first responsive web applications.',
author: 'Lisa Garcia',
type: 'image' as const
}
];
return Array.from({ length: count }, (_, index) => {
const template = contentTemplates[index % contentTemplates.length];
const id = startId + index + 1;
return {
id: id.toString(),
title: `${template.title} #${id}`,
content: template.content,
timestamp: new Date(Date.now() - (index + 1) * 1800000),
author: template.author,
likes: Math.floor(Math.random() * 50),
type: template.type
};
});
}
}