- 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>
303 lines
11 KiB
TypeScript
303 lines
11 KiB
TypeScript
import { Component } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { SKELETON_COMPONENTS } from '../../../../../ui-essentials/src/lib/components/feedback/skeleton-loader/skeleton-loader.component';
|
|
|
|
@Component({
|
|
selector: 'ui-skeleton-loader-demo',
|
|
standalone: true,
|
|
imports: [CommonModule, FormsModule, ...SKELETON_COMPONENTS],
|
|
template: `
|
|
<div class="demo-container">
|
|
<h2>Skeleton Loader Demo</h2>
|
|
<p>Skeleton loaders provide visual placeholders while content is loading, improving perceived performance and user experience.</p>
|
|
|
|
<!-- Basic Sizes -->
|
|
<section class="demo-section">
|
|
<h3>Sizes</h3>
|
|
<div class="demo-row">
|
|
@for (size of sizes; track size) {
|
|
<div class="demo-item">
|
|
<label>{{ size }}</label>
|
|
<ui-skeleton-loader [size]="size" shape="text"></ui-skeleton-loader>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Shapes -->
|
|
<section class="demo-section">
|
|
<h3>Shapes</h3>
|
|
<div class="demo-grid">
|
|
@for (shape of shapes; track shape) {
|
|
<div class="demo-item">
|
|
<label>{{ shape }}</label>
|
|
<ui-skeleton-loader [shape]="shape" size="md"></ui-skeleton-loader>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Animations -->
|
|
<section class="demo-section">
|
|
<h3>Animation Types</h3>
|
|
<div class="demo-row">
|
|
@for (animation of animations; track animation) {
|
|
<div class="demo-item">
|
|
<label>{{ animation }}</label>
|
|
<ui-skeleton-loader
|
|
shape="text"
|
|
[animation]="animation"
|
|
size="md">
|
|
</ui-skeleton-loader>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Width Variants -->
|
|
<section class="demo-section">
|
|
<h3>Width Variants</h3>
|
|
<div class="demo-column">
|
|
@for (width of widths; track width) {
|
|
<div class="demo-item">
|
|
<label>{{ width }}</label>
|
|
<ui-skeleton-loader
|
|
shape="text"
|
|
[width]="width"
|
|
size="md">
|
|
</ui-skeleton-loader>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Pre-built Components -->
|
|
<section class="demo-section">
|
|
<h3>Pre-built Components</h3>
|
|
|
|
<!-- Text Block -->
|
|
<div class="demo-subsection">
|
|
<h4>Text Block</h4>
|
|
<div class="demo-grid">
|
|
<div class="demo-item">
|
|
<label>3 lines (default)</label>
|
|
<ui-skeleton-text-block></ui-skeleton-text-block>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>5 lines</label>
|
|
<ui-skeleton-text-block [lines]="5"></ui-skeleton-text-block>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>Custom last line width</label>
|
|
<ui-skeleton-text-block [lines]="4" lastLineWidth="w-25"></ui-skeleton-text-block>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Profile -->
|
|
<div class="demo-subsection">
|
|
<h4>Profile</h4>
|
|
<div class="demo-grid">
|
|
<div class="demo-item">
|
|
<label>Default</label>
|
|
<ui-skeleton-profile></ui-skeleton-profile>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>Small Avatar</label>
|
|
<ui-skeleton-profile avatarSize="sm"></ui-skeleton-profile>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>No Extra Info</label>
|
|
<ui-skeleton-profile [showExtraInfo]="false"></ui-skeleton-profile>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cards -->
|
|
<div class="demo-subsection">
|
|
<h4>Card</h4>
|
|
<div class="demo-grid">
|
|
<div class="demo-item">
|
|
<label>Full Card</label>
|
|
<ui-skeleton-card></ui-skeleton-card>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>No Image</label>
|
|
<ui-skeleton-card [showImage]="false"></ui-skeleton-card>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>No Actions</label>
|
|
<ui-skeleton-card [showActions]="false"></ui-skeleton-card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Article -->
|
|
<div class="demo-subsection">
|
|
<h4>Article</h4>
|
|
<div class="demo-item">
|
|
<label>Full Article</label>
|
|
<ui-skeleton-article></ui-skeleton-article>
|
|
</div>
|
|
<div class="demo-item">
|
|
<label>Article without Image</label>
|
|
<ui-skeleton-article [showImage]="false"></ui-skeleton-article>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table Rows -->
|
|
<div class="demo-subsection">
|
|
<h4>Table Rows</h4>
|
|
<div class="demo-column">
|
|
<label>Table Skeleton (3 rows)</label>
|
|
@for (row of [1,2,3]; track row) {
|
|
<ui-skeleton-table-row [columns]="4"></ui-skeleton-table-row>
|
|
}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Custom Groups -->
|
|
<section class="demo-section">
|
|
<h3>Skeleton Groups</h3>
|
|
|
|
<div class="demo-subsection">
|
|
<h4>Vertical Group</h4>
|
|
<div class="demo-item">
|
|
<ui-skeleton-group>
|
|
<ui-skeleton-loader shape="heading" size="lg" width="w-75"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="text" width="w-full"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="text" width="w-50"></ui-skeleton-loader>
|
|
</ui-skeleton-group>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="demo-subsection">
|
|
<h4>Horizontal Group</h4>
|
|
<div class="demo-item">
|
|
<ui-skeleton-group variant="horizontal">
|
|
<ui-skeleton-loader shape="avatar" size="md"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="button" customWidth="100px"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="button" customWidth="80px"></ui-skeleton-loader>
|
|
</ui-skeleton-group>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="demo-subsection">
|
|
<h4>Grid Group</h4>
|
|
<div class="demo-item">
|
|
<ui-skeleton-group variant="grid">
|
|
<ui-skeleton-loader shape="card"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="card"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="card"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="card"></ui-skeleton-loader>
|
|
</ui-skeleton-group>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Interactive Demo -->
|
|
<section class="demo-section">
|
|
<h3>Interactive Loading Simulation</h3>
|
|
<div class="demo-interactive">
|
|
<div class="demo-controls">
|
|
<button (click)="toggleLoading()" class="toggle-button">
|
|
{{ isLoading ? 'Show Content' : 'Show Loading' }}
|
|
</button>
|
|
<label>
|
|
Animation:
|
|
<select [(ngModel)]="selectedAnimation" (change)="onAnimationChange()">
|
|
@for (animation of animations; track animation) {
|
|
<option [value]="animation">{{ animation }}</option>
|
|
}
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="demo-content">
|
|
@if (isLoading) {
|
|
<!-- Loading state with selected animation -->
|
|
<ui-skeleton-article
|
|
[animation]="selectedAnimation"
|
|
[showImage]="true">
|
|
</ui-skeleton-article>
|
|
} @else {
|
|
<!-- Actual content -->
|
|
<article class="real-content">
|
|
<h3>Sample Article Title</h3>
|
|
<p class="meta">Published on January 15, 2024 by John Doe</p>
|
|
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPjQwMCB4IDIwMDwvdGV4dD48L3N2Zz4=" alt="Sample image" />
|
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
|
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
|
|
</article>
|
|
}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Best Practices -->
|
|
<section class="demo-section">
|
|
<h3>Best Practices</h3>
|
|
<div class="best-practices">
|
|
<ul>
|
|
<li><strong>Match Content Structure:</strong> Skeleton shapes should closely match the final content layout</li>
|
|
<li><strong>Use Appropriate Animations:</strong> Shimmer for fast loading, pulse for slower operations</li>
|
|
<li><strong>Respect Accessibility:</strong> Include proper ARIA labels and roles</li>
|
|
<li><strong>Consider Performance:</strong> Reduce animation complexity on low-performance devices</li>
|
|
<li><strong>Maintain Consistency:</strong> Use consistent skeleton patterns across your application</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Code Examples -->
|
|
<section class="demo-section">
|
|
<h3>Code Examples</h3>
|
|
<div class="code-examples">
|
|
<div class="code-example">
|
|
<h4>Basic Skeleton</h4>
|
|
<pre><code><ui-skeleton-loader shape="text" size="md"></ui-skeleton-loader></code></pre>
|
|
</div>
|
|
|
|
<div class="code-example">
|
|
<h4>Profile Skeleton</h4>
|
|
<pre><code><ui-skeleton-profile avatarSize="lg"></ui-skeleton-profile></code></pre>
|
|
</div>
|
|
|
|
<div class="code-example">
|
|
<h4>Custom Group</h4>
|
|
<pre><code><ui-skeleton-group variant="horizontal">
|
|
<ui-skeleton-loader shape="avatar" size="sm"></ui-skeleton-loader>
|
|
<ui-skeleton-loader shape="text" width="w-75"></ui-skeleton-loader>
|
|
</ui-skeleton-group></code></pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
`,
|
|
styleUrl: './skeleton-loader-demo.component.scss'
|
|
})
|
|
export class SkeletonLoaderDemoComponent {
|
|
sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
|
|
shapes = ['text', 'heading', 'avatar', 'card', 'button', 'image', 'circle', 'rounded', 'square'] as const;
|
|
animations = ['shimmer', 'pulse', 'wave'] as const;
|
|
widths = ['w-25', 'w-50', 'w-75', 'w-full'] as const;
|
|
|
|
isLoading = true;
|
|
selectedAnimation = 'shimmer' as any;
|
|
|
|
toggleLoading(): void {
|
|
this.isLoading = !this.isLoading;
|
|
}
|
|
|
|
onAnimationChange(): void {
|
|
// Force re-render by toggling loading state
|
|
if (this.isLoading) {
|
|
this.isLoading = false;
|
|
setTimeout(() => {
|
|
this.isLoading = true;
|
|
}, 50);
|
|
}
|
|
}
|
|
} |