From 155bf2e873f2c8673539e60e8f9aac28d169fdc8 Mon Sep 17 00:00:00 2001 From: Jack Short Date: Tue, 1 Nov 2022 19:15:50 -0400 Subject: [PATCH] style: updated collection cards (#5047) --- src/nft/components/collection/Card.css.ts | 22 +- src/nft/components/collection/Card.tsx | 293 ++++++++++-------- .../components/collection/CollectionAsset.tsx | 83 +++-- .../collection/CollectionAssetLoading.tsx | 5 - src/nft/components/icons.tsx | 15 +- src/nft/css/sprinkles.css.ts | 2 +- 6 files changed, 232 insertions(+), 188 deletions(-) diff --git a/src/nft/components/collection/Card.css.ts b/src/nft/components/collection/Card.css.ts index ce4ba2e9d2..8db3e77aa3 100644 --- a/src/nft/components/collection/Card.css.ts +++ b/src/nft/components/collection/Card.css.ts @@ -7,6 +7,8 @@ export const card = style([ overflow: 'hidden', borderStyle: 'solid', borderWidth: '1px', + paddingBottom: '12', + boxShadow: 'shallow', }), { boxSizing: 'border-box', @@ -20,9 +22,6 @@ export const card = style([ }, }, }, - ':hover': { - boxShadow: themeVars.shadows.deep, - }, }, ]) @@ -34,8 +33,13 @@ export const notSelectedCard = style([ card, sprinkles({ backgroundColor: 'backgroundSurface', - borderColor: 'transparent', + borderColor: 'backgroundOutline', }), + { + ':hover': { + backgroundColor: themeVars.colors.stateOverlayHover, + }, + }, ]) export const cardImageHover = style({ @@ -45,9 +49,15 @@ export const cardImageHover = style({ export const selectedCard = style([ card, sprinkles({ - background: 'lightGrayOverlay', - borderColor: 'backgroundOutline', + background: 'backgroundSurface', + borderColor: 'accentAction', + borderWidth: '3px', }), + { + ':hover': { + backgroundColor: themeVars.colors.stateOverlayHover, + }, + }, ]) export const button = style([ diff --git a/src/nft/components/collection/Card.tsx b/src/nft/components/collection/Card.tsx index 9436bb4045..7ed0e1c3fa 100644 --- a/src/nft/components/collection/Card.tsx +++ b/src/nft/components/collection/Card.tsx @@ -1,18 +1,17 @@ import clsx from 'clsx' -import Column from 'components/Column' import { MouseoverTooltip } from 'components/Tooltip' import { Box } from 'nft/components/Box' import { Row } from 'nft/components/Flex' import { + ChevronRightIcon, MinusIconLarge, PauseButtonIcon, PlayButtonIcon, PlusIconLarge, PoolIcon, RarityVerifiedIcon, - SuspiciousIcon20, } from 'nft/components/icons' -import { body, bodySmall, subheadSmall } from 'nft/css/common.css' +import { body, bodySmall } from 'nft/css/common.css' import { themeVars } from 'nft/css/sprinkles.css' import { useIsMobile } from 'nft/hooks' import { GenieAsset, Rarity, UniformHeight, UniformHeights } from 'nft/types' @@ -28,6 +27,9 @@ import { useRef, useState, } from 'react' +import { AlertTriangle } from 'react-feather' +import styled from 'styled-components/macro' +import { ThemedText } from 'theme' import * as styles from './Card.css' @@ -38,6 +40,8 @@ export interface CardContextProps { selected: boolean href: string setHref: (href: string) => void + addAssetToBag: () => void + removeAssetFromBag: () => void } const CardContext = createContext(undefined) @@ -50,14 +54,85 @@ const useCardContext = () => { const baseHref = (asset: GenieAsset) => `/#/nfts/asset/${asset.address}/${asset.tokenId}?origin=collection` +const DetailsLinkContainer = styled.a` + display: flex; + flex-shrink: 0; + text-decoration: none; + color: ${({ theme }) => theme.textSecondary}; + font-size: 14px; + line-height: 20px; + weight: 400; + + :hover { + color: ${({ theme }) => theme.accentAction}; + } +` + +const SuspiciousIcon = styled(AlertTriangle)` + width: 16px; + height: 16px; + color: ${({ theme }) => theme.accentFailure}; +` + +const Erc1155ControlsRow = styled.div` + position: absolute; + display: flex; + width: 100%; + bottom: 12px; + z-index: 2; + justify-content: center; +` + +const Erc1155ControlsContainer = styled.div` + display: flex; + border: 1px solid ${({ theme }) => theme.backgroundOutline}; + border-radius: 12px 12px 12px 12px; + overflow: hidden; +` + +const Erc1155ControlsDisplay = styled(ThemedText.HeadlineSmall)` + display: flex; + padding: 6px 8px; + width: 60px; + background: ${({ theme }) => theme.backgroundBackdrop}; + justify-content: center; + cursor: default; +` + +const Erc1155ControlsInput = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 40px; + background: ${({ theme }) => theme.backgroundInteractive}; + color: ${({ theme }) => theme.textPrimary}; + + :hover { + color: ${({ theme }) => theme.accentAction}; + } +` + +const RankingContainer = styled.div` + position: absolute; + top: 12px; + left: 12px; + z-index: 2; +` + +const StyledImageContainer = styled.div` + position: relative; +` + /* -------- ASSET CARD -------- */ interface CardProps { asset: GenieAsset selected: boolean + addAssetToBag: () => void + removeAssetFromBag: () => void children: ReactNode } -const Container = ({ asset, selected, children }: CardProps) => { +const Container = ({ asset, selected, addAssetToBag, removeAssetFromBag, children }: CardProps) => { const [hovered, toggleHovered] = useReducer((s) => !s, false) const [href, setHref] = useState(baseHref(asset)) @@ -69,8 +144,10 @@ const Container = ({ asset, selected, children }: CardProps) => { toggleHovered, href, setHref, + addAssetToBag, + removeAssetFromBag, }), - [asset, hovered, selected, href] + [asset, hovered, selected, href, addAssetToBag, removeAssetFromBag] ) const assetRef = useRef(null) @@ -82,16 +159,21 @@ const Container = ({ asset, selected, children }: CardProps) => { return ( toggleHovered()} onMouseLeave={() => toggleHovered()} transition="250" + cursor={asset.notForSale ? 'default' : 'pointer'} + onClick={(e: MouseEvent) => { + if (!asset.notForSale) { + e.preventDefault() + !selected ? addAssetToBag() : removeAssetFromBag() + } + }} > {children} @@ -99,6 +181,10 @@ const Container = ({ asset, selected, children }: CardProps) => { ) } +const ImageContainer = ({ children }: { children: ReactNode }) => ( + {children} +) + /* -------- CARD IMAGE -------- */ interface ImageProps { uniformHeight: UniformHeight @@ -365,10 +451,14 @@ const InfoContainer = ({ children }: { children: ReactNode }) => { ) } -const PrimaryRow = ({ children }: { children: ReactNode }) => {children} +const PrimaryRow = ({ children }: { children: ReactNode }) => ( + + {children} + +) const PrimaryDetails = ({ children }: { children: ReactNode }) => ( - + {children} ) @@ -425,100 +515,35 @@ const TertiaryInfo = ({ children }: { children: ReactNode }) => { ) } -interface ButtonProps { - children: ReactNode - quantity: number - selectedChildren: ReactNode - onClick: (e: MouseEvent) => void - onSelectedClick: (e: MouseEvent) => void +interface Erc1155ControlsInterface { + quantity: string } -const Button = ({ children, quantity, selectedChildren, onClick, onSelectedClick }: ButtonProps) => { - const [buttonHovered, toggleButtonHovered] = useReducer((s) => !s, false) - const { asset, selected, setHref } = useCardContext() - const buttonRef = useRef(null) - const isMobile = useIsMobile() - - useLayoutEffect(() => { - if (buttonHovered && buttonRef.current?.matches(':hover') === false) toggleButtonHovered() - }, [buttonHovered]) +const Erc1155Controls = ({ quantity }: Erc1155ControlsInterface) => { + const { addAssetToBag, removeAssetFromBag } = useCardContext() return ( - <> - {!selected || asset.tokenType !== 'ERC1155' ? ( - - selected - ? onSelectedClick(e) - : asset.notForSale - ? () => { - return true - } - : onClick(e) - } - onMouseEnter={() => { - !asset.notForSale && setHref('') - !buttonHovered && toggleButtonHovered() + + + { + e.stopPropagation() + removeAssetFromBag() }} - onMouseLeave={() => { - !asset.notForSale && setHref(baseHref(asset)) - buttonHovered && toggleButtonHovered() - }} - transition="250" > - {selected - ? selectedChildren - : asset.notForSale - ? buttonHovered || isMobile - ? 'See details' - : 'Not for sale' - : children} - - ) : ( - - ) => onSelectedClick(e)} - > - - - {quantity.toString()} - ) => onClick(e)} - > - - - - )} - + + + {quantity} + { + e.stopPropagation() + addAssetToBag() + }} + > + + + + ) } @@ -533,6 +558,22 @@ const MarketplaceIcon = ({ marketplace }: { marketplace: string }) => { ) } +const DetailsLink = () => { + const { asset } = useCardContext() + + return ( + { + e.stopPropagation() + }} + > + Details + + + ) +} + /* -------- RANKING CARD -------- */ interface RankingProps { rarity: Rarity @@ -545,31 +586,33 @@ const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps) const { asset } = useCardContext() return ( - - - cardLogo + + + + cardLogo + + + {rarityVerified + ? `Verified by ${asset.collectionName}` + : `Ranking by ${rarity.primaryProvider === 'Genie' ? fallbackProvider : rarity.primaryProvider}`} + + + } + placement="top" + > + + + {putCommas(provider.rank)} - - {rarityVerified - ? `Verified by ${asset.collectionName}` - : `Ranking by ${rarity.primaryProvider === 'Genie' ? fallbackProvider : rarity.primaryProvider}`} - - - } - placement="top" - > - - - {putCommas(provider.rank)} - - - {rarityVerified ? : null} + + {rarityVerified ? : null} + - - + + ) } const SUSPICIOUS_TEXT = 'Blocked on OpenSea' @@ -577,8 +620,8 @@ const SUSPICIOUS_TEXT = 'Blocked on OpenSea' const Suspicious = () => { return ( {SUSPICIOUS_TEXT}} placement="top"> - - + + ) @@ -632,7 +675,7 @@ const NoContentContainer = ({ uniformHeight }: NoContentContainerProps) => ( width="full" style={{ paddingTop: '100%', - background: `linear-gradient(270deg, ${themeVars.colors.backgroundOutline} 0%, ${themeVars.colors.backgroundSurface} 100%)`, + background: `linear-gradient(90deg, ${themeVars.colors.backgroundSurface} 0%, ${themeVars.colors.backgroundInteractive} 95.83%)`, }} > ( export { Audio, - Button, Container, DetailsContainer, + DetailsLink, + Erc1155Controls, Image, + ImageContainer, InfoContainer, MarketplaceIcon, Pool, diff --git a/src/nft/components/collection/CollectionAsset.tsx b/src/nft/components/collection/CollectionAsset.tsx index b29b156e31..0bcc348769 100644 --- a/src/nft/components/collection/CollectionAsset.tsx +++ b/src/nft/components/collection/CollectionAsset.tsx @@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber' import { useBag } from 'nft/hooks' import { GenieAsset, Markets, UniformHeight } from 'nft/types' import { formatWeiToDecimal, isAudio, isVideo, rarityProviderLogo } from 'nft/utils' -import { MouseEvent, useMemo } from 'react' +import { useMemo } from 'react' import * as Card from './Card' @@ -73,24 +73,45 @@ export const CollectionAsset = ({ }, [asset]) return ( - - {assetMediaType === AssetMediaType.Image ? ( - - ) : assetMediaType === AssetMediaType.Video ? ( - - ) : ( - - )} + { + addAssetsToBag([asset]) + !bagExpanded && !isMobile && toggleBag() + }} + removeAssetFromBag={() => { + removeAssetsFromBag([asset]) + }} + > + + {asset.tokenType === 'ERC1155' && quantity > 0 && } + {asset.rarity && provider && provider.rank && ( + + )} + {assetMediaType === AssetMediaType.Image ? ( + + ) : assetMediaType === AssetMediaType.Video ? ( + + ) : ( + + )} + @@ -98,14 +119,7 @@ export const CollectionAsset = ({ {asset.name ? asset.name : `#${asset.tokenId}`} {asset.susFlag && } - {asset.rarity && provider && provider.rank && ( - - )} + @@ -119,21 +133,6 @@ export const CollectionAsset = ({ )} - { - e.preventDefault() - addAssetsToBag([asset]) - !bagExpanded && !isMobile && toggleBag() - }} - onSelectedClick={(e: MouseEvent) => { - e.preventDefault() - removeAssetsFromBag([asset]) - }} - > - {'Buy now'} - ) diff --git a/src/nft/components/collection/CollectionAssetLoading.tsx b/src/nft/components/collection/CollectionAssetLoading.tsx index 95e0e0c304..afa6cffa67 100644 --- a/src/nft/components/collection/CollectionAssetLoading.tsx +++ b/src/nft/components/collection/CollectionAssetLoading.tsx @@ -14,14 +14,9 @@ export const CollectionAssetLoading = () => { - - - - - ) diff --git a/src/nft/components/icons.tsx b/src/nft/components/icons.tsx index f91711a49d..3ebeadc120 100644 --- a/src/nft/components/icons.tsx +++ b/src/nft/components/icons.tsx @@ -1239,20 +1239,15 @@ export const SuspiciousIcon20 = (props: SVGProps) => ( ) export const MinusIconLarge = (props: SVGProps) => ( - - + + ) export const PlusIconLarge = (props: SVGProps) => ( - - + + + ) diff --git a/src/nft/css/sprinkles.css.ts b/src/nft/css/sprinkles.css.ts index 9ea0982fed..9be67981f3 100644 --- a/src/nft/css/sprinkles.css.ts +++ b/src/nft/css/sprinkles.css.ts @@ -265,7 +265,7 @@ const flexAlignment = [ const overflow = ['hidden', 'inherit', 'scroll', 'visible', 'auto'] as const -const borderWidth = ['0px', '0.5px', '1px', '1.5px', '2px', '4px'] +const borderWidth = ['0px', '0.5px', '1px', '1.5px', '2px', '3px', '4px'] const borderStyle = ['none', 'solid'] as const