Swap polish 02 (#93)
* rework swap UI * Add v2/v3 route toggle UX * improve button and progress styles * put optional route in swap header * Further refinements * Alt swap layout * clean up * Tweak route ui * merge main * update swap header * adjust currency select ui
This commit is contained in:
parent
d7785942b1
commit
4c2cb5b0c1
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { darken, lighten } from 'polished'
|
||||
import { darken } from 'polished'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { ChevronDown, Check } from 'react-feather'
|
||||
@ -32,6 +32,15 @@ const Base = styled(RebassButton)<{
|
||||
z-index: 1;
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
will-change: transform;
|
||||
transition: transform 450ms ease;
|
||||
transform: perspective(1px) translateZ(0);
|
||||
|
||||
&:hover {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
> * {
|
||||
@ -59,14 +68,14 @@ export const ButtonPrimary = styled(Base)`
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.bg3 : theme.primary1) : theme.bg3};
|
||||
color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.text3 : 'white') : theme.text3};
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
|
||||
color: white;
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '1')};
|
||||
opacity: 0.4;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
|
||||
}
|
||||
`
|
||||
|
||||
@ -270,12 +279,14 @@ export const ButtonWhite = styled(Base)`
|
||||
`
|
||||
|
||||
const ButtonConfirmedStyle = styled(Base)`
|
||||
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
|
||||
color: ${({ theme }) => theme.green1};
|
||||
border: 1px solid ${({ theme }) => theme.green1};
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.green1}; */
|
||||
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
/* opacity: 50%; */
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
@ -9,6 +9,7 @@ import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { ButtonGray } from '../Button'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
@ -52,24 +53,24 @@ const Container = styled.div<{ hideInput: boolean }>`
|
||||
}
|
||||
`
|
||||
|
||||
const CurrencySelect = styled.button<{ selected: boolean; hideInput?: boolean }>`
|
||||
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>`
|
||||
align-items: center;
|
||||
font-size: 20px;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primary1)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
border-radius: 12px;
|
||||
/* box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')}; */
|
||||
border-radius: 16px;
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: none;
|
||||
|
||||
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
|
||||
height: ${({ hideInput }) => (hideInput ? '2.4rem' : '2.4rem')};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
margin-right: 12px;
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg3 : darken(0.05, theme.primary1))};
|
||||
@ -79,7 +80,7 @@ const CurrencySelect = styled.button<{ selected: boolean; hideInput?: boolean }>
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: ${({ selected }) => (selected ? '.75rem 0.75rem .75rem 1rem' : '.75rem 0.75rem .75rem 1rem')};
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
@ -88,13 +89,17 @@ const LabelRow = styled.div`
|
||||
color: ${({ theme }) => theme.text1};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0rem 1rem 0.75rem 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const FiatRow = styled(LabelRow)`
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -102,7 +107,7 @@ const Aligner = styled.span`
|
||||
`
|
||||
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
margin: 0 0.25rem 0 0.5rem;
|
||||
margin: 0 0.25rem 0 0.35rem;
|
||||
height: 35%;
|
||||
|
||||
path {
|
||||
@ -113,27 +118,27 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
|
||||
const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '20px' : '18px')};
|
||||
font-size: ${({ active }) => (active ? '18px' : '18px')};
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
border: 1px solid ${({ theme }) => theme.primary5};
|
||||
border-radius: 0.5rem;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
/* margin-left: 0.5rem; */
|
||||
padding: 0;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
margin-left: 0.25rem;
|
||||
|
||||
:hover {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
/* border: 1px solid ${({ theme }) => theme.primary1}; */
|
||||
}
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
/* border: 1px solid ${({ theme }) => theme.primary1}; */
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -166,7 +171,6 @@ export default function CurrencyInputPanel({
|
||||
onUserInput,
|
||||
onMax,
|
||||
showMaxButton,
|
||||
label = 'Input',
|
||||
onCurrencySelect,
|
||||
currency,
|
||||
otherCurrency,
|
||||
@ -210,19 +214,6 @@ export default function CurrencyInputPanel({
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput}>
|
||||
{!hideInput && (
|
||||
<LabelRow style={{ padding: ' 1rem 1rem 0rem 1rem' }}>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
{label}
|
||||
</TYPE.body>
|
||||
<TYPE.label>
|
||||
{fiatValueOfTypedAmount ? '~' : ''}${fiatValueOfTypedAmount?.toSignificant(4) ?? '-'}
|
||||
</TYPE.label>
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
)}
|
||||
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
<CurrencySelect
|
||||
selected={!!currency}
|
||||
@ -261,24 +252,27 @@ export default function CurrencyInputPanel({
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
{!hideInput && (
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={(val) => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
{' '}
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={(val) => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</InputRow>
|
||||
{!hideInput && !hideBalance && (
|
||||
<LabelRow>
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
{account && (
|
||||
<RowFixed>
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<TYPE.body
|
||||
onClick={onMax}
|
||||
color={theme.text3}
|
||||
fontWeight={500}
|
||||
color={theme.text2}
|
||||
fontWeight={400}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
@ -287,17 +281,26 @@ export default function CurrencyInputPanel({
|
||||
selectedCurrencyBalance?.toSignificant(4) +
|
||||
' ' +
|
||||
currency.symbol
|
||||
: ' '}
|
||||
: '-'}
|
||||
</TYPE.body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
|
||||
(Max)
|
||||
</StyledBalanceMax>
|
||||
) : (
|
||||
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
|
||||
{''}
|
||||
</StyledBalanceMax>
|
||||
)}
|
||||
</RowFixed>
|
||||
)}
|
||||
{showMaxButton && (
|
||||
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
|
||||
Max
|
||||
</StyledBalanceMax>
|
||||
)}
|
||||
|
||||
<TYPE.body fontSize={14} color={fiatValueOfTypedAmount ? theme.text2 : theme.text4}>
|
||||
{fiatValueOfTypedAmount ? '~' : ''}$
|
||||
{fiatValueOfTypedAmount ? Number(fiatValueOfTypedAmount?.toSignificant(6)).toLocaleString('en') : '-'}
|
||||
</TYPE.body>
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
</FiatRow>
|
||||
)}
|
||||
</Container>
|
||||
{onCurrencySelect && (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChainId, TokenAmount } from '@uniswap/sdk-core'
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import React, { useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
@ -9,11 +9,11 @@ import styled from 'styled-components'
|
||||
|
||||
import Logo from '../../assets/svg/logo.svg'
|
||||
import LogoDark from '../../assets/svg/logo_white.svg'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useDarkModeManager } from '../../state/user/hooks'
|
||||
import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks'
|
||||
import { useETHBalances } from '../../state/wallet/hooks'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import { CountUp } from 'use-count-up'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
|
||||
import { YellowCard } from '../Card'
|
||||
@ -28,7 +28,6 @@ import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import Modal from '../Modal'
|
||||
import UniBalanceContent from './UniBalanceContent'
|
||||
import usePrevious from '../../hooks/usePrevious'
|
||||
|
||||
const HeaderFrame = styled.div`
|
||||
display: grid;
|
||||
@ -40,10 +39,10 @@ const HeaderFrame = styled.div`
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1); */
|
||||
padding: 0.5rem 1rem;
|
||||
z-index: 21;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
display: flex;
|
||||
@ -314,14 +313,9 @@ export default function Header() {
|
||||
|
||||
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
|
||||
|
||||
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
|
||||
|
||||
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
|
||||
const showClaimPopup = useShowClaimPopup()
|
||||
|
||||
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
|
||||
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
|
||||
|
||||
return (
|
||||
<HeaderFrame>
|
||||
<ClaimModal />
|
||||
@ -376,33 +370,7 @@ export default function Header() {
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
{/* I want to put this in the overflow menu now */}
|
||||
{!availableClaim && aggregateBalance && (
|
||||
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
{account && (
|
||||
<HideSmall>
|
||||
<TYPE.white
|
||||
style={{
|
||||
paddingRight: '.4rem',
|
||||
}}
|
||||
>
|
||||
<CountUp
|
||||
key={countUpValue}
|
||||
isCounting
|
||||
start={parseFloat(countUpValuePrevious)}
|
||||
end={parseFloat(countUpValue)}
|
||||
thousandsSeparator={','}
|
||||
duration={1}
|
||||
/>
|
||||
</TYPE.white>
|
||||
</HideSmall>
|
||||
)}
|
||||
UNI
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
|
||||
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
|
@ -21,7 +21,7 @@ export const Popup = styled.div`
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
|
@ -33,7 +33,7 @@ const MobilePopupInner = styled.div`
|
||||
|
||||
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
|
||||
position: fixed;
|
||||
top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
|
||||
top: ${({ extraPadding }) => (extraPadding ? '72px' : '88px')};
|
||||
right: 1rem;
|
||||
max-width: 355px !important;
|
||||
width: 100%;
|
||||
|
@ -2,26 +2,26 @@ import React, { useContext } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import { ArrowDown } from 'react-feather'
|
||||
import { TYPE } from '../../theme'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const Grouping = styled(AutoColumn)`
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
/* background-color: ${({ theme }) => theme.bg2}; */
|
||||
border-radius: 16px;
|
||||
`
|
||||
|
||||
const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: ${({ theme, confirmed, disabled }) =>
|
||||
disabled ? theme.bg3 : confirmed ? theme.green1 : theme.primary1};
|
||||
border-radius: 12px;
|
||||
border-radius: 50%;
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.text3 : theme.text1)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -37,12 +37,6 @@ const CircleRow = styled.div`
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const StyledArrowDown = styled(ArrowDown)`
|
||||
margin: 0.5rem;
|
||||
min-height: 14px;
|
||||
/* color: ${({ theme }) => theme.text1}; */
|
||||
`
|
||||
|
||||
interface ProgressCirclesProps {
|
||||
steps: boolean[]
|
||||
disabled?: boolean
|
||||
@ -68,13 +62,13 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
|
||||
return (
|
||||
<CircleRow key={i}>
|
||||
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
|
||||
{step ? '✓' : i + 1}
|
||||
{step ? '✓' : i + 1 + '.'}
|
||||
</Circle>
|
||||
<StyledArrowDown size="16" color={theme.text3} />
|
||||
<TYPE.main color={theme.text4}>|</TYPE.main>
|
||||
</CircleRow>
|
||||
)
|
||||
})}
|
||||
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1}</Circle>
|
||||
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1 + '.'}</Circle>
|
||||
</Grouping>
|
||||
</Wrapper>
|
||||
)
|
||||
|
@ -21,8 +21,6 @@ import QuestionHelper from '../QuestionHelper'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import Toggle from '../Toggle'
|
||||
import TransactionSettings from '../TransactionSettings'
|
||||
import { Moon, Sun } from 'react-feather'
|
||||
import { useDarkModeManager } from '../../state/user/hooks'
|
||||
|
||||
const StyledMenuIcon = styled(Settings)`
|
||||
height: 20px;
|
||||
@ -86,6 +84,7 @@ const StyledMenu = styled.div`
|
||||
const MenuFlyout = styled.span`
|
||||
min-width: 20.125rem;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 12px;
|
||||
@ -93,7 +92,7 @@ const MenuFlyout = styled.span`
|
||||
flex-direction: column;
|
||||
font-size: 1rem;
|
||||
position: absolute;
|
||||
top: 3rem;
|
||||
top: 2rem;
|
||||
right: 0rem;
|
||||
z-index: 100;
|
||||
|
||||
@ -134,8 +133,6 @@ export default function SettingsTab() {
|
||||
// show confirmation view before turning on
|
||||
const [showConfirmation, setShowConfirmation] = useState(false)
|
||||
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
return (
|
||||
@ -245,10 +242,6 @@ export default function SettingsTab() {
|
||||
}}
|
||||
/>
|
||||
</RowBetween>
|
||||
{/* WIP */}
|
||||
<StyledMenuButton onClick={() => toggleDarkMode()}>
|
||||
{darkMode ? <Moon color={theme.text2} size={20} /> : <Sun size={20} />}
|
||||
</StyledMenuButton>
|
||||
</AutoColumn>
|
||||
</MenuFlyout>
|
||||
)}
|
||||
|
@ -3,9 +3,9 @@ import styled from 'styled-components'
|
||||
import Popover, { PopoverProps } from '../Popover'
|
||||
|
||||
const TooltipContainer = styled.div`
|
||||
width: 228px;
|
||||
width: 256px;
|
||||
padding: 0.6rem 1rem;
|
||||
line-height: 150%;
|
||||
/* line-height: 150%; */
|
||||
font-weight: 400;
|
||||
`
|
||||
|
||||
@ -13,10 +13,18 @@ interface TooltipProps extends Omit<PopoverProps, 'content'> {
|
||||
text: string
|
||||
}
|
||||
|
||||
interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
|
||||
content: React.ReactNode
|
||||
}
|
||||
|
||||
export default function Tooltip({ text, ...rest }: TooltipProps) {
|
||||
return <Popover content={<TooltipContainer>{text}</TooltipContainer>} {...rest} />
|
||||
}
|
||||
|
||||
export function TooltipContent({ content, ...rest }: TooltipContentProps) {
|
||||
return <Popover content={<TooltipContainer>{content}</TooltipContainer>} {...rest} />
|
||||
}
|
||||
|
||||
export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show'>) {
|
||||
const [show, setShow] = useState(false)
|
||||
const open = useCallback(() => setShow(true), [setShow])
|
||||
@ -29,3 +37,16 @@ export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export function MouseoverTooltipContent({ content, children, ...rest }: Omit<TooltipContentProps, 'show'>) {
|
||||
const [show, setShow] = useState(false)
|
||||
const open = useCallback(() => setShow(true), [setShow])
|
||||
const close = useCallback(() => setShow(false), [setShow])
|
||||
return (
|
||||
<TooltipContent {...rest} show={show} content={content}>
|
||||
<div onMouseEnter={open} onMouseLeave={close}>
|
||||
{children}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const Wrapper = styled.div`
|
||||
padding: 1rem;
|
||||
`
|
||||
const Section = styled(AutoColumn)<{ inline?: boolean }>`
|
||||
padding: ${({ inline }) => (inline ? '0' : '24px')};
|
||||
padding: ${({ inline }) => (inline ? '0' : '0')};
|
||||
`
|
||||
|
||||
const BottomSection = styled(Section)`
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { TradeType } from '@uniswap/sdk-core'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import React, { useContext } from 'react'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import { Field } from '../../state/swap/actions'
|
||||
import { useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from '../../utils/prices'
|
||||
import { computeTradePriceBreakdown } from '../../utils/prices'
|
||||
import { AutoColumn } from '../Column'
|
||||
import QuestionHelper from '../QuestionHelper'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import FormattedPriceImpact from './FormattedPriceImpact'
|
||||
import SwapRoute from './SwapRoute'
|
||||
@ -16,74 +12,28 @@ import SwapRoute from './SwapRoute'
|
||||
function TradeSummary({ trade }: { trade: V2Trade | V3Trade }) {
|
||||
const theme = useContext(ThemeContext)
|
||||
const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade)
|
||||
const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
|
||||
const [allowedSlippage] = useUserSlippageTolerance()
|
||||
const slippageAdjustedAmounts = computeSlippageAdjustedAmounts(trade, allowedSlippage)
|
||||
const price = trade.executionPrice
|
||||
|
||||
return (
|
||||
<>
|
||||
<AutoColumn style={{ padding: '8px 16px' }} gap="8px">
|
||||
<AutoColumn gap={'8px'}>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
Price
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<TYPE.black color={theme.text1} fontSize={14}>
|
||||
{'1 ' +
|
||||
price?.quoteCurrency?.symbol +
|
||||
' = ' +
|
||||
price?.invert()?.toSignificant(6) +
|
||||
' ' +
|
||||
price?.baseCurrency?.symbol}
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
Inverted Price
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<TYPE.black color={theme.text1} fontSize={14}>
|
||||
{'1 ' + price?.baseCurrency?.symbol + ' = ' + price?.toSignificant(6) + ' ' + price?.quoteCurrency?.symbol}
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
{isExactIn ? 'Minimum received' : 'Maximum sold'}
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="Your transaction will revert if there is a large, unfavorable price movement before it is confirmed." />
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.black color={theme.text1} fontSize={14}>
|
||||
{isExactIn
|
||||
? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${trade.outputAmount.currency.symbol}` ??
|
||||
'-'
|
||||
: `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${trade.inputAmount.currency.symbol}` ??
|
||||
'-'}
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
|
||||
Price Impact
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="The difference between the market price and estimated price due to trade size." />
|
||||
{/* <QuestionHelper text="The difference between the market price and estimated price due to trade size." /> */}
|
||||
</RowFixed>
|
||||
<FormattedPriceImpact priceImpact={priceImpactWithoutFee} />
|
||||
</RowBetween>
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
|
||||
Liquidity Provider Fee
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="A portion of each trade goes to liquidity providers as a protocol incentive." />
|
||||
{/* <QuestionHelper text="A portion of each trade goes to liquidity providers as a protocol incentive." /> */}
|
||||
</RowFixed>
|
||||
<TYPE.black fontSize={14} color={theme.text1}>
|
||||
<TYPE.black fontSize={12} color={theme.text1}>
|
||||
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.inputAmount.currency.symbol}` : '-'}
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
@ -116,7 +66,7 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
Route
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="Routing through these tokens resulted in the best price for your trade." />
|
||||
{/* <QuestionHelper text="Routing through these tokens resulted in the best price for your trade." /> */}
|
||||
</span>
|
||||
<SwapRoute trade={trade} />
|
||||
</RowBetween>
|
||||
|
@ -1,28 +1,14 @@
|
||||
import { stringify } from 'qs'
|
||||
import React, { useContext, useMemo } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useLocation } from 'react-router'
|
||||
import { Text } from 'rebass'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import useParsedQueryString from '../../hooks/useParsedQueryString'
|
||||
import useToggledVersion, { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
|
||||
import { StyledInternalLink } from '../../theme'
|
||||
import { YellowCard } from '../Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
function VersionLinkContainer({ children }: { children: React.ReactNode }) {
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
return (
|
||||
<YellowCard style={{ marginTop: '4px', padding: '0.5rem 0.5rem' }}>
|
||||
<AutoColumn gap="sm" justify="center" style={{ alignItems: 'center', textAlign: 'center' }}>
|
||||
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400} color={theme.text1}>
|
||||
{children}
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
)
|
||||
}
|
||||
import { Zap } from 'react-feather'
|
||||
|
||||
export default function BetterTradeLink({
|
||||
version,
|
||||
@ -45,12 +31,24 @@ export default function BetterTradeLink({
|
||||
}, [location, search, version])
|
||||
|
||||
return (
|
||||
<VersionLinkContainer>
|
||||
{otherTradeNonexistent ? 'This trade can be executed on ' : 'There is a better price for this trade on '}
|
||||
<StyledInternalLink to={linkDestination}>
|
||||
<b>Uniswap {version.toUpperCase()} ↗</b>
|
||||
</StyledInternalLink>
|
||||
</VersionLinkContainer>
|
||||
<ButtonPrimary
|
||||
as={Link}
|
||||
to={linkDestination}
|
||||
style={{
|
||||
width: 'fit-content',
|
||||
padding: '.2rem .5rem',
|
||||
wordBreak: 'keep-all',
|
||||
height: '24px',
|
||||
marginLeft: '.25rem',
|
||||
}}
|
||||
>
|
||||
<Zap size={12} style={{ marginRight: '0.25rem' }} />
|
||||
<TYPE.small style={{ lineHeight: '120%' }} fontSize={12}>
|
||||
{otherTradeNonexistent
|
||||
? `No liquidity! Click to trade with ${version.toUpperCase()}`
|
||||
: `Get a better price on ${version.toUpperCase()}`}
|
||||
</TYPE.small>
|
||||
</ButtonPrimary>
|
||||
)
|
||||
}
|
||||
|
||||
@ -70,11 +68,12 @@ export function DefaultVersionLink() {
|
||||
}, [location, search])
|
||||
|
||||
return (
|
||||
<VersionLinkContainer>
|
||||
Showing {version.toUpperCase()} price.{' '}
|
||||
<StyledInternalLink to={linkDestination}>
|
||||
<b>Switch to Uniswap {DEFAULT_VERSION.toUpperCase()} ↗</b>
|
||||
</StyledInternalLink>
|
||||
</VersionLinkContainer>
|
||||
<ButtonPrimary
|
||||
as={Link}
|
||||
to={linkDestination}
|
||||
style={{ width: 'fit-content', marginTop: '4px', padding: '0.5rem 0.5rem' }}
|
||||
>
|
||||
Showing {version.toUpperCase()} price. <b>Switch to Uniswap {DEFAULT_VERSION.toUpperCase()} ↗</b>
|
||||
</ButtonPrimary>
|
||||
)
|
||||
}
|
||||
|
@ -81,10 +81,9 @@ export default function ConfirmSwapModal({
|
||||
trade={trade}
|
||||
disabledConfirm={showAcceptChanges}
|
||||
swapErrorMessage={swapErrorMessage}
|
||||
allowedSlippage={allowedSlippage}
|
||||
/>
|
||||
) : null
|
||||
}, [allowedSlippage, onConfirm, showAcceptChanges, swapErrorMessage, trade])
|
||||
}, [onConfirm, showAcceptChanges, swapErrorMessage, trade])
|
||||
|
||||
// text to show while loading
|
||||
const pendingText = `Swapping ${trade?.maximumAmountIn(allowedSlippage)?.toSignificant(6)} ${
|
||||
|
@ -9,7 +9,7 @@ import { ErrorText, ErrorPill } from './styleds'
|
||||
*/
|
||||
export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) {
|
||||
return (
|
||||
<ErrorText fontWeight={500} fontSize={14} severity={warningSeverity(priceImpact)}>
|
||||
<ErrorText fontWeight={500} fontSize={12} severity={warningSeverity(priceImpact)}>
|
||||
{priceImpact
|
||||
? priceImpact.lessThan(ONE_BIPS)
|
||||
? `-${priceImpact.toFixed(2)}%`
|
||||
@ -21,7 +21,7 @@ export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Pe
|
||||
|
||||
export function SmallFormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) {
|
||||
return (
|
||||
<ErrorPill fontWeight={500} fontSize={14} severity={warningSeverity(priceImpact)}>
|
||||
<ErrorPill fontWeight={500} fontSize={12} severity={warningSeverity(priceImpact)}>
|
||||
{priceImpact
|
||||
? priceImpact.lessThan(ONE_BIPS)
|
||||
? `(-${priceImpact.toFixed(2)}%)`
|
||||
|
@ -1,51 +1,28 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Version } from '../../hooks/useToggledVersion'
|
||||
import Settings from '../Settings'
|
||||
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
|
||||
// import { Info } from 'react-feather'
|
||||
|
||||
const StyledSwapHeader = styled.div`
|
||||
padding: 1rem 1.25rem 0.5rem 1.25rem;
|
||||
width: 100%;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
`
|
||||
//
|
||||
// const InfoLink = styled(ExternalLink)`
|
||||
// width: 100%;
|
||||
// text-align: center;
|
||||
// font-size: 14px;
|
||||
// height: 20px;
|
||||
// margin-right: 8px;
|
||||
// color: ${({ theme }) => theme.text1};
|
||||
// `
|
||||
|
||||
interface SwapHeaderProps {
|
||||
toggledVersion: Version
|
||||
}
|
||||
|
||||
export default function SwapHeader({ toggledVersion }: SwapHeaderProps) {
|
||||
export default function SwapHeader() {
|
||||
return (
|
||||
<StyledSwapHeader>
|
||||
<RowBetween>
|
||||
<TYPE.black fontWeight={500} fontSize={16} style={{ opacity: '0.6' }}>
|
||||
Swap ({toggledVersion})
|
||||
</TYPE.black>
|
||||
<RowFixed>
|
||||
{/* Send icon appears here when expert mode is toggled on */}
|
||||
{/* <Send style={{ marginRight: '16px' }} size="20" onClick={() => onChangeRecipient('')} /> */}
|
||||
{/* This info icon should open info.uniswap.org with the pair */}
|
||||
{/*{trade && (*/}
|
||||
{/* <InfoLink*/}
|
||||
{/* href={'https://info.uniswap.org/pair/' + trade.route.pairs[0].liquidityToken.address}*/}
|
||||
{/* target="_blank"*/}
|
||||
{/* >*/}
|
||||
{/* <Info size="20" style={{ opacity: '0.6' }} />*/}
|
||||
{/* </InfoLink>*/}
|
||||
{/*)}*/}
|
||||
|
||||
<TYPE.black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
|
||||
Swap{' '}
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
{/* <TradeInfo disabled={!trade} trade={trade} /> */}
|
||||
{/* <div style={{ width: '8px' }}></div> */}
|
||||
<Settings />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
|
@ -1,61 +1,30 @@
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import React, { useContext, useMemo, useState } from 'react'
|
||||
import { Repeat } from 'react-feather'
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import { computeTradePriceBreakdown, formatExecutionPrice, warningSeverity } from '../../utils/prices'
|
||||
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow, RowBetween } from '../Row'
|
||||
import { StyledBalanceMaxMini, SwapCallbackError } from './styleds'
|
||||
import {} from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
import { SwapCallbackError } from './styleds'
|
||||
|
||||
export default function SwapModalFooter({
|
||||
trade,
|
||||
onConfirm,
|
||||
allowedSlippage,
|
||||
swapErrorMessage,
|
||||
disabledConfirm,
|
||||
}: {
|
||||
trade: V2Trade | V3Trade
|
||||
allowedSlippage: Percent
|
||||
onConfirm: () => void
|
||||
swapErrorMessage: string | undefined
|
||||
disabledConfirm: boolean
|
||||
}) {
|
||||
const [showInverted, setShowInverted] = useState<boolean>(false)
|
||||
const theme = useContext(ThemeContext)
|
||||
const { priceImpactWithoutFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade])
|
||||
const severity = warningSeverity(priceImpactWithoutFee)
|
||||
|
||||
return (
|
||||
<>
|
||||
<AutoColumn gap="0px">
|
||||
<RowBetween align="center">
|
||||
<Text fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
Price
|
||||
</Text>
|
||||
<Text
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
color={theme.text1}
|
||||
style={{
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
textAlign: 'right',
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
{formatExecutionPrice(trade, showInverted, allowedSlippage)}
|
||||
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
|
||||
<Repeat size={14} />
|
||||
</StyledBalanceMaxMini>
|
||||
</Text>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
|
||||
<AutoRow>
|
||||
<ButtonError
|
||||
onClick={onConfirm}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import React, { useContext } from 'react'
|
||||
import React, { useContext, useState } from 'react'
|
||||
import { ArrowDown, AlertTriangle } from 'react-feather'
|
||||
import { Text } from 'rebass'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { isAddress, shortenAddress } from '../../utils'
|
||||
@ -13,6 +13,30 @@ import CurrencyLogo from '../CurrencyLogo'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TruncatedText, SwapShowAcceptChanges } from './styleds'
|
||||
|
||||
import { AdvancedSwapDetails } from './AdvancedSwapDetails'
|
||||
import { LightCard } from '../Card'
|
||||
|
||||
import { DarkGreyCard } from '../Card'
|
||||
import TradePrice from '../swap/TradePrice'
|
||||
|
||||
export const ArrowWrapper = styled.div`
|
||||
padding: 4px;
|
||||
border-radius: 12px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
position: relative;
|
||||
margin-top: -18px;
|
||||
margin-bottom: -18px;
|
||||
left: calc(50% - 16px);
|
||||
/* transform: rotate(90deg); */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
/* border: 4px solid ${({ theme }) => theme.bg0}; */
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
export default function SwapModalHeader({
|
||||
trade,
|
||||
allowedSlippage,
|
||||
@ -31,41 +55,82 @@ export default function SwapModalHeader({
|
||||
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const [showInverted, setShowInverted] = useState<boolean>(false)
|
||||
|
||||
return (
|
||||
<AutoColumn gap={'md'} style={{ marginTop: '20px' }}>
|
||||
<RowBetween align="flex-end">
|
||||
<RowFixed gap={'0px'}>
|
||||
<CurrencyLogo currency={trade.inputAmount.currency} size={'24px'} style={{ marginRight: '12px' }} />
|
||||
<TruncatedText
|
||||
fontSize={24}
|
||||
fontWeight={500}
|
||||
color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''}
|
||||
>
|
||||
{maximumAmountIn.toSignificant(6)}
|
||||
</TruncatedText>
|
||||
</RowFixed>
|
||||
<RowFixed gap={'0px'}>
|
||||
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
|
||||
{trade.inputAmount.currency.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowFixed>
|
||||
<ArrowDown size="16" color={theme.text2} style={{ marginLeft: '4px', minWidth: '16px' }} />
|
||||
</RowFixed>
|
||||
<RowBetween align="flex-end">
|
||||
<RowFixed gap={'0px'}>
|
||||
<CurrencyLogo currency={trade.outputAmount.currency} size={'24px'} style={{ marginRight: '12px' }} />
|
||||
<TruncatedText fontSize={24} fontWeight={500}>
|
||||
{minimumAmountOut.toSignificant(6)}
|
||||
</TruncatedText>
|
||||
</RowFixed>
|
||||
<RowFixed gap={'0px'}>
|
||||
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
|
||||
{trade.outputAmount.currency.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>
|
||||
<DarkGreyCard padding="0.75rem 1rem">
|
||||
<AutoColumn gap={'8px'}>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
{'From'}
|
||||
</TYPE.body>
|
||||
<TYPE.body fontSize={14} color={theme.text3}>
|
||||
{'$-'}
|
||||
</TYPE.body>
|
||||
</RowBetween>
|
||||
<RowBetween align="center">
|
||||
<RowFixed gap={'0px'}>
|
||||
<CurrencyLogo currency={trade.inputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
{trade.inputAmount.currency.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<RowFixed gap={'0px'}>
|
||||
<TruncatedText
|
||||
fontSize={24}
|
||||
fontWeight={500}
|
||||
color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''}
|
||||
>
|
||||
{maximumAmountIn.toSignificant(6)}
|
||||
</TruncatedText>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</DarkGreyCard>
|
||||
<ArrowWrapper>
|
||||
<ArrowDown size="16" color={theme.text2} />
|
||||
</ArrowWrapper>
|
||||
<DarkGreyCard padding="0.75rem 1rem" style={{ marginBottom: '0.25rem' }}>
|
||||
<AutoColumn gap={'8px'}>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
{'To'}
|
||||
</TYPE.body>
|
||||
<TYPE.body fontSize={14} color={theme.text3}>
|
||||
{'$-'}
|
||||
</TYPE.body>
|
||||
</RowBetween>
|
||||
<RowBetween align="flex-end">
|
||||
<RowFixed gap={'0px'}>
|
||||
<CurrencyLogo currency={trade.outputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
{trade.outputAmount.currency.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<RowFixed gap={'0px'}>
|
||||
<TruncatedText fontSize={24} fontWeight={500}>
|
||||
{minimumAmountOut.toSignificant(6)}
|
||||
</TruncatedText>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</DarkGreyCard>
|
||||
<RowBetween style={{ marginTop: '0.25rem', padding: '0 1rem' }}>
|
||||
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
{'Price:'}
|
||||
</TYPE.body>
|
||||
<TradePrice
|
||||
price={trade.worstExecutionPrice(allowedSlippage)}
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
</RowBetween>
|
||||
|
||||
<LightCard style={{ padding: '.75rem', marginTop: '0.5rem' }}>
|
||||
<AdvancedSwapDetails trade={trade} />
|
||||
</LightCard>
|
||||
|
||||
{showAcceptChanges ? (
|
||||
<SwapShowAcceptChanges justify="flex-start" gap={'0px'}>
|
||||
<RowBetween>
|
||||
@ -82,9 +147,10 @@ export default function SwapModalHeader({
|
||||
</RowBetween>
|
||||
</SwapShowAcceptChanges>
|
||||
) : null}
|
||||
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
|
||||
|
||||
{/* <AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT ? (
|
||||
<TYPE.italic textAlign="left" style={{ width: '100%' }}>
|
||||
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
{`Output is estimated. You will receive at least `}
|
||||
<b>
|
||||
{minimumAmountOut.toSignificant(6)} {trade.outputAmount.currency.symbol}
|
||||
@ -92,7 +158,7 @@ export default function SwapModalHeader({
|
||||
{' or the transaction will revert.'}
|
||||
</TYPE.italic>
|
||||
) : (
|
||||
<TYPE.italic textAlign="left" style={{ width: '100%' }}>
|
||||
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
{`Input is estimated. You will sell at most `}
|
||||
<b>
|
||||
{maximumAmountIn.toSignificant(6)} {trade.inputAmount.currency.symbol}
|
||||
@ -100,7 +166,7 @@ export default function SwapModalHeader({
|
||||
{' or the transaction will revert.'}
|
||||
</TYPE.italic>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</AutoColumn> */}
|
||||
{recipient !== null ? (
|
||||
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
|
||||
<TYPE.main>
|
||||
|
@ -4,8 +4,6 @@ import { useContext } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { StyledBalanceMaxMini } from './styleds'
|
||||
import Switch from '../../assets/svg/switch.svg'
|
||||
|
||||
interface TradePriceProps {
|
||||
price: Price
|
||||
@ -13,11 +11,19 @@ interface TradePriceProps {
|
||||
setShowInverted: (showInverted: boolean) => void
|
||||
}
|
||||
|
||||
const StyledPriceContainer = styled.div`
|
||||
justify-content: flex-end;
|
||||
const StyledPriceContainer = styled.button`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
width: fit-content;
|
||||
padding: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
margin-left: 1rem;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
|
||||
@ -25,7 +31,7 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
|
||||
|
||||
let formattedPrice: string
|
||||
try {
|
||||
formattedPrice = showInverted ? price.toSignificant(6) : price.invert()?.toSignificant(6)
|
||||
formattedPrice = showInverted ? price.toSignificant(4) : price.invert()?.toSignificant(4)
|
||||
} catch (error) {
|
||||
formattedPrice = '0'
|
||||
}
|
||||
@ -35,14 +41,11 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
|
||||
const flipPrice = useCallback(() => setShowInverted(!showInverted), [setShowInverted, showInverted])
|
||||
|
||||
return (
|
||||
<StyledPriceContainer>
|
||||
<StyledPriceContainer onClick={flipPrice}>
|
||||
<div style={{ alignItems: 'center', display: 'flex', width: 'fit-content' }}>
|
||||
<Text fontWeight={500} fontSize={14} color={theme.text2}>
|
||||
<Text fontWeight={500} fontSize={14} color={theme.text1}>
|
||||
{'1 ' + labelInverted + ' = ' + formattedPrice ?? '-'} {label}
|
||||
</Text>
|
||||
<StyledBalanceMaxMini style={{ marginLeft: '0.5rem' }} onClick={flipPrice}>
|
||||
<img width={'16px'} src={Switch} alt="logo" />
|
||||
</StyledBalanceMaxMini>
|
||||
</div>
|
||||
</StyledPriceContainer>
|
||||
)
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { AlertTriangle } from 'react-feather'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { Text } from 'rebass'
|
||||
@ -84,10 +86,11 @@ export const StyledBalanceMaxMini = styled.button`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.25rem 0.35rem;
|
||||
padding: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
|
||||
opacity: 0.6;
|
||||
margin-right: 0.5rem;
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
display: flex;
|
||||
@ -106,8 +109,9 @@ export const StyledBalanceMaxMini = styled.button`
|
||||
|
||||
export const TruncatedText = styled(Text)`
|
||||
text-overflow: ellipsis;
|
||||
width: 220px;
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
`
|
||||
|
||||
// styles
|
||||
@ -184,3 +188,14 @@ export const Separator = styled.div`
|
||||
height: 1px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
`
|
||||
|
||||
export const V2TradeAlertWrapper = styled(Link)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 12px;
|
||||
height: 22px;
|
||||
margin-right: 0.5rem;
|
||||
padding: 0 0.25rem 0 0.5rem;
|
||||
text-decoration: none !important;
|
||||
`
|
||||
|
@ -1,28 +1,28 @@
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails'
|
||||
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
|
||||
import { MouseoverTooltip, MouseoverTooltipContent } from 'components/Tooltip'
|
||||
import JSBI from 'jsbi'
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { ArrowDown, Repeat, Unlock } from 'react-feather'
|
||||
import { ArrowDown, HelpCircle, CheckCircle, Info, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import AddressInputPanel from '../../components/AddressInputPanel'
|
||||
import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
|
||||
import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary, ButtonGray } from '../../components/Button'
|
||||
import { GreyCard } from '../../components/Card'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import CurrencyLogo from '../../components/CurrencyLogo'
|
||||
import Loader from '../../components/Loader'
|
||||
import ProgressSteps from '../../components/ProgressSteps'
|
||||
import { AutoRow } from '../../components/Row'
|
||||
import BetterTradeLink from '../../components/swap/BetterTradeLink'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
||||
import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee'
|
||||
import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal'
|
||||
|
||||
import { ArrowWrapper, BottomGrouping, Dots, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
|
||||
import SwapHeader from '../../components/swap/SwapHeader'
|
||||
import TradePrice from '../../components/swap/TradePrice'
|
||||
import TokenWarningModal from '../../components/TokenWarningModal'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
@ -46,11 +46,15 @@ import {
|
||||
import { useExpertModeManager, useUserSingleHopOnly, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { LinkStyledButton, TYPE } from '../../theme'
|
||||
import { getTradeVersion } from '../../utils/getTradeVersion'
|
||||
import { isTradeBetter } from '../../utils/isTradeBetter'
|
||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
|
||||
import AppBody from '../AppBody'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
import { isTradeBetter } from '../../utils/isTradeBetter'
|
||||
import BetterTradeLink from '../../components/swap/BetterTradeLink'
|
||||
import SwapHeader from '../../components/swap/SwapHeader'
|
||||
|
||||
export default function Swap({ history }: RouteComponentProps) {
|
||||
const loadedUrlParams = useDefaultsFromURLSearch()
|
||||
|
||||
@ -321,7 +325,7 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
onDismiss={handleDismissTokenWarning}
|
||||
/>
|
||||
<AppBody>
|
||||
<SwapHeader toggledVersion={toggledVersion} />
|
||||
<SwapHeader />
|
||||
<Wrapper id="swap-page">
|
||||
<ConfirmSwapModal
|
||||
isOpen={showConfirm}
|
||||
@ -353,14 +357,13 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
id="swap-currency-input"
|
||||
/>
|
||||
<ArrowWrapper clickable>
|
||||
<Repeat
|
||||
<ArrowDown
|
||||
size="16"
|
||||
onClick={() => {
|
||||
setApprovalSubmitted(false) // reset 2 step UI for approvals
|
||||
onSwitchTokens()
|
||||
}}
|
||||
color={currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.text1 : theme.text3}
|
||||
style={{ transform: 'rotate(90deg)' }}
|
||||
/>
|
||||
</ArrowWrapper>
|
||||
<CurrencyInputPanel
|
||||
@ -368,7 +371,7 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
onUserInput={handleTypeOutput}
|
||||
label={independentField === Field.INPUT && !showWrap ? 'To (at least)' : 'To'}
|
||||
showMaxButton={false}
|
||||
hideBalance={true}
|
||||
hideBalance={false}
|
||||
showFiatValue
|
||||
currency={currencies[Field.OUTPUT]}
|
||||
onCurrencySelect={handleOutputSelect}
|
||||
@ -391,20 +394,84 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
<AddressInputPanel id="recipient" value={recipient} onChange={onChangeRecipient} />
|
||||
</>
|
||||
) : null}
|
||||
{trade ? (
|
||||
<TradePrice
|
||||
price={trade.worstExecutionPrice(allowedSlippage)}
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
) : null}
|
||||
{[V3TradeState.VALID, V3TradeState.SYNCING, V3TradeState.NO_ROUTE_FOUND].includes(v3TradeState) ? (
|
||||
toggledVersion === Version.v3 && isTradeBetter(v3Trade, v2Trade) ? (
|
||||
<BetterTradeLink version={Version.v2} otherTradeNonexistent={!v3Trade} />
|
||||
) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? (
|
||||
<BetterTradeLink version={Version.v3} otherTradeNonexistent={!v2Trade} />
|
||||
) : null
|
||||
) : null}
|
||||
|
||||
<RowBetween style={{ justifyContent: !trade ? 'center' : 'space-between' }}>
|
||||
<RowFixed>
|
||||
{[V3TradeState.VALID, V3TradeState.SYNCING, V3TradeState.NO_ROUTE_FOUND].includes(v3TradeState) &&
|
||||
(toggledVersion === Version.v3 && isTradeBetter(v3Trade, v2Trade) ? (
|
||||
<BetterTradeLink version={Version.v2} otherTradeNonexistent={!v3Trade} />
|
||||
) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? (
|
||||
<BetterTradeLink version={Version.v3} otherTradeNonexistent={!v2Trade} />
|
||||
) : (
|
||||
toggledVersion === Version.v2 && (
|
||||
<ButtonGray
|
||||
width="fit-content"
|
||||
padding="0.1rem 0.5rem 0.1rem 0.35rem"
|
||||
as={Link}
|
||||
to="/swap"
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
height: '24px',
|
||||
opacity: 0.8,
|
||||
lineHeight: '120%',
|
||||
marginLeft: '0.25rem',
|
||||
}}
|
||||
>
|
||||
<X color={theme.text3} size={12} />
|
||||
<TYPE.main style={{ lineHeight: '120%' }} fontSize={12}>
|
||||
Routed via V2
|
||||
</TYPE.main>
|
||||
</ButtonGray>
|
||||
)
|
||||
))}
|
||||
|
||||
{toggledVersion === Version.v3 && trade && isTradeBetter(v2Trade, v3Trade) && (
|
||||
<ButtonGray
|
||||
width="fit-content"
|
||||
padding="0.1rem 0.5rem"
|
||||
disabled
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
height: '24px',
|
||||
opacity: 0.4,
|
||||
marginLeft: '0.25rem',
|
||||
}}
|
||||
>
|
||||
<TYPE.black fontSize={12}>V3</TYPE.black>
|
||||
</ButtonGray>
|
||||
)}
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
{trade ? (
|
||||
<TradePrice
|
||||
price={trade.worstExecutionPrice(allowedSlippage)}
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
) : (
|
||||
<TYPE.main></TYPE.main>
|
||||
)}
|
||||
{trade && (
|
||||
<MouseoverTooltipContent content={<AdvancedSwapDetails trade={trade} />}>
|
||||
<Info
|
||||
size={16}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
height: '24px',
|
||||
opacity: 0.4,
|
||||
margin: '0 .75rem 0 .5rem',
|
||||
}}
|
||||
color={theme.text1}
|
||||
/>
|
||||
</MouseoverTooltipContent>
|
||||
)}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
|
||||
<BottomGrouping>
|
||||
{swapIsUnsupported ? (
|
||||
<ButtonPrimary disabled={true}>
|
||||
@ -447,19 +514,29 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
<span style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<CurrencyLogo
|
||||
currency={currencies[Field.INPUT]}
|
||||
size={'16px'}
|
||||
size={'20px'}
|
||||
style={{ marginRight: '8px' }}
|
||||
/>
|
||||
{/* we need to shorten this string on mobile */}
|
||||
{'Allow Uniswap to spend your ' + currencies[Field.INPUT]?.symbol}
|
||||
{approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED
|
||||
? currencies[Field.INPUT]?.symbol + ' unlocked for trading.'
|
||||
: 'Allow Uniswap to spend your ' + currencies[Field.INPUT]?.symbol}
|
||||
</span>
|
||||
{approvalState === ApprovalState.PENDING ? (
|
||||
<Loader stroke="white" />
|
||||
) : (approvalSubmitted && approvalState === ApprovalState.APPROVED) ||
|
||||
signatureState === UseERC20PermitState.SIGNED ? (
|
||||
<Unlock size="16" stroke="white" />
|
||||
<CheckCircle size="20" color={theme.green1} />
|
||||
) : (
|
||||
<Unlock size="16" stroke="white" />
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
'You cannot swap until you give Uniswap permission to spend your ' +
|
||||
currencies[Field.INPUT]?.symbol +
|
||||
'. You only have to do this once per token and only when you are selling a token for the first time.'
|
||||
}
|
||||
>
|
||||
<HelpCircle size="20" color={'white'} />
|
||||
</MouseoverTooltip>
|
||||
)}
|
||||
</AutoRow>
|
||||
</ButtonConfirmed>
|
||||
@ -491,13 +568,6 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
</Text>
|
||||
</ButtonError>
|
||||
</AutoColumn>
|
||||
{showApproveFlow && (
|
||||
<ProgressSteps
|
||||
steps={[
|
||||
approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</AutoRow>
|
||||
) : (
|
||||
<ButtonError
|
||||
|
@ -28,7 +28,7 @@ export default createReducer(initialState, (builder) =>
|
||||
.addCase(setOpenModal, (state, action) => {
|
||||
state.openModal = action.payload
|
||||
})
|
||||
.addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => {
|
||||
.addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 25000 } }) => {
|
||||
state.popupList = (key ? state.popupList.filter((popup) => popup.key !== key) : state.popupList).concat([
|
||||
{
|
||||
key: key || nanoid(),
|
||||
|
@ -47,9 +47,9 @@ export function colors(darkMode: boolean): Colors {
|
||||
text5: darkMode ? '#2C2F36' : '#EDEEF2',
|
||||
|
||||
// backgrounds / greys
|
||||
bg0: darkMode ? '#191B1F' : '#FFFFFF',
|
||||
bg1: darkMode ? '#212429' : '#F7F8FA',
|
||||
bg2: darkMode ? '#2C2F36' : '#EDEEF2',
|
||||
bg0: darkMode ? '#191B1F' : '#F7F8FA',
|
||||
bg1: darkMode ? '#212429' : '#EDEEF2',
|
||||
bg2: darkMode ? '#2C2F36' : '#F0F0F0',
|
||||
bg3: darkMode ? '#40444F' : '#CED0D9',
|
||||
bg4: darkMode ? '#565A69' : '#888D9B',
|
||||
bg5: darkMode ? '#6C7284' : '#888D9B',
|
||||
@ -221,7 +221,7 @@ html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
|
||||
font-feature-settings: 'ss01' on, 'cv01' on, 'cv03' on;
|
||||
|
||||
}
|
||||
`
|
||||
|
Loading…
Reference in New Issue
Block a user