feat(trading): use mini scroll in deal ticket, market info for and landing dialog (#3269)
This commit is contained in:
parent
e8adff25c4
commit
58c6652e29
@ -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]()}
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
1
libs/ui-toolkit/src/components/tiny-scroll/index.tsx
Normal file
1
libs/ui-toolkit/src/components/tiny-scroll/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export * from './tiny-scroll';
|
20
libs/ui-toolkit/src/components/tiny-scroll/tiny-scroll.tsx
Normal file
20
libs/ui-toolkit/src/components/tiny-scroll/tiny-scroll.tsx
Normal 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>
|
||||
)
|
||||
);
|
Loading…
Reference in New Issue
Block a user