Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update mobile web trade form for better ux experience on isolated trades #1108

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions src/components/Collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const $Trigger = styled(Trigger)`
--trigger-textColor: inherit;
--trigger-icon-width: 0.75em;
--trigger-icon-color: inherit;
--icon-size: var(--trigger-icon-width);
`;
const $TriggerIcon = styled.span`
width: var(--trigger-icon-width);
Expand Down
9 changes: 5 additions & 4 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,9 @@ export const Icon = styled(
}: ElementProps & StyleProps & { size?: string }) =>
Component ? <Component className={className} {...props} /> : null
)`
width: ${({ size }) => size ?? '1em'};
height: ${({ size }) => size ?? '1em'};
min-width: ${({ size }) => size ?? '1em'};
min-height: ${({ size }) => size ?? '1em'};
--icon-size: ${({ size }) => size ?? ''};
width: var(--icon-size, 1em);
height: var(--icon-size, 1em);
min-width: var(--icon-size, 1em);
min-height: var(--icon-size, 1em);
`;
2 changes: 2 additions & 0 deletions src/styles/layoutMixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const scrollAreaFadeStart = css`

/* Rules */
position: relative;
overflow: hidden;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotta imagine this is going to cause pain SOMEWHERE, but I've got no clue where


&:before {
content: '';
Expand All @@ -64,6 +65,7 @@ const scrollAreaFadeEnd = css`

/* Rules */
position: relative;
overflow: hidden;

&:after {
content: '';
Expand Down
129 changes: 28 additions & 101 deletions src/views/TradeBoxOrderView.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
import { useCallback } from 'react';

import { OrderSide } from '@dydxprotocol/v4-client-js';
import { shallowEqual } from 'react-redux';
import styled, { css } from 'styled-components';
import styled from 'styled-components';

import { AbacusOrderSide, TradeInputField } from '@/constants/abacus';
import { TradeInputField } from '@/constants/abacus';
import { OnboardingState } from '@/constants/account';
import { STRING_KEYS } from '@/constants/localization';
import { TradeTypes } from '@/constants/trade';

import { useStringGetter } from '@/hooks/useStringGetter';

import { layoutMixins } from '@/styles/layoutMixins';

import { Tabs } from '@/components/Tabs';

import { getOnboardingState } from '@/state/accountSelectors';
import { useAppSelector } from '@/state/appTypes';
import { getTradeSide } from '@/state/inputsSelectors';

import abacusStateManager from '@/lib/abacus';
import { getSimpleStyledOutputType } from '@/lib/genericFunctionalComponentUtils';
import { getSelectedOrderSide } from '@/lib/tradeData';

import { TradeSideTabs } from './TradeSideTabs';
import { TradeForm } from './forms/TradeForm';
import { MarginModeSelector } from './forms/TradeForm/MarginModeSelector';
import { TargetLeverageButton } from './forms/TradeForm/TargetLeverageButton';
import { MarginAndLeverageButtons } from './forms/TradeForm/MarginAndLeverageButtons';
import { useTradeTypeOptions } from './forms/TradeForm/useTradeTypeOptions';

export const TradeBoxOrderView = () => {
const stringGetter = useStringGetter();
const onTradeTypeChange = useCallback((tradeType?: TradeTypes) => {
if (tradeType) {
abacusStateManager.clearTradeInputValues();
Expand All @@ -42,54 +33,25 @@ export const TradeBoxOrderView = () => {
const onboardingState = useAppSelector(getOnboardingState);
const allowChangingOrderType = onboardingState === OnboardingState.AccountConnected;

const side = useAppSelector(getTradeSide, shallowEqual);
const selectedOrderSide = getSelectedOrderSide(side);

const sharedContent = (
<div tw="flex min-h-full flex-col gap-0.25">
<$MarginAndLeverageButtons>
<MarginModeSelector openInTradeBox />
<TargetLeverageButton />
</$MarginAndLeverageButtons>
<$OrderTypeTabs
key={selectedTradeType}
value={selectedTradeType}
items={tradeTypeItems}
onValueChange={onTradeTypeChange}
dividerStyle="underline"
disabled={!allowChangingOrderType}
sharedContent={
<$Container>
<TradeForm />
</$Container>
}
/>
</div>
);

const items = [
{ value: OrderSide.BUY, label: stringGetter({ key: STRING_KEYS.BUY_LONG }) },
{ value: OrderSide.SELL, label: stringGetter({ key: STRING_KEYS.SELL_SHORT }) },
];
return (
<$TradeSideTabs
fullWidthTabs
dividerStyle="underline"
activeTab={selectedOrderSide}
key={selectedOrderSide}
value={selectedOrderSide}
items={items}
onValueChange={(newSide: OrderSide) => {
abacusStateManager.setTradeValue({
value:
newSide === OrderSide.BUY
? AbacusOrderSide.Buy.rawValue
: AbacusOrderSide.Sell.rawValue,
field: TradeInputField.side,
});
}}
disabled={!allowChangingOrderType}
sharedContent={sharedContent}
<TradeSideTabs
sharedContent={
<div tw="flex min-h-full flex-col">
<$MarginAndLeverageButtons openInTradeBox />
<$OrderTypeTabs
value={selectedTradeType}
items={tradeTypeItems}
onValueChange={onTradeTypeChange}
dividerStyle="underline"
disabled={!allowChangingOrderType}
sharedContent={
<$Container>
<TradeForm />
</$Container>
}
/>
</div>
}
/>
);
};
Expand All @@ -98,51 +60,16 @@ const $Container = styled.div`
${layoutMixins.scrollArea}
`;

const tabsStyle = css`
--trigger-active-underline-size: 2px;
overflow: hidden;
> header {
justify-content: space-around;
}
const $MarginAndLeverageButtons = styled(MarginAndLeverageButtons)`
padding: 0.75rem 1rem;
box-shadow: inset 0 calc(-1 * var(--border-width)) var(--border-color);
`;

const $OrderTypeTabs = styled(Tabs)`
${tabsStyle}
--tabs-height: 2.125rem;
--trigger-active-backgroundColor: var(--trigger-backgroundColor);
` as typeof Tabs;

const tradeSideTabsType = getSimpleStyledOutputType(Tabs, {} as { activeTab: OrderSide });

const $TradeSideTabs = styled(Tabs)<{ activeTab: OrderSide }>`
${tabsStyle}
${({ activeTab }) =>
activeTab === OrderSide.BUY
? css`
--trigger-active-underline-backgroundColor: var(--color-gradient-positive);
--trigger-active-underlineColor: var(--color-positive);
--trigger-active-textColor: var(--color-positive);
--trigger-hover-textColor: var(--color-text-2);
`
: css`
--trigger-active-underline-backgroundColor: var(--color-gradient-negative);
--trigger-active-underlineColor: var(--color-negative);
--trigger-active-textColor: var(--color-negative);
--trigger-hover-textColor: var(--color-text-2);
`};
` as typeof tradeSideTabsType;

const $MarginAndLeverageButtons = styled.div`
display: flex;
gap: 0.5rem;
padding: 0.75rem 1rem;

border-bottom: var(--border);

abbr,
button {
${layoutMixins.flexExpandToSpace}
height: 2.25rem;
flex-basis: 100%;
> header {
justify-content: space-around;
}
`;
` as typeof Tabs;
85 changes: 85 additions & 0 deletions src/views/TradeSideTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';

import { OrderSide } from '@dydxprotocol/v4-client-js';
import { shallowEqual } from 'react-redux';
import styled, { css } from 'styled-components';

import { AbacusOrderSide, TradeInputField } from '@/constants/abacus';
import { OnboardingState } from '@/constants/account';
import { STRING_KEYS } from '@/constants/localization';

import { useStringGetter } from '@/hooks/useStringGetter';

import { Tabs } from '@/components/Tabs';

import { getOnboardingState } from '@/state/accountSelectors';
import { useAppSelector } from '@/state/appTypes';
import { getTradeSide } from '@/state/inputsSelectors';

import abacusStateManager from '@/lib/abacus';
import { getSimpleStyledOutputType } from '@/lib/genericFunctionalComponentUtils';
import { getSelectedOrderSide } from '@/lib/tradeData';

type ElementProps = {
sharedContent: React.ReactNode;
};

type StyleProps = {
className?: string;
};

export const TradeSideTabs = ({ sharedContent, className }: ElementProps & StyleProps) => {
const stringGetter = useStringGetter();

const onboardingState = useAppSelector(getOnboardingState);
const allowChangingOrderType = onboardingState === OnboardingState.AccountConnected;

const side = useAppSelector(getTradeSide, shallowEqual);
const selectedOrderSide = getSelectedOrderSide(side);

const items = [
{ value: OrderSide.BUY, label: stringGetter({ key: STRING_KEYS.BUY_LONG }) },
{ value: OrderSide.SELL, label: stringGetter({ key: STRING_KEYS.SELL_SHORT }) },
];

return (
<$TradeSideTabs
className={className}
fullWidthTabs
dividerStyle="underline"
activeTab={selectedOrderSide}
value={selectedOrderSide}
items={items}
onValueChange={(newSide: OrderSide) => {
abacusStateManager.setTradeValue({
value:
newSide === OrderSide.BUY
? AbacusOrderSide.Buy.rawValue
: AbacusOrderSide.Sell.rawValue,
field: TradeInputField.side,
});
}}
disabled={!allowChangingOrderType}
sharedContent={sharedContent}
/>
);
};

const tradeSideTabsType = getSimpleStyledOutputType(Tabs, {} as { activeTab: OrderSide });

const $TradeSideTabs = styled(Tabs)<{ activeTab: OrderSide }>`
${({ activeTab }) =>
activeTab === OrderSide.BUY
? css`
--trigger-active-underline-backgroundColor: var(--color-positive-dark);
--trigger-active-underlineColor: var(--color-positive);
--trigger-active-textColor: var(--color-positive);
--trigger-hover-textColor: var(--color-text-2);
`
: css`
--trigger-active-underline-backgroundColor: var(--color-negative-dark);
--trigger-active-underlineColor: var(--color-negative);
--trigger-active-textColor: var(--color-negative);
--trigger-hover-textColor: var(--color-text-2);
`};
` as typeof tradeSideTabsType;
43 changes: 8 additions & 35 deletions src/views/dialogs/TradeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ import { MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints } from '@/hooks/useBreakpoints';
import { useStringGetter } from '@/hooks/useStringGetter';

import { layoutMixins } from '@/styles/layoutMixins';

import { Dialog, DialogPlacement } from '@/components/Dialog';
import { GreenCheckCircle } from '@/components/GreenCheckCircle';
import { Icon, IconName } from '@/components/Icon';
import { Ring } from '@/components/Ring';
import { TradeForm } from '@/views/forms/TradeForm';

import { MarginModeSelector } from '../forms/TradeForm/MarginModeSelector';
import { TargetLeverageButton } from '../forms/TradeForm/TargetLeverageButton';
import { TradeSideToggle } from '../forms/TradeForm/TradeSideToggle';

export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: DialogProps<TradeDialogProps>) => {
const { isMobile } = useBreakpoints();
const stringGetter = useStringGetter();

const [currentStep, setCurrentStep] = useState<MobilePlaceOrderSteps>(
MobilePlaceOrderSteps.EditOrder
);
Expand All @@ -44,14 +39,9 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: DialogProps<Trad
{...{
[MobilePlaceOrderSteps.EditOrder]: {
title: (
<$TopActionsRow tw="flex gap-0.5">
<$MarginAndLeverageButtons tw="flex gap-0.5">
<MarginModeSelector openInTradeBox={false} tw="flex-1" />
<$TargetLeverageButton />
</$MarginAndLeverageButtons>

<TradeSideToggle />
</$TopActionsRow>
<div tw="inlineRow h-[--dialog-icon-size]">
{stringGetter({ key: STRING_KEYS.TRADE })}
</div>
),
},
[MobilePlaceOrderSteps.PreviewOrder]: {
Expand Down Expand Up @@ -98,34 +88,17 @@ const $Dialog = styled(Dialog)<{ currentStep: MobilePlaceOrderSteps }>`
--dialog-title-gap: 0.25rem;
--dialog-icon-size: 2rem;

> div {
min-height: 1px;
}

${({ currentStep }) =>
currentStep === MobilePlaceOrderSteps.EditOrder &&
css`
--dialog-icon-size: 2.5rem;
`}
`;

const $TopActionsRow = styled.div`
> * {
${layoutMixins.flexExpandToSpace}
}
`;

const $MarginAndLeverageButtons = styled.div`
abbr,
button {
${layoutMixins.flexExpandToSpace}
}
`;

const $TargetLeverageButton = styled(TargetLeverageButton)`
flex: 1;

button {
width: 100%;
}
`;

const $TradeForm = styled(TradeForm)`
--tradeBox-content-paddingTop: 1rem;
--tradeBox-content-paddingRight: 1.5rem;
Expand Down
Loading
Loading