chore: initial import
This commit is contained in:
220
apps/visualizer/src/features/chart/ChartSideToolbar.tsx
Normal file
220
apps/visualizer/src/features/chart/ChartSideToolbar.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import ChartToolMenu, { type ToolMenuSection } from './ChartToolMenu';
|
||||
import {
|
||||
IconBrush,
|
||||
IconCrosshair,
|
||||
IconCursor,
|
||||
IconEye,
|
||||
IconFib,
|
||||
IconLock,
|
||||
IconPlus,
|
||||
IconRuler,
|
||||
IconSmile,
|
||||
IconText,
|
||||
IconResetView,
|
||||
IconTrash,
|
||||
IconTrendline,
|
||||
IconZoom,
|
||||
IconZoomOut,
|
||||
} from './ChartIcons';
|
||||
|
||||
type ActiveTool = 'cursor' | 'fib-retracement';
|
||||
|
||||
type Props = {
|
||||
timeframe: string;
|
||||
activeTool: ActiveTool;
|
||||
hasFib: boolean;
|
||||
onToolChange: (tool: ActiveTool) => void;
|
||||
onZoomIn: () => void;
|
||||
onZoomOut: () => void;
|
||||
onResetView: () => void;
|
||||
onClearFib: () => void;
|
||||
};
|
||||
|
||||
export default function ChartSideToolbar({
|
||||
timeframe,
|
||||
activeTool,
|
||||
hasFib,
|
||||
onToolChange,
|
||||
onZoomIn,
|
||||
onZoomOut,
|
||||
onResetView,
|
||||
onClearFib,
|
||||
}: Props) {
|
||||
const [openMenu, setOpenMenu] = useState<'fib' | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!openMenu) return;
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') setOpenMenu(null);
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}, [openMenu]);
|
||||
|
||||
const fibMenuSections: ToolMenuSection[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'fib',
|
||||
title: 'FIBONACCI',
|
||||
items: [
|
||||
{ id: 'fib-retracement', label: 'Fib Retracement', icon: <IconFib />, shortcut: 'Click 2 points' },
|
||||
{ id: 'fib-tb-ext', label: 'Trend-Based Fib Extension', icon: <IconTrendline /> },
|
||||
{ id: 'fib-channel', label: 'Fib Channel', icon: <IconFib /> },
|
||||
{ id: 'fib-time-zone', label: 'Fib Time Zone', icon: <IconFib /> },
|
||||
{ id: 'fib-speed-fan', label: 'Fib Speed Resistance Fan', icon: <IconFib /> },
|
||||
{ id: 'fib-tb-time', label: 'Trend-Based Fib Time', icon: <IconTrendline /> },
|
||||
{ id: 'fib-circles', label: 'Fib Circles', icon: <IconFib /> },
|
||||
{ id: 'fib-spiral', label: 'Fib Spiral', icon: <IconFib /> },
|
||||
{ id: 'fib-speed-arcs', label: 'Fib Speed Resistance Arcs', icon: <IconFib /> },
|
||||
{ id: 'fib-wedge', label: 'Fib Wedge', icon: <IconFib /> },
|
||||
{ id: 'pitchfan', label: 'Pitchfan', icon: <IconTrendline /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'gann',
|
||||
title: 'GANN',
|
||||
items: [
|
||||
{ id: 'gann-box', label: 'Gann Box', icon: <IconTrendline /> },
|
||||
{ id: 'gann-square-fixed', label: 'Gann Square Fixed', icon: <IconTrendline /> },
|
||||
{ id: 'gann-fan', label: 'Gann Fan', icon: <IconTrendline /> },
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="chartTools">
|
||||
<div className="chartSideToolbar">
|
||||
<button
|
||||
type="button"
|
||||
className={['chartToolBtn', activeTool === 'cursor' ? 'chartToolBtn--active' : ''].filter(Boolean).join(' ')}
|
||||
title="Cursor"
|
||||
aria-label="Cursor"
|
||||
onClick={() => {
|
||||
onToolChange('cursor');
|
||||
setOpenMenu(null);
|
||||
}}
|
||||
>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconCursor />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Crosshair" aria-label="Crosshair" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconCrosshair />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Add" aria-label="Add" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconPlus />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Trendline" aria-label="Trendline" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconTrendline />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={['chartToolBtn', activeTool === 'fib-retracement' ? 'chartToolBtn--active' : ''].filter(Boolean).join(' ')}
|
||||
title="Fibonacci"
|
||||
aria-label="Fibonacci"
|
||||
onClick={() => setOpenMenu((m) => (m === 'fib' ? null : 'fib'))}
|
||||
>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconFib />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Brush" aria-label="Brush" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconBrush />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Text" aria-label="Text" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconText />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Emoji" aria-label="Emoji" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconSmile />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Ruler" aria-label="Ruler" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconRuler />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div className="chartToolBtn__spacer" />
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Zoom In" aria-label="Zoom In" onClick={onZoomIn}>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconZoom />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Zoom Out" aria-label="Zoom Out" onClick={onZoomOut}>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconZoomOut />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Reset View" aria-label="Reset View" onClick={onResetView}>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconResetView />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Lock" aria-label="Lock" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconLock />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="chartToolBtn"
|
||||
title="Clear Fib"
|
||||
aria-label="Clear Fib"
|
||||
onClick={onClearFib}
|
||||
disabled={!hasFib}
|
||||
>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconTrash />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" className="chartToolBtn" title="Visibility" aria-label="Visibility" disabled>
|
||||
<span className="chartToolBtn__icon" aria-hidden="true">
|
||||
<IconEye />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{openMenu === 'fib' ? (
|
||||
<>
|
||||
<div className="chartToolMenuBackdrop" onClick={() => setOpenMenu(null)} />
|
||||
<ChartToolMenu
|
||||
timeframeLabel={timeframe}
|
||||
sections={fibMenuSections}
|
||||
onSelectItem={(id) => {
|
||||
if (id === 'fib-retracement') onToolChange('fib-retracement');
|
||||
setOpenMenu(null);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user