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>
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
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
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user