feat: various improvements to banner (#5317)

* initial commit

* chore: responsive improvements

* feat: responsive carousel

* chore: fix carousel

* chore: less magic, components

* chore: bump friction a bit to compensate for carousel changes

* fix: position of loading bubbles

* chore: fix max-width on fullscreen breakpoints not taking 100 space

* fix full screen max-width

* chore: remove first card offset
This commit is contained in:
Mike Grabowski 2022-11-21 19:14:20 +01:00 committed by GitHub
parent 6aac978754
commit 7465a0e999
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 128 additions and 129 deletions

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.64864 2L1 7.65256L10.5 17.1487L20 7.65256L14.3514 2H6.64864ZM6.13513 5.59458C8.5352 3.18398 12.4648 3.18396 14.8649 5.59456L16.9189 7.64866L14.8649 9.70272C12.4648 12.1133 8.5352 12.1133 6.13513 9.70274L4.08109 7.64866L6.13513 5.59458ZM7.54702 7.64848C7.54702 9.27987 8.86966 10.6012 10.4997 10.6012C12.1298 10.6012 13.4524 9.27987 13.4524 7.64848C13.4524 6.01708 12.1298 4.69576 10.4997 4.69576C8.86966 4.69576 7.54702 6.01708 7.54702 7.64848ZM10.4997 8.93225C9.791 8.93225 9.21593 8.35778 9.21593 7.64848C9.21593 6.93917 9.791 6.3647 10.4997 6.3647C11.2084 6.3647 11.7835 6.93917 11.7835 7.64848C11.7835 8.35778 11.2084 8.93225 10.4997 8.93225Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 820 B

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 1C5.0302 1 1 5.0302 1 10C1 14.9698 5.0302 19 10 19C14.9698 19 19 14.9698 19 10C19 5.0302 14.9716 1 10 1ZM5.4406 10.3024L5.4784 10.2412L7.8202 6.5782C7.8544 6.526 7.9354 6.5314 7.9606 6.589C8.3512 7.4656 8.6896 8.5564 8.5312 9.235C8.4646 9.514 8.2792 9.892 8.0704 10.2412C8.0434 10.2916 8.0146 10.342 7.9822 10.3906C7.9678 10.4122 7.9426 10.4248 7.9156 10.4248H5.509C5.4442 10.4248 5.4064 10.3546 5.4406 10.3024ZM15.8752 11.5624C15.8752 11.5966 15.8554 11.6254 15.8266 11.638C15.6448 11.7154 15.0238 12.0016 14.7664 12.3598C14.1076 13.276 13.6054 14.5864 12.4804 14.5864H7.7896C6.1264 14.5864 4.78 13.2346 4.78 11.566V11.512C4.78 11.4688 4.816 11.4328 4.861 11.4328H7.4746C7.5268 11.4328 7.5646 11.4796 7.561 11.5318C7.5412 11.701 7.5736 11.8756 7.6546 12.034C7.8094 12.349 8.1316 12.5452 8.479 12.5452H9.7732V11.5354H8.4934C8.4286 11.5354 8.389 11.4598 8.4268 11.4058C8.4412 11.3842 8.4556 11.3626 8.4736 11.3374C8.5942 11.1646 8.767 10.8982 8.9398 10.594C9.0568 10.3888 9.1702 10.1692 9.262 9.9496C9.28 9.91 9.2944 9.8686 9.3106 9.829C9.3358 9.7588 9.361 9.6922 9.379 9.6274C9.397 9.5716 9.4132 9.514 9.4276 9.46C9.4708 9.2728 9.4888 9.0748 9.4888 8.8696C9.4888 8.7886 9.4852 8.704 9.478 8.6248C9.4744 8.5366 9.4636 8.4484 9.4528 8.3602C9.4456 8.2828 9.4312 8.2054 9.4168 8.1262C9.397 8.0092 9.3718 7.8922 9.343 7.7752L9.3322 7.7302C9.3106 7.6492 9.2908 7.5736 9.2656 7.4926C9.1918 7.2406 9.109 6.994 9.019 6.7636C8.9866 6.6718 8.9506 6.5836 8.9128 6.4972C8.8588 6.364 8.803 6.2434 8.7526 6.13C8.7256 6.0778 8.704 6.031 8.6824 5.9824C8.6572 5.9284 8.632 5.8744 8.605 5.8222C8.587 5.7826 8.5654 5.7448 8.551 5.7088L8.3926 5.4172C8.371 5.3776 8.407 5.329 8.4502 5.3416L9.4402 5.6098H9.4438C9.4456 5.6098 9.4456 5.6098 9.4474 5.6098L9.577 5.6476L9.721 5.6872L9.7732 5.7016V5.1148C9.7732 4.8304 10 4.6 10.2826 4.6C10.423 4.6 10.5508 4.6576 10.6408 4.7512C10.7326 4.8448 10.7902 4.9726 10.7902 5.1148V5.9878L10.8964 6.0166C10.9036 6.0202 10.9126 6.0238 10.9198 6.0292C10.945 6.0472 10.9828 6.076 11.0296 6.112C11.0674 6.1408 11.107 6.1768 11.1538 6.2146C11.2492 6.292 11.3644 6.391 11.4886 6.5044C11.521 6.5332 11.5534 6.562 11.584 6.5926C11.7442 6.742 11.9242 6.9166 12.097 7.111C12.1456 7.1668 12.1924 7.2208 12.241 7.2802C12.2878 7.3396 12.34 7.3972 12.3832 7.4548C12.4426 7.5322 12.5038 7.6132 12.5596 7.6978C12.5848 7.7374 12.6154 7.7788 12.6388 7.8184C12.7108 7.9246 12.772 8.0344 12.8314 8.1442C12.8566 8.1946 12.8818 8.2504 12.9034 8.3044C12.97 8.452 13.0222 8.6014 13.0546 8.7526C13.0654 8.785 13.0726 8.8192 13.0762 8.8516V8.8588C13.087 8.902 13.0906 8.9488 13.0942 8.9974C13.1086 9.1504 13.1014 9.3052 13.069 9.46C13.0546 9.5248 13.0366 9.586 13.015 9.6526C12.9916 9.7156 12.97 9.7804 12.9412 9.8434C12.8854 9.9712 12.8206 10.1008 12.7432 10.2196C12.718 10.2646 12.6874 10.3114 12.6586 10.3564C12.6262 10.4032 12.592 10.4482 12.5632 10.4914C12.5218 10.5472 12.4786 10.6048 12.4336 10.657C12.394 10.711 12.3544 10.765 12.3094 10.8136C12.2482 10.8874 12.1888 10.9558 12.1258 11.0224C12.0898 11.0656 12.0502 11.1106 12.0088 11.1502C11.9692 11.1952 11.9278 11.2348 11.8918 11.2708C11.8288 11.3338 11.7784 11.3806 11.7352 11.422L11.6326 11.5138C11.6182 11.5282 11.5984 11.5354 11.5786 11.5354H10.7902V12.5452H11.782C12.0034 12.5452 12.214 12.4678 12.385 12.322C12.4426 12.2716 12.6964 12.052 12.997 11.7208C13.0078 11.7082 13.0204 11.701 13.0348 11.6974L15.7726 10.9054C15.8248 10.891 15.8752 10.9288 15.8752 10.9828V11.5624Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.8 KiB

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.146 4.52803C15.767 3.18049 13.8805 2.35 11.8 2.35C7.57502 2.35 4.15 5.77502 4.15 10C4.15 14.225 7.57502 17.65 11.8 17.65C13.8805 17.65 15.767 16.8195 17.146 15.472C15.501 17.617 12.912 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C12.912 1 15.501 2.38301 17.146 4.52803Z" fill="#5D6785"/>
<path d="M6.08317 14.3776C7.18644 15.4556 8.69563 16.12 10.36 16.12C13.74 16.12 16.48 13.38 16.48 10C16.48 6.62002 13.74 3.88 10.36 3.88C8.69563 3.88 7.18644 4.54439 6.08317 5.62243C7.39916 3.90641 9.47037 2.8 11.8 2.8C15.7765 2.8 19 6.02355 19 10C19 13.9764 15.7764 17.2 11.8 17.2C9.47037 17.2 7.39916 16.0936 6.08317 14.3776Z" fill="#5D6785"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4 10C15.4 12.9823 12.9823 15.4 10 15.4C7.01766 15.4 4.6 12.9823 4.6 10C4.6 7.01766 7.01766 4.6 10 4.6C12.9823 4.6 15.4 7.01766 15.4 10ZM13.6 10C13.6 11.9882 11.9882 13.6 10 13.6C8.01177 13.6 6.4 11.9882 6.4 10C6.4 8.01178 8.01177 6.4 10 6.4C11.9882 6.4 13.6 8.01178 13.6 10Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -6,7 +6,7 @@ import { calculateCardIndex } from 'nft/utils'
import { Suspense, useCallback, useMemo, useState } from 'react' import { Suspense, useCallback, useMemo, useState } from 'react'
import { useQuery } from 'react-query' import { useQuery } from 'react-query'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import styled, { css } from 'styled-components/macro' import styled from 'styled-components/macro'
import { opacify } from 'theme/utils' import { opacify } from 'theme/utils'
import { Carousel, LoadingCarousel } from './Carousel' import { Carousel, LoadingCarousel } from './Carousel'
@ -16,14 +16,12 @@ const BannerContainer = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
padding: 32px 16px 0 16px; padding-top: 32px;
position: relative; position: relative;
`
// Safari has issues with blur / overflow @media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
// https://stackoverflow.com/a/71353198 padding: 32px 16px 0 16px;
const fixBlurOnSafari = css` }
transform: translate3d(0, 0, 0);
` `
const AbsoluteFill = styled.div` const AbsoluteFill = styled.div`
@ -34,13 +32,15 @@ const AbsoluteFill = styled.div`
bottom: 0; bottom: 0;
` `
// Safari has issues with blur / overflow, forcing GPU rendering with `translate3d` fixes it
// https://stackoverflow.com/a/71353198
const BannerBackground = styled(AbsoluteFill)<{ backgroundImage: string }>` const BannerBackground = styled(AbsoluteFill)<{ backgroundImage: string }>`
${fixBlurOnSafari} transform: translate3d(0, 0, 0) scaleY(1.1);
background-image: ${(props) => `url(${props.backgroundImage})`}; background-image: ${(props) => `url(${props.backgroundImage})`};
filter: blur(62px); filter: blur(62px);
opacity: ${({ theme }) => (theme.darkMode ? 0.3 : 0.2)}; opacity: ${({ theme }) => (theme.darkMode ? 0.3 : 0.2)};
transform: scaleY(1.1);
` `
const PlainBackground = styled(AbsoluteFill)` const PlainBackground = styled(AbsoluteFill)`

@ -1,19 +1,8 @@
import { useWindowSize } from 'hooks/useWindowSize'
import { ChevronLeftIcon, ChevronRightIcon } from 'nft/components/icons' import { ChevronLeftIcon, ChevronRightIcon } from 'nft/components/icons'
import { calculateCardIndex, calculateFirstCardIndex, calculateRank } from 'nft/utils' import { calculateCardIndex, calculateFirstCardIndex, calculateRank } from 'nft/utils'
import { ReactNode, useCallback, useEffect, useRef, useState } from 'react' import { ReactNode, useCallback, useEffect, useRef } from 'react'
import { a, useSprings } from 'react-spring' import { a, useSprings } from 'react-spring'
import styled, { css } from 'styled-components/macro' import styled from 'styled-components/macro'
const MAX_CARD_WIDTH = 530
const carouselHeightStyle = css`
height: 296px;
@media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
height: 316px;
}
`
const CarouselContainer = styled.div` const CarouselContainer = styled.div`
display: flex; display: flex;
@ -22,38 +11,44 @@ const CarouselContainer = styled.div`
` `
const CarouselCardContainer = styled.div` const CarouselCardContainer = styled.div`
${carouselHeightStyle}
position: relative; position: relative;
width: 100%; width: 100%;
max-width: ${MAX_CARD_WIDTH}px;
overflow-x: hidden; overflow-x: hidden;
max-width: 100%;
height: 390px;
@media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
max-width: 600px;
}
` `
const CarouselItemCard = styled(a.div)` const CarouselItemCard = styled(a.div)`
${carouselHeightStyle}
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 4px 12px 32px; padding: 4px 12px 32px;
position: absolute; position: absolute;
will-change: transform; will-change: transform;
width: calc(100%);
height: calc(100%);
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) { @media screen and (min-width: ${({ theme }) => theme.breakpoint.md}px) {
padding: 4px 32px 32px; padding: 4px 32px 32px;
} }
` `
const CarouselItemIcon = styled.div` const CarouselItemIcon = styled.div`
${carouselHeightStyle}
align-items: center; align-items: center;
color: ${({ theme }) => theme.textPrimary}; color: ${({ theme }) => theme.accentAction};
cursor: pointer; cursor: pointer;
display: none; display: none;
user-select: none; user-select: none;
height: calc(100%);
padding: 4px 0 32px;
@media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { @media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
display: flex; display: flex;
} }
:hover { :hover {
opacity: ${({ theme }) => theme.opacity.hover}; opacity: ${({ theme }) => theme.opacity.hover};
} }
@ -65,53 +60,43 @@ interface CarouselProps {
toggleNextSlide: (idx: number) => void toggleNextSlide: (idx: number) => void
} }
const FIRST_CARD_OFFSET = 0 const MAX_CARD_WIDTH = 800
export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselProps) => { export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselProps) => {
const { width } = useWindowSize()
const carouselCardContainerRef = useRef<HTMLDivElement>(null)
const [cardWidth, setCardWidth] = useState(MAX_CARD_WIDTH)
useEffect(() => {
if (carouselCardContainerRef.current) {
setCardWidth(Math.min(carouselCardContainerRef.current.offsetWidth, MAX_CARD_WIDTH))
}
}, [width])
const idx = useCallback((x: number, l = children.length) => calculateCardIndex(x, l), [children]) const idx = useCallback((x: number, l = children.length) => calculateCardIndex(x, l), [children])
const getPos = useCallback( const getPos = useCallback(
(i: number, firstVis: number, firstVisIdx: number) => calculateFirstCardIndex(i, firstVis, firstVisIdx, idx), (i: number, firstVis: number, firstVisIdx: number) => calculateFirstCardIndex(i, firstVis, firstVisIdx, idx),
[idx] [idx]
) )
const [springs, set] = useSprings(children.length, (i) => ({ const [springs, set] = useSprings(children.length, (i) => ({
x: (i < children.length - 1 ? i : -1) * cardWidth + FIRST_CARD_OFFSET, x: (i < children.length - 1 ? i : -1) * MAX_CARD_WIDTH,
})) }))
const prev = useRef([0, 1]) const prev = useRef([0, 1])
const runSprings = useCallback( const runSprings = useCallback(
(y: number, vy: number) => { (y: number, vy: number) => {
const firstVis = idx(Math.floor(y / cardWidth) % children.length) const firstVis = idx(Math.floor(y / MAX_CARD_WIDTH) % children.length)
const firstVisIdx = vy < 0 ? children.length - 2 : 1 const firstVisIdx = vy < 0 ? children.length - 2 : 1
set((i) => { set((i) => {
const position = getPos(i, firstVis, firstVisIdx) const position = getPos(i, firstVis, firstVisIdx)
const prevPosition = getPos(i, prev.current[0], prev.current[1]) const prevPosition = getPos(i, prev.current[0], prev.current[1])
const rank = calculateRank(firstVis, firstVisIdx, position, children.length, y) const rank = calculateRank(firstVis, firstVisIdx, position, children.length, y)
return { return {
x: (-y % (cardWidth * children.length)) + cardWidth * rank + FIRST_CARD_OFFSET, x: (-y % (MAX_CARD_WIDTH * children.length)) + MAX_CARD_WIDTH * rank,
immediate: vy < 0 ? prevPosition > position : prevPosition < position, immediate: vy < 0 ? prevPosition > position : prevPosition < position,
config: { tension: 250, friction: 30 }, config: { tension: 250, friction: 35 },
} }
}) })
prev.current = [firstVis, firstVisIdx] prev.current = [firstVis, firstVisIdx]
}, },
[idx, getPos, set, cardWidth, children.length] [idx, getPos, set, children.length]
) )
const direction = useRef(0) const direction = useRef(0)
useEffect(() => { useEffect(() => {
runSprings(activeIndex * cardWidth, direction.current) runSprings(activeIndex * MAX_CARD_WIDTH, direction.current)
}, [activeIndex, cardWidth, runSprings]) }, [activeIndex, runSprings])
const toggleSlide = useCallback( const toggleSlide = useCallback(
(next: -1 | 1) => { (next: -1 | 1) => {
@ -135,15 +120,9 @@ export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselPro
<CarouselItemIcon onClick={() => toggleSlide(-1)}> <CarouselItemIcon onClick={() => toggleSlide(-1)}>
<ChevronLeftIcon width="16px" height="16px" /> <ChevronLeftIcon width="16px" height="16px" />
</CarouselItemIcon> </CarouselItemIcon>
<CarouselCardContainer ref={carouselCardContainerRef}> <CarouselCardContainer>
{springs.map(({ x }, i) => ( {springs.map(({ x }, i) => (
<CarouselItemCard <CarouselItemCard key={i} style={{ x }}>
key={i}
style={{
width: cardWidth,
x,
}}
>
{children[i]} {children[i]}
</CarouselItemCard> </CarouselItemCard>
))} ))}

@ -6,7 +6,7 @@ import { VerifiedIcon } from 'nft/components/icons'
import { Markets, TrendingCollection } from 'nft/types' import { Markets, TrendingCollection } from 'nft/types'
import { formatWeiToDecimal } from 'nft/utils' import { formatWeiToDecimal } from 'nft/utils'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme/components/text'
const CarouselCardContainer = styled.div` const CarouselCardContainer = styled.div`
display: flex; display: flex;
@ -14,13 +14,10 @@ const CarouselCardContainer = styled.div`
background-color: ${({ theme }) => theme.backgroundSurface}; background-color: ${({ theme }) => theme.backgroundSurface};
border: 1px solid ${({ theme }) => theme.backgroundOutline}; border: 1px solid ${({ theme }) => theme.backgroundOutline};
border-radius: 20px; border-radius: 20px;
gap: 8px;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) {
gap: 20px;
}
` `
const CarouselCardBorder = styled.div` const CarouselCardBorder = styled.div`
width: 100%; width: 100%;
position: relative; position: relative;
@ -58,12 +55,6 @@ const CarouselCardBorder = styled.div`
const CardHeaderContainer = styled.div<{ src: string }>` const CardHeaderContainer = styled.div<{ src: string }>`
position: relative; position: relative;
width: 100%;
height: 108px;
padding-top: 32px;
padding-bottom: 16px;
padding-left: 28px;
padding-right: 28px;
background-image: ${({ src }) => `url(${src})`}; background-image: ${({ src }) => `url(${src})`};
background-size: cover; background-size: cover;
background-position: center; background-position: center;
@ -71,12 +62,6 @@ const CardHeaderContainer = styled.div<{ src: string }>`
const LoadingCardHeaderContainer = styled.div` const LoadingCardHeaderContainer = styled.div`
position: relative; position: relative;
width: 100%;
height: 108px;
padding-top: 32px;
padding-bottom: 16px;
padding-left: 28px;
padding-right: 28px;
animation: ${loadingAnimation} 1.5s infinite; animation: ${loadingAnimation} 1.5s infinite;
animation-fill-mode: both; animation-fill-mode: both;
background: linear-gradient( background: linear-gradient(
@ -89,15 +74,15 @@ const LoadingCardHeaderContainer = styled.div`
background-size: 400%; background-size: 400%;
` `
const CardHeaderRow = styled.div` const CardHeaderColumn = styled.div`
position: relative; position: relative;
z-index: 1;
display: flex; display: flex;
gap: 8px; flex: 1;
align-items: center; align-items: center;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) { flex-direction: column;
gap: 12px; gap: 8px;
} padding: 40px;
z-index: 1;
` `
const CardNameRow = styled.div` const CardNameRow = styled.div`
@ -126,7 +111,7 @@ const LoadingCollectionNameContainer = styled(LoadingBubble)`
const HeaderOverlay = styled.div` const HeaderOverlay = styled.div`
position: absolute; position: absolute;
height: 108px; bottom: 0px;
top: 0px; top: 0px;
right: 0px; right: 0px;
left: 0px; left: 0px;
@ -135,16 +120,16 @@ const HeaderOverlay = styled.div`
` `
const CollectionImage = styled.img` const CollectionImage = styled.img`
width: 60px; width: 86px;
height: 60px; height: 86px;
background: ${({ theme }) => theme.accentTextLightPrimary}; background: ${({ theme }) => theme.accentTextLightPrimary};
border: 2px solid ${({ theme }) => theme.accentTextLightPrimary}; border: 2px solid ${({ theme }) => theme.accentTextLightPrimary};
border-radius: 100px; border-radius: 100px;
` `
const LoadingCollectionImage = styled.div` const LoadingCollectionImage = styled.div`
width: 60px; width: 86px;
height: 60px; height: 86px;
border-radius: 100px; border-radius: 100px;
animation: ${loadingAnimation} 1.5s infinite; animation: ${loadingAnimation} 1.5s infinite;
animation-fill-mode: both; animation-fill-mode: both;
@ -158,44 +143,42 @@ const LoadingCollectionImage = styled.div`
background-size: 400%; background-size: 400%;
` `
const CardBottomContainer = styled.div`
display: grid;
grid-template-columns: auto auto auto;
row-gap: 8px;
column-gap: 20px;
padding-right: 28px;
padding-left: 28px;
padding-bottom: 20px;
justify-content: space-between;
@media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.lg}px`}) {
row-gap: 16px;
}
`
const HeaderRow = styled.div`
color: ${({ theme }) => theme.userThemeColor};
font-size: 14px;
font-weight: 500;
line-height: 20px;
row-gap: 8px;
@media only screen and (min-width: ${({ theme }) => `${theme.breakpoint.lg}px`}) {
font-size: 16px;
line-height: 24px;
row-gap: 12px;
}
`
const LoadingTableElement = styled(LoadingBubble)` const LoadingTableElement = styled(LoadingBubble)`
width: 50px; width: 50px;
` `
const TableElement = styled.div` const TableElement = styled.div`
color: ${({ theme }) => theme.textSecondary}; display: flex;
font-size: 14px; align-items: center;
font-weight: 400; gap: 6px;
line-height: 20px; `
const FirstColumnTextWrapper = styled.div`
@media (min-width: ${({ theme }) => theme.breakpoint.sm}px) and (max-width: ${({ theme }) => theme.breakpoint.lg}px) {
display: none;
}
}
`
const CardBottomContainer = styled.div`
display: grid;
flex: 1;
gap: 8px;
grid-template-columns: auto auto auto;
padding: 16px 16px 20px;
${TableElement}:nth-child(3n-1), ${LoadingTableElement}:nth-child(3n-1) {
justify-self: center;
}
${TableElement}:nth-child(3n), ${LoadingTableElement}:nth-child(3n) {
justify-self: right;
}
`
const MarketplaceIcon = styled.img`
width: 20px;
height: 20px;
` `
interface MarketplaceRowProps { interface MarketplaceRowProps {
@ -207,12 +190,23 @@ interface MarketplaceRowProps {
export const MarketplaceRow = ({ marketplace, floorInEth, listings }: MarketplaceRowProps) => { export const MarketplaceRow = ({ marketplace, floorInEth, listings }: MarketplaceRowProps) => {
return ( return (
<> <>
<TableElement>{marketplace}</TableElement>
<TableElement> <TableElement>
{floorInEth !== undefined ? formatNumberOrString(floorInEth, NumberType.NFTTokenFloorPriceTrailingZeros) : '-'}{' '} <MarketplaceIcon src={`/nft/svgs/marketplaces/${marketplace}-grey.svg`} alt={`${marketplace} icon`} />
ETH <FirstColumnTextWrapper>
<ThemedText.BodySmall color="textSecondary">{marketplace}</ThemedText.BodySmall>
</FirstColumnTextWrapper>
</TableElement>
<TableElement>
<ThemedText.BodySmall color="textSecondary">
{floorInEth !== undefined
? formatNumberOrString(floorInEth, NumberType.NFTTokenFloorPriceTrailingZeros)
: '-'}{' '}
ETH
</ThemedText.BodySmall>
</TableElement>
<TableElement>
<ThemedText.BodySmall color="textSecondary">{listings ?? '-'}</ThemedText.BodySmall>
</TableElement> </TableElement>
<TableElement>{listings ?? '-'}</TableElement>
</> </>
) )
} }
@ -238,9 +232,22 @@ export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => {
<CarouselCardHeader collection={collection} /> <CarouselCardHeader collection={collection} />
<CardBottomContainer> <CardBottomContainer>
<> <>
<HeaderRow>Uniswap</HeaderRow> <TableElement>
<HeaderRow>{formatWeiToDecimal(collection.floor.toString())} ETH Floor</HeaderRow> <MarketplaceIcon src="/nft/svgs/marketplaces/uniswap-magenta.svg" alt="Uniswap icon" />
<HeaderRow>{gqlCollection.marketplaceCount?.reduce((acc, cur) => acc + cur.count, 0)} Listings</HeaderRow> <FirstColumnTextWrapper>
<ThemedText.SubHeaderSmall color="userThemeColor">Uniswap</ThemedText.SubHeaderSmall>
</FirstColumnTextWrapper>
</TableElement>
<TableElement>
<ThemedText.SubHeaderSmall color="userThemeColor">
{formatWeiToDecimal(collection.floor.toString())} ETH Floor
</ThemedText.SubHeaderSmall>
</TableElement>
<TableElement>
<ThemedText.SubHeaderSmall color="userThemeColor">
{gqlCollection.marketplaceCount?.reduce((acc, cur) => acc + cur.count, 0)} Listings
</ThemedText.SubHeaderSmall>
</TableElement>
{MARKETS_TO_CHECK.map((market) => { {MARKETS_TO_CHECK.map((market) => {
const marketplace = gqlCollection.marketplaceCount?.find( const marketplace = gqlCollection.marketplaceCount?.find(
(marketplace) => marketplace.marketplace === market (marketplace) => marketplace.marketplace === market
@ -280,7 +287,7 @@ const CarouselCardHeader = ({ collection }: { collection: TrendingCollection })
const theme = useTheme() const theme = useTheme()
return ( return (
<CardHeaderContainer src={collection.bannerImageUrl}> <CardHeaderContainer src={collection.bannerImageUrl}>
<CardHeaderRow> <CardHeaderColumn>
<CollectionImage src={collection.imageUrl} /> <CollectionImage src={collection.imageUrl} />
<CardNameRow> <CardNameRow>
<CollectionNameContainer> <CollectionNameContainer>
@ -294,7 +301,7 @@ const CarouselCardHeader = ({ collection }: { collection: TrendingCollection })
</IconContainer> </IconContainer>
)} )}
</CardNameRow> </CardNameRow>
</CardHeaderRow> </CardHeaderColumn>
<HeaderOverlay /> <HeaderOverlay />
</CardHeaderContainer> </CardHeaderContainer>
) )
@ -308,10 +315,10 @@ export const LoadingCarouselCard = ({ collection }: { collection?: TrendingColle
<CarouselCardHeader collection={collection} /> <CarouselCardHeader collection={collection} />
) : ( ) : (
<LoadingCardHeaderContainer> <LoadingCardHeaderContainer>
<CardHeaderRow> <CardHeaderColumn>
<LoadingCollectionImage /> <LoadingCollectionImage />
<LoadingCollectionNameContainer /> <LoadingCollectionNameContainer />
</CardHeaderRow> </CardHeaderColumn>
<HeaderOverlay /> <HeaderOverlay />
</LoadingCardHeaderContainer> </LoadingCardHeaderContainer>
)} )}

@ -12,7 +12,6 @@ const TextWrapper = styled(Text)<{ color: keyof string }>`
type TextProps = Omit<TextPropsOriginal, 'css'> type TextProps = Omit<TextPropsOriginal, 'css'>
// todo: export each component individually // todo: export each component individually
export const ThemedText = { export const ThemedText = {
// todo: there should be just one `Body` with default color, no need to make all variations // todo: there should be just one `Body` with default color, no need to make all variations
BodyPrimary(props: TextProps) { BodyPrimary(props: TextProps) {