Загрузка данных
import classNames from 'classnames';
import { Divider } from 'exchange-elements/v2';
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
import { Observable } from 'rxjs';
import { Button } from '@src/components/Button';
import { MenuList } from '@src/components/Menu';
import SplitDropdown from '@src/components/SplitDropdown';
import {
gannAndFibonacciTools,
geometricShapes,
measurementTools,
trendLines,
} from '@src/components/Toolbar/constants';
import { Tooltip } from '@src/components/Tooltip';
import { DrawingsNames } from '@src/constants';
import { t } from '@src/translations';
import { ActiveDrawingTool } from '@src/types';
import { ensureDefined, useObservable } from '@src/utils';
import {
CrossIcon,
EyeBrushIcon,
HeartIcon,
LayersIcon,
MagnetIcon,
PencilLockerIcon,
RulerIcon,
SplineCurveIcon,
TrashIcon,
TypeIcon,
UnlockIcon,
ZoomInIcon,
} from '../Icon';
import styles from './index.module.scss';
interface ToolbarProps {
toggleDOM: () => void;
addDrawing: (name: DrawingsNames) => void;
setEndlessDrawingsMode: (value: boolean) => void;
isEndlessDrawingsMode$: Observable<boolean>;
activateCrosshair: () => void;
activeTool$: Observable<ActiveDrawingTool>;
}
const implemented = {
crosshair: true,
lines: true,
fib: true,
rectangle: true,
text: true,
XABCD: false,
position: true,
icons: false,
};
export default function Toolbar({
toggleDOM,
addDrawing,
setEndlessDrawingsMode,
isEndlessDrawingsMode$,
activateCrosshair,
activeTool$,
}: ToolbarProps) {
const [selectedLineType, setSelectedLineType] = useState<DrawingsNames>(DrawingsNames.trendLine);
const [selectedMeasurementTool, setSelectedMeasurementTool] = useState(DrawingsNames.fixedRangeProfile);
const [selectedGeometricShape, setSelectedGeometricShape] = useState(DrawingsNames.rectangle);
const [selectedGannAndFibonacci, setSelectedGannAndFibonacci] = useState(DrawingsNames.fibonacciRetracement);
const isEndlessDrawingsMode = useObservable(isEndlessDrawingsMode$);
const activeTool = useObservable(activeTool$, 'crosshair');
const toolbarRef = useRef<HTMLDivElement | null>(null);
const createDrawingHandler = (setter: Dispatch<SetStateAction<DrawingsNames>>) => (value: DrawingsNames) => {
setter(value);
addDrawing(value);
};
const findOptionByValue = <T extends { value: string }>(options: T[], selectedValue: T['value']): T | undefined =>
options.find((item) => item.value === selectedValue);
const selectedTrendLineOption = findOptionByValue(trendLines(), selectedLineType);
const selectedMeasurementToolOption = findOptionByValue(measurementTools(), selectedMeasurementTool);
const selectedGeometricShapeOption = findOptionByValue(geometricShapes(), selectedGeometricShape);
const selectedGannAndFibonacciOption = findOptionByValue(gannAndFibonacciTools(), selectedGannAndFibonacci);
const isButtonPressed = (tool: ActiveDrawingTool): boolean => activeTool === tool;
useEffect(() => {
if (activeTool === 'crosshair') {
return;
}
if (trendLines().some(({ value }) => value === activeTool)) {
setSelectedLineType(activeTool);
return;
}
if (gannAndFibonacciTools().some(({ value }) => value === activeTool)) {
setSelectedGannAndFibonacci(activeTool);
return;
}
if (geometricShapes().some(({ value }) => value === activeTool)) {
setSelectedGeometricShape(activeTool);
return;
}
if (measurementTools().some(({ value }) => value === activeTool)) {
setSelectedMeasurementTool(activeTool);
}
}, [activeTool]);
return (
<div
ref={toolbarRef}
className={styles.toolbar}
>
<div className={classNames(styles.group)}>
{implemented.crosshair && (
<Tooltip
showMessageOnFocus
label={t('Crosshair')}
location="right"
>
<Button
onClick={activateCrosshair}
pressed={isButtonPressed('crosshair')}
label={<CrossIcon />}
/>
</Tooltip>
)}
{implemented.lines && (
<SplitDropdown
anchorRef={toolbarRef}
mainContent={ensureDefined(selectedTrendLineOption?.icon)}
mainTooltip={ensureDefined(selectedTrendLineOption).label}
onMainClick={() => addDrawing(selectedLineType)}
menuTooltip={t('Trend lines')}
mainButtonClassName={getButtonClassName(selectedLineType)}
>
<MenuList
mode="single"
value={selectedLineType}
options={trendLines()}
onClick={createDrawingHandler(setSelectedLineType)}
/>
</SplitDropdown>
)}
{implemented.fib && (
<SplitDropdown
anchorRef={toolbarRef}
mainContent={ensureDefined(selectedGannAndFibonacciOption?.icon)}
mainTooltip={ensureDefined(selectedGannAndFibonacciOption).label}
onMainClick={() => addDrawing(selectedGannAndFibonacci)}
menuTooltip={t('Gann and Fibonacci')}
mainButtonClassName={getButtonClassName(selectedGannAndFibonacci)}
>
<MenuList
mode="single"
value={selectedGannAndFibonacci}
options={gannAndFibonacciTools()}
onClick={createDrawingHandler(setSelectedGannAndFibonacci)}
/>
</SplitDropdown>
)}
{implemented.rectangle && (
<SplitDropdown
anchorRef={toolbarRef}
mainContent={ensureDefined(selectedGeometricShapeOption?.icon)}
mainTooltip={ensureDefined(selectedGeometricShapeOption).label}
onMainClick={() => addDrawing(selectedGeometricShape)}
menuTooltip={t('Geometric shapes')}
mainButtonClassName={getButtonClassName(selectedGeometricShape)}
>
<MenuList
mode="single"
value={selectedGeometricShape}
options={geometricShapes()}
onClick={createDrawingHandler(setSelectedGeometricShape)}
/>
</SplitDropdown>
)}
{implemented.text && (
<Tooltip
showMessageOnFocus
label={t('Text')}
location="right"
>
<Button
pressed={isButtonPressed(DrawingsNames.text)}
onClick={() => addDrawing(DrawingsNames.text)}
label={<TypeIcon />}
/>
</Tooltip>
)}
{implemented.XABCD && (
<Tooltip
showMessageOnFocus
label={t('XABCD template')}
location="right"
>
<Button
onClick={() => {}}
label={<SplineCurveIcon />}
/>
</Tooltip>
)}
{implemented.position && (
<SplitDropdown
anchorRef={toolbarRef}
mainContent={ensureDefined(selectedMeasurementToolOption?.icon)}
mainTooltip={ensureDefined(selectedMeasurementToolOption).label}
menuTooltip={t('Measurement tools')}
onMainClick={() => addDrawing(selectedMeasurementTool)}
mainButtonClassName={getButtonClassName(selectedMeasurementTool)}
>
<MenuList
mode="single"
value={selectedMeasurementTool}
options={measurementTools()}
onClick={createDrawingHandler(setSelectedMeasurementTool)}
/>
</SplitDropdown>
)}
{implemented.icons && (
<Tooltip
label={t('Pin')}
location="right"
showMessageOnFocus
>
<Button
onClick={() => {}}
label={<HeartIcon />}
/>
</Tooltip>
)}
</div>
<Divider
direction="horizontal"
pt={{ divider: { className: styles.divider } }}
/>
<div className={classNames(styles.group)}>
<Tooltip
showMessageOnFocus
label={t('Ruler')}
location="right"
>
<Button
pressed={isButtonPressed(DrawingsNames.ruler)}
onClick={() => addDrawing(DrawingsNames.ruler)}
label={<RulerIcon />}
/>
</Tooltip>
<Tooltip
showMessageOnFocus
label={t('Upscale')}
location="right"
className={styles.notImplemented}
>
<Button
onClick={() => {}}
label={<ZoomInIcon />}
/>
</Tooltip>
</div>
<Divider
direction="horizontal"
pt={{ divider: { className: classNames(styles.divider) } }}
/>
<div className={classNames(styles.group)}>
<Tooltip
showMessageOnFocus
label={t('Magnet allows you to attract objects points to the nearest bar prices')}
location="right"
className={styles.notImplemented}
>
<Button
onClick={() => {}}
label={<MagnetIcon />}
/>
</Tooltip>
<Tooltip
showMessageOnFocus
label={t('Endless drawing mode')}
location="right"
>
<Button
pressed={isEndlessDrawingsMode}
onClick={() => setEndlessDrawingsMode(!isEndlessDrawingsMode)}
label={<PencilLockerIcon />}
/>
</Tooltip>
<Tooltip
showMessageOnFocus
label={t('Fix all objects')}
location="right"
className={styles.notImplemented}
>
<Button
onClick={() => {}}
label={<UnlockIcon />}
/>
</Tooltip>
<Tooltip
showMessageOnFocus
label={t('Hide all drawing objects')}
location="right"
className={styles.notImplemented}
>
<Button
onClick={() => {}}
label={<EyeBrushIcon />}
/>
</Tooltip>
</div>
<Divider
direction="horizontal"
pt={{ divider: { className: classNames(styles.divider) } }}
/>
<div className={classNames(styles.group)}>
<Tooltip
showMessageOnFocus
label={t('Clear objects')}
location="right"
className={styles.notImplemented}
>
<Button
onClick={() => {}}
label={<TrashIcon />}
/>
</Tooltip>
<Tooltip
showMessageOnFocus
label={t('DOM tree')}
location="right"
>
<Button
onClick={toggleDOM}
label={<LayersIcon />}
/>
</Tooltip>
</div>
</div>
);
}
import classNames from 'classnames';
import { PropsWithChildren, RefObject } from 'react';
import { Button } from '@src/components/Button';
import { DropdownPosition } from '@src/components/Dropdown/types';
import SmallChevronLeftIcon from '@src/components/Icon/SmallChevronLeft';
import { Tooltip } from '@src/components/Tooltip';
import { Dropdown } from '../Dropdown';
import styles from './index.module.scss';
interface SplitDropdownProps extends PropsWithChildren {
position?: DropdownPosition;
mainButtonClassName?: string;
mainContent: JSX.Element;
menuButtonContent?: JSX.Element;
mainTooltip?: string;
menuTooltip?: string;
anchorRef?: RefObject<HTMLDivElement>;
onMainClick?: () => void;
tooltipClassName?: string;
}
export default function SplitDropdown({
position = 'right',
mainButtonClassName = '',
mainContent,
menuButtonContent = <SmallChevronLeftIcon />,
mainTooltip,
menuTooltip,
anchorRef,
onMainClick,
tooltipClassName,
children,
}: SplitDropdownProps) {
return (
<Dropdown
position={position}
anchorRef={anchorRef}
buttonClassName={mainButtonClassName}
renderTrigger={({ isOpen, onToggle }) => (
<div className={`${styles.dropdown} ${isOpen ? styles.open : ''}`}>
<Tooltip
tooltipClassName={classNames(styles.tooltip__main, tooltipClassName)}
showMessageOnFocus={!isOpen}
location="right"
label={mainTooltip}
>
<Button
className={mainButtonClassName}
pressed={isOpen}
onClick={onMainClick}
label={mainContent}
/>
</Tooltip>
<Tooltip
tooltipClassName={tooltipClassName}
showMessageOnFocus={!isOpen}
location="right"
label={menuTooltip}
>
<Button
className={styles.menu_button}
pressed={isOpen}
onClick={onToggle}
label={menuButtonContent}
/>
</Tooltip>
</div>
)}
>
{children}
</Dropdown>
);
}
.dropdown {
position: relative;
display: flex;
align-items: stretch;
width: var(--space-2000);
height: var(--space-2000);
&:hover .menu_button,
&:focus-within .menu_button,
&.open .menu_button {
opacity: 1;
pointer-events: auto;
}
.menu_button {
min-width: var(--space-0625);
padding: 0;
opacity: 0;
border-radius: var(--space-0125);
pointer-events: none;
div {
p {
svg {
width: var(--space-0625);
height: var(--space-0625);
transform: rotate(-180deg);
}
}
}
&.pressed div p svg {
transform: rotate(0deg);
}
}
}
.tooltip__main {
margin-left: var(--space-0375);
}