73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { coerceFiniteNumber } from '../../utils/safeFormat';
|
|
|
|
const SEGMENTS = 25;
|
|
|
|
interface Props {
|
|
matchRate: number;
|
|
}
|
|
|
|
const VirtualSignalEqualizer: React.FC<Props> = ({ matchRate }) => {
|
|
const rate = coerceFiniteNumber(matchRate) ?? 0;
|
|
const litCount = useMemo(
|
|
() => Math.round((Math.min(100, Math.max(0, rate)) / 100) * SEGMENTS),
|
|
[rate],
|
|
);
|
|
|
|
const segments = useMemo(() => {
|
|
const items: Array<{ lit: boolean; tone: 'red' | 'gold' | 'blue' | 'peak' }> = [];
|
|
const third = SEGMENTS / 3;
|
|
for (let i = 0; i < SEGMENTS; i++) {
|
|
const lit = i < litCount;
|
|
if (!lit) {
|
|
items.push({ lit: false, tone: 'blue' });
|
|
continue;
|
|
}
|
|
let tone: 'red' | 'gold' | 'blue' | 'peak';
|
|
if (rate >= 100 && i === SEGMENTS - 1) {
|
|
tone = 'peak';
|
|
} else if (i < third) {
|
|
tone = 'red';
|
|
} else if (i < third * 2) {
|
|
tone = 'gold';
|
|
} else {
|
|
tone = 'blue';
|
|
}
|
|
items.push({ lit: true, tone });
|
|
}
|
|
return items;
|
|
}, [litCount, rate]);
|
|
|
|
/** 신호등 열 높이에 맞춘 20% 눈금 (가상매매·알림 공통) */
|
|
const ticks = [100, 80, 60, 40, 20, 0];
|
|
|
|
return (
|
|
<div className="vtd-sig-eq">
|
|
<div className="vtd-sig-eq-scale" aria-hidden>
|
|
{ticks.map(t => (
|
|
<span key={t} className="vtd-sig-eq-tick">{t}%</span>
|
|
))}
|
|
</div>
|
|
<div className="vtd-sig-eq-bar-wrap">
|
|
<div className="vtd-sig-eq-bar">
|
|
{segments.map((seg, i) => (
|
|
<div
|
|
key={i}
|
|
className={[
|
|
'vtd-sig-eq-seg',
|
|
seg.lit ? 'vtd-sig-eq-seg--lit' : '',
|
|
seg.lit ? `vtd-sig-eq-seg--${seg.tone}` : '',
|
|
].filter(Boolean).join(' ')}
|
|
/>
|
|
))}
|
|
</div>
|
|
{rate >= 100 && (
|
|
<div className="vtd-sig-eq-badge">100% MATCH</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VirtualSignalEqualizer;
|