feat: [DetailsV2] add link and hover state to trait component (#6472)
* hide trait container when asset has no traits * add traits header row * trait value rows and scroll behaviour * row with placeholder values * add random filler values and proper scrollbar styles * working rarity graph * bar border radius * move rarity graph to its own file * always show scrim * working scrim and move traitrow to its own file * cleanup * remove padding * move scrollbar right * add snapshot tests * add comment about randomly generated rarities * cleanup * only pass traits * justify * not important * cleanup scrim styles * remove comment * add scroll state hook * lint * update test * object over map * remove spaces * justify content * add ticket * add comments * update snapshot * add link and hover state to trait component * correct padding * respond to comments * add component and use css for vis --------- Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
This commit is contained in:
parent
924e83139b
commit
ff0209a78f
@ -47,7 +47,7 @@ export const DataPage = ({ asset }: { asset: GenieAsset }) => {
|
||||
<DataPageHeader />
|
||||
<ContentContainer>
|
||||
<LeftColumn>
|
||||
{!!asset.traits?.length && <DataPageTraits traits={asset.traits} />}
|
||||
{!!asset.traits?.length && <DataPageTraits asset={asset} />}
|
||||
<DataPageDescription />
|
||||
</LeftColumn>
|
||||
<DataPageTable />
|
||||
|
@ -4,7 +4,7 @@ import { render } from 'test-utils/render'
|
||||
import { DataPageTraits } from './DataPageTraits'
|
||||
|
||||
it('data page trait component does not load with asset with no traits', () => {
|
||||
const { asFragment } = render(<DataPageTraits traits={TEST_NFT_ASSET.traits ?? []} />)
|
||||
const { asFragment } = render(<DataPageTraits asset={TEST_NFT_ASSET} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
@ -3,7 +3,7 @@ import Column from 'components/Column'
|
||||
import { ScrollBarStyles } from 'components/Common'
|
||||
import Row from 'components/Row'
|
||||
import { useSubscribeScrollState } from 'nft/hooks'
|
||||
import { Trait } from 'nft/types'
|
||||
import { GenieAsset } from 'nft/types'
|
||||
import { useMemo } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
@ -57,13 +57,15 @@ const Scrim = styled.div<{ isBottom?: boolean }>`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const TraitsContent = ({ traits }: { traits?: Trait[] }) => {
|
||||
const TraitsContent = ({ asset }: { asset: GenieAsset }) => {
|
||||
const { userCanScroll, scrollRef, scrollProgress, scrollHandler } = useSubscribeScrollState()
|
||||
|
||||
// This is needed to prevent rerenders when handling scrolls
|
||||
const traitRows = useMemo(() => {
|
||||
return traits?.map((trait) => <TraitRow trait={trait} key={trait.trait_type + ':' + trait.trait_value} />)
|
||||
}, [traits])
|
||||
return asset.traits?.map((trait) => (
|
||||
<TraitRow collectionAddress={asset.address} trait={trait} key={trait.trait_type + ':' + trait.trait_value} />
|
||||
))
|
||||
}, [asset.address, asset.traits])
|
||||
|
||||
return (
|
||||
<Column>
|
||||
@ -96,7 +98,7 @@ enum TraitTabsKeys {
|
||||
Traits = 'traits',
|
||||
}
|
||||
|
||||
export const DataPageTraits = ({ traits }: { traits: Trait[] }) => {
|
||||
export const DataPageTraits = ({ asset }: { asset: GenieAsset }) => {
|
||||
const TraitTabs: Map<string, Tab> = useMemo(
|
||||
() =>
|
||||
new Map([
|
||||
@ -105,12 +107,12 @@ export const DataPageTraits = ({ traits }: { traits: Trait[] }) => {
|
||||
{
|
||||
title: <Trans>Traits</Trans>,
|
||||
key: TraitTabsKeys.Traits,
|
||||
content: <TraitsContent traits={traits} />,
|
||||
count: traits?.length,
|
||||
content: <TraitsContent asset={asset} />,
|
||||
count: asset.traits?.length,
|
||||
},
|
||||
],
|
||||
]),
|
||||
[traits]
|
||||
[asset]
|
||||
)
|
||||
return <TabbedComponent tabs={TraitTabs} />
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import Row from 'components/Row'
|
||||
import { Trait } from 'nft/types'
|
||||
import styled from 'styled-components/macro'
|
||||
@ -13,6 +14,7 @@ const RarityBar = styled.div<{ $color?: string }>`
|
||||
interface RarityValue {
|
||||
threshold: number
|
||||
color: string
|
||||
caption: React.ReactNode
|
||||
}
|
||||
|
||||
enum RarityLevel {
|
||||
@ -27,26 +29,31 @@ const RarityLevels: { [key in RarityLevel]: RarityValue } = {
|
||||
[RarityLevel.VeryCommon]: {
|
||||
threshold: 0.8,
|
||||
color: colors.gray500,
|
||||
caption: <Trans>Very common</Trans>,
|
||||
},
|
||||
[RarityLevel.Common]: {
|
||||
threshold: 0.6,
|
||||
color: colors.green300,
|
||||
caption: <Trans>Common</Trans>,
|
||||
},
|
||||
[RarityLevel.Rare]: {
|
||||
threshold: 0.4,
|
||||
color: colors.blueVibrant,
|
||||
caption: <Trans>Rare</Trans>,
|
||||
},
|
||||
[RarityLevel.VeryRare]: {
|
||||
threshold: 0.2,
|
||||
color: colors.purpleVibrant,
|
||||
caption: <Trans>Very rare</Trans>,
|
||||
},
|
||||
[RarityLevel.ExtremelyRare]: {
|
||||
threshold: 0,
|
||||
color: colors.magentaVibrant,
|
||||
caption: <Trans>Extremely rare</Trans>,
|
||||
},
|
||||
}
|
||||
|
||||
function getRarityLevel(rarity: number) {
|
||||
export function getRarityLevel(rarity: number) {
|
||||
switch (true) {
|
||||
case rarity > RarityLevels[RarityLevel.VeryCommon].threshold:
|
||||
return RarityLevels[RarityLevel.VeryCommon]
|
||||
|
@ -2,21 +2,45 @@ import Column from 'components/Column'
|
||||
import Row from 'components/Row'
|
||||
import { Trait } from 'nft/types'
|
||||
import { formatEth } from 'nft/utils'
|
||||
import qs from 'qs'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { RarityGraph } from './RarityGraph'
|
||||
import { getRarityLevel, RarityGraph } from './RarityGraph'
|
||||
|
||||
const SubheaderTiny = styled.div`
|
||||
const TraitRowLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const SubheaderTiny = styled.div<{ $color?: string }>`
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
color: ${({ theme, $color }) => ($color ? $color : theme.textSecondary)};
|
||||
`
|
||||
|
||||
const TraitValue = styled(Column)`
|
||||
const SubheaderTinyHidden = styled(SubheaderTiny)`
|
||||
opacity: 0;
|
||||
`
|
||||
|
||||
const TraitRowContainer = styled(Row)`
|
||||
padding: 12px 18px 12px 0px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.hoverDefault};
|
||||
${SubheaderTinyHidden} {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const TraitColumnValue = styled(Column)<{ $flex?: number; $alignItems?: string }>`
|
||||
gap: 4px;
|
||||
flex: 3;
|
||||
flex: ${({ $flex }) => $flex ?? 3};
|
||||
align-items: ${({ $alignItems }) => $alignItems};
|
||||
`
|
||||
|
||||
const TraitRowValue = styled(ThemedText.BodySmall)<{ $flex?: number; $justifyContent?: string }>`
|
||||
@ -27,21 +51,31 @@ const TraitRowValue = styled(ThemedText.BodySmall)<{ $flex?: number; $justifyCon
|
||||
justify-content: ${({ $justifyContent }) => $justifyContent};
|
||||
`
|
||||
|
||||
export const TraitRow = ({ trait }: { trait: Trait }) => {
|
||||
// TODO: Replace with actual rarity, count, and floor price when BE supports
|
||||
export const TraitRow = ({ trait, collectionAddress }: { trait: Trait; collectionAddress: string }) => {
|
||||
// TODO(NFT-1189): Replace with actual rarity, count, and floor price when BE supports
|
||||
// rarity eventually should be number of items with this trait / total number of items, smaller rarity means more rare
|
||||
const randomRarity = Math.random()
|
||||
const rarityLevel = getRarityLevel(randomRarity)
|
||||
const params = qs.stringify(
|
||||
{ traits: [`("${trait.trait_type}","${trait.trait_value}")`] },
|
||||
{
|
||||
arrayFormat: 'comma',
|
||||
}
|
||||
)
|
||||
return (
|
||||
<Row padding="12px 18px 12px 0px">
|
||||
<TraitValue>
|
||||
<SubheaderTiny>{trait.trait_type}</SubheaderTiny>
|
||||
<ThemedText.BodyPrimary lineHeight="20px">{trait.trait_value}</ThemedText.BodyPrimary>
|
||||
</TraitValue>
|
||||
<TraitRowValue $flex={2}>{formatEth(randomRarity * 1000)} ETH</TraitRowValue>
|
||||
<TraitRowValue>{Math.round(randomRarity * 10000)}</TraitRowValue>
|
||||
<TraitRowValue $flex={1.5} $justifyContent="flex-end">
|
||||
<RarityGraph trait={trait} rarity={randomRarity} />
|
||||
</TraitRowValue>
|
||||
</Row>
|
||||
<TraitRowLink to={`/nfts/collection/${collectionAddress}?${params}`}>
|
||||
<TraitRowContainer>
|
||||
<TraitColumnValue>
|
||||
<SubheaderTiny>{trait.trait_type}</SubheaderTiny>
|
||||
<ThemedText.BodyPrimary lineHeight="20px">{trait.trait_value}</ThemedText.BodyPrimary>
|
||||
</TraitColumnValue>
|
||||
<TraitRowValue $flex={2}>{formatEth(randomRarity * 1000)} ETH</TraitRowValue>
|
||||
<TraitRowValue>{Math.round(randomRarity * 10000)}</TraitRowValue>
|
||||
<TraitColumnValue $flex={1.5} $alignItems="flex-end">
|
||||
<SubheaderTinyHidden $color={rarityLevel.color}>{rarityLevel.caption}</SubheaderTinyHidden>
|
||||
<RarityGraph trait={trait} rarity={randomRarity} />
|
||||
</TraitColumnValue>
|
||||
</TraitRowContainer>
|
||||
</TraitRowLink>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user