/** 매매 알림 팝업 표시 위치 */ export type TradeAlertPopupPosition = 'right' | 'left' | 'bottom'; /** 매매 알림 팝업 배치 방식 */ export type TradeAlertPopupLayout = 'stack' | 'grid' | 'strip' | 'single'; export const TRADE_ALERT_POPUP_POSITIONS: { id: TradeAlertPopupPosition; label: string; desc: string }[] = [ { id: 'right', label: '화면 우측', desc: '우측 상단부터 아래로 쌓입니다 (기본)' }, { id: 'left', label: '화면 좌측', desc: '좌측 상단부터 아래로 쌓입니다' }, { id: 'bottom', label: '화면 하단', desc: '화면 하단 중앙에 표시됩니다' }, ]; export const TRADE_ALERT_POPUP_LAYOUTS: { id: TradeAlertPopupLayout; label: string; desc: string }[] = [ { id: 'stack', label: '세로 스택', desc: '알림을 위·아래로 순서대로 쌓아 표시' }, { id: 'grid', label: '그리드 (N×N)', desc: '알림을 격자 형태로 배치' }, { id: 'strip', label: '가로 나열', desc: '알림을 가로로 나열 (스크롤)' }, { id: 'single', label: '최신 1건', desc: '가장 최근 알림 1건만 표시' }, ]; export const TRADE_ALERT_GRID_COL_OPTIONS = [2, 3, 4] as const; export function normalizeTradeAlertPopupPosition(raw?: string | null): TradeAlertPopupPosition { if (raw === 'left' || raw === 'bottom') return raw; return 'right'; } export function normalizeTradeAlertPopupLayout(raw?: string | null): TradeAlertPopupLayout { if (raw === 'grid' || raw === 'strip' || raw === 'single') return raw; return 'stack'; } export function normalizeTradeAlertGridCols(raw?: number | null): number { const n = raw ?? 2; return Math.min(4, Math.max(2, Math.round(n))); } /** CSS 클래스 조합 */ export function tradeAlertPopupClassNames( position: TradeAlertPopupPosition, layout: TradeAlertPopupLayout, ): { stack: string; cards: string } { return { stack: `tsn-stack tsn-stack--${position} tsn-layout--${layout}`, cards: `tsn-cards tsn-cards--${layout}`, }; } const POSITION_SHORT: Record = { right: '우측', left: '좌측', bottom: '하단', }; const LAYOUT_SHORT: Record = { stack: '스택', grid: '그리드', strip: '가로', single: '1건', }; export function tradeAlertPopupModeKey( position: TradeAlertPopupPosition, layout: TradeAlertPopupLayout, ): string { return `${position}:${layout}`; } export function parseTradeAlertPopupModeKey(key: string): { position: TradeAlertPopupPosition; layout: TradeAlertPopupLayout; } { const [pos, lay] = key.split(':'); return { position: normalizeTradeAlertPopupPosition(pos), layout: normalizeTradeAlertPopupLayout(lay), }; } export function formatTradeAlertPopupMode( position: TradeAlertPopupPosition, layout: TradeAlertPopupLayout, ): string { return `${POSITION_SHORT[position]} · ${LAYOUT_SHORT[layout]}`; } /** 메뉴바 드롭다운용 — 위치별 배치 옵션 */ export function buildTradeAlertPopupModeOptions(): { position: TradeAlertPopupPosition; layout: TradeAlertPopupLayout; key: string; label: string; }[] { const options: { position: TradeAlertPopupPosition; layout: TradeAlertPopupLayout; key: string; label: string; }[] = []; for (const pos of TRADE_ALERT_POPUP_POSITIONS) { for (const lay of TRADE_ALERT_POPUP_LAYOUTS) { options.push({ position: pos.id, layout: lay.id, key: tradeAlertPopupModeKey(pos.id, lay.id), label: `${pos.label} · ${lay.label}`, }); } } return options; } /** @deprecated useToastPageCapacity 훅 사용 */ export function getToastPageSize( layout: TradeAlertPopupLayout, gridCols: number, ): number { switch (layout) { case 'single': return 1; case 'strip': return 4; case 'grid': return normalizeTradeAlertGridCols(gridCols) * 4; case 'stack': default: return 5; } }