Files
goldenChart/frontend/src/utils/marketSearchList.ts
T

118 lines
3.4 KiB
TypeScript

import type { MarketInfo, TickerData } from '../hooks/useMarketTicker';
export type MarketSortKey = 'name' | 'price' | 'change' | 'volume';
export type MarketSortDir = 'asc' | 'desc';
export interface MarketSearchRow {
market: string;
korean_name: string;
english_name: string;
symbol: string;
}
function compareNullableNum(
a: number | null | undefined,
b: number | null | undefined,
mul: number,
): number {
if (a == null && b == null) return 0;
if (a == null) return 1;
if (b == null) return -1;
return mul * (a - b);
}
export function marketInfosToRows(infos: MarketInfo[]): MarketSearchRow[] {
return infos.map(m => ({
market: m.market,
korean_name: m.koreanName,
english_name: m.englishName,
symbol: m.market.replace('KRW-', ''),
}));
}
export function filterMarketRows(rows: MarketSearchRow[], query: string): MarketSearchRow[] {
const q = query.trim();
if (!q) return rows;
const upper = q.toUpperCase();
const lower = q.toLowerCase();
return rows.filter(m =>
m.korean_name.includes(q)
|| m.symbol.toUpperCase().includes(upper)
|| m.english_name.toLowerCase().includes(lower)
|| m.market.toUpperCase().includes(upper),
);
}
/** MarketSearchPanel 과 동일 — 관심종목 우선·거래대금 또는 사용자 정렬 */
export function buildSortedMarketRows(
rows: MarketSearchRow[],
tickers: Map<string, TickerData>,
favs: Set<string>,
sortKey: MarketSortKey | null,
sortDir: MarketSortDir,
): MarketSearchRow[] {
if (!sortKey) {
return [...rows].sort((a, b) => {
const fa = favs.has(a.market) ? 1 : 0;
const fb = favs.has(b.market) ? 1 : 0;
if (fb !== fa) return fb - fa;
const va = tickers.get(a.market)?.accTradePrice24 ?? 0;
const vb = tickers.get(b.market)?.accTradePrice24 ?? 0;
return vb - va;
});
}
const mul = sortDir === 'asc' ? 1 : -1;
return [...rows].sort((a, b) => {
switch (sortKey) {
case 'name':
return mul * a.korean_name.localeCompare(b.korean_name, 'ko');
case 'price':
return compareNullableNum(
tickers.get(a.market)?.tradePrice,
tickers.get(b.market)?.tradePrice,
mul,
);
case 'change':
return compareNullableNum(
tickers.get(a.market)?.changeRate,
tickers.get(b.market)?.changeRate,
mul,
);
case 'volume':
return mul * (
(tickers.get(a.market)?.accTradePrice24 ?? 0)
- (tickers.get(b.market)?.accTradePrice24 ?? 0)
);
default:
return 0;
}
});
}
export function buildSortedMarketCodes(
marketInfos: MarketInfo[],
tickers: Map<string, TickerData>,
favs: Set<string>,
query: string,
sortKey: MarketSortKey | null,
sortDir: MarketSortDir,
): string[] {
const rows = filterMarketRows(marketInfosToRows(marketInfos), query);
return buildSortedMarketRows(rows, tickers, favs, sortKey, sortDir).map(r => r.market);
}
export function adjacentMarketInList(
orderedMarkets: string[],
currentMarket: string,
direction: 'prev' | 'next',
): string | null {
if (orderedMarkets.length === 0) return null;
let idx = orderedMarkets.indexOf(currentMarket);
if (idx < 0) {
idx = direction === 'next' ? -1 : orderedMarkets.length;
}
const nextIdx = direction === 'prev' ? idx - 1 : idx + 1;
if (nextIdx < 0 || nextIdx >= orderedMarkets.length) return null;
return orderedMarkets[nextIdx] ?? null;
}