/* ============================================================================ * 서명전에 — TDS Skeleton 컴포넌트 * · 데이터 로딩 중 콘텐츠의 기본 레이아웃을 임시로 보여 주는 컴포넌트. * * · 두 가지 사용 방식: * pattern — 미리 정의된 9 개 패턴 중 선택 (기본 "topList") * custom — 자유롭게 조립한 타입 배열 (있으면 pattern 무시) * * · 9 개 프리셋 패턴 → 내부 시퀀스: * topList → ["title", "list"] * topListWithIcon → ["title", "listWithIcon"] * amountTopList → ["title", "subtitle", "list"] * amountTopListWithIcon → ["title", "subtitle", "listWithIcon"] * subtitleList → ["subtitle", "list"] * subtitleListWithIcon → ["subtitle", "listWithIcon"] * listOnly → ["list"] * listWithIconOnly → ["listWithIcon"] * cardOnly → ["card"] * * · custom 가능한 타입: * "title" — 굵고 큰 바 (24px) * "subtitle" — 얇은 바 (16px) * "list" — 가로형 바 2 줄 * "listWithIcon" — 좌측 40px 원형 + 우측 바 2 줄 * "card" — 직사각형 블록 (120px) * "spacer(NN)" — NN 픽셀 빈 공간 (예: "spacer(20)") * * · repeatLastItemCount: number | "infinite" * - 기본 3 — 마지막 요소가 총 3 번 반복돼요. * - "infinite" — 30 회로 캡 (성능 보호). * - 숫자 범위 외(0 이하/30 초과/소수)는 [1, 30] 으로 클램프. * * · play: "show" | "hide" * - hide → 컴포넌트 자체가 렌더되지 않음 (return null). * * · background: "white" | "grey" | "greyOpacity100" * - 시각적 바(bar/icon/card) 의 색을 결정. * - 컨테이너 배경은 transparent — 부모의 배경에 자연스럽게 얹힘. * - white: 어두운 부모용 / grey(기본): 일반 / greyOpacity100: 진한 회색. * * · 접근성: * role="status" + aria-busy="true" + aria-live="polite" * + aria-label="콘텐츠 로딩 중" (외부에서 aria-label 오버라이드 가능) * ========================================================================== */ const __SKELETON_PATTERNS = { topList: ["title", "list"], topListWithIcon: ["title", "listWithIcon"], amountTopList: ["title", "subtitle", "list"], amountTopListWithIcon: ["title", "subtitle", "listWithIcon"], subtitleList: ["subtitle", "list"], subtitleListWithIcon: ["subtitle", "listWithIcon"], listOnly: ["list"], listWithIconOnly: ["listWithIcon"], cardOnly: ["card"], }; const __SKELETON_BG_CLASS = { white: "tds-skeleton--bg-white", grey: "tds-skeleton--bg-grey", greyOpacity100: "tds-skeleton--bg-grey-opacity-100", }; const __SKELETON_PLAY = { show: 1, hide: 1 }; const __SKELETON_TYPES = { title: 1, subtitle: 1, list: 1, listWithIcon: 1, card: 1, }; const __SKELETON_SPACER_RE = /^spacer\((\d+)\)$/; function __isSkeletonSpacer(t) { return typeof t === "string" && __SKELETON_SPACER_RE.test(t); } function __getSkeletonSpacerSize(t) { const m = __SKELETON_SPACER_RE.exec(t); return m ? parseInt(m[1], 10) : 0; } // ----- 단일 아이템 렌더 ----- function __renderSkeletonItem(type, key) { if (__isSkeletonSpacer(type)) { const n = __getSkeletonSpacerSize(type); return (