Merge pull request #16 from jejolare-dev/dev

finish: redesign
This commit is contained in:
Dmitrii Kolpakov 2025-08-28 18:23:54 +07:00 committed by GitHub
commit 8c4d0f1bfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 492 additions and 273 deletions

View file

@ -4,7 +4,7 @@
outline: none;
padding: 6px 12px;
border-radius: 5px;
background: #273666;
background: var(--action-btn-bg);
font-size: 12px;
font-weight: 500;
line-height: 100%;
@ -16,8 +16,7 @@
}
&:hover {
opacity: 0.8;
background: #273666;
background: var(--action-btn-bg);
}
&.primary {
@ -31,4 +30,4 @@
&.danger {
color: #ff6767;
}
}
}

View file

@ -4,10 +4,23 @@
overflow: auto;
padding-bottom: 3px;
&.sm {
gap: 5px !important;
>div {
a {
font-size: 14px !important;
padding: 8px !important;
border-radius: 8px !important;
line-height: 100%;
}
}
}
&.tab {
gap: 3px;
> div {
>div {
a {
font-size: 16px;
font-weight: 500;
@ -26,7 +39,7 @@
}
}
> div {
>div {
position: relative;
.profile__filters__notification {
@ -58,4 +71,4 @@
}
}
}
}
}

View file

