import { bindGlobalStyle, getGlobalStylesId, CssProps, RefProps } from 'lupine.web'; import { Skeleton } from './skeleton'; export type SkeletonCardProps = { class?: string; style?: CssProps; width?: string; height?: string; animated?: boolean; }; export const SkeletonCard = (props: SkeletonCardProps) => { const css: CssProps = { display: 'flex', gap: '16px', alignItems: 'flex-start', width: '100%', height: '100%', padding: '24px', boxSizing: 'border-box', border: '1px solid var(--secondary-border-color, #eee)', borderRadius: '12px', backgroundColor: 'var(--primary-bg-color, #fff)', overflow: 'hidden', '.&-content': { flex: 1, display: 'flex', flexDirection: 'column', gap: '8px', minWidth: 0, }, }; const globalCssId = getGlobalStylesId(css); bindGlobalStyle(globalCssId, css); const animated = props.animated !== false; let ro: ResizeObserver | null = null; let timer: any = null; const contentRef: RefProps = {}; const ref: RefProps = { referToCssId: globalCssId, onLoad: async () => { const container = ref.current as HTMLElement; const refreshLines = () => { if (timer) clearTimeout(timer); timer = setTimeout(() => { // Calculate available vertical space in the content column const style = window.getComputedStyle(container); const paddingTop = parseFloat(style.paddingTop); const paddingBottom = parseFloat(style.paddingBottom); const availableHeight = container.clientHeight - paddingTop - paddingBottom; // Headings are 20px, sub-lines are 16px. Gap is 8px. // Let's assume the first line is always title: 20px // Then remaining lines take 16px + 8px gap = 24px each. let remainingHeight = availableHeight; const lines = []; // Title Line (20px) if (remainingHeight >= 20) { lines.push(); remainingHeight -= 28; // 20px height + 8px gap } // Sub line 1 (16px) full width if (remainingHeight >= 16) { lines.push(); remainingHeight -= 24; // 16px height + 8px gap } // Generate remaining lines (80% width for visual variety) while (remainingHeight >= 16) { // Give the last line a slightly different width if desired, but 80% is good lines.push( 0.5 ? '80%' : '90%'} height='16px' animated={animated} />); remainingHeight -= 24; } if (contentRef.mountInnerComponent) { contentRef.mountInnerComponent(<>{lines}); } }, 50); }; ro = new ResizeObserver(() => refreshLines()); ro.observe(container); }, onUnload: async () => { if (ro) ro.disconnect(); if (timer) clearTimeout(timer); }, }; const mergedStyle = { width: props.width || '100%', height: props.height || '100%', ...props.style, }; // Avatar size is fixed to 48px to represent a typical user profile return ( ); };