Commit 2b5769ac authored by Jack Short's avatar Jack Short Committed by GitHub

chore: removing VE from cards (#6038)

* moving cards to styled components

* converting rows to styled components

* image

* finished images

* handling all media

* removed VE from cards

* no content colors

* removing aspect ratio calc

* responding to all comments
parent b811afd1
import { style } from '@vanilla-extract/css'
import { calc } from '@vanilla-extract/css-utils'
import { sprinkles, themeVars, vars } from 'nft/css/sprinkles.css'
export const card = style([
sprinkles({
overflow: 'hidden',
paddingBottom: '12',
borderRadius: '16',
}),
{
boxSizing: 'border-box',
WebkitBoxSizing: 'border-box',
boxShadow: vars.color.cardDropShadow,
backgroundColor: themeVars.colors.backgroundSurface,
':after': {
content: '',
position: 'absolute',
top: '0px',
right: ' 0px',
bottom: ' 0px',
left: '0px',
border: ' 1px solid',
borderRadius: '16px',
borderColor: '#5D678524',
pointerEvents: 'none',
},
},
])
export const loadingBackground = style({
background: `linear-gradient(270deg, ${themeVars.colors.backgroundOutline} 0%, ${themeVars.colors.backgroundSurface} 100%)`,
})
export const cardImageHover = style({
transform: 'scale(1.15)',
})
export const selectedCard = style([
card,
sprinkles({
background: 'backgroundSurface',
}),
{
':after': {
border: '2px solid',
borderColor: vars.color.accentAction,
},
},
])
export const button = style([
sprinkles({
display: 'flex',
width: 'full',
position: 'relative',
paddingY: '8',
marginTop: { sm: '8', md: '10' },
marginBottom: '12',
borderRadius: '12',
border: 'none',
justifyContent: 'center',
transition: '250',
cursor: 'pointer',
}),
{
lineHeight: '16px',
},
])
export const marketplaceIcon = style([
sprinkles({
display: 'inline-block',
width: '16',
height: '16',
borderRadius: '4',
flexShrink: '0',
marginLeft: '8',
}),
{
verticalAlign: 'top',
},
])
export const rarityInfo = style([
sprinkles({
display: 'flex',
borderRadius: '4',
height: '16',
color: 'textPrimary',
background: 'backgroundInteractive',
fontSize: '10',
fontWeight: 'semibold',
paddingX: '4',
}),
{
lineHeight: '12px',
letterSpacing: '0.04em',
backdropFilter: 'blur(6px)',
},
])
export const playbackSwitch = style([
sprinkles({
position: 'absolute',
width: '40',
height: '40',
zIndex: '1',
}),
{
marginLeft: calc.subtract('100%', '50px'),
transform: 'translateY(-56px)',
},
])
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import clsx from 'clsx' import { t, Trans } from '@lingui/macro'
import Column from 'components/Column'
import { OpacityHoverState } from 'components/Common' import { OpacityHoverState } from 'components/Common'
import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip' import { MouseoverTooltip } from 'components/Tooltip'
import { NftStandard } from 'graphql/data/__generated__/types-and-hooks' import { NftStandard } from 'graphql/data/__generated__/types-and-hooks'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { import {
MinusIconLarge, MinusIconLarge,
PauseButtonIcon, PauseButtonIcon,
...@@ -14,8 +14,6 @@ import { ...@@ -14,8 +14,6 @@ import {
RarityVerifiedIcon, RarityVerifiedIcon,
VerifiedIcon, VerifiedIcon,
} from 'nft/components/icons' } from 'nft/components/icons'
import { body, bodySmall, buttonTextMedium, subhead } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useIsMobile } from 'nft/hooks' import { useIsMobile } from 'nft/hooks'
import { GenieAsset, Rarity, UniformAspectRatio, UniformAspectRatios, WalletAsset } from 'nft/types' import { GenieAsset, Rarity, UniformAspectRatio, UniformAspectRatios, WalletAsset } from 'nft/types'
import { fallbackProvider, isAudio, isVideo, putCommas } from 'nft/utils' import { fallbackProvider, isAudio, isVideo, putCommas } from 'nft/utils'
...@@ -32,11 +30,11 @@ import { ...@@ -32,11 +30,11 @@ import {
useRef, useRef,
useState, useState,
} from 'react' } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle, Pause, Play } from 'react-feather'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { colors } from 'theme/colors'
import * as styles from './Card.css' import { opacify } from 'theme/utils'
/* -------- ASSET CONTEXT -------- */ /* -------- ASSET CONTEXT -------- */
export interface CardContextProps { export interface CardContextProps {
...@@ -166,6 +164,31 @@ const StyledImageContainer = styled.div<{ isDisabled?: boolean }>` ...@@ -166,6 +164,31 @@ const StyledImageContainer = styled.div<{ isDisabled?: boolean }>`
cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')}; cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')};
` `
const CardContainer = styled.div<{ selected: boolean }>`
position: relative;
border-radius: ${BORDER_RADIUS}px;
background-color: ${({ theme }) => theme.backgroundSurface};
overflow: hidden;
padding-bottom: 12px;
border-radius: 16px;
box-shadow: rgba(0, 0, 0, 10%) 0px 4px 12px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
:after {
content: '';
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
border: ${({ selected }) => (selected ? '2px' : '1px')} solid;
border-radius: 16px;
border-color: ${({ theme, selected }) => (selected ? theme.accentAction : opacify(12, colors.gray500))};
pointer-events: none;
}
`
/* -------- ASSET CARD -------- */ /* -------- ASSET CARD -------- */
interface CardProps { interface CardProps {
asset: GenieAsset | WalletAsset asset: GenieAsset | WalletAsset
...@@ -220,19 +243,16 @@ const Container = ({ ...@@ -220,19 +243,16 @@ const Container = ({
return ( return (
<CardContext.Provider value={providerValue}> <CardContext.Provider value={providerValue}>
<Box <CardContainer
position="relative" selected={selected}
ref={assetRef} ref={assetRef}
borderRadius={BORDER_RADIUS}
className={selected ? styles.selectedCard : styles.card}
draggable={false} draggable={false}
onMouseEnter={toggleHover} onMouseEnter={toggleHover}
onMouseLeave={toggleHover} onMouseLeave={toggleHover}
transition="250"
onClick={isDisabled ? () => null : onClick ?? handleAssetInBag} onClick={isDisabled ? () => null : onClick ?? handleAssetInBag}
> >
{children} {children}
</Box> </CardContainer>
</CardContext.Provider> </CardContext.Provider>
) )
} }
...@@ -277,6 +297,13 @@ function getHeightFromAspectRatio(uniformAspectRatio: UniformAspectRatio, render ...@@ -277,6 +297,13 @@ function getHeightFromAspectRatio(uniformAspectRatio: UniformAspectRatio, render
: renderedHeight : renderedHeight
} }
function getMediaAspectRatio(
uniformAspectRatio?: UniformAspectRatio,
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
): string {
return uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'
}
interface ImageProps { interface ImageProps {
uniformAspectRatio?: UniformAspectRatio uniformAspectRatio?: UniformAspectRatio
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
...@@ -284,6 +311,29 @@ interface ImageProps { ...@@ -284,6 +311,29 @@ interface ImageProps {
setRenderedHeight?: (renderedHeight: number | undefined) => void setRenderedHeight?: (renderedHeight: number | undefined) => void
} }
const StyledMediaContainer = styled(Row)`
overflow: hidden;
border-top-left-radius: ${BORDER_RADIUS}px;
border-top-right-radius: ${BORDER_RADIUS}px;
`
const StyledImage = styled.img<{
hovered: boolean
imageLoading: boolean
$aspectRatio?: string
$hidden?: boolean
}>`
width: 100%;
aspect-ratio: ${({ $aspectRatio }) => $aspectRatio};
transition: ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.ease} transform`};
will-change: transform;
object-fit: contain;
visibility: ${({ $hidden }) => ($hidden ? 'hidden' : 'visible')};
transform: ${({ hovered }) => hovered && 'scale(1.15)'};
background: ${({ theme, imageLoading }) =>
imageLoading && `linear-gradient(270deg, ${theme.backgroundOutline} 0%, ${theme.backgroundSurface} 100%)`};
`
const Image = ({ const Image = ({
uniformAspectRatio = UniformAspectRatios.square, uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio, setUniformAspectRatio,
...@@ -300,40 +350,50 @@ const Image = ({ ...@@ -300,40 +350,50 @@ const Image = ({
} }
return ( return (
<Box display="flex" overflow="hidden" borderTopLeftRadius={BORDER_RADIUS} borderTopRightRadius={BORDER_RADIUS}> <StyledMediaContainer>
<Box <StyledImage
as="img"
width="full"
style={{
aspectRatio: `${uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'}`,
transition: 'transform 0.25s ease 0s',
}}
src={asset.imageUrl || asset.smallImageUrl} src={asset.imageUrl || asset.smallImageUrl}
objectFit="contain" $aspectRatio={getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio)}
hovered={hovered && !isMobile}
imageLoading={!loaded}
draggable={false} draggable={false}
onError={() => setNoContent(true)} onError={() => setNoContent(true)}
onLoad={(e) => { onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight) handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setLoaded(true) setLoaded(true)
}} }}
className={clsx(hovered && !isMobile && styles.cardImageHover, !loaded && styles.loadingBackground)}
/> />
</Box> </StyledMediaContainer>
) )
} }
function getMediaAspectRatio(
uniformAspectRatio: UniformAspectRatio,
setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void
): string {
return uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'
}
interface MediaProps { interface MediaProps {
shouldPlay: boolean shouldPlay: boolean
setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void setCurrentTokenPlayingMedia: (tokenId: string | undefined) => void
} }
const PlaybackButton = styled.div`
position: absolute;
height: 40px;
width: 40px;
z-index: 1;
margin-left: calc(100% - 50px);
transform: translateY(-56px);
`
const StyledVideo = styled.video<{
$aspectRatio?: string
}>`
width: 100%;
aspect-ratio: ${({ $aspectRatio }) => $aspectRatio};
`
const StyledInnerMediaContainer = styled(Row)`
position: absolute;
left: 0px;
top: 0px;
`
const Video = ({ const Video = ({
uniformAspectRatio = UniformAspectRatios.square, uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio, setUniformAspectRatio,
...@@ -360,52 +420,38 @@ const Video = ({ ...@@ -360,52 +420,38 @@ const Video = ({
return ( return (
<> <>
<Box display="flex" overflow="hidden"> <StyledMediaContainer>
<Box <StyledImage
as="img"
alt={asset.name || asset.tokenId}
width="full"
style={{
aspectRatio: getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio),
transition: 'transform 0.25s ease 0s',
willChange: 'transform',
}}
src={asset.imageUrl || asset.smallImageUrl} src={asset.imageUrl || asset.smallImageUrl}
objectFit="contain" alt={asset.name || asset.tokenId}
$aspectRatio={getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio)}
hovered={hovered && !isMobile}
imageLoading={!imageLoaded}
draggable={false} draggable={false}
onError={() => setNoContent(true)} onError={() => setNoContent(true)}
onLoad={(e) => { onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight) handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setImageLoaded(true) setImageLoaded(true)
}} }}
visibility={shouldPlay ? 'hidden' : 'visible'} $hidden={shouldPlay}
className={clsx(hovered && !isMobile && styles.cardImageHover, !imageLoaded && styles.loadingBackground)}
/> />
</Box> </StyledMediaContainer>
{shouldPlay ? ( {shouldPlay ? (
<> <>
<Box className={styles.playbackSwitch}> <PlaybackButton>
<PauseButtonIcon <Pause
width="100%" size="24px"
height="100%"
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
setCurrentTokenPlayingMedia(undefined) setCurrentTokenPlayingMedia(undefined)
}} }}
className="playback-icon"
/> />
</Box> </PlaybackButton>
<Box position="absolute" left="0" top="0" display="flex"> <StyledInnerMediaContainer>
<Box <StyledVideo
as="video" $aspectRatio={getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio)}
ref={vidRef} ref={vidRef}
width="full"
style={{
aspectRatio: `${
uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto'
}`,
}}
onEnded={(e) => { onEnded={(e) => {
e.preventDefault() e.preventDefault()
setCurrentTokenPlayingMedia(undefined) setCurrentTokenPlayingMedia(undefined)
...@@ -414,29 +460,32 @@ const Video = ({ ...@@ -414,29 +460,32 @@ const Video = ({
playsInline playsInline
> >
<source src={asset.animationUrl} /> <source src={asset.animationUrl} />
</Box> </StyledVideo>
</Box> </StyledInnerMediaContainer>
</> </>
) : ( ) : (
<Box className={styles.playbackSwitch}> <PlaybackButton>
{((!isMobile && hovered) || isMobile) && ( {((!isMobile && hovered) || isMobile) && (
<PlayButtonIcon <Play
width="100%" size="24px"
height="100%"
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
setCurrentTokenPlayingMedia(asset.tokenId) setCurrentTokenPlayingMedia(asset.tokenId)
}} }}
className="playback-icon"
/> />
)} )}
</Box> </PlaybackButton>
)} )}
</> </>
) )
} }
const StyledAudio = styled.audio`
width: 100%;
height: 100%;
`
const Audio = ({ const Audio = ({
uniformAspectRatio = UniformAspectRatios.square, uniformAspectRatio = UniformAspectRatios.square,
setUniformAspectRatio, setUniformAspectRatio,
...@@ -463,29 +512,25 @@ const Audio = ({ ...@@ -463,29 +512,25 @@ const Audio = ({
return ( return (
<> <>
<Box display="flex" overflow="hidden"> <StyledMediaContainer>
<Box <StyledImage
as="img"
alt={asset.name || asset.tokenId}
width="full"
style={{
aspectRatio: getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio),
transition: 'transform 0.4s ease 0s',
}}
src={asset.imageUrl || asset.smallImageUrl} src={asset.imageUrl || asset.smallImageUrl}
objectFit="contain" alt={asset.name || asset.tokenId}
$aspectRatio={getMediaAspectRatio(uniformAspectRatio, setUniformAspectRatio)}
hovered={hovered && !isMobile}
imageLoading={!imageLoaded}
draggable={false} draggable={false}
onError={() => setNoContent(true)} onError={() => setNoContent(true)}
onLoad={(e) => { onLoad={(e) => {
handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight) handleUniformAspectRatio(uniformAspectRatio, e, setUniformAspectRatio, renderedHeight, setRenderedHeight)
setImageLoaded(true) setImageLoaded(true)
setImageLoaded(true)
}} }}
className={clsx(hovered && !isMobile && styles.cardImageHover, !imageLoaded && styles.loadingBackground)}
/> />
</Box> </StyledMediaContainer>
{shouldPlay ? ( {shouldPlay ? (
<> <>
<Box className={styles.playbackSwitch}> <PlaybackButton>
<PauseButtonIcon <PauseButtonIcon
width="100%" width="100%"
height="100%" height="100%"
...@@ -494,26 +539,22 @@ const Audio = ({ ...@@ -494,26 +539,22 @@ const Audio = ({
e.stopPropagation() e.stopPropagation()
setCurrentTokenPlayingMedia(undefined) setCurrentTokenPlayingMedia(undefined)
}} }}
className="playback-icon"
/> />
</Box> </PlaybackButton>
<Box position="absolute" left="0" top="0" display="flex"> <StyledInnerMediaContainer>
<Box <StyledAudio
as="audio"
ref={audRef} ref={audRef}
width="full"
height="full"
onEnded={(e) => { onEnded={(e) => {
e.preventDefault() e.preventDefault()
setCurrentTokenPlayingMedia(undefined) setCurrentTokenPlayingMedia(undefined)
}} }}
> >
<source src={asset.animationUrl} /> <source src={asset.animationUrl} />
</Box> </StyledAudio>
</Box> </StyledInnerMediaContainer>
</> </>
) : ( ) : (
<Box className={styles.playbackSwitch}> <PlaybackButton>
{((!isMobile && hovered) || isMobile) && ( {((!isMobile && hovered) || isMobile) && (
<PlayButtonIcon <PlayButtonIcon
width="100%" width="100%"
...@@ -523,10 +564,9 @@ const Audio = ({ ...@@ -523,10 +564,9 @@ const Audio = ({
e.stopPropagation() e.stopPropagation()
setCurrentTokenPlayingMedia(asset.tokenId) setCurrentTokenPlayingMedia(asset.tokenId)
}} }}
className="playback-icon"
/> />
)} )}
</Box> </PlaybackButton>
)} )}
</> </>
) )
...@@ -537,36 +577,39 @@ interface CardDetailsContainerProps { ...@@ -537,36 +577,39 @@ interface CardDetailsContainerProps {
children: ReactNode children: ReactNode
} }
const StyledDetailsContainer = styled(Column)`
position: relative;
padding: 12px 12px 0px;
justify-content: space-between;
transition: ${({ theme }) => `${theme.transition.duration.medium}`};
`
const DetailsContainer = ({ children }: CardDetailsContainerProps) => { const DetailsContainer = ({ children }: CardDetailsContainerProps) => {
return ( return <StyledDetailsContainer>{children}</StyledDetailsContainer>
<Row
position="relative"
paddingX="12"
paddingTop="12"
justifyContent="space-between"
flexDirection="column"
transition="250"
>
{children}
</Row>
)
} }
const StyledInfoContainer = styled.div`
overflow: hidden;
width: 100%;
`
const InfoContainer = ({ children }: { children: ReactNode }) => { const InfoContainer = ({ children }: { children: ReactNode }) => {
return ( return <StyledInfoContainer>{children}</StyledInfoContainer>
<Box overflow="hidden" width="full">
{children}
</Box>
)
} }
const TruncatedTextRow = styled(Row)` const TruncatedTextRow = styled(ThemedText.BodySmall)`
display: flex;
padding: 2px; padding: 2px;
white-space: pre; white-space: pre;
text-overflow: ellipsis; text-overflow: ellipsis;
display: block; display: block;
overflow: hidden; overflow: hidden;
flex: 1; `
const AssetNameRow = styled(TruncatedTextRow)`
color: ${({ theme }) => theme.textPrimary};
font-size: 16px !important;
font-weight: 400;
` `
interface ProfileNftDetailsProps { interface ProfileNftDetailsProps {
...@@ -574,6 +617,18 @@ interface ProfileNftDetailsProps { ...@@ -574,6 +617,18 @@ interface ProfileNftDetailsProps {
hideDetails: boolean hideDetails: boolean
} }
const PrimaryRowContainer = styled.div`
overflow: hidden;
width: 100%;
flex-wrap: nowrap;
`
const FloorPriceRow = styled(TruncatedTextRow)`
font-size: 16px;
font-weight: 600;
line-height: 20px;
`
const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => { const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => {
const assetName = () => { const assetName = () => {
if (!asset.name && !asset.tokenId) return if (!asset.name && !asset.tokenId) return
...@@ -583,89 +638,95 @@ const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => { ...@@ -583,89 +638,95 @@ const ProfileNftDetails = ({ asset, hideDetails }: ProfileNftDetailsProps) => {
const shouldShowUserListedPrice = !asset.notForSale && asset.asset_contract.tokenType !== NftStandard.Erc1155 const shouldShowUserListedPrice = !asset.notForSale && asset.asset_contract.tokenType !== NftStandard.Erc1155
return ( return (
<Box overflow="hidden" width="full" flexWrap="nowrap"> <PrimaryRowContainer>
<PrimaryRow> <PrimaryRow>
<PrimaryDetails> <PrimaryDetails>
<TruncatedTextRow className={bodySmall} style={{ color: themeVars.colors.textSecondary }}> <TruncatedTextRow color="textSecondary">
{!!asset.asset_contract.name && <span>{asset.asset_contract.name}</span>} {!!asset.asset_contract.name && <span>{asset.asset_contract.name}</span>}
</TruncatedTextRow> </TruncatedTextRow>
{asset.collectionIsVerified && <VerifiedIcon height="18px" width="18px" />} {asset.collectionIsVerified && <VerifiedIcon height="18px" width="18px" />}
</PrimaryDetails> </PrimaryDetails>
{!hideDetails && <DetailsLink />} {!hideDetails && <DetailsLink />}
</PrimaryRow> </PrimaryRow>
<Row justifyItems="flex-start"> <Row>
<TruncatedTextRow <AssetNameRow>{assetName()}</AssetNameRow>
className={body}
style={{
color: themeVars.colors.textPrimary,
}}
>
{assetName()}
</TruncatedTextRow>
{asset.susFlag && <Suspicious />} {asset.susFlag && <Suspicious />}
</Row> </Row>
<TruncatedTextRow className={buttonTextMedium} style={{ color: themeVars.colors.textPrimary }}> <FloorPriceRow>
{shouldShowUserListedPrice && asset.floor_sell_order_price {shouldShowUserListedPrice && asset.floor_sell_order_price
? `${floorFormatter(asset.floor_sell_order_price)} ETH` ? `${floorFormatter(asset.floor_sell_order_price)} ETH`
: ' '} : ' '}
</TruncatedTextRow> </FloorPriceRow>
</Box> </PrimaryRowContainer>
) )
} }
const PrimaryRow = ({ children }: { children: ReactNode }) => ( const StyledPrimaryRow = styled(Row)`
<Row gap="8" justifyContent="space-between"> gap: 8px;
{children} justify-content: space-between;
</Row> `
)
const PrimaryRow = ({ children }: { children: ReactNode }) => <StyledPrimaryRow>{children}</StyledPrimaryRow>
const StyledPrimaryDetails = styled(Row)`
justify-items: center;
overflow: hidden;
white-space: nowrap;
`
const PrimaryDetails = ({ children }: { children: ReactNode }) => ( const PrimaryDetails = ({ children }: { children: ReactNode }) => (
<Row justifyItems="center" overflow="hidden" whiteSpace="nowrap"> <StyledPrimaryDetails>{children}</StyledPrimaryDetails>
{children}
</Row>
) )
const PrimaryInfoContainer = styled.div`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: 400;
font-size: 16px;
line-height: 24px;
`
const PrimaryInfo = ({ children }: { children: ReactNode }) => { const PrimaryInfo = ({ children }: { children: ReactNode }) => {
return ( return <PrimaryInfoContainer>{children}</PrimaryInfoContainer>
<Box overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" className={body}>
{children}
</Box>
)
} }
const SecondaryRow = ({ children }: { children: ReactNode }) => ( const StyledSecondaryRow = styled(Row)`
<Row height="20" justifyContent="space-between" marginTop="6"> height: 20px;
{children} justify-content: space-between;
</Row> margin-top: 6px;
) `
const SecondaryRow = ({ children }: { children: ReactNode }) => <StyledSecondaryRow>{children}</StyledSecondaryRow>
const StyledSecondaryDetails = styled(Row)`
overflow: hidden;
white-space: nowrap;
`
const SecondaryDetails = ({ children }: { children: ReactNode }) => ( const SecondaryDetails = ({ children }: { children: ReactNode }) => (
<Row overflow="hidden" whiteSpace="nowrap"> <StyledSecondaryDetails>{children}</StyledSecondaryDetails>
{children}
</Row>
) )
const SecondaryInfoContainer = styled.div`
color: ${({ theme }) => theme.textPrimary};
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 20px;
`
const SecondaryInfo = ({ children }: { children: ReactNode }) => { const SecondaryInfo = ({ children }: { children: ReactNode }) => {
return ( return <SecondaryInfoContainer>{children}</SecondaryInfoContainer>
<Box
color="textPrimary"
overflow="hidden"
whiteSpace="nowrap"
textOverflow="ellipsis"
style={{ lineHeight: '20px' }}
className={subhead}
>
{children}
</Box>
)
} }
const TertiaryInfoContainer = styled.div`
color: ${({ theme }) => theme.textSecondary};
margin-top: 8px;
`
const TertiaryInfo = ({ children }: { children: ReactNode }) => { const TertiaryInfo = ({ children }: { children: ReactNode }) => {
return ( return <TertiaryInfoContainer>{children}</TertiaryInfoContainer>
<Box marginTop="8" color="textSecondary">
{children}
</Box>
)
} }
interface Erc1155ControlsInterface { interface Erc1155ControlsInterface {
...@@ -700,15 +761,18 @@ const Erc1155Controls = ({ quantity }: Erc1155ControlsInterface) => { ...@@ -700,15 +761,18 @@ const Erc1155Controls = ({ quantity }: Erc1155ControlsInterface) => {
) )
} }
const StyledMarketplaceIcon = styled.img`
display: inline-block;
width: 16px;
height: 16px;
border-radius: 4px;
flex-shrink: 0;
margin-left: 8px;
vertical-align: top;
`
const MarketplaceIcon = ({ marketplace }: { marketplace: string }) => { const MarketplaceIcon = ({ marketplace }: { marketplace: string }) => {
return ( return <StyledMarketplaceIcon alt={marketplace} src={`/nft/svgs/marketplaces/${marketplace}.svg`} />
<Box
as="img"
alt={marketplace}
src={`/nft/svgs/marketplaces/${marketplace}.svg`}
className={styles.marketplaceIcon}
/>
)
} }
const DetailsLink = () => { const DetailsLink = () => {
...@@ -721,7 +785,7 @@ const DetailsLink = () => { ...@@ -721,7 +785,7 @@ const DetailsLink = () => {
e.stopPropagation() e.stopPropagation()
}} }}
> >
<Box data-testid="nft-details-link">Details</Box> <div data-testid="nft-details-link">Details</div>
</DetailsLinkContainer> </DetailsLinkContainer>
) )
} }
...@@ -734,6 +798,28 @@ interface RankingProps { ...@@ -734,6 +798,28 @@ interface RankingProps {
rarityLogo?: string rarityLogo?: string
} }
const RarityLogoContainer = styled(Row)`
margin-right: 4px;
width: 16px;
`
const RarityText = styled(ThemedText.BodySmall)`
display: flex;
`
const RarityInfo = styled(Row)`
height: 16px;
border-radius: 4px;
color: ${({ theme }) => theme.textPrimary};
background: ${({ theme }) => theme.backgroundInteractive};
font-size: 10px;
font-weight: 600;
padding: 0px 4px;
line-height: 12px;
letter-spacing: 0.04em;
backdrop-filter: blur(6px);
`
const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps) => { const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps) => {
const { asset } = useCardContext() const { asset } = useCardContext()
...@@ -744,44 +830,49 @@ const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps) ...@@ -744,44 +830,49 @@ const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps)
<MouseoverTooltip <MouseoverTooltip
text={ text={
<Row> <Row>
<Box display="flex" marginRight="4"> <RarityLogoContainer>
<img src={rarityLogo} alt="cardLogo" width={16} /> <img src={rarityLogo} alt="cardLogo" width={16} height={16} />
</Box> </RarityLogoContainer>
<Box width="full" className={bodySmall}> <RarityText>
{rarityVerified {rarityVerified
? `Verified by ${ ? `Verified by ${
('collectionName' in asset && asset.collectionName) || ('collectionName' in asset && asset.collectionName) ||
('asset_contract' in asset && asset.asset_contract?.name) ('asset_contract' in asset && asset.asset_contract?.name)
}` }`
: `Ranking by ${rarity.primaryProvider === 'Genie' ? fallbackProvider : rarity.primaryProvider}`} : `Ranking by ${rarity.primaryProvider === 'Genie' ? fallbackProvider : rarity.primaryProvider}`}
</Box> </RarityText>
</Row> </Row>
} }
placement="top" placement="top"
> >
<Box className={styles.rarityInfo}> <RarityInfo>
<Box paddingTop="2" paddingBottom="2" display="flex"> <Row padding="2px 0px">{putCommas(provider.rank)}</Row>
{putCommas(provider.rank)} <Row>{rarityVerified ? <RarityVerifiedIcon /> : null}</Row>
</Box> </RarityInfo>
<Box display="flex" height="16">
{rarityVerified ? <RarityVerifiedIcon /> : null}
</Box>
</Box>
</MouseoverTooltip> </MouseoverTooltip>
</RankingContainer> </RankingContainer>
)} )}
</> </>
) )
} }
const SUSPICIOUS_TEXT = 'Blocked on OpenSea'
const SUSPICIOUS_TEXT = t`Blocked on OpenSea`
const SuspiciousIconContainer = styled(Row)`
flex-shrink: 0;
margin-left: 4px;
`
const PoolIconContainer = styled(SuspiciousIconContainer)`
color: ${({ theme }) => theme.textSecondary};
`
const Suspicious = () => { const Suspicious = () => {
return ( return (
<MouseoverTooltip text={<Box className={bodySmall}>{SUSPICIOUS_TEXT}</Box>} placement="top"> <MouseoverTooltip text={<ThemedText.BodySmall>{SUSPICIOUS_TEXT}</ThemedText.BodySmall>} placement="top">
<Box display="flex" flexShrink="0" marginLeft="4"> <SuspiciousIconContainer>
<SuspiciousIcon /> <SuspiciousIcon />
</Box> </SuspiciousIconContainer>
</MouseoverTooltip> </MouseoverTooltip>
) )
} }
...@@ -790,44 +881,46 @@ const Pool = () => { ...@@ -790,44 +881,46 @@ const Pool = () => {
return ( return (
<MouseoverTooltip <MouseoverTooltip
text={ text={
<Box className={bodySmall}> <ThemedText.BodySmall>
This NFT is part of a liquidity pool. Buying this will increase the price of the remaining pooled NFTs. This NFT is part of a liquidity pool. Buying this will increase the price of the remaining pooled NFTs.
</Box> </ThemedText.BodySmall>
} }
placement="top" placement="top"
> >
<Box display="flex" flexShrink="0" marginLeft="4" color="textSecondary"> <PoolIconContainer>
<PoolIcon width="20" height="20" /> <PoolIcon width="20" height="20" />
</Box> </PoolIconContainer>
</MouseoverTooltip> </MouseoverTooltip>
) )
} }
const NoContentContainerBackground = styled.div<{ height?: number }>`
position: relative;
width: 100%;
height: ${({ height }) => (height ? `${height}px` : 'auto')};
padding-top: 100%;
background: ${({ theme }) =>
`linear-gradient(90deg, ${theme.backgroundSurface} 0%, ${theme.backgroundInteractive} 95.83%)`};
`
const NoContentText = styled(ThemedText.BodyPrimary)`
position: absolute;
text-align: center;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
color: ${colors.gray500};
`
const NoContentContainer = ({ height }: { height?: number }) => ( const NoContentContainer = ({ height }: { height?: number }) => (
<> <>
<Box <NoContentContainerBackground height={height}>
position="relative" <NoContentText>
width="full" <Trans>Content not</Trans>
style={{
height: height ? `${height}px` : 'auto',
paddingTop: '100%',
background: `linear-gradient(90deg, ${themeVars.colors.backgroundSurface} 0%, ${themeVars.colors.backgroundInteractive} 95.83%)`,
}}
>
<Box
position="absolute"
textAlign="center"
left="1/2"
top="1/2"
style={{ transform: 'translate3d(-50%, -50%, 0)' }}
color="gray500"
className={body}
>
Content not
<br /> <br />
available yet <Trans>available yet</Trans>
</Box> </NoContentText>
</Box> </NoContentContainerBackground>
</> </>
) )
......
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