@ -2,6 +2,7 @@ import Link from 'next/link';
import { nanoid } from 'nanoid';
import HorizontalSelectProps from '@/interfaces/props/components/UI/HorizontalSelect/HorizontalSelectProps';
import HorizontalSelectValue from '@/interfaces/common/HorizontalSelectValue';
import { classes } from '@/utils/utils';
import NotificationIndicator from '../NotificationIndicator/NotificationIndicator';
import styles from './HorizontalSelect.module.scss';
@ -13,7 +14,12 @@ function HorizontalSelect<T extends HorizontalSelectValue>(props: HorizontalSele
return (
<div
style={props.withNotifications ? { paddingTop: '20px' } : {}}
className={`${styles.horizontal_select} ${className || ''} ${props.isTab ? styles.tab : ''}`}
className={classes(
styles.horizontal_select,
className,
props.isTab && styles.tab,
props.isSm && styles.sm,
)}
>
{props.body.map((e) => (
<div key={nanoid(16)}>

View file

@ -1,4 +1,11 @@
.back_btn {
display: flex;
gap: 12px;
}
&.sm {
padding: 12px 22px;
font-size: 14px;
font-weight: 500;
}
}

View file

@ -1,14 +1,19 @@
import { ReactComponent as ArrowWhiteIcon } from '@/assets/images/UI/arrow_white.svg';
import Button from '@/components/UI/Button/Button';
import { useRouter } from 'next/router';
import { classes } from '@/utils/utils';
import styles from './BackButton.module.scss';
import { BackButtonProps } from './types';
function BackButton() {
function BackButton({ className, isSm }: BackButtonProps) {
const router = useRouter();
return (
<Button className={styles.back_btn} transparent={true} onClick={router.back}>
{/* <img src={ArrowWhiteIcon} alt="arrow"/> */}
<Button
className={classes(styles.back_btn, className, isSm && styles.sm)}
transparent={true}
onClick={router.back}
>
<ArrowWhiteIcon />
Back
</Button>

View file

@ -0,0 +1,4 @@
export interface BackButtonProps {
className?: string;
isSm?: boolean;
}

View file

@ -12,6 +12,39 @@
&.lg {
padding-inline: 60px;
height: 65px;
.header__logo {
width: 180px;
height: 38px;
}
.header__currency__check {
height: 48px !important;
gap: 25px !important;
}
.header__account__wrapper {
.header__account__info {
p {
&:first-child {
font-size: 16px;
}
font-size: 14px;
}
}
.header__account__btn {
min-width: 48px !important;
height: 48px !important;
}
}
.header__connect_btn {
height: 50px;
}
@media screen and (max-width: 1600px) {
padding-inline: 40px;
@ -27,7 +60,7 @@
width: 202px;
height: 40px;
> a {
>a {
display: flex;
}
}
@ -77,6 +110,7 @@
.header__login {
width: 100%;
display: flex;
align-items: center;
gap: 10px;
transition: none;
}
@ -106,7 +140,7 @@
.header__button__wrapper {
position: relative;
> .offers__tooltip {
>.offers__tooltip {
padding: 13px 20px;
position: absolute;
background: #11316b;
@ -206,7 +240,7 @@
display: flex;
gap: 5px;
> span {
>span {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
@ -231,6 +265,7 @@
}
&.header__menu__mobile {
.header__account,
.header__login {
padding: 20px;
@ -255,11 +290,11 @@
cursor: pointer;
transition: none;
> svg {
>svg {
scale: 0.8;
}
> div {
>div {
width: 24px;
height: 2px;
margin-bottom: 6px;
@ -286,7 +321,7 @@
max-height: 200px;
overflow: hidden;
> div {
>div {
width: 100%;
}
@ -391,4 +426,4 @@
background-color: #0000004c;
z-index: 1;
transform: all 0;
}
}

View file

@ -355,9 +355,11 @@ function Header({ isLg }: { isLg?: boolean }) {
</div>
<div className={styles.header__desktop__navigation}>
<NavBar />
<NavBar isLg={isLg} />
</div>
{Menu()}
<BurgerButton className={styles.header__burger} />
<div className={styles.header__account__mobile} style={mobileHeaderStyle}>

View file

@ -1,4 +1,25 @@
.nav {
&.lg {
a {
gap: 8px;
h6 {
font-size: 14px;
font-weight: 600;
line-height: 140%;
}
svg {
transform: scale(0.9);
}
.badge {
padding: 3px;
min-width: 22px;
}
}
}
a {
display: flex;
align-items: center;
@ -73,4 +94,4 @@
font-size: 32px;
}
}
}
}

View file

@ -9,6 +9,7 @@ import NavBarProps from '@/interfaces/props/components/default/Header/NavBar/Nav
import NotificationIndicator from '@/components/UI/NotificationIndicator/NotificationIndicator';
import { useContext } from 'react';
import { Store } from '@/store/store-reducer';
import { classes } from '@/utils/utils';
import styles from './NavBar.module.scss';
function NavBar(props: NavBarProps) {
@ -40,14 +41,18 @@ function NavBar(props: NavBarProps) {
<Link href={href} className={linkClass}>
<Img />
<h6>{title}</h6>
<NotificationIndicator count={notifications} />
<NotificationIndicator className={styles.badge} count={notifications} />
</Link>
);
}
return (
<nav
className={`${styles.nav} ${!props.mobile ? styles.nav__desktop : styles.nav__mobile}`}
className={classes(
styles.nav,
!props.mobile ? styles.nav__desktop : styles.nav__mobile,
props.isLg && styles.lg,
)}
>
<NavItem
title={'Exchange'}

View file

@ -1,235 +1,194 @@
import { useEffect, useState, useRef, useMemo } from 'react';
import useAdvancedTheme from '@/hook/useTheme';
import CandleChartProps from '@/interfaces/props/pages/dex/trading/CandleChartProps/CandleChartProps';
import ReactECharts from 'echarts-for-react';
import Decimal from 'decimal.js';
import * as echarts from 'echarts';
import CandleRow from '@/interfaces/common/CandleRow';
import testCandles from './testCandles.json';
import type CandleChartProps from '@/interfaces/props/pages/dex/trading/CandleChartProps/CandleChartProps';
import styles from './styles.module.scss';
const TESTING_MODE = false;
import { ResultCandle } from './types';
import {
buildCandles,
d,
diffFmt,
fmt,
pickWindowIndices,
tsLabel,
zoomStartByPeriod,
chartColors,
} from './utils';
function CandleChart(props: CandleChartProps) {
type ResultCandle = [number, number, number, number, number];
const { theme } = useAdvancedTheme();
const chartRef = useRef(null);
const upColor = '#16D1D6cc';
const upBorderColor = '#16D1D6cc';
const downColor = '#ff6767cc';
const downBorderColor = '#ff6767cc';
const chartRef = useRef<ReactECharts>(null);
const [candles, setCandles] = useState<ResultCandle[]>([]);
const [isLoaded, setIsLoaded] = useState(false);
function timestampToString(timestamp: string) {
const targetPattern =
new Date(parseInt(timestamp, 10)).toDateString() === new Date().toDateString()
? 'hh:mm:ss'
: 'hh:mm:ss\ndd-MM-yyyy';
return echarts.format.formatTime(targetPattern, parseInt(timestamp, 10));
}
function prepareCandles() {
const candles = (TESTING_MODE ? (testCandles as CandleRow[]) : props.candles)
.map((candle) => {
const result = [
parseInt(candle.timestamp, 10),
candle.shadow_top || 0,
candle.shadow_bottom || 0,
candle.body_first || 0,
candle.body_second || 0,
];
return result as ResultCandle;
})
.filter((e) => e[0])
.map((e) => {
const decimals = e.map((el, i) => ({
value: i !== 0 ? new Decimal(el) : undefined,
index: i,
}));
for (const decimal of decimals) {
if (decimal.value !== undefined) {
if (decimal.value.lessThan(0.00001)) {
e[decimal.index] = 0;
}
}
}
return e;
});
return candles;
}
useEffect(() => {
const newCandles = prepareCandles();
setCandles(newCandles);
setCandles(buildCandles(props.candles));
setIsLoaded(true);
}, [props.candles]);
// shown candle
const shownIdx = candles.length ? candles.length - 1 : null;
const prevIdx = shownIdx && shownIdx > 0 ? shownIdx - 1 : null;
const shown = shownIdx !== null ? candles[shownIdx] : undefined;
const prev = prevIdx !== null ? candles[prevIdx] : undefined;
const O = shown?.[3];
const H = shown?.[1];
const L = shown?.[2];
const C = shown?.[4];
const P = prev?.[4];
const delta = diffFmt(C, P);
const option = useMemo(() => {
function splitData(rawData: ResultCandle[]) {
const categoryData = [];
const values = [];
for (let i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i][0]);
values.push(rawData[i].slice(1));
}
return {
categoryData,
values,
};
}
const data0 = splitData(candles);
const now = new Date().getTime();
const zoomStartTime = (() => {
const date = +new Date();
const hr1 = 60 * 60 * 1000;
const hoursDecrement = 24 * hr1;
const daysDecrement = 7 * 24 * hr1;
const weeksDecrement = 4 * 7 * 24 * hr1;
const monthsDecrement = 52 * 7 * 24 * hr1;
switch (props.period) {
case '1h':
return date - hoursDecrement;
case '1d':
return date - daysDecrement;
case '1w':
return date - weeksDecrement;
case '1m':
return date - monthsDecrement;
default:
return date - hr1;
}
})();
const closestDateToStart = data0.categoryData.reduce((acc, curr) => {
const currDiff = Math.abs(curr - zoomStartTime);
const accDiff = Math.abs(acc - zoomStartTime);
return currDiff < accDiff ? curr : acc;
}, now);
const lastDate = data0.categoryData[data0.categoryData.length - 1];
const closestDateIndex = data0.categoryData.indexOf(closestDateToStart);
const lastDateIndex = data0.categoryData.indexOf(lastDate);
const timestamps = candles.map((c) => c[0]);
const { startIdx, endIdx } = pickWindowIndices(timestamps, zoomStartByPeriod(props.period));
return {
grid: {
top: '5%',
left: '-1%',
right: '6%',
bottom: '10%',
},
grid: { top: '5%', left: '0%', right: '10%', bottom: '10%' },
xAxis: {
type: 'category',
// data: data0.categoryData,
boundaryGap: true,
axisLine: { onZero: false },
min: 'dataMin',
max: 'dataMax',
splitLine: {
show: true,
lineStyle: {
color: theme === 'light' ? '#e3e3e8' : '#1f1f4a',
color: theme === 'light' ? chartColors.light : chartColors.dark,
},
},
min: 'dataMin',
max: 'dataMax',
axisLine: { onZero: false },
axisLabel: { formatter: (v: string) => tsLabel(parseInt(v, 10)) },
axisPointer: {
show: true,
type: 'line',
label: {
formatter: (params: { value: string }) => timestampToString(params.value),
backgroundColor: '#4A90E2',
color: '#ffffff',
formatter: (p: { value: string }) => tsLabel(parseInt(p.value, 10)),
backgroundColor: chartColors.default,
color: '#fff',
},
},
},
yAxis: {
scale: true,
position: 'right',
splitArea: { show: false },
min: (v: { min: number; max: number }) => {
const min = d(v.min);
const range = d(v.max).minus(min);
const pad = range.lte(0) ? d(v.max).mul(0.05) : range.mul(0.1);
return min.minus(pad).toNumber();
},
max: (v: { min: number; max: number }) => {
const min = d(v.min);
const max = d(v.max);
const range = max.minus(min);
const pad = range.lte(0) ? max.mul(0.05) : range.mul(0.1);
return max.plus(pad).toNumber();
},
splitLine: {
show: true,
lineStyle: {
color: theme === 'light' ? chartColors.light : chartColors.dark,
},
},
axisLabel: {
formatter: timestampToString,
formatter: (val: number | string) => d(val).toDecimalPlaces(6).toString(),
},
},
yAxis: {
scale: true,
splitArea: { show: false },
min: 0,
max: (value: { max: string }) => new Decimal(value.max).mul(1.1).toNumber(),
axisPointer: {
show: true,
type: 'line',
label: {
show: true,
backgroundColor: '#4A90E2',
color: '#ffffff',
},
},
splitLine: {
show: true,
lineStyle: {
color: theme === 'light' ? '#e3e3e8' : '#1f1f4a',
color: '#fff',
backgroundColor: (p: {
seriesData?: Array<{ value?: (number | string)[] }>;
}) => {
const ts = p.seriesData?.[0]?.value?.[0];
const idx = candles.findIndex((c) => c[0] === ts);
if (idx === -1) return chartColors.default;
const [, , , o, c] = candles[idx];
let color = chartColors.default;
if (c > o) {
color = chartColors.green;
} else if (c < o) {
color = chartColors.red;
}
return color;
},
},
},
},
dataZoom: [
{
type: 'inside',
startValue: closestDateIndex,
endValue: lastDateIndex,
},
],
dataZoom: [{ type: 'inside', startValue: startIdx, endValue: endIdx }],
series: [
{
name: 'Candle Chart',
type: 'candlestick',
data: candles,
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor,
},
barWidth: '75%',
dimensions: ['date', 'highest', 'lowest', 'open', 'close'],
encode: {
x: 'date',
y: ['open', 'close', 'highest', 'lowest'],
itemStyle: {
color: chartColors.green,
color0: chartColors.red,
borderColor: chartColors.green,
borderColor0: chartColors.red,
},
dimensions: ['date', 'highest', 'lowest', 'open', 'close'],
encode: { x: 'date', y: ['open', 'close', 'highest', 'lowest'] },
large: true,
largeThreshold: 2000000,
largeThreshold: 2_000_000,
},
],
};
}, [candles, theme]);
console.log('option', option);
}, [candles, props.period, theme]);
return (
<div className={styles.chart}>
{/* Header */}
<div className={styles.chart__top}>
<div className={styles.chart__top_item}>
<p>
{props.currencyNames.firstCurrencyName}/
{props.currencyNames.secondCurrencyName}
</p>
</div>
<div className={styles.chart__top_item}>
<p style={{ opacity: 0.7 }}>O</p>
<span style={{ color: delta.color }}>{fmt(O)}</span>
</div>
<div className={styles.chart__top_item}>
<span style={{ opacity: 0.7 }}>H</span>
<span style={{ color: delta.color }}>{fmt(H)}</span>
</div>
<div className={styles.chart__top_item}>
<span style={{ opacity: 0.7 }}>L</span>
<span style={{ color: delta.color }}>{fmt(L)}</span>
</div>
<div className={styles.chart__top_item}>
<span style={{ opacity: 0.7 }}>C</span>
<span style={{ color: delta.color }}>{fmt(C)}</span>
</div>
<div className={styles.chart__top_item}>
<p style={{ color: delta.color }}>{delta.txt}</p>
</div>
</div>
<ReactECharts
option={option}
style={{
height: '100%',
width: '100%',
}}
opts={{
devicePixelRatio: 2,
}}
lazyUpdate={true}
notMerge={true}
ref={chartRef}
option={option}
style={{ height: '100%', width: '100%' }}
opts={{ devicePixelRatio: 2 }}
lazyUpdate
notMerge
/>
{!candles?.length && isLoaded && (
{!candles.length && isLoaded && (
<h1 className={styles.chart__lowVolume}>[ Low volume ]</h1>
)}
</div>

View file

@ -4,7 +4,38 @@
height: auto;
height: 100%;
> canvas {
&__top {
padding-bottom: 10px;
background-color: var(--main-bg-color);
width: 100%;
position: absolute;
left: 0;
top: 18px;
display: flex;
align-items: center;
gap: 10px;
z-index: 2;
&_item {
display: flex;
align-items: center;
gap: 2px;
p {
color: var(--footer-selected-link);
}
p,
span {
font-size: 11px;
font-weight: 400;
line-height: 100%;
white-space: nowrap;
}
}
}
>canvas {
width: 100%;
height: 100%;
cursor: crosshair;
@ -27,4 +58,4 @@
font-size: 36px;
}
}
}
}

View file

@ -0,0 +1 @@
export type ResultCandle = [ts: number, high: number, low: number, open: number, close: number];

View file

@ -0,0 +1,116 @@
import CandleRow from '@/interfaces/common/CandleRow';
import CandleChartProps from '@/interfaces/props/pages/dex/trading/CandleChartProps/CandleChartProps';
import Decimal from 'decimal.js';
import * as echarts from 'echarts';
import { ResultCandle } from './types';
import testCandles from './testCandles.json';
export const TESTING_MODE = false;
export const chartColors = {
green: '#16D1D6',
red: '#FF6767',
default: '#3D3D6C',
light: '#e3e3e8',
dark: '#1f1f4a',
textColor: '#ffffff',
};
export const d = (v: number | string) => new Decimal(v);
export const fmt = (n?: number | string) =>
n === undefined || n === null || Number.isNaN(Number(n))
? '-'
: d(n).toDecimalPlaces(6).toString();
export function diffFmt(curr?: number | string, prev?: number | string) {
if (curr === undefined || prev === undefined || Number(prev) === 0) {
return { txt: '-', color: '#9CA3AF' };
}
const diff = d(curr).minus(prev);
const pct = diff.div(prev).mul(100);
let sign = '';
if (diff.greaterThan(0)) {
sign = '+';
} else if (diff.isZero()) {
sign = '';
}
const txt = `${sign}${diff.toDecimalPlaces(8).toString()} (${sign}${pct.toDecimalPlaces(2).toString()}%)`;
let color = '#9CA3AF';
if (diff.greaterThan(0)) {
color = chartColors.green;
} else if (diff.lessThan(0)) {
color = chartColors.red;
}
return { txt, color };
}
const isTodayTs = (ms: number) => new Date(ms).toDateString() === new Date().toDateString();
export const tsLabel = (ms: number) =>
echarts.format.formatTime(isTodayTs(ms) ? 'hh:mm:ss' : 'hh:mm:ss\ndd-MM-yyyy', ms);
export function buildCandles(src: CandleRow[]): ResultCandle[] {
const arr = (TESTING_MODE ? (testCandles as CandleRow[]) : src).map(
(c) =>
[
parseInt(c.timestamp, 10),
c.shadow_top || 0,
c.shadow_bottom || 0,
c.body_first || 0,
c.body_second || 0,
] as ResultCandle,
);
return arr
.filter((e) => e[0])
.map((e) => {
for (let i = 1; i < 5; i++) if (d(e[i]).lessThan(0.00001)) e[i] = 0;
return e;
});
}
export function zoomStartByPeriod(period: CandleChartProps['period']) {
const now = Date.now();
const hr = 3600_000;
switch (period) {
case '1sec':
return now - 1_000;
case '1min':
return now - hr / 60;
case '5min':
return now - 5 * (hr / 60);
case '15min':
return now - 15 * (hr / 60);
case '30min':
return now - 30 * (hr / 60);
case '1h':
return now - hr * 24;
case '4h':
return now - hr * 4;
case '1d':
return now - hr * 24 * 7;
case '1w':
return now - hr * 24 * 7 * 4;
case '1m':
return now - hr * 24 * 7 * 52;
default:
return now - hr;
}
}
export function pickWindowIndices(timestamps: number[], startTs: number) {
if (!timestamps.length) return { startIdx: 0, endIdx: 0 };
const nearest = timestamps.reduce(
(a, b) => (Math.abs(b - startTs) < Math.abs(a - startTs) ? b : a),
timestamps[0],
);
return {
startIdx: timestamps.indexOf(nearest),
endIdx: timestamps.length - 1,
};
}

View file

@ -2,12 +2,12 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
gap: 10px;
&__name {
display: flex;
align-items: center;
gap: 5px;
gap: 8px;
font-size: 14px;
font-weight: 400;
}

View file

@ -5,9 +5,9 @@ import styles from './styles.module.scss';
function StatItem({ Img, title, value, className, coefficient }: StatItemProps) {
return (
<div className={classes(styles.statItem, className)}>
<div className={styles.statItem__nav}>
<div className={styles.statItem__top}>
<Img />
<p className={styles.statItem__nav_title}>{title}</p>
<p className={styles.statItem__top_title}>{title}</p>
</div>
<div className={styles.statItem__content}>

View file

@ -3,16 +3,20 @@
flex-direction: column;
gap: 6px;
&__nav {
&__top {
display: flex;
align-items: center;
gap: 5px;
svg {
transform: scale(0.9);
}
&_title {
color: var(--footer-selected-link);
white-space: nowrap;
font-size: 14px;
font-weight: 700;
font-size: 12px;
font-weight: 400;
}
}
@ -24,13 +28,13 @@
&_val {
white-space: nowrap;
font-size: 14px;
font-weight: 400;
font-weight: 500;
}
&_coefficient {
white-space: nowrap;
font-size: 14px;
font-weight: 400;
font-weight: 500;
&.green {
color: #16d1d6;

View file

@ -3,9 +3,9 @@ import { ReactComponent as UpIcon } from '@/assets/images/UI/up_icon.svg';
import { ReactComponent as DownIcon } from '@/assets/images/UI/down_icon.svg';
import { ReactComponent as VolumeIcon } from '@/assets/images/UI/volume_icon.svg';
import BackButton from '@/components/default/BackButton/BackButton';
import { roundTo, notationToString } from '@/utils/utils';
import { roundTo, notationToString, classes } from '@/utils/utils';
import styles from './styles.module.scss';
import StatItem from '../StatItem';
import StatItem from './components/StatItem';
import { TradingHeaderProps } from './types';
import CurrencyIcon from './components/CurrencyIcon';
import AssetRow from './components/AssetRow';
@ -36,56 +36,60 @@ const TradingHeader = ({
{
Img: ClockIcon,
title: '24h change',
value: `${roundTo(notationToString(pairStats?.rate || 0), 4)} ${secondCurrencyName}`,
value: `${roundTo(notationToString(pairStats?.rate || 0), 4)}`,
coefficient: coefficientOutput,
},
{
Img: UpIcon,
title: '24h high',
value: `${roundTo(notationToString(pairStats?.high || 0), 4)} ${secondCurrencyName}`,
value: `${roundTo(notationToString(pairStats?.high || 0), 4)}`,
},
{
Img: DownIcon,
title: '24h low',
value: `${roundTo(notationToString(pairStats?.low || 0), 4)} ${secondCurrencyName}`,
value: `${roundTo(notationToString(pairStats?.low || 0), 4)}`,
},
{
Img: VolumeIcon,
title: '24h volume',
value: `${roundTo(notationToString(pairStats?.volume || 0), 4)} ${secondCurrencyName}`,
title: `24h volume (${firstCurrencyName})`,
value: `${roundTo(notationToString(pairStats?.volume || 0), 4)}`,
},
];
return (
<div className={styles.header}>
<div className={styles.header__currency}>
<div className={styles.header__currency_icon}>
<CurrencyIcon code={firstAssetId} />
</div>
<div className={styles.header__stats}>
<div className={styles.header__currency}>
<div className={styles.header__currency_icon}>
<CurrencyIcon code={firstAssetId} />
</div>
<div className={styles.header__currency_item}>
<p className={styles.currencyName}>
{!pairData ? (
'...'
) : (
<>
{firstCurrencyName}
<span>/{secondCurrencyName}</span>
</>
)}
</p>
<div className={styles.price}>
<p className={styles.price__secondCurrency}>
{roundTo(notationToString(pairStats?.rate || 0), 4)}{' '}
{secondCurrencyName}
<div className={styles.header__currency_item}>
<p className={styles.currencyName}>
{!pairData ? (
'...'
) : (
<>
{firstCurrencyName}
<span>/{secondCurrencyName}</span>
</>
)}
</p>
{pairRateUsd && <p className={styles.price__usd}>~ ${pairRateUsd}</p>}
<div className={styles.price}>
<p
className={classes(
styles.price__secondCurrency,
coefficientOutput >= 0 ? styles.green : styles.red,
)}
>
{roundTo(notationToString(pairStats?.rate || 0, 8))}
</p>
{pairRateUsd && <p className={styles.price__usd}>~ ${pairRateUsd}</p>}
</div>
</div>
</div>
</div>
<div className={styles.header__stats}>
{pairData && firstAssetLink && secondAssetLink && (
<div className={styles.header__stats_assets}>
<AssetRow
@ -115,7 +119,7 @@ const TradingHeader = ({
))}
</div>
<BackButton />
<BackButton isSm />
</div>
);
};

View file

@ -9,11 +9,13 @@
&__currency {
display: flex;
align-items: center;
gap: 12px;
gap: 10px;
padding-right: 20px;
border-right: 1px solid var(--delimiter-color);
&_icon {
min-width: 48px;
min-height: 48px;
min-width: 40px;
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
@ -21,7 +23,7 @@
border-radius: 50%;
> img {
width: 26px;
width: 24px;
height: auto;
}
}
@ -30,9 +32,10 @@
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 6px;
.currencyName {
font-size: 18px;
font-size: 16px;
font-weight: 600;
span {
@ -48,6 +51,14 @@
&__secondCurrency {
font-weight: 400;
font-size: 14px;
&.green {
color: #16d1d6;
}
&.red {
color: #ff6767;
}
}
&__usd {

View file

@ -2,22 +2,16 @@ import PeriodState from '@/interfaces/states/pages/dex/trading/InputPanelItem/Pe
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
export const periods: PeriodState[] = [
{
name: '1H',
code: '1h',
},
{
name: '1D',
code: '1d',
},
{
name: '1W',
code: '1w',
},
{
name: '1M',
code: '1m',
},
{ name: '1s', code: '1sec' },
{ name: '1m', code: '1min' },
{ name: '5m', code: '5min' },
{ name: '15m', code: '15min' },
{ name: '30m', code: '30min' },
{ name: '1h', code: '1h' },
{ name: '4h', code: '4h' },
{ name: '1d', code: '1d' },
{ name: '1w', code: '1w' },
{ name: '1M', code: '1m' },
];
export const buySellValues: SelectValue[] = [

View file

@ -1,3 +1,3 @@
type Period = '1h' | '1d' | '1w' | '1m';
type Period = '1sec' | '1min' | '5min' | '15min' | '30min' | '1h' | '4h' | '1d' | '1w' | '1m';
export default Period;

View file

@ -8,6 +8,7 @@ interface HorizontalSelectProps<T extends HorizontalSelectValue> {
setValue: Dispatch<SetStateAction<T>>;
className?: string;
isTab?: boolean;
isSm?: boolean;
}
export default HorizontalSelectProps;

View file

@ -1,5 +1,6 @@
interface NavBarProps {
mobile?: boolean;
isLg?: boolean;
}
export default NavBarProps;

View file

@ -4,6 +4,10 @@ import Period from '@/interfaces/common/Period';
interface CandleChartProps {
candles: CandleRow[];
period: Period;
currencyNames: {
firstCurrencyName: string;
secondCurrencyName: string;
};
}
export default CandleChartProps;

View file

@ -1,7 +1,6 @@
import styles from '@/styles/Trading.module.scss';
import Footer from '@/components/default/Footer/Footer';
import Header from '@/components/default/Header/Header';
import Dropdown from '@/components/UI/Dropdown/Dropdown';
import HorizontalSelect from '@/components/UI/HorizontalSelect/HorizontalSelect';
import { useCallback, useState } from 'react';
import { cancelOrder } from '@/utils/methods';
@ -31,9 +30,6 @@ import useMatrixAddresses from '@/hook/useMatrixAddresses';
import takeOrderClick from '@/utils/takeOrderClick';
import useUpdateUser from '@/hook/useUpdateUser';
const CHART_OPTIONS = [{ name: 'Zano Chart' }, { name: 'Trading View', disabled: true }];
const DEFAULT_CHART = CHART_OPTIONS[0];
function Trading() {
const { alertState, alertSubtitle, setAlertState } = useAlert();
const { elementRef: orderListRef, scrollToElement: scrollToOrdersList } =
@ -44,7 +40,7 @@ function Trading() {
const fetchUser = useUpdateUser();
const [ordersHistory, setOrdersHistory] = useState<PageOrderData[]>([]);
const [userOrders, setUserOrders] = useState<OrderRow[]>([]);
const [periodsState, setPeriodsState] = useState<PeriodState>(periods[0]);
const [periodsState, setPeriodsState] = useState<PeriodState>(periods[5]);
const [pairData, setPairData] = useState<PairData | null>(null);
const [candles, setCandles] = useState<CandleRow[]>([]);
const [trades, setTrades] = useState<Trade[]>([]);
@ -177,18 +173,16 @@ function Trading() {
value={periodsState}
setValue={setPeriodsState}
isTab
/>
<Dropdown
body={CHART_OPTIONS}
className={styles.settings__dropdown}
selfContained
value={DEFAULT_CHART}
setValue={() => undefined}
isSm
/>
</div>
{candlesLoaded ? (
<CandleChart candles={candles} period={periodsState.code} />
<CandleChart
currencyNames={currencyNames}
candles={candles}
period={periodsState.code}
/>
) : (
<ContentPreloader style={{ height: '100%' }} />
)}

View file

@ -52,4 +52,5 @@
--dex-tooltip-border-color: #1f8feb26;
--dex-sell-percentage: #272757;
--dex-buy-percentage: #103262;
}
--action-btn-bg: #273666;
}

View file

@ -52,4 +52,5 @@
--dex-tooltip-border-color: #1f8feb33;
--dex-sell-percentage: #fceded;
--dex-buy-percentage: #e5f8f8;
}
--action-btn-bg: #e5f8f8;
}