Загрузка данных
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a27153d781625fb8b87dcd91bde52149efa0f954..e2f361f973674d2576eef0a6e41276b6b94c9375 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
# latest
+- Исправлена обработка и отображение фокуса при работе с drawing-инструментами и горячими клавишами
- Добавлена возможность копирования изображения графика в буфер обмена
- Добавлены тултипы для элементов управления в шапке графика
- Добавлен параметр `showSnapshotButton?: boolean` для управления отображением кнопки «Снимок графика»
diff --git a/src/components/IndicatorsSelect/index.module.scss b/src/components/Button/index.module.scss
similarity index 77%
rename from src/components/IndicatorsSelect/index.module.scss
rename to src/components/Button/index.module.scss
index 309d105fde3e6c8bfd1d14250041e0a31373cc75..f81bf9ec3ace933f2a7ebe086af52c146565f8b7 100644
--- a/src/components/IndicatorsSelect/index.module.scss
+++ b/src/components/Button/index.module.scss
@@ -1,5 +1,5 @@
@use '../../theme/mixins' as m;
-.button {
+.button.button {
@include m.buttonBase;
}
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0d649ecb62eabb93925608c532fafad6c6745db9
--- /dev/null
+++ b/src/components/Button/index.tsx
@@ -0,0 +1,25 @@
+import classNames from 'classnames';
+import { Button as ExchangeButton } from 'exchange-elements/v2';
+import { type ComponentProps } from 'react';
+
+import styles from './index.module.scss';
+
+export type ExchangeButtonProps = ComponentProps<typeof ExchangeButton>;
+
+interface ButtonProps extends ExchangeButtonProps {
+ pressed?: boolean;
+ selected?: boolean;
+}
+
+export function Button({ size = 'sm', pressed = false, selected = false, className, ...props }: ButtonProps) {
+ return (
+ <ExchangeButton
+ {...props}
+ size={size}
+ className={classNames(styles.button, className, {
+ [styles.pressed]: pressed,
+ [styles.selected]: selected,
+ })}
+ />
+ );
+}
diff --git a/src/components/CompareLegendRow/index.tsx b/src/components/CompareLegendRow/index.tsx
index 78f0b647faedfc8ef65759acab55bb0a2fd9382a..ea29fc2f8b2d56e7363b457e5ca98e8cb49d2608 100644
--- a/src/components/CompareLegendRow/index.tsx
+++ b/src/components/CompareLegendRow/index.tsx
@@ -1,5 +1,4 @@
-import { Button } from 'exchange-elements/v2';
-
+import { Button } from '@src/components/Button';
import { TrashIcon } from '@src/components/Icon';
import { CompareLegendItem } from '@src/core/Legend';
@@ -31,7 +30,6 @@ export function CompareLegendRow({ item, onRemove }: CompareLegendRowProps) {
{onRemove && (
<Button
- size="sm"
className={styles.button}
onClick={handleDelete}
label={<TrashIcon />}
diff --git a/src/components/ControlBar/index.module.scss b/src/components/ControlBar/index.module.scss
index 7f7c391b21858b5a9bb3dc7fb9eb72a80503aa52..5639eb57ead6d60cfd2aea7e664475adb09b87df 100644
--- a/src/components/ControlBar/index.module.scss
+++ b/src/components/ControlBar/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.controller {
display: flex;
justify-content: center;
@@ -22,7 +20,5 @@
.button {
background-color: var(--neutral-3);
-
- @include m.buttonBase;
}
}
diff --git a/src/components/ControlBar/index.tsx b/src/components/ControlBar/index.tsx
index c00ded0917fac6419ff50cc501c9f51dc38cda04..876b7ddf2f0e050b282b6cfd1a1f14e3a8a70cb0 100644
--- a/src/components/ControlBar/index.tsx
+++ b/src/components/ControlBar/index.tsx
@@ -1,11 +1,11 @@
import classNames from 'classnames';
-import { Button } from 'exchange-elements/v2';
import { Observable } from 'rxjs';
import { LongPressButton } from '@components/LongPressButton';
import { Resize } from '@core/Chart';
+import { Button } from '@src/components/Button';
import { Direction } from '@src/types';
import { useObservable } from '@src/utils';
@@ -33,13 +33,11 @@ export function ControlBar({ scroll, zoom, reset, visible }: ControlBarProps) {
<section className={classNames(styles.controller, { [styles.controller_visible]: visibleControlBar })}>
<div className={styles.group}>
<Button
- size="sm"
className={styles.button}
onClick={() => zoom(Resize.Shrink)}
label={<MinusIcon />}
/>
<Button
- size="sm"
className={styles.button}
onClick={() => zoom(Resize.Expand)}
label={<PlusIcon />}
@@ -47,7 +45,6 @@ export function ControlBar({ scroll, zoom, reset, visible }: ControlBarProps) {
</div>
<div className={styles.group}>
<LongPressButton
- size="sm"
className={styles.button}
delayMs={ControlBarDefaults.longPressThreshold}
onExecute={() => scroll(Direction.Left)}
@@ -55,7 +52,6 @@ export function ControlBar({ scroll, zoom, reset, visible }: ControlBarProps) {
label={<ChevronLeftIcon />}
/>
<LongPressButton
- size="sm"
className={styles.button}
delayMs={ControlBarDefaults.longPressThreshold}
onExecute={() => scroll(Direction.Right)}
@@ -64,7 +60,6 @@ export function ControlBar({ scroll, zoom, reset, visible }: ControlBarProps) {
/>
</div>
<Button
- size="sm"
className={styles.button}
onClick={reset}
label={<RefreshIcon />}
diff --git a/src/components/DOM/index.module.scss b/src/components/DOM/index.module.scss
index 0a2c8ac8b99677777b3457870d727363083538d2..35dca99aad5e475d9167410436edef82a62c42d5 100644
--- a/src/components/DOM/index.module.scss
+++ b/src/components/DOM/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.eyeBlurred {
stroke-opacity: 0.3;
}
@@ -30,8 +28,6 @@
}
& .button {
- @include m.buttonBase;
-
&Content {
display: flex;
align-items: flex-start;
diff --git a/src/components/DOM/index.tsx b/src/components/DOM/index.tsx
index db56a43e8d22f5222d2768c1c87a6d00bb2d77eb..e1c5b2d9042fb598e3a8ff738a47327bbfed586c 100644
--- a/src/components/DOM/index.tsx
+++ b/src/components/DOM/index.tsx
@@ -1,4 +1,4 @@
-import { Button, Divider } from 'exchange-elements/v2';
+import { Divider } from 'exchange-elements/v2';
import { Fragment } from 'react/jsx-runtime';
import { Observable } from 'rxjs';
@@ -6,6 +6,7 @@ import { Observable } from 'rxjs';
import { TrashIcon } from '@components/Icon';
import { IDOMObject } from '@core/DOMObject';
+import { Button } from '@src/components/Button';
import { useObservable } from '@src/utils';
import EyeBrushIcon from '../Icon/EyeBrush';
@@ -40,28 +41,24 @@ function DOMRow({ elem }: DOMRowProps) {
<div className={styles.controlGroup}>
<Button
- size="sm"
className={styles.button}
onClick={() => elem.delete()}
label={<TrashIcon />}
/>
<Button
- size="sm"
className={styles.button}
onClick={handleToggleHidden}
label={<EyeBrushIcon className={hidden ? styles.eyeBlurred : ''} />}
/>
<Button
- size="sm"
className={styles.button}
onClick={() => elem.moveDown()}
label="↑"
/>
<Button
- size="sm"
className={styles.button}
onClick={() => elem.moveUp()}
label="↓"
diff --git a/src/components/Dropdown/index.module.scss b/src/components/Dropdown/index.module.scss
index b531f637de8ab438fc3b4f407330276c882ef4d6..534d73e6b0fc898120da8ab961f3ca12c3f6f306 100644
--- a/src/components/Dropdown/index.module.scss
+++ b/src/components/Dropdown/index.module.scss
@@ -4,10 +4,6 @@ $menu-width: 200px;
.dropdown {
position: relative;
-
- .button {
- @include m.buttonBase;
- }
}
.menu {
diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx
index 20194ceba77c09a47978685ef820bef74863d79d..5440e77bc5debfa87fb535606897651e84683a1b 100644
--- a/src/components/Dropdown/index.tsx
+++ b/src/components/Dropdown/index.tsx
@@ -1,6 +1,7 @@
-import { Button } from 'exchange-elements/v2';
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
+import { Button } from '@src/components/Button';
+
import { Portal } from '@src/components/Portal';
import styles from './index.module.scss';
@@ -134,9 +135,9 @@ export function Dropdown({
})
) : (
<Button
- size="sm"
onClick={handleToggle}
- className={`${styles.button} ${isOpenMenu ? styles.pressed : ''} ${buttonClassName}`}
+ className={buttonClassName}
+ pressed={isOpenMenu}
label={selectedValue}
/>
)}
diff --git a/src/components/EntitySettingsModal/index.tsx b/src/components/EntitySettingsModal/index.tsx
index 6a4704ec811e43c205668d3cf15514589a77a4aa..1ea04f36b8de8b736fab8c07c7688f36c9cd7488 100644
--- a/src/components/EntitySettingsModal/index.tsx
+++ b/src/components/EntitySettingsModal/index.tsx
@@ -1,7 +1,8 @@
import classNames from 'classnames';
-import { Button } from 'exchange-elements/v2';
import { useEffect, useMemo, useState } from 'react';
+import { Button } from '@src/components/Button';
+
import {
CheckboxField,
ColorField,
diff --git a/src/components/FloatingToolbar/index.module.scss b/src/components/FloatingToolbar/index.module.scss
index e1b3336c773ac7186fae9225628df26698980e2c..c6a95e2485a72a99e4c84823cab7baebb793c94b 100644
--- a/src/components/FloatingToolbar/index.module.scss
+++ b/src/components/FloatingToolbar/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
$handle-size: 3px;
.toolbar {
@@ -40,8 +38,4 @@ $handle-size: 3px;
background-color: var(--neutral-700);
}
}
-
- .button {
- @include m.buttonBase;
- }
}
diff --git a/src/components/FloatingToolbar/index.tsx b/src/components/FloatingToolbar/index.tsx
index cc37844c83e1bae161e184cc010aa444ba7d2297..00665083359e13e1d18520135377d72a00d2050e 100644
--- a/src/components/FloatingToolbar/index.tsx
+++ b/src/components/FloatingToolbar/index.tsx
@@ -1,7 +1,8 @@
import classNames from 'classnames';
-import { Button } from 'exchange-elements/v2';
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
+import { Button } from '@src/components/Button';
+
import { GearIcon, LockIcon, LockOpenIcon, TrashIcon } from '@src/components/Icon';
import { ToolbarColorControl } from '@src/components/ToolbarColorControl';
import { Tooltip } from '@src/components/Tooltip';
@@ -252,8 +253,6 @@ export function FloatingDrawingToolbar({
location="top"
>
<Button
- size="sm"
- className={styles.button}
onClick={onOpenSettings}
label={<GearIcon />}
/>
@@ -265,10 +264,7 @@ export function FloatingDrawingToolbar({
location="top"
>
<Button
- size="sm"
- className={classNames(styles.button, {
- [styles.pressed]: isLocked,
- })}
+ pressed={isLocked}
onClick={onToggleLock}
label={isLocked ? <LockIcon /> : <LockOpenIcon />}
/>
@@ -280,8 +276,6 @@ export function FloatingDrawingToolbar({
location="top"
>
<Button
- size="sm"
- className={styles.button}
onClick={onDelete}
label={<TrashIcon />}
/>
diff --git a/src/components/Footer/index.module.scss b/src/components/Footer/index.module.scss
index ebbe9f5a66c667e2cdddfa5220d062f4341f6241..a5ca289da1a431f7effb9931395f3f9d64151575 100644
--- a/src/components/Footer/index.module.scss
+++ b/src/components/Footer/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.footer {
width: 100%;
height: 100%;
@@ -17,10 +15,6 @@
overflow: auto;
}
- .interval {
- @include m.buttonBase;
- }
-
.time {
text-align: right;
font-size: var(--space-0750);
diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx
index dc387ce53eb87e3146750035ea4c10a98d62adcb..0b0e970e1ea939b03fb99d669e7ee3b7593a8752 100644
--- a/src/components/Footer/index.tsx
+++ b/src/components/Footer/index.tsx
@@ -1,10 +1,11 @@
import classNames from 'classnames';
import dayjs from 'dayjs';
-import { Button } from 'exchange-elements/v2';
import { useEffect, useState } from 'react';
import { Observable } from 'rxjs';
+import { Button } from '@src/components/Button';
+
import { Tooltip } from '@src/components/Tooltip';
import { CHART_SCROLLABLE_CLASSNAME } from '@src/constants';
import { t } from '@src/translations';
@@ -51,9 +52,7 @@ export function Footer({ setInterval: setIntervalValue, intervalObs, supportedTi
>
<Button
key={interval}
- size="sm"
- variant="solid"
- className={`${styles.interval} ${interval === selectedInterval ? styles.pressed : ''}`}
+ pressed={interval === selectedInterval}
onClick={() => setIntervalValue(interval)}
label={t(interval)}
/>
diff --git a/src/components/Header/index.module.scss b/src/components/Header/index.module.scss
index 41ea068763c3323b03513251abc9105fc5f75222..fccd2975339abbf4224171b36646f9723d55e95b 100644
--- a/src/components/Header/index.module.scss
+++ b/src/components/Header/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.header {
width: 100%;
height: var(--size-sm);
@@ -15,10 +13,6 @@
gap: var(--space-0500);
}
- .button {
- @include m.buttonBase;
- }
-
.divider {
width: var(--borderWidth-md);
height: var(--space-1000);
diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx
index 13e0b61e5746d5cb1929c0ce79f735d00aea6899..5b82d5dbb7acf6e1e471ef84c1397b1f643e6d0d 100644
--- a/src/components/Header/index.tsx
+++ b/src/components/Header/index.tsx
@@ -1,10 +1,11 @@
-import { Button, Divider } from 'exchange-elements/v2';
+import { Divider } from 'exchange-elements/v2';
import { useEffect, useState } from 'react';
import { type Observable, of } from 'rxjs';
import { IndicatorsSelect } from '@components/IndicatorsSelect';
+import { Button } from '@src/components/Button';
import { Tooltip } from '@src/components/Tooltip';
import { IndicatorsIds } from '@src/constants';
import { FullscreenController } from '@src/core/Fullscreen';
@@ -113,8 +114,7 @@ export function Header({
label={t(isToolbarOpen ? 'Hide drawing toolbar' : 'Show drawing toolbar')}
>
<Button
- size="sm"
- className={`${styles.button} ${isToolbarOpen ? styles.pressed : ''}`}
+ pressed={isToolbarOpen}
onClick={handleOpenToolbar}
label={<BurgerMenuIcon />}
/>
@@ -127,8 +127,6 @@ export function Header({
label={t('Symbol search')}
>
<Button
- size="sm"
- className={styles.button}
onClick={openSymbolSearchModal}
label={<SearchIcon />}
/>
@@ -141,8 +139,6 @@ export function Header({
label={t('Compare symbols')}
>
<Button
- size="sm"
- className={styles.button}
onClick={openCompareModal}
label={<PlusCircleIcon />}
/>
@@ -185,8 +181,6 @@ export function Header({
{showSettingsModal && (
<Button
- size="sm"
- className={styles.button}
onClick={showSettingsModal}
label={t('Settings')}
/>
@@ -222,8 +216,6 @@ export function Header({
label={t('Undo')}
>
<Button
- size="sm"
- className={styles.button}
onClick={undoRedo.undo}
disabled={!undoRedoState?.canUndo}
label={<UndoIcon />}
@@ -235,8 +227,6 @@ export function Header({
label={t('Redo')}
>
<Button
- size="sm"
- className={styles.button}
onClick={undoRedo.redo}
disabled={!undoRedoState?.canRedo}
label={<RedoIcon />}
@@ -274,8 +264,7 @@ export function Header({
label={t(isFullscreen ? 'Exit fullscreen' : 'Fullscreen')}
>
<Button
- size="sm"
- className={`${styles.button} ${isFullscreen ? styles.pressed : ''}`}
+ pressed={isFullscreen}
onClick={() => fullscreen.toggle()}
label={<FullscreenIcon />}
/>
diff --git a/src/components/IndicatorsSelect/index.tsx b/src/components/IndicatorsSelect/index.tsx
index f4afc19cab7eb9d9c9d2e2a8669661d67934e2a6..f2acd3c44d5e986743f5d284d8e23d10d85bbb5b 100644
--- a/src/components/IndicatorsSelect/index.tsx
+++ b/src/components/IndicatorsSelect/index.tsx
@@ -1,4 +1,4 @@
-import { Button } from 'exchange-elements/v2';
+import { Button } from '@src/components/Button';
import { IndicatorsMenu } from '@src/components/Menu/IndicatorsMenu';
@@ -9,8 +9,6 @@ import { t } from '@src/translations';
import { Dropdown } from '../Dropdown';
import { VolumeIndicatorIcon } from '../Icon';
-import styles from './index.module.scss';
-
interface IndicatorsSelectProps {
addIndicatorToChart: (id: IndicatorsIds) => void;
isMXT: boolean;
@@ -23,8 +21,6 @@ export function IndicatorsSelect({ addIndicatorToChart, isMXT }: IndicatorsSelec
label={t('Add volume indicator')}
>
<Button
- size="sm"
- className={`${styles.button}`}
onClick={() => addIndicatorToChart(IndicatorsIds.Volume)}
label={
<>
diff --git a/src/components/Legend/index.tsx b/src/components/Legend/index.tsx
index df262834d7ad4d3760f24eabadbc60da90c21a6a..fe29c0c893be66e3bc2cf7d2f2d04e2f8ffd0b44 100644
--- a/src/components/Legend/index.tsx
+++ b/src/components/Legend/index.tsx
@@ -1,9 +1,8 @@
-import { Button } from 'exchange-elements/v2';
-
import { Observable } from 'rxjs';
import { GearIcon, TrashIcon } from '@components/Icon';
import { LegendModel, ohlcValuesToShowForMainSerie } from '@core/Legend';
+import { Button } from '@src/components/Button';
import { OHLCConfig } from '@src/types';
import { formatDisplayText, useObservable } from '@src/utils';
@@ -78,7 +77,6 @@ export const LegendComponent = ({ ohlcConfig, viewModel }: LegendProps) => {
{item.settings && (
<Button
- size="sm"
className={styles.button}
onClick={item.settings}
label={<GearIcon />}
@@ -87,7 +85,6 @@ export const LegendComponent = ({ ohlcConfig, viewModel }: LegendProps) => {
{item.remove && (
<Button
- size="sm"
className={styles.button}
onClick={item.remove}
label={<TrashIcon />}
diff --git a/src/components/LongPressButton/index.tsx b/src/components/LongPressButton/index.tsx
index baa1a72257fa0321095e40d63b7605876e7ca120..c932fc71eccc2067b445610350b1f14bc876c95e 100644
--- a/src/components/LongPressButton/index.tsx
+++ b/src/components/LongPressButton/index.tsx
@@ -1,7 +1,8 @@
-import { Button, type ButtonProps } from 'exchange-elements/v2';
import { ReactNode, useEffect, useRef, useState } from 'react';
-export type LongPressButtonProps = ButtonProps & {
+import { Button, ExchangeButtonProps } from '@src/components/Button';
+
+export type LongPressButtonProps = ExchangeButtonProps & {
onExecute: () => void;
delayMs: number;
refreshMs: number;
diff --git a/src/components/PriceScaleControls/index.module.scss b/src/components/PriceScaleControls/index.module.scss
index bf673c11c63c3433ade440831d44b767c9580b84..20348b6cd907fa657e284fad6daefcc5366debba 100644
--- a/src/components/PriceScaleControls/index.module.scss
+++ b/src/components/PriceScaleControls/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.root {
width: 100%;
display: flex;
@@ -11,8 +9,6 @@
pointer-events: none;
.button {
- @include m.buttonBase;
-
min-width: var(--space-1500);
width: var(--space-1500);
height: var(--space-1500);
diff --git a/src/components/PriceScaleControls/index.tsx b/src/components/PriceScaleControls/index.tsx
index a0b1edfba0eb82250d74eb6ef3b1d03665c9da63..8ae507b8547555792115599db51de9650454c84f 100644
--- a/src/components/PriceScaleControls/index.tsx
+++ b/src/components/PriceScaleControls/index.tsx
@@ -1,5 +1,6 @@
import classNames from 'classnames';
-import { Button } from 'exchange-elements/v2';
+
+import { Button } from '@src/components/Button';
import { Tooltip } from '@src/components/Tooltip';
import { CHART_PRICE_SCALE_CONTROLS_CONTENT } from '@src/constants';
@@ -35,10 +36,8 @@ export function PriceScaleControls({
>
<Tooltip label={t('AutoScale')}>
<Button
- size="sm"
- className={classNames(styles.button, {
- [styles.pressed]: isAutoScale,
- })}
+ className={styles.button}
+ pressed={isAutoScale}
onClick={onToggleAutoScale}
label="A"
/>
@@ -46,10 +45,8 @@ export function PriceScaleControls({
<Tooltip label={t('Logarithmic')}>
<Button
- size="sm"
- className={classNames(styles.button, {
- [styles.pressed]: isLogarithmic,
- })}
+ className={styles.button}
+ pressed={isLogarithmic}
onClick={onToggleLogarithmic}
label="L"
/>
diff --git a/src/components/SplitDropdown/index.module.scss b/src/components/SplitDropdown/index.module.scss
index 7db06b7f205ebf03a3122210c669d93e8ff5ae7c..d86d1e831eeb2265a1f3a4e7a58cc6e97dd5e6e5 100644
--- a/src/components/SplitDropdown/index.module.scss
+++ b/src/components/SplitDropdown/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.dropdown {
position: relative;
display: flex;
@@ -8,13 +6,22 @@
height: var(--space-2000);
&:hover .menu_button,
+ &:focus-within .menu_button,
&.open .menu_button {
opacity: 1;
pointer-events: auto;
}
- .button {
- @include m.buttonBase;
+ &.open {
+ .menu_button {
+ div {
+ p {
+ svg {
+ transform: rotate(0deg);
+ }
+ }
+ }
+ }
}
.menu_button {
@@ -33,10 +40,6 @@
}
}
}
-
- &.pressed div p svg {
- transform: rotate(0deg);
- }
}
}
diff --git a/src/components/SplitDropdown/index.tsx b/src/components/SplitDropdown/index.tsx
index 09bc42bbb49a6ccf5fe00f07b3f19713b91981f0..f8cafab419763ee58121a234f225cd73968cf1bd 100644
--- a/src/components/SplitDropdown/index.tsx
+++ b/src/components/SplitDropdown/index.tsx
@@ -1,10 +1,9 @@
import classNames from 'classnames';
-import { Button } from 'exchange-elements/v2';
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';
@@ -13,7 +12,6 @@ import styles from './index.module.scss';
interface SplitDropdownProps extends PropsWithChildren {
position?: DropdownPosition;
- mainButtonClassName?: string;
mainContent: JSX.Element;
menuButtonContent?: JSX.Element;
@@ -24,18 +22,21 @@ interface SplitDropdownProps extends PropsWithChildren {
anchorRef?: RefObject<HTMLDivElement>;
onMainClick?: () => void;
+ mainButtonClassName?: string;
+ mainButtonPressed?: boolean;
tooltipClassName?: string;
}
export default function SplitDropdown({
position = 'right',
- mainButtonClassName = '',
mainContent,
menuButtonContent = <SmallChevronLeftIcon />,
mainTooltip,
menuTooltip,
anchorRef,
onMainClick,
+ mainButtonClassName = '',
+ mainButtonPressed = false,
tooltipClassName,
children,
}: SplitDropdownProps) {
@@ -45,7 +46,11 @@ export default function SplitDropdown({
anchorRef={anchorRef}
buttonClassName={mainButtonClassName}
renderTrigger={({ isOpen, onToggle }) => (
- <div className={`${styles.dropdown} ${isOpen ? styles.open : ''}`}>
+ <div
+ className={classNames(styles.dropdown, {
+ [styles.open]: isOpen,
+ })}
+ >
<Tooltip
tooltipClassName={classNames(styles.tooltip__main, tooltipClassName)}
showMessageOnFocus={!isOpen}
@@ -53,8 +58,8 @@ export default function SplitDropdown({
label={mainTooltip}
>
<Button
- size="sm"
- className={`${styles.button} ${isOpen ? styles.pressed : ''} ${mainButtonClassName}`}
+ className={mainButtonClassName}
+ pressed={mainButtonPressed || isOpen}
onClick={onMainClick}
label={mainContent}
/>
@@ -66,8 +71,8 @@ export default function SplitDropdown({
label={menuTooltip}
>
<Button
- size="sm"
- className={`${styles.button} ${styles.menu_button} ${isOpen ? styles.pressed : ''}`}
+ className={styles.menu_button}
+ pressed={isOpen}
onClick={onToggle}
label={menuButtonContent}
/>
diff --git a/src/components/Toolbar/index.module.scss b/src/components/Toolbar/index.module.scss
index 40c96c4b87db684dc706d7eacead581ebd933687..54380f230091d986f3ab1ae16e70f73982329449 100644
--- a/src/components/Toolbar/index.module.scss
+++ b/src/components/Toolbar/index.module.scss
@@ -1,5 +1,3 @@
-@use '../../theme/mixins' as m;
-
.toolbar {
display: grid;
grid-template-columns: 1fr;
@@ -15,19 +13,6 @@
gap: var(--space-0500);
}
- .button {
- @include m.buttonBase;
-
- .chevron {
- width: var(--space-0250);
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- border-radius: var(--space-0125);
- left: 0;
- }
- }
-
.divider {
width: var(--space-2000);
height: var(--borderWidth-md);
diff --git a/src/components/Toolbar/index.tsx b/src/components/Toolbar/index.tsx
index 992c35fcbc3f6a062052af92bfb8c8a234522712..9ecd7d254c7d8ea73a17ddedb59314688303d7f0 100644
--- a/src/components/Toolbar/index.tsx
+++ b/src/components/Toolbar/index.tsx
@@ -1,11 +1,12 @@
import classNames from 'classnames';
-import { Button, Divider } from 'exchange-elements/v2';
+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';
@@ -90,10 +91,7 @@ export default function Toolbar({
const selectedGeometricShapeOption = findOptionByValue(geometricShapes(), selectedGeometricShape);
const selectedGannAndFibonacciOption = findOptionByValue(gannAndFibonacciTools(), selectedGannAndFibonacci);
- const getButtonClassName = (tool: ActiveDrawingTool) =>
- classNames(styles.button, {
- [styles.pressed]: activeTool === tool,
- });
+ const isButtonPressed = (tool: ActiveDrawingTool): boolean => activeTool === tool;
useEffect(() => {
if (activeTool === 'crosshair') {
@@ -130,9 +128,8 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
onClick={activateCrosshair}
- className={getButtonClassName('crosshair')}
+ pressed={isButtonPressed('crosshair')}
label={<CrossIcon />}
/>
</Tooltip>
@@ -145,7 +142,7 @@ export default function Toolbar({
mainTooltip={ensureDefined(selectedTrendLineOption).label}
onMainClick={() => addDrawing(selectedLineType)}
menuTooltip={t('Trend lines')}
- mainButtonClassName={getButtonClassName(selectedLineType)}
+ mainButtonPressed={isButtonPressed(selectedLineType)}
>
<MenuList
mode="single"
@@ -163,7 +160,7 @@ export default function Toolbar({
mainTooltip={ensureDefined(selectedGannAndFibonacciOption).label}
onMainClick={() => addDrawing(selectedGannAndFibonacci)}
menuTooltip={t('Gann and Fibonacci')}
- mainButtonClassName={getButtonClassName(selectedGannAndFibonacci)}
+ mainButtonPressed={isButtonPressed(selectedGannAndFibonacci)}
>
<MenuList
mode="single"
@@ -181,7 +178,7 @@ export default function Toolbar({
mainTooltip={ensureDefined(selectedGeometricShapeOption).label}
onMainClick={() => addDrawing(selectedGeometricShape)}
menuTooltip={t('Geometric shapes')}
- mainButtonClassName={getButtonClassName(selectedGeometricShape)}
+ mainButtonPressed={isButtonPressed(selectedGeometricShape)}
>
<MenuList
mode="single"
@@ -199,8 +196,7 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
- className={getButtonClassName(DrawingsNames.text)}
+ pressed={isButtonPressed(DrawingsNames.text)}
onClick={() => addDrawing(DrawingsNames.text)}
label={<TypeIcon />}
/>
@@ -214,8 +210,6 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<SplineCurveIcon />}
/>
@@ -229,7 +223,7 @@ export default function Toolbar({
mainTooltip={ensureDefined(selectedMeasurementToolOption).label}
menuTooltip={t('Measurement tools')}
onMainClick={() => addDrawing(selectedMeasurementTool)}
- mainButtonClassName={getButtonClassName(selectedMeasurementTool)}
+ mainButtonPressed={isButtonPressed(selectedMeasurementTool)}
>
<MenuList
mode="single"
@@ -247,8 +241,6 @@ export default function Toolbar({
showMessageOnFocus
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<HeartIcon />}
/>
@@ -268,8 +260,7 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
- className={getButtonClassName(DrawingsNames.ruler)}
+ pressed={isButtonPressed(DrawingsNames.ruler)}
onClick={() => addDrawing(DrawingsNames.ruler)}
label={<RulerIcon />}
/>
@@ -282,8 +273,6 @@ export default function Toolbar({
className={styles.notImplemented}
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<ZoomInIcon />}
/>
@@ -303,8 +292,6 @@ export default function Toolbar({
className={styles.notImplemented}
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<MagnetIcon />}
/>
@@ -316,8 +303,7 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
- className={`${styles.button} ${isEndlessDrawingsMode ? styles.pressed : ''}`}
+ pressed={isEndlessDrawingsMode}
onClick={() => setEndlessDrawingsMode(!isEndlessDrawingsMode)}
label={<PencilLockerIcon />}
/>
@@ -330,8 +316,6 @@ export default function Toolbar({
className={styles.notImplemented}
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<UnlockIcon />}
/>
@@ -344,8 +328,6 @@ export default function Toolbar({
className={styles.notImplemented}
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<EyeBrushIcon />}
/>
@@ -365,8 +347,6 @@ export default function Toolbar({
className={styles.notImplemented}
>
<Button
- size="sm"
- className={styles.button}
onClick={() => {}}
label={<TrashIcon />}
/>
@@ -378,8 +358,6 @@ export default function Toolbar({
location="right"
>
<Button
- size="sm"
- className={styles.button}
onClick={toggleDOM}
label={<LayersIcon />}
/>
diff --git a/src/core/Chart.ts b/src/core/Chart.ts
index 6c6f5845519220db0a06321e8216b99e814e8e92..104dd6377a98d514c2255b45d36f2d31a31ab0a0 100644
--- a/src/core/Chart.ts
+++ b/src/core/Chart.ts
@@ -252,7 +252,11 @@ export class Chart implements ISerializable<ChartSnapshot> {
});
};
- private onPointerDown = () => {
+ private onPointerDown = (event: PointerEvent) => {
+ if (event.composedPath().includes(this.lwcChart.chartElement())) {
+ this.container.focus({ preventScroll: true });
+ }
+
this.isPointerDown = true;
this.didResetOnDrag = false;
};
diff --git a/src/core/ContainerManager.ts b/src/core/ContainerManager.ts
index 9b43031c8bbb8069503e5f7e2aabfd406848ac0b..11c6d3d18384ef9675dc15292c26c8ecd8a4337c 100644
--- a/src/core/ContainerManager.ts
+++ b/src/core/ContainerManager.ts
@@ -92,6 +92,8 @@ export class ContainerManager {
chartAreaContainer.style.height = '100%';
chartAreaContainer.style.minHeight = '0';
chartAreaContainer.style.minWidth = '0';
+ chartAreaContainer.style.outline = 'none';
+ chartAreaContainer.tabIndex = -1;
chartAreaContainer.style.cursor = 'crosshair';
const toolBarContainer = document.createElement('div');
diff --git a/src/theme/_mixins.scss b/src/theme/_mixins.scss
index d67e4b8296c0b450be2697c9113d274d8b70335d..cdcb54e0371a44a97c1a7a73c31ffb2a6ac666a3 100644
--- a/src/theme/_mixins.scss
+++ b/src/theme/_mixins.scss
@@ -27,6 +27,7 @@
}
@mixin buttonBase {
+ position: relative;
min-width: var(--space-2000);
display: flex;
justify-content: start;
@@ -58,6 +59,19 @@
color: var(--btn-text-hover);
}
+ &:focus-visible {
+ outline: none;
+
+ &::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border: var(--borderWidth-md) solid var(--neutral-12);
+ border-radius: inherit;
+ pointer-events: none;
+ }
+ }
+
&.pressed {
background-color: var(--btn-bg-pressed);
color: var(--btn-text-pressed);