Position styles (#55)

* Better position list layout WIP

* Position list updates

* add badge data and current price hover

* merge cleanup

* fix missing library

* position page improvements

* Clean up position page and overview

* layout and color updates

* Clean up page

* Clean up position page

* clean up errors

* Add icons

* Merge main

* Position styles tweaks

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
This commit is contained in:
Callil Capuozzo 2021-05-03 11:50:54 -04:00 committed by GitHub
parent 93d33947da
commit 1619386ab4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 566 additions and 361 deletions

@ -89,7 +89,7 @@
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.", "toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
"tokenSearchPlaceholder": "Search name or paste address", "tokenSearchPlaceholder": "Search name or paste address",
"selectFee": "Select Fee", "selectFee": "Select Fee",
"selectLiquidityRange": "Select Liquidity Range", "selectLiquidityRange": "Select Price Range",
"selectPool": "Select Fee Tier", "selectPool": "Select Fee Tier",
"depositAmounts": "Deposit Amounts", "depositAmounts": "Deposit Amounts",
"fee": "fee", "fee": "fee",

@ -0,0 +1,63 @@
import React from 'react'
import Badge, { BadgeVariant } from 'components/Badge'
import styled from 'styled-components'
import { MouseoverTooltip } from '../../components/Tooltip'
import { useTranslation } from 'react-i18next'
import { AlertCircle } from 'react-feather'
const BadgeWrapper = styled.div`
font-size: 14px;
display: flex;
justify-content: flex-end;
`
const BadgeText = styled.div`
font-weight: 500;
font-size: 14px;
`
const ActiveDot = styled.span`
background-color: ${({ theme }) => theme.success};
border-radius: 50%;
height: 8px;
width: 8px;
margin-right: 4px;
`
export const DarkBadge = styled.div`
width: fit-content;
border-radius: 8px;
background-color: ${({ theme }) => theme.bg0};
padding: 4px 6px;
`
export default function RangeBadge({ inRange }: { inRange?: boolean }) {
const { t } = useTranslation()
return (
<BadgeWrapper>
{inRange ? (
<MouseoverTooltip
text={`The price of this pair is within your selected range. Your positions is earning fees.`}
>
<Badge variant={BadgeVariant.DEFAULT}>
<ActiveDot /> &nbsp;
<BadgeText>{t('In range')}</BadgeText>
</Badge>
</MouseoverTooltip>
) : (
<MouseoverTooltip
text={`The price of this pair is outside of your selected range. Your positions is not earning fees.`}
>
<Badge variant={BadgeVariant.WARNING}>
<AlertCircle width={14} height={14} />
&nbsp;
<BadgeText>{t('Out of range')}</BadgeText>
</Badge>
</MouseoverTooltip>
)}
</BadgeWrapper>
)
}

@ -37,6 +37,10 @@ const Base = styled(RebassButton)<{
> * { > * {
user-select: none; user-select: none;
} }
> a {
text-decoration: none;
}
` `
export const ButtonPrimary = styled(Base)` export const ButtonPrimary = styled(Base)`

@ -9,19 +9,17 @@ const DesktopHeader = styled.div`
display: none; display: none;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
opacity: 0.6; padding: 8px 8px 8px 8px;
padding: 8px 8px 0 8px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
align-items: center; align-items: center;
display: flex; display: flex;
margin: 0 0 8px 0;
& > div:first-child { display: grid;
flex: 1 1 auto; grid-template-columns: 1fr 1fr;
} & > div:last-child {
& > div:not(:first-child) {
text-align: right; text-align: right;
min-width: 18%; margin-right: 12px;
} }
} }
` `
@ -45,10 +43,11 @@ export default function PositionList({ positions }: PositionListProps) {
return ( return (
<> <>
<DesktopHeader> <DesktopHeader>
<div>{t('Position')}</div> <div>
<div>{t('Range')}</div> {t('Your positions')}
<div>{t('Liquidity')}</div> {positions && ' (' + positions.length + ')'}
<div>{t('Fees Earned')}</div> </div>
<div>{t('Price range')}</div>
</DesktopHeader> </DesktopHeader>
<MobileHeader>Your positions</MobileHeader> <MobileHeader>Your positions</MobileHeader>
{positions.map((p) => { {positions.map((p) => {

@ -1,21 +1,22 @@
import React, { useMemo } from 'react' import React, { useMemo, useState } from 'react'
import { Position } from '@uniswap/v3-sdk' import { Position } from '@uniswap/v3-sdk'
import Badge, { BadgeVariant } from 'components/Badge' import Badge, { BadgeVariant } from 'components/Badge'
import DoubleCurrencyLogo from 'components/DoubleLogo' import DoubleCurrencyLogo from 'components/DoubleLogo'
import { usePool } from 'hooks/usePools' import { usePool } from 'hooks/usePools'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { AlertTriangle } from 'react-feather' import { AlertCircle } from 'react-feather'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
import { MEDIA_WIDTHS } from 'theme' import { MEDIA_WIDTHS } from 'theme'
import { PositionDetails } from 'types/position' import { PositionDetails } from 'types/position'
import { TokenAmount, WETH9, Price, Token, Percent } from '@uniswap/sdk-core' import { WETH9, Price, Token, Percent } from '@uniswap/sdk-core'
import { formatPrice, formatTokenAmount } from 'utils/formatTokenAmount' import { formatPrice } from 'utils/formatTokenAmount'
import Loader from 'components/Loader' import Loader from 'components/Loader'
import { unwrappedToken } from 'utils/wrappedCurrency' import { unwrappedToken } from 'utils/wrappedCurrency'
import { useV3PositionFees } from 'hooks/useV3PositionFees'
import { DAI, USDC, USDT, WBTC } from '../../constants' import { DAI, USDC, USDT, WBTC } from '../../constants'
import { MouseoverTooltip } from '../Tooltip'
import { RowFixed } from 'components/Row'
const ActiveDot = styled.span` const ActiveDot = styled.span`
background-color: ${({ theme }) => theme.success}; background-color: ${({ theme }) => theme.success};
@ -28,29 +29,29 @@ const Row = styled(Link)`
align-items: center; align-items: center;
border-radius: 20px; border-radius: 20px;
display: flex; display: flex;
flex-direction: column; justify-content: space-between;
color: ${({ theme }) => theme.text1}; color: ${({ theme }) => theme.text1};
margin: 8px 0; margin: 8px 0;
padding: 8px; padding: 16px;
text-decoration: none; text-decoration: none;
font-weight: 500; font-weight: 500;
background-color: ${({ theme }) => theme.bg1};
&:first-of-type { &:first-of-type {
margin: 0 0 8px 0; margin: 0 0 8px 0;
} }
&:last-of-type { &:last-of-type {
margin: 8px 0 0 0; margin: 8px 0 0 0;
} }
& > div:not(:first-child) { & > div:not(:first-child) {
text-align: right; text-align: right;
min-width: 18%; }
:hover {
background-color: ${({ theme }) => theme.bg2};
} }
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex-direction: row; flex-direction: row;
} }
:hover {
background-color: ${({ theme }) => theme.bg1};
}
` `
const BadgeText = styled.div` const BadgeText = styled.div`
font-weight: 500; font-weight: 500;
@ -60,63 +61,44 @@ const BadgeWrapper = styled.div`
font-size: 14px; font-size: 14px;
` `
const DataLineItem = styled.div` const DataLineItem = styled.div`
text-align: right;
font-size: 14px; font-size: 14px;
` `
const RangeLineItem = styled(DataLineItem)`
display: flex;
flex-direction: column;
cursor: pointer;
justify-self: flex-end;
`
const DoubleArrow = styled.span` const DoubleArrow = styled.span`
color: ${({ theme }) => theme.text3}; color: ${({ theme }) => theme.text3};
` `
const RangeData = styled.div`
display: flex; const RangeText = styled.span`
flex-direction: column; background-color: ${({ theme }) => theme.bg2};
width: 100%; padding: 0.25rem 0.5rem;
& > div { border-radius: 8px;
align-items: center;
display: flex;
justify-content: space-between;
width: 100%;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: block;
& > div {
display: block;
}
}
` `
const AmountData = styled.div`
display: none; const ExtentsText = styled.span`
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { color: ${({ theme }) => theme.text3};
display: block; font-size: 14px;
} margin-right: 4px;
`
const FeeData = styled.div`
display: none;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: block;
}
`
const LabelData = styled.div`
align-items: center;
display: flex;
flex: 1 1 auto;
justify-content: space-between;
width: 100%;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: block;
}
` `
const PrimaryPositionIdData = styled.div` const PrimaryPositionIdData = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
padding: 6px 0 12px 0;
> * { > * {
margin-right: 8px; margin-right: 8px;
} }
` `
const DataText = styled.div` const DataText = styled.div`
font-weight: 500; font-weight: 600;
font-size: 18px;
` `
export interface PositionListItemProps { export interface PositionListItemProps {
@ -207,31 +189,33 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
return undefined return undefined
}, [liquidity, pool, tickLower, tickUpper]) }, [liquidity, pool, tickLower, tickUpper])
// liquidity amounts in tokens
const amount0: TokenAmount | undefined = position?.amount0
const amount1: TokenAmount | undefined = position?.amount1
const formattedAmount0 = formatTokenAmount(amount0, 4)
const formattedAmount1 = formatTokenAmount(amount1, 4)
// prices // prices
const { priceLower, priceUpper, base } = getPriceOrderingFromPositionForUI(position) let { priceLower, priceUpper, base, quote } = getPriceOrderingFromPositionForUI(position)
const inverted = token1 ? base?.equals(token1) : undefined const inverted = token1 ? base?.equals(token1) : undefined
const currencyQuote = inverted ? currency0 : currency1 const currencyQuote = inverted ? currency0 : currency1
const currencyBase = inverted ? currency1 : currency0 const currencyBase = inverted ? currency1 : currency0
// fees
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails)
// check if price is within range // check if price is within range
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false
const positionSummaryLink = '/pool/' + positionDetails.tokenId const positionSummaryLink = '/pool/' + positionDetails.tokenId
const [manuallyInverted, setManuallyInverted] = useState(true)
if (manuallyInverted) {
;[priceLower, priceUpper, base, quote] = [priceUpper?.invert(), priceLower?.invert(), quote, base]
}
const quotePrice = useMemo(() => {
return manuallyInverted
? position?.pool.priceOf(position?.pool.token0)
: position?.pool.priceOf(position?.pool.token1)
}, [manuallyInverted, position?.pool])
return ( return (
<Row to={positionSummaryLink}> <Row to={positionSummaryLink}>
<LabelData> <RowFixed>
<PrimaryPositionIdData> <PrimaryPositionIdData>
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={16} margin /> <DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={18} margin />
<DataText> <DataText>
&nbsp;{currencyQuote?.symbol}&nbsp;/&nbsp;{currencyBase?.symbol} &nbsp;{currencyQuote?.symbol}&nbsp;/&nbsp;{currencyBase?.symbol}
</DataText> </DataText>
@ -242,61 +226,63 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
</PrimaryPositionIdData> </PrimaryPositionIdData>
<BadgeWrapper> <BadgeWrapper>
{outOfRange ? ( {outOfRange ? (
<Badge variant={BadgeVariant.WARNING}> <MouseoverTooltip
<AlertTriangle width={14} height={14} style={{ marginRight: '4px' }} /> text={`The price of this pair is outside of your selected range. Your positions is not earning fees. Current price: ${quotePrice?.toSignificant(
&nbsp; 6
<BadgeText>{t('Out of range')}</BadgeText> )} ${manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} / ${
</Badge> manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol
}`}
>
<Badge variant={BadgeVariant.WARNING}>
<AlertCircle width={14} height={14} style={{ marginRight: '' }} />
&nbsp;
<BadgeText>{t('Out of range')}</BadgeText>
</Badge>
</MouseoverTooltip>
) : ( ) : (
<Badge variant={BadgeVariant.DEFAULT}> <MouseoverTooltip
<ActiveDot /> &nbsp; text={`The price of this pair is within your selected range. Your positions is earning fees. Current price: ${quotePrice?.toSignificant(
<BadgeText>{t('Active')}</BadgeText> 6
</Badge> )} ${manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} / ${
manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol
}`}
>
<Badge variant={BadgeVariant.DEFAULT}>
<ActiveDot /> &nbsp;
<BadgeText>{t('In range')}</BadgeText>
</Badge>
</MouseoverTooltip>
)} )}
</BadgeWrapper> </BadgeWrapper>
</LabelData> </RowFixed>
<RangeData>
{priceLower && priceUpper ? ( {priceLower && priceUpper ? (
<> <>
<DataLineItem> {' '}
{formatPrice(priceLower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(priceUpper, 4)}{' '} <RangeLineItem
{currencyQuote?.symbol} onClick={(e) => {
&nbsp;/&nbsp; e.stopPropagation()
{currencyBase?.symbol} setManuallyInverted(!manuallyInverted)
</DataLineItem> }}
</> >
) : ( <span>
<Loader /> <RangeText>
)} <ExtentsText>Min: </ExtentsText>
</RangeData> {formatPrice(priceLower, 4)} {manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} {' / '}{' '}
<AmountData> {manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol}
{formattedAmount0 && formattedAmount1 ? ( </RangeText>{' '}
<> <DoubleArrow></DoubleArrow>{' '}
<DataLineItem> <RangeText>
{inverted ? formattedAmount0 : formattedAmount1}&nbsp;{currencyQuote?.symbol} <ExtentsText>Max:</ExtentsText>
</DataLineItem> {formatPrice(priceUpper, 4)} {manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} {' / '}{' '}
<DataLineItem> {manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol}
{inverted ? formattedAmount1 : formattedAmount0}&nbsp;{currencyBase?.symbol} </RangeText>{' '}
</DataLineItem> </span>
</> </RangeLineItem>
) : ( </>
<Loader /> ) : (
)} <Loader />
</AmountData> )}
<FeeData>
{feeValue0 && feeValue1 ? (
<>
<DataLineItem>
{formatTokenAmount(inverted ? feeValue0 : feeValue1, 4)}&nbsp;{currencyQuote?.symbol}
</DataLineItem>
<DataLineItem>
{formatTokenAmount(inverted ? feeValue1 : feeValue0, 4)}&nbsp;{currencyBase?.symbol}
</DataLineItem>
</>
) : (
<Loader />
)}
</FeeData>
</Row> </Row>
) )
} }

