goldenChat base source add
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 상단 메뉴바 — 알림 팝업 표시 위치·방식 (아이콘 드롭다운)
|
||||
*/
|
||||
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;
|
||||
Reference in New Issue
Block a user