goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
/** 매매 알림 팝업 표시 위치 */
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<TradeAlertPopupPosition, string> = {
right: '우측',
left: '좌측',
bottom: '하단',
};
const LAYOUT_SHORT: Record<TradeAlertPopupLayout, string> = {
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;
}
}