@ -3,6 +3,15 @@ import { Currency } from '@uniswap/sdk-core'
import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle' import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle'
import { useActiveWeb3React } from 'hooks' import { useActiveWeb3React } from 'hooks'
import { wrappedCurrency } from 'utils/wrappedCurrency' import { wrappedCurrency } from 'utils/wrappedCurrency'
import Switch from '../../assets/svg/switch.svg'
import { useDarkModeManager } from '../../state/user/hooks'
import styled from 'styled-components'
const StyledSwitchIcon = styled.img<{ darkMode: boolean }>`
margin: 0 4px;
opacity: 0.4;
filter: ${({ darkMode }) => (darkMode ? 'invert(0)' : 'invert(1)')};
`
// the order of displayed base currencies from left to right is always in sort order // the order of displayed base currencies from left to right is always in sort order
// currencyA is treated as the preferred base currency // currencyA is treated as the preferred base currency
@ -22,14 +31,21 @@ export default function RateToggle({
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB) const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
const [darkMode] = useDarkModeManager()
return tokenA && tokenB ? ( return tokenA && tokenB ? (
<ToggleWrapper width="fit-content"> <div style={{ width: 'fit-content', display: 'flex', alignItems: 'center' }}>
<ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}> <ToggleWrapper width="fit-content">
{isSorted ? currencyA.symbol : currencyB.symbol} <ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}>
</ToggleElement> {isSorted ? currencyA.symbol : currencyB.symbol} {' price'}
<ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}> </ToggleElement>
{isSorted ? currencyB.symbol : currencyA.symbol} <StyledSwitchIcon onClick={handleRateToggle} width={'16px'} src={Switch} alt="logo" darkMode={darkMode} />
</ToggleElement>
</ToggleWrapper> <ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyB.symbol : currencyA.symbol}
{' price'}
</ToggleElement>
</ToggleWrapper>
</div>
) : null ) : null
} }

