132 lines
4.5 KiB
TypeScript
132 lines
4.5 KiB
TypeScript
/**
|
|
* 상단 메뉴바 — 알림 팝업 표시 위치·방식 (아이콘 드롭다운)
|
|
*/
|
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
import TradeAlertPopupModeIcon from './TradeAlertPopupModeIcon';
|
|
import {
|
|
TRADE_ALERT_GRID_COL_OPTIONS,
|
|
buildTradeAlertPopupModeOptions,
|
|
formatTradeAlertPopupMode,
|
|
normalizeTradeAlertGridCols,
|
|
normalizeTradeAlertPopupLayout,
|
|
normalizeTradeAlertPopupPosition,
|
|
tradeAlertPopupModeKey,
|
|
type TradeAlertPopupLayout,
|
|
type TradeAlertPopupPosition,
|
|
} from '../utils/tradeAlertPopupLayout';
|
|
|
|
interface Props {
|
|
position?: string;
|
|
layout?: string;
|
|
gridCols?: number;
|
|
onPositionChange?: (v: TradeAlertPopupPosition) => void;
|
|
onLayoutChange?: (v: TradeAlertPopupLayout) => void;
|
|
onGridColsChange?: (v: number) => void;
|
|
}
|
|
|
|
export const TradeAlertPopupMenubarSelect: React.FC<Props> = ({
|
|
position: positionRaw = 'right',
|
|
layout: layoutRaw = 'stack',
|
|
gridCols: gridColsRaw = 2,
|
|
onPositionChange,
|
|
onLayoutChange,
|
|
onGridColsChange,
|
|
}) => {
|
|
const position = normalizeTradeAlertPopupPosition(positionRaw);
|
|
const layout = normalizeTradeAlertPopupLayout(layoutRaw);
|
|
const gridCols = normalizeTradeAlertGridCols(gridColsRaw);
|
|
const modeKey = tradeAlertPopupModeKey(position, layout);
|
|
const modeOptions = useMemo(() => buildTradeAlertPopupModeOptions(), []);
|
|
|
|
const [open, setOpen] = useState(false);
|
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onDoc = (e: MouseEvent) => {
|
|
if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
|
|
setOpen(false);
|
|
}
|
|
};
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') setOpen(false);
|
|
};
|
|
document.addEventListener('mousedown', onDoc);
|
|
document.addEventListener('keydown', onKey);
|
|
return () => {
|
|
document.removeEventListener('mousedown', onDoc);
|
|
document.removeEventListener('keydown', onKey);
|
|
};
|
|
}, [open]);
|
|
|
|
const selectMode = useCallback((
|
|
nextPos: TradeAlertPopupPosition,
|
|
nextLay: TradeAlertPopupLayout,
|
|
) => {
|
|
if (nextPos !== position) onPositionChange?.(nextPos);
|
|
if (nextLay !== layout) onLayoutChange?.(nextLay);
|
|
setOpen(false);
|
|
}, [position, layout, onPositionChange, onLayoutChange]);
|
|
|
|
const currentLabel = formatTradeAlertPopupMode(position, layout);
|
|
|
|
return (
|
|
<div className="tmb-notify-layout" ref={rootRef}>
|
|
<button
|
|
type="button"
|
|
className={`tmb-notify-layout-trigger${open ? ' tmb-notify-layout-trigger--open' : ''}`}
|
|
onClick={() => setOpen(v => !v)}
|
|
aria-label={`알림 표시 방식: ${currentLabel}`}
|
|
aria-expanded={open}
|
|
aria-haspopup="listbox"
|
|
title={`알림 표시: ${currentLabel}`}
|
|
>
|
|
<TradeAlertPopupModeIcon position={position} layout={layout} size={20} />
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="tmb-notify-layout-menu" role="listbox" aria-label="알림 표시 방식">
|
|
{modeOptions.map(opt => {
|
|
const active = opt.key === modeKey;
|
|
return (
|
|
<button
|
|
key={opt.key}
|
|
type="button"
|
|
role="option"
|
|
aria-selected={active}
|
|
className={`tmb-notify-layout-option${active ? ' tmb-notify-layout-option--active' : ''}`}
|
|
onClick={() => selectMode(opt.position, opt.layout)}
|
|
>
|
|
<span className="tmb-notify-layout-option-icon">
|
|
<TradeAlertPopupModeIcon position={opt.position} layout={opt.layout} size={22} />
|
|
</span>
|
|
<span className="tmb-notify-layout-option-label">{opt.label}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
|
|
{layout === 'grid' && (
|
|
<div className="tmb-notify-layout-cols-row">
|
|
<span className="tmb-notify-layout-cols-label">그리드 열</span>
|
|
<div className="tmb-notify-layout-cols-btns">
|
|
{TRADE_ALERT_GRID_COL_OPTIONS.map(n => (
|
|
<button
|
|
key={n}
|
|
type="button"
|
|
className={`tmb-notify-layout-col-btn${gridCols === n ? ' tmb-notify-layout-col-btn--active' : ''}`}
|
|
onClick={() => onGridColsChange?.(n)}
|
|
>
|
|
{n}열
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TradeAlertPopupMenubarSelect;
|