Commit 0208ccd7 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

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: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 12df4b39
...@@ -74,10 +74,6 @@ const ListingHeader = styled(Row)` ...@@ -74,10 +74,6 @@ const ListingHeader = styled(Row)`
const GridWrapper = styled.div` const GridWrapper = styled.div`
margin-top: 24px; margin-top: 24px;
margin-bottom: 48px; margin-bottom: 48px;
@media screen and (min-width: ${SMALL_MEDIA_BREAKPOINT}) {
margin-left: 40px;
}
` `
const MobileListButtonWrapper = styled.div` const MobileListButtonWrapper = styled.div`
......
...@@ -3,12 +3,13 @@ import { t } from '@lingui/macro' ...@@ -3,12 +3,13 @@ import { t } from '@lingui/macro'
import Column from 'components/Column' import Column from 'components/Column'
import Row from 'components/Row' import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip' import { MouseoverTooltip } from 'components/Tooltip'
import { RowsCollpsedIcon, RowsExpandedIcon } from 'nft/components/icons'
import { useSellAsset } from 'nft/hooks' import { useSellAsset } from 'nft/hooks'
import { ListingMarket, ListingWarning, WalletAsset } from 'nft/types' import { ListingMarket, ListingWarning, WalletAsset } from 'nft/types'
import { LOOKS_RARE_CREATOR_BASIS_POINTS } from 'nft/utils' import { LOOKS_RARE_CREATOR_BASIS_POINTS } from 'nft/utils'
import { formatEth, formatUsdPrice } from 'nft/utils/currency' import { formatEth, formatUsdPrice } from 'nft/utils/currency'
import { fetchPrice } from 'nft/utils/fetchPrice' 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 styled from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme' import { BREAKPOINTS, ThemedText } from 'theme'
...@@ -28,20 +29,45 @@ const PastPriceInfo = styled(Column)` ...@@ -28,20 +29,45 @@ const PastPriceInfo = styled(Column)`
` `
const RemoveMarketplaceWrap = styled(RemoveIconWrap)` 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)` const MarketIconWrapper = styled(Column)`
position: relative; position: relative;
cursor: pointer; cursor: pointer;
margin-right: 16px;
` `
const MarketIcon = styled.img` const MarketIcon = styled.img<{ index: number }>`
width: 28px; width: 20px;
height: 28px; height: 20px;
border-radius: 4px; border-radius: 4px;
object-fit: cover; 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)` const FeeColumnWrapper = styled(Column)`
...@@ -84,6 +110,8 @@ interface MarketplaceRowProps { ...@@ -84,6 +110,8 @@ interface MarketplaceRowProps {
asset: WalletAsset asset: WalletAsset
showMarketplaceLogo: boolean showMarketplaceLogo: boolean
expandMarketplaceRows?: boolean expandMarketplaceRows?: boolean
rowHovered?: boolean
toggleExpandMarketplaceRows: DispatchWithoutAction
} }
export const MarketplaceRow = ({ export const MarketplaceRow = ({
...@@ -95,14 +123,16 @@ export const MarketplaceRow = ({ ...@@ -95,14 +123,16 @@ export const MarketplaceRow = ({
asset, asset,
showMarketplaceLogo, showMarketplaceLogo,
expandMarketplaceRows, expandMarketplaceRows,
toggleExpandMarketplaceRows,
rowHovered,
}: MarketplaceRowProps) => { }: MarketplaceRowProps) => {
const [listPrice, setListPrice] = useState<number>() const [listPrice, setListPrice] = useState<number>()
const [globalOverride, setGlobalOverride] = useState(false) const [globalOverride, setGlobalOverride] = useState(false)
const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice const showGlobalPrice = globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride && globalPrice
const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice) const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice)
const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace) const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace)
const [hovered, setHovered] = useState(false) const [marketIconHovered, toggleMarketIconHovered] = useReducer((s) => !s, false)
const handleHover = () => setHovered(!hovered) const [marketRowHovered, toggleMarketRowHovered] = useReducer((s) => !s, false)
const price = showGlobalPrice ? globalPrice : listPrice const price = showGlobalPrice ? globalPrice : listPrice
...@@ -186,7 +216,7 @@ export const MarketplaceRow = ({ ...@@ -186,7 +216,7 @@ export const MarketplaceRow = ({
} }
return ( return (
<Row> <Row onMouseEnter={toggleMarketRowHovered} onMouseLeave={toggleMarketRowHovered}>
<PastPriceInfo> <PastPriceInfo>
<ThemedText.BodySmall color="textSecondary" lineHeight="20px"> <ThemedText.BodySmall color="textSecondary" lineHeight="20px">
{asset.floorPrice ? `${asset.floorPrice.toFixed(3)} ETH` : '-'} {asset.floorPrice ? `${asset.floorPrice.toFixed(3)} ETH` : '-'}
...@@ -198,22 +228,25 @@ export const MarketplaceRow = ({ ...@@ -198,22 +228,25 @@ export const MarketplaceRow = ({
</ThemedText.BodySmall> </ThemedText.BodySmall>
</PastPriceInfo> </PastPriceInfo>
<Row flex="2"> <Row flex="3">
{showMarketplaceLogo && ( {(expandMarketplaceRows || selectedMarkets.length > 1) && (
<MarketIconWrapper <MarketIconsWrapper onMouseEnter={toggleMarketIconHovered} onMouseLeave={toggleMarketIconHovered}>
onMouseEnter={handleHover} {selectedMarkets.map((market, index) => (
onMouseLeave={handleHover} <MarketIconWrapper
onClick={(e) => { key={market.name + asset.collection?.address + asset.tokenId}
e.stopPropagation() onClick={(e) => {
removeAssetMarketplace(asset, selectedMarkets[0]) e.stopPropagation()
removeMarket && removeMarket() removeAssetMarketplace(asset, selectedMarkets[0])
}} removeMarket && removeMarket()
> }}
<MarketIcon alt={selectedMarkets[0].name} src={selectedMarkets[0].icon} /> >
<RemoveMarketplaceWrap hovered={hovered}> <MarketIcon alt={selectedMarkets[0].name} src={market.icon} index={index} />
<img width="32px" src="/nft/svgs/minusCircle.svg" alt="Remove item" /> <RemoveMarketplaceWrap hovered={marketIconHovered && (expandMarketplaceRows ?? false)}>
</RemoveMarketplaceWrap> <img width="20px" src="/nft/svgs/minusCircle.svg" alt="Remove item" />
</MarketIconWrapper> </RemoveMarketplaceWrap>
</MarketIconWrapper>
))}
</MarketIconsWrapper>
)} )}
{globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride ? ( {globalPriceMethod === SetPriceMethod.SAME_PRICE && !globalOverride ? (
<PriceTextInput <PriceTextInput
...@@ -224,7 +257,6 @@ export const MarketplaceRow = ({ ...@@ -224,7 +257,6 @@ export const MarketplaceRow = ({
globalOverride={globalOverride} globalOverride={globalOverride}
warning={warning} warning={warning}
asset={asset} asset={asset}
shrink={expandMarketplaceRows}
/> />
) : ( ) : (
<PriceTextInput <PriceTextInput
...@@ -235,9 +267,13 @@ export const MarketplaceRow = ({ ...@@ -235,9 +267,13 @@ export const MarketplaceRow = ({
globalOverride={globalOverride} globalOverride={globalOverride}
warning={warning} warning={warning}
asset={asset} asset={asset}
shrink={expandMarketplaceRows}
/> />
)} )}
{rowHovered && ((expandMarketplaceRows && marketRowHovered) || selectedMarkets.length > 1) && (
<ExpandMarketIconWrapper onClick={toggleExpandMarketplaceRows}>
{expandMarketplaceRows ? <RowsExpandedIcon /> : <RowsCollpsedIcon />}
</ExpandMarketIconWrapper>
)}
</Row> </Row>
<FeeColumnWrapper> <FeeColumnWrapper>
......
import Column from 'components/Column' import Column from 'components/Column'
import Row from 'components/Row' 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 { useSellAsset } from 'nft/hooks'
import { ListingMarket, WalletAsset } from 'nft/types' import { ListingMarket, WalletAsset } from 'nft/types'
import { Dispatch, useEffect, useState } from 'react' import { Dispatch, useEffect, useReducer, useState } from 'react'
import styled, { css } from 'styled-components/macro' import { Trash2 } from 'react-feather'
import styled, { css, useTheme } from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme' import { BREAKPOINTS, ThemedText } from 'theme'
import { opacify } from 'theme/utils'
import { MarketplaceRow } from './MarketplaceRow' import { MarketplaceRow } from './MarketplaceRow'
import { SetPriceMethod } from './NFTListingsGrid' import { SetPriceMethod } from './NFTListingsGrid'
import { RemoveIconWrap } from './shared'
const NFTListRowWrapper = styled(Row)` const NFTListRowWrapper = styled(Row)`
margin: 24px 0px; padding: 24px 0px;
align-items: center; 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)` const NFTInfoWrapper = styled(Row)`
...@@ -23,27 +46,11 @@ const NFTInfoWrapper = styled(Row)` ...@@ -23,27 +46,11 @@ const NFTInfoWrapper = styled(Row)`
margin-bottom: auto; 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` const NFTImage = styled.img`
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: 8px; border-radius: 8px;
` margin-right: 8px;
const RemoveIcon = styled.img`
width: 32px;
height: 32px;
` `
const HideTextOverflow = css` const HideTextOverflow = css`
...@@ -74,6 +81,7 @@ const CollectionName = styled(ThemedText.BodySmall)` ...@@ -74,6 +81,7 @@ const CollectionName = styled(ThemedText.BodySmall)`
const MarketPlaceRowWrapper = styled(Column)` const MarketPlaceRowWrapper = styled(Column)`
gap: 24px; gap: 24px;
flex: 1.5; flex: 1.5;
margin-right: 12px;
@media screen and (min-width: ${BREAKPOINTS.md}px) { @media screen and (min-width: ${BREAKPOINTS.md}px) {
flex: 2; flex: 2;
...@@ -103,37 +111,41 @@ export const NFTListRow = ({ ...@@ -103,37 +111,41 @@ export const NFTListRow = ({
setGlobalPrice, setGlobalPrice,
selectedMarkets, selectedMarkets,
}: NFTListRowProps) => { }: NFTListRowProps) => {
const [expandMarketplaceRows, setExpandMarketplaceRows] = useState(false) const [expandMarketplaceRows, toggleExpandMarketplaceRows] = useReducer((s) => !s, false)
const removeAsset = useSellAsset((state) => state.removeSellAsset) const removeAsset = useSellAsset((state) => state.removeSellAsset)
const [localMarkets, setLocalMarkets] = useState([]) const [localMarkets, setLocalMarkets] = useState<ListingMarket[]>([])
const [hovered, setHovered] = useState(false) const [hovered, toggleHovered] = useReducer((s) => !s, false)
const handleHover = () => setHovered(!hovered) const theme = useTheme()
useEffect(() => { useEffect(() => {
setLocalMarkets(JSON.parse(JSON.stringify(selectedMarkets))) setLocalMarkets(JSON.parse(JSON.stringify(selectedMarkets)))
selectedMarkets.length < 2 && setExpandMarketplaceRows(false) selectedMarkets.length < 2 && expandMarketplaceRows && toggleExpandMarketplaceRows()
}, [selectedMarkets]) }, [expandMarketplaceRows, selectedMarkets])
return ( return (
<NFTListRowWrapper> <NFTListRowWrapper
<NFTInfoWrapper> onMouseEnter={() => {
{localMarkets.length > 1 && ( !hovered && toggleHovered()
<ExpandMarketIconWrapper onClick={() => setExpandMarketplaceRows(!expandMarketplaceRows)}> }}
{expandMarketplaceRows ? <RowsExpandedIcon /> : <RowsCollpsedIcon />} onMouseLeave={() => {
</ExpandMarketIconWrapper> hovered && toggleHovered()
}}
>
<RemoveIconContainer>
{hovered && (
<Trash2
size={20}
color={theme.textSecondary}
cursor="pointer"
onClick={() => {
removeAsset(asset)
}}
/>
)} )}
<NFTImageWrapper </RemoveIconContainer>
onMouseEnter={handleHover}
onMouseLeave={handleHover} <NFTInfoWrapper>
onClick={() => { <NFTImage alt={asset.name} src={asset.imageUrl || '/nft/svgs/image-placeholder.svg'} />
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>
<TokenInfoWrapper> <TokenInfoWrapper>
<TokenName>{asset.name ? asset.name : `#${asset.tokenId}`}</TokenName> <TokenName>{asset.name ? asset.name : `#${asset.tokenId}`}</TokenName>
<CollectionName> <CollectionName>
...@@ -154,8 +166,10 @@ export const NFTListRow = ({ ...@@ -154,8 +166,10 @@ export const NFTListRow = ({
removeMarket={() => localMarkets.splice(index, 1)} removeMarket={() => localMarkets.splice(index, 1)}
asset={asset} asset={asset}
showMarketplaceLogo={true} showMarketplaceLogo={true}
key={index} key={asset.name + market.name}
expandMarketplaceRows={expandMarketplaceRows} expandMarketplaceRows={expandMarketplaceRows}
rowHovered={hovered}
toggleExpandMarketplaceRows={toggleExpandMarketplaceRows}
/> />
) )
}) })
...@@ -167,6 +181,8 @@ export const NFTListRow = ({ ...@@ -167,6 +181,8 @@ export const NFTListRow = ({
selectedMarkets={localMarkets} selectedMarkets={localMarkets}
asset={asset} asset={asset}
showMarketplaceLogo={false} showMarketplaceLogo={false}
rowHovered={hovered}
toggleExpandMarketplaceRows={toggleExpandMarketplaceRows}
/> />
)} )}
</MarketPlaceRowWrapper> </MarketPlaceRowWrapper>
......
...@@ -26,6 +26,10 @@ const TableHeader = styled.div` ...@@ -26,6 +26,10 @@ const TableHeader = styled.div`
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
line-height: 20px; line-height: 20px;
@media screen and (min-width: ${BREAKPOINTS.sm}px) {
margin-left: 48px;
}
` `
const NFTHeader = styled.div` const NFTHeader = styled.div`
...@@ -34,10 +38,7 @@ const NFTHeader = styled.div` ...@@ -34,10 +38,7 @@ const NFTHeader = styled.div`
const PriceHeaders = styled(Row)` const PriceHeaders = styled(Row)`
flex: 1.5; flex: 1.5;
margin-right: 12px;
@media screen and (min-width: ${BREAKPOINTS.md}px) {
flex: 2;
}
@media screen and (min-width: ${BREAKPOINTS.md}px) { @media screen and (min-width: ${BREAKPOINTS.md}px) {
flex: 3; flex: 3;
...@@ -54,7 +55,7 @@ const PriceInfoHeader = styled.div` ...@@ -54,7 +55,7 @@ const PriceInfoHeader = styled.div`
` `
const DropdownAndHeaderWrapper = styled(Row)` const DropdownAndHeaderWrapper = styled(Row)`
flex: 2; flex: 3;
gap: 4px; gap: 4px;
` `
...@@ -125,6 +126,7 @@ const RowDivider = styled.hr` ...@@ -125,6 +126,7 @@ const RowDivider = styled.hr`
border-radius: 20px; border-radius: 20px;
border-width: 0.5px; border-width: 0.5px;
border-style: solid; border-style: solid;
margin: 0;
border-color: ${({ theme }) => theme.backgroundInteractive}; border-color: ${({ theme }) => theme.backgroundInteractive};
` `
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment