spb/qwhpi Public
QHPI — Quebec Housing Price Index: quality-adjusted, hierarchically pooled housing price indexes.
Python 63.9%
TypeScript 25.4%
CSS 5.5%
TeX 3.5%
SQL 0.8%
Makefile 0.5%
Dockerfile 0.5%
1/**2 * =============================================================================3 * QWHPI — Quebec Weekly Housing Price Index4 * Author : Simon-Pierre Boucher5 * Contact : contact@spboucher.ai6 * File : web/components/Skeleton.tsx7 * Purpose : Shimmer skeleton loaders — tiles, charts, tables (no "Loading…").8 * =============================================================================9 */1011export function SkeletonBlock({ h = 20, w = "100%", style = {} }: {12 h?: number; w?: number | string; style?: React.CSSProperties;13}) {14 return (15 <div className="skeleton" aria-hidden16 style={{ height: h, width: w, ...style }} />17 );18}1920export function TileSkeleton() {21 return (22 <div className="card stat-tile" aria-busy="true">23 <SkeletonBlock h={11} w="55%" />24 <SkeletonBlock h={32} w="70%" style={{ marginTop: 8 }} />25 <SkeletonBlock h={12} w="45%" style={{ marginTop: 6 }} />26 </div>27 );28}2930export function ChartSkeleton({ height = 340 }: { height?: number }) {31 return (32 <div className="card" aria-busy="true" aria-label="Chart loading">33 <SkeletonBlock h={14} w={220} />34 <SkeletonBlock h={height - 60} style={{ marginTop: 14 }} />35 </div>36 );37}3839export function TableSkeleton({ rows = 8 }: { rows?: number }) {40 return (41 <div className="card" aria-busy="true" style={{ padding: "10px 14px" }}>42 {Array.from({ length: rows }, (_, i) => (43 <SkeletonBlock key={i} h={16}44 style={{ marginTop: i ? 12 : 4, opacity: 1 - i * 0.09 }} />45 ))}46 </div>47 );48}4950export function DashboardSkeleton() {51 return (52 <>53 <div className="grid cols-4" style={{ marginTop: 24 }}>54 {Array.from({ length: 4 }, (_, i) => <TileSkeleton key={i} />)}55 </div>56 <div style={{ marginTop: 16 }}>57 <TableSkeleton rows={10} />58 </div>59 </>60 );61}62