feat(trading): use mini scroll in deal ticket, market info for and landing dialog (#3269)

This commit is contained in:
Bartłomiej Głownia 2023-03-25 01:01:12 +01:00 committed by GitHub
parent e8adff25c4
commit 58c6652e29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 140 deletions

View File

@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
import { useMarketList } from '@vegaprotocol/market-list';
import { t } from '@vegaprotocol/i18n';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
import { Link as UILink, TinyScroll } from '@vegaprotocol/ui-toolkit';
import type { OnCellClickHandler } from '../select-market';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/market-list';
import {
@ -55,8 +55,8 @@ export const SelectMarketLandingTable = ({
const showProposed = (markets?.length || 0) <= 5;
return (
<>
<div
className="max-h-[60vh] overflow-x-auto"
<TinyScroll
className="max-h-[60vh] overflow-x-auto -mr-4 pr-4"
data-testid="select-market-list"
>
<p className="text-neutral-500 dark:text-neutral-400 mb-4">
@ -78,7 +78,7 @@ export const SelectMarketLandingTable = ({
))}
</tbody>
</table>
</div>
</TinyScroll>
<div className="mt-4 text-md">
<Link
to={Links[Routes.MARKETS]()}

View File

@ -20,6 +20,7 @@ import {
InputError,
Intent,
Notification,
TinyScroll,
} from '@vegaprotocol/ui-toolkit';
import {
@ -160,136 +161,140 @@ export const DealTicket = ({
if (!order || !normalizedOrder) return null;
return (
<form
onSubmit={isReadOnly ? undefined : handleSubmit(onSubmit)}
className="p-4"
noValidate
>
<Controller
name="type"
control={control}
rules={{
validate: validateType(
marketData.marketTradingMode,
marketData.trigger
),
}}
render={() => (
<TypeSelector
value={order.type}
onSelect={(type) => {
if (type === OrderType.TYPE_NETWORK) return;
update({
type,
// when changing type also update the tif to what was last used of new type
timeInForce: lastTIF[type] || order.timeInForce,
expiresAt: undefined,
});
clearErrors('expiresAt');
}}
market={market}
marketData={marketData}
errorMessage={errors.type?.message}
/>
)}
/>
<Controller
name="side"
control={control}
render={() => (
<SideSelector
value={order.side}
onSelect={(side) => {
update({ side });
}}
/>
)}
/>
<DealTicketAmount
control={control}
orderType={order.type}
market={market}
marketData={marketData}
sizeError={errors.size?.message}
priceError={errors.price?.message}
update={update}
size={order.size}
price={order.price}
/>
<Controller
name="timeInForce"
control={control}
rules={{
validate: validateTimeInForce(
marketData.marketTradingMode,
marketData.trigger
),
}}
render={() => (
<TimeInForceSelector
value={order.timeInForce}
orderType={order.type}
onSelect={(timeInForce) => {
update({ timeInForce });
// Set tif value for the given order type, so that when switching
// types we know the last used TIF for the given order type
setLastTIF((curr) => ({
...curr,
[order.type]: timeInForce,
expiresAt: undefined,
}));
clearErrors('expiresAt');
}}
market={market}
marketData={marketData}
errorMessage={errors.timeInForce?.message}
/>
)}
/>
{order.type === Schema.OrderType.TYPE_LIMIT &&
order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT && (
<Controller
name="expiresAt"
control={control}
rules={{
validate: validateExpiration,
}}
render={() => (
<ExpirySelector
value={order.expiresAt}
onSelect={(expiresAt) =>
update({
expiresAt: expiresAt || undefined,
})
}
errorMessage={errors.expiresAt?.message}
/>
)}
/>
)}
<SummaryMessage
errorMessage={errors.summary?.message}
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
margin={totalMargin}
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
/>
<DealTicketButton
disabled={Object.keys(errors).length >= 1 || isReadOnly}
variant={order.side === Schema.Side.SIDE_BUY ? 'ternary' : 'secondary'}
/>
<DealTicketFeeDetails
order={normalizedOrder}
market={market}
marketData={marketData}
margin={margin}
totalMargin={totalMargin}
balance={marginAccountBalance}
/>
</form>
<TinyScroll className="h-full overflow-auto">
<form
onSubmit={isReadOnly ? undefined : handleSubmit(onSubmit)}
className="p-4"
noValidate
>
<Controller
name="type"
control={control}
rules={{
validate: validateType(
marketData.marketTradingMode,
marketData.trigger
),
}}
render={() => (
<TypeSelector
value={order.type}
onSelect={(type) => {
if (type === OrderType.TYPE_NETWORK) return;
update({
type,
// when changing type also update the tif to what was last used of new type
timeInForce: lastTIF[type] || order.timeInForce,
expiresAt: undefined,
});
clearErrors('expiresAt');
}}
market={market}
marketData={marketData}
errorMessage={errors.type?.message}
/>
)}
/>
<Controller
name="side"
control={control}
render={() => (
<SideSelector
value={order.side}
onSelect={(side) => {
update({ side });
}}
/>
)}
/>
<DealTicketAmount
control={control}
orderType={order.type}
market={market}
marketData={marketData}
sizeError={errors.size?.message}
priceError={errors.price?.message}
update={update}
size={order.size}
price={order.price}
/>
<Controller
name="timeInForce"
control={control}
rules={{
validate: validateTimeInForce(
marketData.marketTradingMode,
marketData.trigger
),
}}
render={() => (
<TimeInForceSelector
value={order.timeInForce}
orderType={order.type}
onSelect={(timeInForce) => {
update({ timeInForce });
// Set tif value for the given order type, so that when switching
// types we know the last used TIF for the given order type
setLastTIF((curr) => ({
...curr,
[order.type]: timeInForce,
expiresAt: undefined,
}));
clearErrors('expiresAt');
}}
market={market}
marketData={marketData}
errorMessage={errors.timeInForce?.message}
/>
)}
/>
{order.type === Schema.OrderType.TYPE_LIMIT &&
order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT && (
<Controller
name="expiresAt"
control={control}
rules={{
validate: validateExpiration,
}}
render={() => (
<ExpirySelector
value={order.expiresAt}
onSelect={(expiresAt) =>
update({
expiresAt: expiresAt || undefined,
})
}
errorMessage={errors.expiresAt?.message}
/>
)}
/>
)}
<SummaryMessage
errorMessage={errors.summary?.message}
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
margin={totalMargin}
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
/>
<DealTicketButton
disabled={Object.keys(errors).length >= 1 || isReadOnly}
variant={
order.side === Schema.Side.SIDE_BUY ? 'ternary' : 'secondary'
}
/>
<DealTicketFeeDetails
order={normalizedOrder}
market={market}
marketData={marketData}
margin={margin}
totalMargin={totalMargin}
balance={marginAccountBalance}
/>
</form>
</TinyScroll>
);
};

View File

@ -1,4 +1,3 @@
import styles from './orderbook.module.scss';
import colors from 'tailwindcss/colors';
import { useEffect, useRef, useState, useCallback, Fragment } from 'react';
import classNames from 'classnames';
@ -14,7 +13,7 @@ import {
import * as Schema from '@vegaprotocol/types';
import { OrderbookRow } from './orderbook-row';
import { createRow } from './orderbook-data';
import { Checkbox, Icon, Splash } from '@vegaprotocol/ui-toolkit';
import { Checkbox, Icon, Splash, TinyScroll } from '@vegaprotocol/ui-toolkit';
import type { OrderbookData, OrderbookRowData } from './orderbook-data';
interface OrderbookProps extends OrderbookData {
@ -547,8 +546,8 @@ export const Orderbook = ({
{t('Cumulative vol')}
</div>
</div>
<div
className={`h-full overflow-auto relative ${styles['scroll']}`}
<TinyScroll
className="h-full overflow-auto relative"
onScroll={onScroll}
ref={scrollElement}
data-testid="scroll"
@ -580,7 +579,7 @@ export const Orderbook = ({
testId={'best-static-offer-price'}
/>
)}
</div>
</TinyScroll>
<div
className="absolute bottom-0 grid grid-cols-4 gap-2 border-t border-default mt-2 z-10 bg-white dark:bg-black w-full"
ref={footerElement}

View File

@ -9,6 +9,7 @@ import {
ExternalLink,
Link as UILink,
Splash,
TinyScroll,
} from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { generatePath, Link } from 'react-router-dom';
@ -71,7 +72,9 @@ export const MarketInfoContainer = ({
return (
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
{data ? (
<Info market={data} onSelect={(id) => onSelect?.(id)} />
<TinyScroll className="h-full overflow-auto">
<Info market={data} onSelect={(id) => onSelect?.(id)} />
</TinyScroll>
) : (
<Splash>
<p>{t('Could not load market')}</p>

View File

@ -40,6 +40,7 @@ export * from './tabs';
export * from './text-area';
export * from './theme-switcher';
export * from './thumbs';
export * from './tiny-scroll';
export * from './toast';
export * from './toggle';
export * from './tooltip';

View File

@ -0,0 +1 @@
export * from './tiny-scroll';

View File

@ -0,0 +1,20 @@
import styles from './tiny-scroll.module.scss';
import { forwardRef } from 'react';
import classNames from 'classnames';
import type { HTMLAttributes, ReactNode } from 'react';
export interface Props extends HTMLAttributes<HTMLDivElement> {
children: ReactNode;
}
export const TinyScroll = forwardRef<HTMLDivElement, Props>(
({ children, className, ...props }, ref) => (
<div
ref={ref}
className={classNames(className, styles['scroll'])}
{...props}
>
{children}
</div>
)
);