feat: [ListV2] Changes to Multiple Market handling and row hovering (#5953)
* add overlay to row hover * add remove icon on hover * working expand and collapse icons * fixed margin * don't show collpase on nonhovered rows * display mutliple markets * remove market * 0 margin divider * hide on mobile * better mobile check * margin adjustment and hover border radius * address comments --------- Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
This commit is contained in:
parent
12df4b3981
commit
0208ccd7d2
@ -74,10 +74,6 @@ const ListingHeader = styled(Row)`
|
||||
const GridWrapper = styled.div`
|
||||
margin-top: 24px;
|
||||
margin-bottom: 48px;
|
||||
|
||||
@media screen and (min-width: ${SMALL_MEDIA_BREAKPOINT}) {
|
||||
margin-left: 40px;
|
||||
}
|
||||
`
|
||||
|
||||
const MobileListButtonWrapper = styled.div`
|
||||
|
@ -3,12 +3,13 @@ import { t } from '@lingui/macro'
|
||||
import Column from 'components/Column'
|
||||
import Row from 'components/Row'
|
||||
import { MouseoverTooltip } from 'components/Tooltip'
|
||||
import { RowsCollpsedIcon, RowsExpandedIcon } from 'nft/components/icons'
|
||||
import { useSellAsset } from 'nft/hooks'
|
||||
import { ListingMarket, ListingWarning, WalletAsset } from 'nft/types'
|
||||
import { LOOKS_RARE_CREATOR_BASIS_POINTS } from 'nft/utils'
|
||||
import { formatEth, formatUsdPrice } from 'nft/utils/currency'
|
||||
import { fetchPrice } from 'nft/utils/fetchPrice'
|
||||
import { Dispatch, useEffect, useMemo, useState } from 'react'
|
||||
import React, { Dispatch, DispatchWithoutAction, useEffect, useMemo, useReducer, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { BREAKPOINTS, ThemedText } from 'theme'
|
||||
|
||||
@ -28,20 +29,45 @@ const PastPriceInfo = styled(Column)`
|
||||
`
|
||||
|
||||
const RemoveMarketplaceWrap = styled(RemoveIconWrap)`
|
||||
top: 11px;
|
||||
top: 8px;
|
||||
left: 16px;
|
||||
z-index: 3;
|
||||
`
|
||||
|
||||
const MarketIconsWrapper = styled(Row)`
|
||||
position: relative;
|
||||
margin-right: 12px;
|
||||
width: 44px;
|
||||
justify-content: flex-end;
|
||||
|
||||
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const MarketIconWrapper = styled(Column)`
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin-right: 16px;
|
||||
`
|
||||
|
||||
const MarketIcon = styled.img`
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
const MarketIcon = styled.img<{ index: number }>`
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
z-index: ${({ index }) => 2 - index};
|
||||
margin-left: ${({ index }) => `${index === 0 ? 0 : -8}px`};
|
||||
outline: 1px solid ${({ theme }) => theme.backgroundInteractive};
|
||||
`
|
||||
|
||||
const ExpandMarketIconWrapper = styled.div`
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
height: 28px;
|
||||
|
||||
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const FeeColumnWrapper = styled(Column)`
|
||||
@ -84,6 +110,8 @@ interface MarketplaceRowProps {
|
||||
asset: WalletAsset
|
||||
showMarketplaceLogo: boolean
|
||||
expandMarketplaceRows?: boolean
|
||||
rowHovered?: boolean
|
||||
toggleExpandMarketplaceRows: DispatchWithoutAction
|
||||
}
|
||||
|
||||
export const MarketplaceRow = ({
|
||||
@ -95,14 +123,16 @@ export const MarketplaceRow = ({
|
||||
asset,
|
||||
showMarketplaceLogo,
|
||||
expandMarketplaceRows,
|
||||
toggleExpandMarketplaceRows,
|
||||
rowHovered,
|
||||
}: MarketplaceRowProps) => {
|
||||
const [listPrice, setListPrice] = useState<number>()
|
||||
const [globalOverride, setGlobalOverride] = useState(false)
|
||||
const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice
|
||||
const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice)
|
||||
const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace)
|
||||
const [hovered, setHovered] = useState(false)
|
||||
const handleHover = () => setHovered(!hovered)
|
||||
const [marketIconHovered, toggleMarketIconHovered] = useReducer((s) => !s, false)
|
||||
const [marketRowHovered, toggleMarketRowHovered] = useReducer((s) => !s, false)
|
||||
|
||||
const price = showGlobalPrice ? globalPrice : listPrice
|
||||
|
||||
@ -186,7 +216,7 @@ export const MarketplaceRow = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Row onMouseEnter={toggleMarketRowHovered} onMouseLeave={toggleMarketRowHovered}>
|
||||
<PastPriceInfo>
|
||||
<ThemedText.BodySmall color="textSecondary" lineHeight="20px">
|
||||
{asset.floorPrice ? `${asset.floorPrice.toFixed(3)} ETH` : '-'}
|
||||
@ -198,22 +228,25 @@ export const MarketplaceRow = ({
|
||||
</ThemedText.BodySmall>
|
||||
</PastPriceInfo>
|
||||
|
||||
<Row flex="2">
|
||||
{showMarketplaceLogo && (
|
||||
<MarketIconWrapper
|
||||
onMouseEnter={handleHover}
|
||||
onMouseLeave={handleHover}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeAssetMarketplace(asset, selectedMarkets[0])
|
||||
removeMarket && removeMarket()
|
||||
}}
|
||||
>
|
||||
<MarketIcon alt={selectedMarkets[0].name} src={selectedMarkets[0].icon} />
|
||||
<RemoveMarketplaceWrap hovered={hovered}>
|
||||
<img width="32px" src="/nft/svgs/minusCircle.svg" alt="Remove item" />
|
||||
</RemoveMarketplaceWrap>
|
||||
</MarketIconWrapper>
|
||||
<Row flex="3">
|
||||
{(expandMarketplaceRows || selectedMarkets.length > 1) && (
|
||||
<MarketIconsWrapper onMouseEnter={toggleMarketIconHovered} onMouseLeave={toggleMarketIconHovered}>
|
||||
{selectedMarkets.map((market, index) => (
|
||||
<MarketIconWrapper
|
||||
key={market.name + asset.collection?.address + asset.tokenId}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeAssetMarketplace(asset, selectedMarkets[0])
|
||||
removeMarket && removeMarket()
|
||||
}}
|
||||
>
|
||||
<MarketIcon alt={selectedMarkets[0].name} src={market.icon} index={index} />
|
||||
<RemoveMarketplaceWrap hovered={marketIconHovered && (expandMarketplaceRows ?? false)}>
|
||||
<img width="20px" src="/nft/svgs/minusCircle.svg" alt="Remove item" />
|
||||
</RemoveMarketplaceWrap>
|
||||
</MarketIconWrapper>
|
||||
))}
|
||||
</MarketIconsWrapper>
|
||||
)}
|
||||
{globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride ? (
|
||||
<PriceTextInput
|
||||
@ -224,7 +257,6 @@ export const MarketplaceRow = ({
|
||||
globalOverride={globalOverride}
|
||||
warning={warning}
|
||||
asset={asset}
|
||||
shrink={expandMarketplaceRows}
|
||||
/>
|
||||
) : (
|
||||
<PriceTextInput
|
||||
@ -235,9 +267,13 @@ export const MarketplaceRow = ({
|
||||
globalOverride={globalOverride}
|
||||
warning={warning}
|
||||
asset={asset}
|
||||
shrink={expandMarketplaceRows}
|
||||
/>
|
||||
)}
|
||||
{rowHovered && ((expandMarketplaceRows && marketRowHovered) || selectedMarkets.length > 1) && (
|
||||
<ExpandMarketIconWrapper onClick={toggleExpandMarketplaceRows}>
|
||||
{expandMarketplaceRows ? <RowsExpandedIcon /> : <RowsCollpsedIcon />}
|
||||
</ExpandMarketIconWrapper>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<FeeColumnWrapper>
|
||||
|
@ -1,19 +1,42 @@
|
||||
import Column from 'components/Column'
|
||||
import Row from 'components/Row'
|
||||
import { RowsCollpsedIcon, RowsExpandedIcon, VerifiedIcon } from 'nft/components/icons'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { useSellAsset } from 'nft/hooks'
|
||||
import { ListingMarket, WalletAsset } from 'nft/types'
|
||||
import { Dispatch, useEffect, useState } from 'react'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { Dispatch, useEffect, useReducer, useState } from 'react'
|
||||
import { Trash2 } from 'react-feather'
|
||||
import styled, { css, useTheme } from 'styled-components/macro'
|
||||
import { BREAKPOINTS, ThemedText } from 'theme'
|
||||
import { opacify } from 'theme/utils'
|
||||
|
||||
import { MarketplaceRow } from './MarketplaceRow'
|
||||
import { SetPriceMethod } from './NFTListingsGrid'
|
||||
import { RemoveIconWrap } from './shared'
|
||||
|
||||
const NFTListRowWrapper = styled(Row)`
|
||||
margin: 24px 0px;
|
||||
padding: 24px 0px;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme }) => opacify(24, theme.backgroundOutline)};
|
||||
}
|
||||
`
|
||||
|
||||
const RemoveIconContainer = styled.div`
|
||||
width: 48px;
|
||||
height: 44px;
|
||||
padding-left: 12px;
|
||||
align-self: flex-start;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: ${({ theme }) => theme.opacity.hover};
|
||||
}
|
||||
`
|
||||
|
||||
const NFTInfoWrapper = styled(Row)`
|
||||
@ -23,27 +46,11 @@ const NFTInfoWrapper = styled(Row)`
|
||||
margin-bottom: auto;
|
||||
`
|
||||
|
||||
const ExpandMarketIconWrapper = styled.div`
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const NFTImageWrapper = styled.div`
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 48px;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const NFTImage = styled.img`
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
`
|
||||
|
||||
const RemoveIcon = styled.img`
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const HideTextOverflow = css`
|
||||
@ -74,6 +81,7 @@ const CollectionName = styled(ThemedText.BodySmall)`
|
||||
const MarketPlaceRowWrapper = styled(Column)`
|
||||
gap: 24px;
|
||||
flex: 1.5;
|
||||
margin-right: 12px;
|
||||
|
||||
@media screen and (min-width: ${BREAKPOINTS.md}px) {
|
||||
flex: 2;
|
||||
@ -103,37 +111,41 @@ export const NFTListRow = ({
|
||||
setGlobalPrice,
|
||||
selectedMarkets,
|
||||
}: NFTListRowProps) => {
|
||||
const [expandMarketplaceRows, setExpandMarketplaceRows] = useState(false)
|
||||
const [expandMarketplaceRows, toggleExpandMarketplaceRows] = useReducer((s) => !s, false)
|
||||
const removeAsset = useSellAsset((state) => state.removeSellAsset)
|
||||
const [localMarkets, setLocalMarkets] = useState([])
|
||||
const [hovered, setHovered] = useState(false)
|
||||
const handleHover = () => setHovered(!hovered)
|
||||
const [localMarkets, setLocalMarkets] = useState<ListingMarket[]>([])
|
||||
const [hovered, toggleHovered] = useReducer((s) => !s, false)
|
||||
const theme = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
setLocalMarkets(JSON.parse(JSON.stringify(selectedMarkets)))
|
||||
selectedMarkets.length < 2 && setExpandMarketplaceRows(false)
|
||||
}, [selectedMarkets])
|
||||
selectedMarkets.length < 2 && expandMarketplaceRows && toggleExpandMarketplaceRows()
|
||||
}, [expandMarketplaceRows, selectedMarkets])
|
||||
|
||||
return (
|
||||
<NFTListRowWrapper>
|
||||
<NFTInfoWrapper>
|
||||
{localMarkets.length > 1 && (
|
||||
<ExpandMarketIconWrapper onClick={() => setExpandMarketplaceRows(!expandMarketplaceRows)}>
|
||||
{expandMarketplaceRows ? <RowsExpandedIcon /> : <RowsCollpsedIcon />}
|
||||
</ExpandMarketIconWrapper>
|
||||
<NFTListRowWrapper
|
||||
onMouseEnter={() => {
|
||||
!hovered && toggleHovered()
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
hovered && toggleHovered()
|
||||
}}
|
||||
>
|
||||
<RemoveIconContainer>
|
||||
{hovered && (
|
||||
<Trash2
|
||||
size={20}
|
||||
color={theme.textSecondary}
|
||||
cursor="pointer"
|
||||
onClick={() => {
|
||||
removeAsset(asset)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<NFTImageWrapper
|
||||
onMouseEnter={handleHover}
|
||||
onMouseLeave={handleHover}
|
||||
onClick={() => {
|
||||
removeAsset(asset)
|
||||
}}
|
||||
>
|
||||
<RemoveIconWrap hovered={hovered}>
|
||||
<RemoveIcon src="/nft/svgs/minusCircle.svg" alt="Remove item" />
|
||||
</RemoveIconWrap>
|
||||
<NFTImage alt={asset.name} src={asset.imageUrl || '/nft/svgs/image-placeholder.svg'} />
|
||||
</NFTImageWrapper>
|
||||
</RemoveIconContainer>
|
||||
|
||||
<NFTInfoWrapper>
|
||||
<NFTImage alt={asset.name} src={asset.imageUrl || '/nft/svgs/image-placeholder.svg'} />
|
||||
<TokenInfoWrapper>
|
||||
<TokenName>{asset.name ? asset.name : `#${asset.tokenId}`}</TokenName>
|
||||
<CollectionName>
|
||||
@ -154,8 +166,10 @@ export const NFTListRow = ({
|
||||
removeMarket={() => localMarkets.splice(index, 1)}
|
||||
asset={asset}
|
||||
showMarketplaceLogo={true}
|
||||
key={index}
|
||||
key={asset.name + market.name}
|
||||
expandMarketplaceRows={expandMarketplaceRows}
|
||||
rowHovered={hovered}
|
||||
toggleExpandMarketplaceRows={toggleExpandMarketplaceRows}
|
||||
/>
|
||||
)
|
||||
})
|
||||
@ -167,6 +181,8 @@ export const NFTListRow = ({
|
||||
selectedMarkets={localMarkets}
|
||||
asset={asset}
|
||||
showMarketplaceLogo={false}
|
||||
rowHovered={hovered}
|
||||
toggleExpandMarketplaceRows={toggleExpandMarketplaceRows}
|
||||
/>
|
||||
)}
|
||||
</MarketPlaceRowWrapper>
|
||||
|
@ -26,6 +26,10 @@ const TableHeader = styled.div`
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
|
||||
@media screen and (min-width: ${BREAKPOINTS.sm}px) {
|
||||
margin-left: 48px;
|
||||
}
|
||||
`
|
||||
|
||||
const NFTHeader = styled.div`
|
||||
@ -34,10 +38,7 @@ const NFTHeader = styled.div`
|
||||
|
||||
const PriceHeaders = styled(Row)`
|
||||
flex: 1.5;
|
||||
|
||||
@media screen and (min-width: ${BREAKPOINTS.md}px) {
|
||||
flex: 2;
|
||||
}
|
||||
margin-right: 12px;
|
||||
|
||||
@media screen and (min-width: ${BREAKPOINTS.md}px) {
|
||||
flex: 3;
|
||||
@ -54,7 +55,7 @@ const PriceInfoHeader = styled.div`
|
||||
`
|
||||
|
||||
const DropdownAndHeaderWrapper = styled(Row)`
|
||||
flex: 2;
|
||||
flex: 3;
|
||||
gap: 4px;
|
||||
`
|
||||
|
||||
@ -125,6 +126,7 @@ const RowDivider = styled.hr`
|
||||
border-radius: 20px;
|
||||
border-width: 0.5px;
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
border-color: ${({ theme }) => theme.backgroundInteractive};
|
||||
`
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user