@ -4,11 +4,11 @@ import styled from 'styled-components'
export const ToggleWrapper = styled.button<{ width?: string }>` export const ToggleWrapper = styled.button<{ width?: string }>`
display: flex; display: flex;
align-items: center; align-items: center;
width: ${({ width }) => width ?? '100%'} width: ${({ width }) => width ?? '100%'};
padding: 1px; padding: 2px;
background: ${({ theme }) => theme.bg0}; background: ${({ theme }) => theme.bg1};
border-radius: 8px; border-radius: 8px;
border: ${({ theme }) => '2px solid ' + theme.bg2}; border: ${({ theme }) => '1px solid ' + theme.bg1};
cursor: pointer; cursor: pointer;
outline: none; outline: none;
` `
@ -21,7 +21,7 @@ export const ToggleElement = styled.span<{ isActive?: boolean; fontSize?: string
border-radius: 6px; border-radius: 6px;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
background: ${({ theme, isActive }) => (isActive ? theme.bg2 : 'none')}; background: ${({ theme, isActive }) => (isActive ? theme.bg0 : 'none')};
color: ${({ theme, isActive }) => (isActive ? theme.text1 : theme.text3)}; color: ${({ theme, isActive }) => (isActive ? theme.text1 : theme.text3)};
font-size: ${({ fontSize }) => fontSize ?? '1rem'}; font-size: ${({ fontSize }) => fontSize ?? '1rem'};
font-weight: 500; font-weight: 500;
@ -32,6 +32,15 @@ export const ToggleElement = styled.span<{ isActive?: boolean; fontSize?: string
} }
` `
export const ToggleText = styled.div`
color: ${({ theme }) => theme.text3};
font-size: 12px;
margin-right: 0.5rem;
width: 100%;
white-space: nowrap;
padding: 0 0 0 4px;
`
export interface ToggleProps { export interface ToggleProps {
options: string[] options: string[]
activeIndex: number activeIndex: number

@ -0,0 +1,73 @@
import React from 'react'
import styled from 'styled-components'
import { TYPE } from 'theme'
import { useTranslation } from 'react-i18next'
import { ExternalLink } from '../../theme'
const CTASection = styled.section`
display: grid;
grid-template-columns: 2fr 1fr;
gap: 8px;
`
const CTA1 = styled(ExternalLink)`
background-color: ${({ theme }) => theme.bg1};
padding: 32px;
border-radius: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 220px;
border: 1px solid ${({ theme }) => theme.bg4};
* {
color: ${({ theme }) => theme.text1};
text-decoration: none !important;
}
:hover {
border: 1px solid ${({ theme }) => theme.bg5};
background-color: ${({ theme }) => theme.bg2};
text-decoration: none;
* {
text-decoration: none !important;
}
}
`
export default function CTACards() {
const { t } = useTranslation()
return (
<CTASection>
<CTA1 href={''}>
<span>
<TYPE.largeHeader fontWeight={400} style={{ alignItems: 'center', display: 'flex', marginBottom: '24px' }}>
{t('Whats new in V3 Liquidity Pools?')}
</TYPE.largeHeader>
<TYPE.body fontWeight={300} style={{ alignItems: 'center', display: 'flex' }}>
{t(
'Learn all about concentrated liquidity and get informed about how to choose ranges that make sense for you.'
)}
</TYPE.body>
</span>
<TYPE.largeHeader fontWeight={400} style={{ alignItems: 'center', display: 'flex' }}>
{t('↗')}
</TYPE.largeHeader>
</CTA1>
<CTA1 href={''}>
<span>
<TYPE.largeHeader fontWeight={400} style={{ alignItems: 'center', display: 'flex', marginBottom: '24px' }}>
{t('Top pools')}
</TYPE.largeHeader>
<TYPE.body fontWeight={300} style={{ alignItems: 'center', display: 'flex' }}>
{t('Explore the top pools with Uniswap Analytics.')}
</TYPE.body>
</span>
<TYPE.largeHeader fontWeight={400} style={{ alignItems: 'center', display: 'flex' }}>
{t('↗')}
</TYPE.largeHeader>
</CTA1>
</CTASection>
)
}

@ -1,5 +1,6 @@
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { PoolState, usePool } from 'hooks/usePools' import { PoolState, usePool } from 'hooks/usePools'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
@ -9,15 +10,14 @@ import { usePositionTokenURI } from '../../hooks/usePositionTokenURI'
import { LoadingRows } from './styleds' import { LoadingRows } from './styleds'
import styled from 'styled-components' import styled from 'styled-components'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import Row, { RowBetween, RowFixed } from 'components/Row' import { RowBetween, RowFixed } from 'components/Row'
import DoubleCurrencyLogo from 'components/DoubleLogo' import DoubleCurrencyLogo from 'components/DoubleLogo'
import { ButtonText, TYPE } from 'theme' import { TYPE } from 'theme'
import Badge, { BadgeVariant } from 'components/Badge' import Badge from 'components/Badge'
import { calculateGasMargin } from 'utils' import { calculateGasMargin } from 'utils'
import { ButtonConfirmed, ButtonPrimary } from 'components/Button' import { ButtonConfirmed, ButtonPrimary, ButtonGray } from 'components/Button'
import { DarkCard, DarkGreyCard } from 'components/Card' import { DarkCard, LightCard } from 'components/Card'
import CurrencyLogo from 'components/CurrencyLogo' import CurrencyLogo from 'components/CurrencyLogo'
import { AlertTriangle, ToggleLeft, ToggleRight } from 'react-feather'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { currencyId } from 'utils/currencyId' import { currencyId } from 'utils/currencyId'
import { formatTokenAmount } from 'utils/formatTokenAmount' import { formatTokenAmount } from 'utils/formatTokenAmount'
@ -31,44 +31,24 @@ import ReactGA from 'react-ga'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { Dots } from 'components/swap/styleds' import { Dots } from 'components/swap/styleds'
import { getPriceOrderingFromPositionForUI } from '../../components/PositionListItem' import { getPriceOrderingFromPositionForUI } from '../../components/PositionListItem'
import useTheme from '../../hooks/useTheme'
import { MinusCircle, PlusCircle } from 'react-feather'
import RateToggle from '../../components/RateToggle'
import { useSingleCallResult } from 'state/multicall/hooks' import { useSingleCallResult } from 'state/multicall/hooks'
import RangeBadge from '../../components/Badge/RangeBadge'
const PageWrapper = styled.div` const PageWrapper = styled.div`
min-width: 800px; min-width: 800px;
` max-width: 960px;
const BadgeWrapper = styled.div`
font-size: 14px;
` `
const BadgeText = styled.div` const BadgeText = styled.div`
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
` `
const ResponsiveGrid = styled.div`
width: 100%;
display: grid;
grid-gap: 1em;
grid-template-columns: 1.5fr repeat(2, 1fr);
@media screen and (max-width: 900px) {
grid-template-columns: 1.5fr repeat(2, 1fr);
& :nth-child(4) {
display: none;
}
}
@media screen and (max-width: 700px) {
grid-template-columns: 20px 1.5fr repeat(2, 1fr);
& :nth-child(4) {
display: none;
}
& :nth-child(5) {
display: none;
}
}
`
// responsive text // responsive text
// disable the warning because we don't use the end prop, we just want to filter it out // disable the warning because we don't use the end prop, we just want to filter it out
@ -80,14 +60,6 @@ const Label = styled(({ end, ...props }) => <TYPE.label {...props} />)<{ end?: b
align-items: center; align-items: center;
` `
const ActiveDot = styled.span`
background-color: ${({ theme }) => theme.success};
border-radius: 50%;
height: 8px;
width: 8px;
margin-right: 4px;
`
export const DarkBadge = styled.div` export const DarkBadge = styled.div`
width: fit-content; width: fit-content;
border-radius: 8px; border-radius: 8px;
@ -95,6 +67,37 @@ export const DarkBadge = styled.div`
padding: 4px 6px; padding: 4px 6px;
` `
const ExtentsText = styled.span`
color: ${({ theme }) => theme.text3};
font-size: 14px;
text-align: center;
margin-right: 4px;
`
const HoverText = styled(TYPE.main)`
text-decoration: none;
color: ${({ theme }) => theme.text3};
:hover {
color: ${({ theme }) => theme.text1};
text-decoration: none;
}
`
const DoubleArrow = styled.span`
color: ${({ theme }) => theme.text3};
margin: 0 1rem;
`
const ResponsiveButtonPrimary = styled(ButtonPrimary)`
border-radius: 12px;
padding: 6px 8px;
width: fit-content;
${({ theme }) => theme.mediaWidth.upToSmall`
flex: 1 1 auto;
width: 49%;
`};
`
function CurrentPriceCard({ function CurrentPriceCard({
inverted, inverted,
pool, pool,
@ -106,22 +109,20 @@ function CurrentPriceCard({
currencyQuote?: Currency currencyQuote?: Currency
currencyBase?: Currency currencyBase?: Currency
}) { }) {
const { t } = useTranslation()
if (!pool || !currencyQuote || !currencyBase) { if (!pool || !currencyQuote || !currencyBase) {
return null return null
} }
return ( return (
<DarkGreyCard width="32%"> <LightCard padding="12px ">
<AutoColumn gap="sm" justify="flex-start"> <AutoColumn gap="md" justify="center">
<TYPE.main>Current</TYPE.main> <ExtentsText>{t('Current price')}</ExtentsText>
<RowFixed> <TYPE.label textAlign="center">
<TYPE.label> {(inverted ? pool.token1Price : pool.token0Price).toSignificant(4)} {currencyQuote?.symbol}
{(inverted ? pool.token1Price : pool.token0Price).toSignificant(4)} {currencyQuote?.symbol} / 1{' '} </TYPE.label>
{currencyBase?.symbol}
</TYPE.label>
</RowFixed>
</AutoColumn> </AutoColumn>
</DarkGreyCard> </LightCard>
) )
} }
@ -132,6 +133,7 @@ export function PositionPage({
}: RouteComponentProps<{ tokenId?: string }>) { }: RouteComponentProps<{ tokenId?: string }>) {
const { t } = useTranslation() const { t } = useTranslation()
const { chainId, account, library } = useActiveWeb3React() const { chainId, account, library } = useActiveWeb3React()
const theme = useTheme()
const parsedTokenId = tokenIdFromUrl ? BigNumber.from(tokenIdFromUrl) : undefined const parsedTokenId = tokenIdFromUrl ? BigNumber.from(tokenIdFromUrl) : undefined
const { loading, position: positionDetails } = useV3PositionFromTokenId(parsedTokenId) const { loading, position: positionDetails } = useV3PositionFromTokenId(parsedTokenId)
@ -171,8 +173,10 @@ export function PositionPage({
pool && typeof tickLower === 'number' && typeof tickUpper === 'number' pool && typeof tickLower === 'number' && typeof tickUpper === 'number'
? pool.tickCurrent >= tickLower && pool.tickCurrent < tickUpper ? pool.tickCurrent >= tickLower && pool.tickCurrent < tickUpper
: false : false
const below = pool && typeof tickLower === 'number' ? pool.tickCurrent < tickLower : false
const above = pool && typeof tickUpper === 'number' ? pool.tickCurrent >= tickUpper : false // keep will need to be able to draw the range visualization
// const below = pool && typeof tickLower === 'number' ? pool.tickCurrent < tickLower : false
// const above = pool && typeof tickUpper === 'number' ? pool.tickCurrent >= tickUpper : false
// fees // fees
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails) const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails)
@ -254,204 +258,240 @@ export function PositionPage({
</LoadingRows> </LoadingRows>
) : ( ) : (
<PageWrapper> <PageWrapper>
<AutoColumn gap="lg"> <AutoColumn gap="md">
<AutoColumn gap="sm"> <AutoColumn gap="sm">
<Link style={{ textDecoration: 'none', width: 'fit-content', marginBottom: '0.5rem' }} to="/pool">
<HoverText>{'← Back to overview'}</HoverText>
</Link>
<RowBetween> <RowBetween>
<RowFixed> <RowFixed>
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={20} margin={true} /> <DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={24} margin={true} />
<TYPE.label fontSize={'20px'} mr="10px"> <TYPE.label fontSize={'24px'} mr="10px">
&nbsp;{currencyQuote?.symbol}&nbsp;/&nbsp;{currencyBase?.symbol} &nbsp;{currencyQuote?.symbol}&nbsp;/&nbsp;{currencyBase?.symbol}
</TYPE.label> </TYPE.label>
<Badge> <Badge style={{ marginRight: '8px' }}>
<BadgeText>{new Percent(feeAmount, 1_000_000).toSignificant()}%</BadgeText> <BadgeText>{new Percent(feeAmount, 1_000_000).toSignificant()}%</BadgeText>
</Badge> </Badge>
<RangeBadge inRange={inRange} />
</RowFixed> </RowFixed>
{ownsNFT && ( {ownsNFT && (
<RowFixed> <RowFixed>
{feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0) || !!collectMigrationHash ? (
<ButtonConfirmed
disabled={collecting || !!collectMigrationHash}
confirmed={!!collectMigrationHash && !isCollectPending}
mr="15px"
width="175px"
padding="8px"
style={{ borderRadius: '12px' }}
onClick={collect}
>
{!!collectMigrationHash && !isCollectPending ? (
'Collected'
) : isCollectPending || collecting ? (
<Dots>Collecting</Dots>
) : (
'Collect fees'
)}
</ButtonConfirmed>
) : null}
{currency0 && currency1 && feeAmount && tokenId ? ( {currency0 && currency1 && feeAmount && tokenId ? (
<Link to={`/increase/${currencyId(currency0)}/${currencyId(currency1)}/${feeAmount}/${tokenId}`}> <ButtonGray
<ButtonPrimary mr="15px" width="175px" padding="8px" borderRadius="12px"> as={Link}
Add liquidity to={`/increase/${currencyId(currency0)}/${currencyId(currency1)}/${feeAmount}/${tokenId}`}
</ButtonPrimary> width="fit-content"
</Link> padding="6px 8px"
borderRadius="12px"
style={{ marginRight: '8px' }}
>
<PlusCircle size={16} style={{ marginRight: '8px' }} />{' '}
<TYPE.body color={theme.text1}>{t('Add Liquidity')}</TYPE.body>
</ButtonGray>
) : null} ) : null}
{tokenId && ( {tokenId && (
<Link to={`/remove/${tokenId}`}> <ResponsiveButtonPrimary
<ButtonPrimary width="175px" padding="8px" borderRadius="12px"> as={Link}
Remove liquidity to={`/remove/${tokenId}`}
</ButtonPrimary> width="fit-content"
</Link> padding="6px 8px"
borderRadius="12px"
>
<MinusCircle size={16} style={{ marginRight: '8px' }} /> {t('Remove Liquidity')}
</ResponsiveButtonPrimary>
)} )}
</RowFixed> </RowFixed>
)} )}
</RowBetween> </RowBetween>
<RowBetween> <RowBetween></RowBetween>
<BadgeWrapper>
{inRange ? (
<Badge variant={BadgeVariant.DEFAULT}>
<ActiveDot /> &nbsp;
<BadgeText>{t('Active')}</BadgeText>
</Badge>
) : (
<Badge variant={BadgeVariant.WARNING}>
<AlertTriangle width={14} height={14} style={{ marginRight: '4px' }} />
&nbsp;
<BadgeText>{t('Out of range')}</BadgeText>
</Badge>
)}
</BadgeWrapper>
</RowBetween>
</AutoColumn> </AutoColumn>
<Row align="stretch"> <RowBetween align="flex-start">
{'result' in metadata ? ( {'result' in metadata ? (
<div style={{ marginRight: 12 }}> <DarkCard
<img src={metadata.result.image} /> width="100%"
</div> height="100%"
style={{
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'space-around',
marginRight: '12px',
maxWidth: '360px',
}}
>
<div style={{ marginRight: 12 }}>
<img height="400px" src={metadata.result.image} />
</div>
</DarkCard>
) : null} ) : null}
<DarkCard> <AutoColumn gap="sm" style={{ width: '100%' }}>
<AutoColumn gap="lg"> <DarkCard>
<ResponsiveGrid> <AutoColumn gap="lg" style={{ width: '100%' }}>
<Label>Tokens</Label> <AutoColumn gap="md">
<Label end={true}>Liquidity</Label> <Label>Position liquidity</Label>
<Label end={true}>Fees</Label> <TYPE.largeHeader fontSize="36px" fontWeight={500}>
</ResponsiveGrid> $1222.22
<ResponsiveGrid> </TYPE.largeHeader>
<RowFixed> </AutoColumn>
<CurrencyLogo currency={currencyQuote} />
<TYPE.label ml="10px">{currencyQuote?.symbol}</TYPE.label>
</RowFixed>
<Label end={true}>
{inverted ? position?.amount0.toSignificant(4) : position?.amount1.toSignificant(4)}
</Label>
<Label end={true}>
{inverted
? feeValue0
? formatTokenAmount(feeValue0, 4)
: '-'
: feeValue1
? formatTokenAmount(feeValue1, 4)
: '-'}
</Label>
</ResponsiveGrid>
<ResponsiveGrid>
<RowFixed>
<CurrencyLogo currency={currencyBase} />
<TYPE.label ml="10px">{currencyBase?.symbol}</TYPE.label>
</RowFixed>
<Label end={true}>
{inverted ? position?.amount1.toSignificant(4) : position?.amount0.toSignificant(4)}
</Label>
<Label end={true}> <LightCard padding="12px 16px">
{inverted <AutoColumn gap="md">
? feeValue1 <RowBetween>
? formatTokenAmount(feeValue1, 4) <RowFixed>
: '-' <CurrencyLogo currency={currencyQuote} size={'20px'} style={{ marginRight: '0.5rem' }} />
: feeValue0 <TYPE.main>
? formatTokenAmount(feeValue0, 4) {inverted ? position?.amount0.toSignificant(4) : position?.amount1.toSignificant(4)}
: '-'} </TYPE.main>
</Label> </RowFixed>
</ResponsiveGrid> <TYPE.main>{currencyQuote?.symbol}</TYPE.main>
</AutoColumn> </RowBetween>
</DarkCard> <RowBetween>
</Row> <RowFixed>
<CurrencyLogo currency={currencyBase} size={'20px'} style={{ marginRight: '0.5rem' }} />
<TYPE.main>
{inverted ? position?.amount1.toSignificant(4) : position?.amount0.toSignificant(4)}
</TYPE.main>
</RowFixed>
<TYPE.main>{currencyBase?.symbol}</TYPE.main>
</RowBetween>
</AutoColumn>
</LightCard>
</AutoColumn>
</DarkCard>
<span style={{ width: '24px' }}></span>
<DarkCard>
<AutoColumn gap="lg" style={{ width: '100%' }}>
<AutoColumn gap="md">
<RowBetween style={{ alignItems: 'flex-start' }}>
<AutoColumn gap="md">
<Label>Fees Earned</Label>
<TYPE.largeHeader color={theme.green1} fontSize="36px" fontWeight={500}>
$2.22
</TYPE.largeHeader>
</AutoColumn>
{feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0) || !!collectMigrationHash ? (
<ButtonConfirmed
disabled={collecting || !!collectMigrationHash}
confirmed={!!collectMigrationHash && !isCollectPending}
width="fit-content"
style={{ borderRadius: '12px' }}
padding="4px 8px"
onClick={collect}
>
{!!collectMigrationHash && !isCollectPending ? (
<TYPE.main color={theme.text1}> Collected</TYPE.main>
) : isCollectPending || collecting ? (
<TYPE.main color={theme.text1}>
{' '}
<Dots>Collecting</Dots>
</TYPE.main>
) : (
<>
<TYPE.main color={theme.white}>Collect fees</TYPE.main>
</>
)}
</ButtonConfirmed>
) : null}
</RowBetween>
</AutoColumn>
<LightCard padding="12px 16px">
<AutoColumn gap="md">
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currencyQuote} size={'20px'} style={{ marginRight: '0.5rem' }} />
<TYPE.main>
{inverted
? feeValue0
? formatTokenAmount(feeValue0, 4)
: '-'
: feeValue1
? formatTokenAmount(feeValue1, 4)
: '-'}
</TYPE.main>
</RowFixed>
<TYPE.main>{currencyQuote?.symbol}</TYPE.main>
</RowBetween>
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currencyBase} size={'20px'} style={{ marginRight: '0.5rem' }} />
<TYPE.main>
{inverted
? feeValue0
? formatTokenAmount(feeValue1, 4)
: '-'
: feeValue1
? formatTokenAmount(feeValue0, 4)
: '-'}
</TYPE.main>
</RowFixed>
<TYPE.main>{currencyBase?.symbol}</TYPE.main>
</RowBetween>
</AutoColumn>
</LightCard>
</AutoColumn>
</DarkCard>
</AutoColumn>
</RowBetween>
<DarkCard> <DarkCard>
<AutoColumn gap="lg"> <AutoColumn gap="md">
<TYPE.label display="flex"> <RowBetween>
Position Limits <Label display="flex" style={{ marginRight: '12px' }}>
<ButtonText style={{ marginLeft: '10px', color: 'inherit' }}> Price range
{manuallyInverted ? ( </Label>
<ToggleLeft onClick={() => setManuallyInverted(false)} />
) : (
<ToggleRight onClick={() => setManuallyInverted(true)} />
)}
</ButtonText>
</TYPE.label>
{below && ( <RowFixed>
<CurrentPriceCard <RangeBadge inRange={inRange} />
inverted={inverted} <span style={{ width: '8px' }} />
pool={pool} {currencyBase && currencyQuote && (
currencyQuote={currencyQuote} <RateToggle
currencyBase={currencyBase} currencyA={currencyBase}
/> currencyB={currencyQuote}
)} handleRateToggle={() => setManuallyInverted(!manuallyInverted)}
/>
)}
</RowFixed>
</RowBetween>
<RowBetween> <RowBetween>
<DarkGreyCard width="32%"> <LightCard padding="12px" width="100%">
<AutoColumn gap="sm" justify="flex-start"> <AutoColumn gap="12px" justify="center">
<TYPE.main>Lower</TYPE.main> <ExtentsText>Min</ExtentsText>
<RowFixed> <RowFixed>
<TYPE.label> <TYPE.label textAlign="center">
{priceLower?.toSignificant(4)} {currencyQuote?.symbol} / 1 {currencyBase?.symbol} {priceLower?.toSignificant(4)} {currencyQuote?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<DarkBadge> <TYPE.subHeader color={theme.text3} textAlign="center">
<RowFixed> Your position will be <CurrencyLogo currency={inverted ? currency1 : currency0} size="12px" /> 100%{' '}
<TYPE.label mr="6px">100%</TYPE.label> {inverted ? currency1?.symbol : currency0?.symbol} at this price
<CurrencyLogo currency={inverted ? currency1 : currency0} size="16px" /> </TYPE.subHeader>
<TYPE.label ml="4px">{inverted ? currency1?.symbol : currency0?.symbol}</TYPE.label>
</RowFixed>
</DarkBadge>
</AutoColumn> </AutoColumn>
</DarkGreyCard> </LightCard>
{inRange && ( <DoubleArrow></DoubleArrow>
<CurrentPriceCard <LightCard padding="12px" width="100%">
inverted={inverted} <AutoColumn gap="12px" justify="center">
pool={pool} <ExtentsText>Max</ExtentsText>
currencyQuote={currencyQuote}
currencyBase={currencyBase}
/>
)}
<DarkGreyCard width="32%">
<AutoColumn gap="sm" justify="flex-start">
<TYPE.main>Upper</TYPE.main>
<RowFixed> <RowFixed>
<TYPE.label> <TYPE.label textAlign="center">
{priceUpper?.toSignificant(4)} {currencyQuote?.symbol} / 1 {currencyBase?.symbol} {priceUpper?.toSignificant(4)} {currencyQuote?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<DarkBadge> <TYPE.subHeader color={theme.text3} textAlign="center">
<RowFixed> Your position will be <CurrencyLogo currency={inverted ? currency0 : currency1} size="12px" /> 100%{' '}
<TYPE.label mr="6px">100%</TYPE.label> {inverted ? currency0?.symbol : currency1?.symbol} at this price
<CurrencyLogo currency={inverted ? currency0 : currency1} size="16px" /> </TYPE.subHeader>
<TYPE.label ml="4px">{inverted ? currency0?.symbol : currency1?.symbol}</TYPE.label>
</RowFixed>
</DarkBadge>
</AutoColumn> </AutoColumn>
</DarkGreyCard> </LightCard>
{above && (
<CurrentPriceCard
inverted={inverted}
pool={pool}
currencyQuote={currencyQuote}
currencyBase={currencyBase}
/>
)}
</RowBetween> </RowBetween>
<CurrentPriceCard
inverted={inverted}
pool={pool}
currencyQuote={currencyQuote}
currencyBase={currencyBase}
/>
</AutoColumn> </AutoColumn>
</DarkCard> </DarkCard>
</AutoColumn> </AutoColumn>

@ -15,6 +15,8 @@ import styled, { ThemeContext } from 'styled-components'
import { HideSmall, TYPE } from 'theme' import { HideSmall, TYPE } from 'theme'
import { LoadingRows } from './styleds' import { LoadingRows } from './styleds'
import CTACards from './CTACards'
const PageWrapper = styled(AutoColumn)` const PageWrapper = styled(AutoColumn)`
max-width: 870px; max-width: 870px;
width: 100%; width: 100%;
@ -76,7 +78,7 @@ const ResponsiveButtonPrimary = styled(ButtonPrimary)`
const MainContentWrapper = styled.main` const MainContentWrapper = styled.main`
background-color: ${({ theme }) => theme.bg0}; background-color: ${({ theme }) => theme.bg0};
padding: 16px; padding: 8px;
border-radius: 20px; border-radius: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -131,7 +133,7 @@ export default function Pool() {
<AutoColumn gap="lg" style={{ width: '100%' }}> <AutoColumn gap="lg" style={{ width: '100%' }}>
<TitleRow style={{ marginTop: '1rem' }} padding={'0'}> <TitleRow style={{ marginTop: '1rem' }} padding={'0'}>
<HideSmall> <HideSmall>
<TYPE.mediumHeader>Your Positions</TYPE.mediumHeader> <TYPE.mediumHeader>{t('Pools Overview')}</TYPE.mediumHeader>
</HideSmall> </HideSmall>
<ButtonRow> <ButtonRow>
<Menu <Menu
@ -152,6 +154,8 @@ export default function Pool() {
</ButtonRow> </ButtonRow>
</TitleRow> </TitleRow>
<CTACards />
<MainContentWrapper> <MainContentWrapper>
{positionsLoading ? ( {positionsLoading ? (
<LoadingRows> <LoadingRows>
@ -194,6 +198,16 @@ export default function Pool() {
</NoLiquidity> </NoLiquidity>
)} )}
</MainContentWrapper> </MainContentWrapper>
<RowFixed justify="center" style={{ width: '100%' }}>
<ButtonGray
as={Link}
to="/migrate/v2"
id="import-pool-link"
style={{ padding: '8px 16px', borderRadius: '12px', width: 'fit-content' }}
>
<TYPE.subHeader>{t('Looking for your V2 Liquidity')}?</TYPE.subHeader>
</ButtonGray>
</RowFixed>
</AutoColumn> </AutoColumn>
</AutoColumn> </AutoColumn>
</PageWrapper> </PageWrapper>

@ -68,6 +68,7 @@ const loadingAnimation = keyframes`
export const LoadingRows = styled.div` export const LoadingRows = styled.div`
display: grid; display: grid;
min-width: 75%; min-width: 75%;
max-width: 960px;
grid-column-gap: 0.5em; grid-column-gap: 0.5em;
grid-row-gap: 0.8em; grid-row-gap: 0.8em;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);

@ -143,7 +143,7 @@ export const TYPE = {
return <TextWrapper fontWeight={500} color={'primary1'} {...props} /> return <TextWrapper fontWeight={500} color={'primary1'} {...props} />
}, },
label(props: TextProps) { label(props: TextProps) {
return <TextWrapper fontWeight={600} color={'text1'} {...props} /> return <TextWrapper fontWeight={500} color={'text1'} {...props} />
}, },
black(props: TextProps) { black(props: TextProps) {
return <TextWrapper fontWeight={500} color={'text1'} {...props} /> return <TextWrapper fontWeight={500} color={'text1'} {...props} />