import { ChangeDetectionStrategy, Component, computed, input, numberAttribute } from '@angular/core';
/**
* Loading placeholder. Hidden from assistive tech (aria-hidden); mark the loading container with
* aria-busy="true" and announce completion instead. The shimmer stops under prefers-reduced-motion.
*/
@Component({
selector: 'au-skeleton',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@for (line of lineWidths(); track $index) {
}
`,
host: { 'aria-hidden': 'true', '[attr.data-shape]': 'shape()' },
styles: `
:host { display: grid; gap: 0.5rem; }
.au-skeleton__block {
display: block; border-radius: calc(var(--au-radius) * 0.5);
background: linear-gradient(90deg, var(--au-surface-2) 25%, color-mix(in srgb, var(--au-surface-2) 55%, var(--au-border)) 50%, var(--au-surface-2) 75%);
background-size: 200% 100%; animation: au-shimmer 1.4s ease-in-out infinite;
}
:host([data-shape='circle']) .au-skeleton__block { border-radius: 50%; }
@keyframes au-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .au-skeleton__block { animation: none; } }
`,
})
export class AuSkeleton {
readonly shape = input<'text' | 'rect' | 'circle'>('text');
/** Number of text lines; the last one is shorter, like a paragraph. */
readonly lines = input(1, { transform: numberAttribute });
readonly width = input('100%');
readonly height = input(undefined);
protected readonly lineWidths = computed(() => {
if (this.shape() !== 'text') return [this.width()];
const n = Math.max(1, this.lines());
return Array.from({ length: n }, (_, i) => (n > 1 && i === n - 1 ? '60%' : this.width()));
});
protected readonly heightValue = computed(() => this.height() ?? (this.shape() === 'text' ? '0.9rem' : this.shape() === 'circle' ? this.width() : '6rem'));
}