Commit d5685156 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

feat: mobile filters menu (#5163)

* header and scroll

* allow sweep buy now off

* generic filter row header

* begin building sort dropdown

* add file

* working checkmark

* remove icons

* updating scroll to work with mobile

* prevent scorlling behind menu

* hover styles

* remove console.log

* respond to comments

* revert null

* styled component header

* filter item styled component

* padding for items, slider, and inputs

* fixed scroll on mobile
Co-authored-by: default avatarAlex Ball <alex.ball@uniswap.org>
parent 5325b5f8
......@@ -33,13 +33,19 @@ const IconWrapper = styled.button`
display: flex;
padding: 2px;
opacity: 1;
transition: 125ms ease opacity;
:hover {
opacity: 0.6;
&:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
:active {
opacity: 0.4;
&:active {
opacity: ${({ theme }) => theme.opacity.click};
}
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `opacity ${duration.medium} ${timing.ease}`};
`
interface BagHeaderProps {
numberOfAssets: number
......
......@@ -18,10 +18,9 @@ import { CollectionAsset } from 'nft/components/collection/CollectionAsset'
import * as styles from 'nft/components/collection/CollectionNfts.css'
import { SortDropdown } from 'nft/components/common/SortDropdown'
import { Center, Column, Row } from 'nft/components/Flex'
import { NonRarityIcon, RarityIcon, SweepIcon } from 'nft/components/icons'
import { SweepIcon } from 'nft/components/icons'
import { bodySmall, buttonTextMedium, headlineMedium } from 'nft/css/common.css'
import { loadingAsset } from 'nft/css/loading.css'
import { vars } from 'nft/css/sprinkles.css'
import {
CollectionFilters,
initialCollectionFilterState,
......@@ -57,8 +56,6 @@ interface CollectionNftsProps {
}
const rarityStatusCache = new Map<string, boolean>()
const nonRarityIcon = <NonRarityIcon width="20" height="20" viewBox="2 2 22 22" color={vars.color.blue400} />
const rarityIcon = <RarityIcon width="20" height="20" viewBox="2 2 24 24" color={vars.color.blue400} />
const ActionsContainer = styled.div`
display: flex;
......@@ -199,6 +196,39 @@ export const CollectionNftsAndMenuLoading = () => (
</Column>
)
export const getSortDropdownOptions = (setSortBy: (sortBy: SortBy) => void, hasRarity: boolean): DropDownOption[] => {
const options = [
{
displayText: 'Price: Low to High',
onClick: () => setSortBy(SortBy.LowToHigh),
reverseIndex: 2,
sortBy: SortBy.LowToHigh,
},
{
displayText: 'Price: High to Low',
onClick: () => setSortBy(SortBy.HighToLow),
reverseIndex: 1,
sortBy: SortBy.HighToLow,
},
]
return hasRarity
? options.concat([
{
displayText: 'Rarity: Rare to Common',
onClick: () => setSortBy(SortBy.RareToCommon),
reverseIndex: 4,
sortBy: SortBy.RareToCommon,
},
{
displayText: 'Rarity: Common to Rare',
onClick: () => setSortBy(SortBy.CommonToRare),
reverseIndex: 3,
sortBy: SortBy.CommonToRare,
},
])
: options
}
export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => {
const { chainId } = useWeb3React()
const traits = useCollectionFilters((state) => state.traits)
......@@ -223,6 +253,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
const reset = useCollectionFilters((state) => state.reset)
const setMin = useCollectionFilters((state) => state.setMinPrice)
const setMax = useCollectionFilters((state) => state.setMaxPrice)
const setHasRarity = useCollectionFilters((state) => state.setHasRarity)
const toggleBag = useBag((state) => state.toggleBag)
const bagExpanded = useBag((state) => state.bagExpanded)
......@@ -272,51 +303,14 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
setIsCollectionNftsLoading(isLoadingNext)
}, [isLoadingNext, setIsCollectionNftsLoading])
const hasRarity = getRarityStatus(rarityStatusCache, collectionStats?.address, collectionNfts)
const hasRarity = useMemo(() => {
const hasRarity = getRarityStatus(rarityStatusCache, collectionStats?.address, collectionNfts) ?? false
setHasRarity(hasRarity)
return hasRarity
}, [collectionStats.address, collectionNfts, setHasRarity])
const sortDropDownOptions: DropDownOption[] = useMemo(
() =>
hasRarity
? [
{
displayText: 'Low to High',
onClick: () => setSortBy(SortBy.LowToHigh),
icon: nonRarityIcon,
reverseIndex: 2,
},
{
displayText: 'High to Low',
onClick: () => setSortBy(SortBy.HighToLow),
icon: nonRarityIcon,
reverseIndex: 1,
},
{
displayText: 'Rare to Common',
onClick: () => setSortBy(SortBy.RareToCommon),
icon: rarityIcon,
reverseIndex: 4,
},
{
displayText: 'Common to Rare',
onClick: () => setSortBy(SortBy.CommonToRare),
icon: rarityIcon,
reverseIndex: 3,
},
]
: [
{
displayText: 'Low to High',
onClick: () => setSortBy(SortBy.LowToHigh),
icon: nonRarityIcon,
reverseIndex: 2,
},
{
displayText: 'High to Low',
onClick: () => setSortBy(SortBy.HighToLow),
icon: nonRarityIcon,
reverseIndex: 1,
},
],
() => getSortDropdownOptions(setSortBy, hasRarity),
[hasRarity, setSortBy]
)
......@@ -436,10 +430,10 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
{!hasErc1155s ? (
<SweepButton
toggled={sweepIsOpen}
disabled={!buyNow}
disabled={hasErc1155s}
className={buttonTextMedium}
onClick={() => {
if (!buyNow || hasErc1155s) return
if (hasErc1155s) return
if (!sweepIsOpen) {
scrollToTop()
if (!bagExpanded && !isMobile) toggleBag()
......
......@@ -402,6 +402,7 @@ export const CollectionStats = ({ stats, isMobile }: { stats: GenieCollection; i
{(stats.description || isCollectionStatsLoading) && isMobile && (
<CollectionDescription description={stats.description ?? ''} />
)}
<div id="nft-anchor-mobile" />
<StatsRow isMobile display={{ sm: 'flex', md: 'none' }} stats={stats} marginTop="20" marginBottom="12" />
</Box>
)
......
......@@ -8,8 +8,12 @@ import { subhead } from 'nft/css/common.css'
import { useCollectionFilters } from 'nft/hooks'
import { Trait } from 'nft/hooks/useCollectionFilters'
import { TraitPosition } from 'nft/hooks/useTraitsOpen'
import { useReducer } from 'react'
import { DropDownOption } from 'nft/types'
import { useMemo, useReducer } from 'react'
import { isMobile } from 'utils/userAgent'
import { FilterSortDropdown } from '../common/SortDropdown'
import { getSortDropdownOptions } from './CollectionNfts'
import { TraitSelect } from './TraitSelect'
export const Filters = ({ traitsByGroup }: { traitsByGroup: Record<string, Trait[]> }) => {
......@@ -17,12 +21,19 @@ export const Filters = ({ traitsByGroup }: { traitsByGroup: Record<string, Trait
buyNow: state.buyNow,
setBuyNow: state.setBuyNow,
}))
const setSortBy = useCollectionFilters((state) => state.setSortBy)
const hasRarity = useCollectionFilters((state) => state.hasRarity)
const [buyNowHovered, toggleBuyNowHover] = useReducer((state) => !state, false)
const handleBuyNowToggle = () => {
setBuyNow(!buyNow)
}
const sortDropDownOptions: DropDownOption[] = useMemo(
() => getSortDropdownOptions(setSortBy, hasRarity ?? false),
[hasRarity, setSortBy]
)
return (
<Box className={styles.container}>
<Row width="full" justifyContent="space-between"></Row>
......@@ -47,6 +58,7 @@ export const Filters = ({ traitsByGroup }: { traitsByGroup: Record<string, Trait
<span />
</Checkbox>
</Row>
{isMobile && <FilterSortDropdown sortDropDownOptions={sortDropDownOptions} />}
<MarketplaceSelect />
<PriceRange />
{Object.entries(traitsByGroup).length > 0 && (
......
......@@ -9,9 +9,21 @@ import { subheadSmall } from 'nft/css/common.css'
import { useCollectionFilters } from 'nft/hooks/useCollectionFilters'
import { TraitPosition, useTraitsOpen } from 'nft/hooks/useTraitsOpen'
import { FormEvent, useEffect, useMemo, useReducer, useState } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { Checkbox } from '../layout/Checkbox'
const FilterItemWrapper = styled(Row)`
justify-content: space-between;
padding: 10px 16px 10px 12px;
cursor: pointer;
border-radius: 12px;
&:hover {
background: ${({ theme }) => theme.backgroundInteractive};
}
`
export const MARKETPLACE_ITEMS = {
looksrare: 'LooksRare',
nft20: 'NFT20',
......@@ -22,6 +34,23 @@ export const MARKETPLACE_ITEMS = {
sudoswap: 'SudoSwap',
}
export const FilterItem = ({
title,
element,
onClick,
}: {
title: string
element: JSX.Element
onClick: React.MouseEventHandler<HTMLElement>
}) => {
return (
<FilterItemWrapper onClick={onClick}>
<ThemedText.BodyPrimary>{title}</ThemedText.BodyPrimary>
<ThemedText.SubHeaderSmall>{element}</ThemedText.SubHeaderSmall>
</FilterItemWrapper>
)
}
const MarketplaceItem = ({
title,
value,
......@@ -54,67 +83,32 @@ const MarketplaceItem = ({
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.MARKETPLACE })
}
return (
<Row
key={value}
justifyContent="space-between"
maxWidth="full"
overflowX={'hidden'}
overflowY={'hidden'}
fontWeight="normal"
className={`${subheadSmall} ${styles.subRowHover}`}
paddingLeft="12"
paddingRight="16"
borderRadius="12"
cursor="pointer"
maxHeight="44"
style={{ paddingBottom: '22px', paddingTop: '22px' }}
onMouseEnter={toggleHover}
onMouseLeave={toggleHover}
onClick={handleCheckbox}
>
<Box as="span" fontSize="14" fontWeight="normal">
{title}{' '}
const checkbox = (
<Checkbox checked={isCheckboxSelected} hovered={hovered} onChange={handleCheckbox}>
<Box as="span" color="textSecondary" marginLeft="4" paddingRight="12">
{count}
</Box>
<Checkbox checked={isCheckboxSelected} hovered={hovered} onChange={handleCheckbox}>
<Box as="span" color="textSecondary" marginLeft="4" paddingRight={'12'}>
{count}
</Box>
</Checkbox>
</Row>
</Checkbox>
)
}
export const MarketplaceSelect = () => {
const {
addMarket,
removeMarket,
markets: selectedMarkets,
marketCount,
} = useCollectionFilters(({ markets, marketCount, removeMarket, addMarket }) => ({
markets,
marketCount,
removeMarket,
addMarket,
}))
const [isOpen, setOpen] = useState(!!selectedMarkets.length)
const setTraitsOpen = useTraitsOpen((state) => state.setTraitsOpen)
const MarketplaceItems = useMemo(
() =>
Object.entries(MARKETPLACE_ITEMS).map(([value, title]) => (
<MarketplaceItem
key={value}
title={title}
value={value}
count={marketCount?.[value] || 0}
{...{ addMarket, removeMarket, isMarketSelected: selectedMarkets.includes(value) }}
/>
)),
[addMarket, marketCount, removeMarket, selectedMarkets]
return (
<div key={value} onMouseEnter={toggleHover} onMouseLeave={toggleHover}>
<FilterItem title={title} element={checkbox} onClick={handleCheckbox} />
</div>
)
}
export const FilterDropdown = ({
title,
items,
onClick,
isOpen,
}: {
title: string
items: JSX.Element[]
onClick: React.MouseEventHandler<HTMLElement>
isOpen: boolean
}) => {
return (
<>
<Box className={styles.detailsOpen} opacity={isOpen ? '1' : '0'} />
......@@ -137,13 +131,9 @@ export const MarketplaceSelect = () => {
lineHeight="20"
borderRadius="12"
maxHeight="48"
onClick={(e) => {
e.preventDefault()
setOpen(!isOpen)
setTraitsOpen(TraitPosition.MARKPLACE_INDEX, !isOpen)
}}
onClick={onClick}
>
Marketplaces
{title}
<Box display="flex" alignItems="center">
<Box
className={styles.chevronContainer}
......@@ -156,9 +146,48 @@ export const MarketplaceSelect = () => {
</Box>
</Box>
<Column className={styles.filterDropDowns} paddingBottom="8" paddingLeft="0">
{MarketplaceItems}
{items}
</Column>
</Box>
</>
)
}
export const MarketplaceSelect = () => {
const {
addMarket,
removeMarket,
markets: selectedMarkets,
marketCount,
} = useCollectionFilters(({ markets, marketCount, removeMarket, addMarket }) => ({
markets,
marketCount,
removeMarket,
addMarket,
}))
const [isOpen, setOpen] = useState(!!selectedMarkets.length)
const setTraitsOpen = useTraitsOpen((state) => state.setTraitsOpen)
const MarketplaceItems = useMemo(
() =>
Object.entries(MARKETPLACE_ITEMS).map(([value, title]) => (
<MarketplaceItem
key={value}
title={title}
value={value}
count={marketCount?.[value] || 0}
{...{ addMarket, removeMarket, isMarketSelected: selectedMarkets.includes(value) }}
/>
)),
[addMarket, marketCount, removeMarket, selectedMarkets]
)
const onClick: React.MouseEventHandler<HTMLElement> = (e) => {
e.preventDefault()
setOpen(!isOpen)
setTraitsOpen(TraitPosition.MARKPLACE_INDEX, !isOpen)
}
return <FilterDropdown title={'Marketplaces'} items={MarketplaceItems} onClick={onClick} isOpen={isOpen} />
}
......@@ -6,14 +6,12 @@ import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { NumericInput } from 'nft/components/layout/Input'
import { body } from 'nft/css/common.css'
import { useIsMobile } from 'nft/hooks'
import { useCollectionFilters } from 'nft/hooks/useCollectionFilters'
import { usePriceRange } from 'nft/hooks/usePriceRange'
import { TraitPosition } from 'nft/hooks/useTraitsOpen'
import { scrollToTop } from 'nft/utils/scrollToTop'
import { default as Slider } from 'rc-slider'
import { FormEvent, useEffect, useState } from 'react'
import { FocusEventHandler } from 'react'
import { FocusEventHandler, FormEvent, useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
......@@ -38,7 +36,6 @@ export const PriceRange = () => {
const setPrevMinMax = usePriceRange((state) => state.setPrevMinMax)
const theme = useTheme()
const isMobile = useIsMobile()
const location = useLocation()
useEffect(() => {
......@@ -142,11 +139,11 @@ export const PriceRange = () => {
return (
<TraitsHeader title="Price range" index={TraitPosition.PRICE_RANGE_INDEX}>
<Row gap="12" marginTop="12" color="textPrimary">
<Row marginTop="12" color="textPrimary" justifyContent="space-between">
<Row position="relative">
<NumericInput
style={{
width: isMobile ? '100%' : '126px',
width: '126px',
}}
className={styles.priceInput}
placeholder={priceRangeLow}
......@@ -157,10 +154,10 @@ export const PriceRange = () => {
/>
</Row>
<Box className={body}>to</Box>
<Row position="relative" flex="1">
<Row position="relative">
<NumericInput
style={{
width: isMobile ? '100%' : '126px',
width: '126px',
}}
className={styles.priceInput}
placeholder={priceRangeHigh}
......
......@@ -3,8 +3,14 @@ import { Box } from 'nft/components/Box'
import * as styles from 'nft/components/collection/Filters.css'
import { ChevronUpIcon } from 'nft/components/icons'
import { subheadSmall } from 'nft/css/common.css'
import { useIsMobile } from 'nft/hooks'
import { TraitPosition, useTraitsOpen } from 'nft/hooks/useTraitsOpen'
import { ReactNode, useEffect, useState } from 'react'
import styled from 'styled-components/macro'
const ChildreMobileWrapper = styled.div<{ isMobile: boolean }>`
padding: ${({ isMobile }) => (isMobile ? '0px 16px 0px 12px' : '0px')};
`
interface TraitsHeaderProps {
title: string
......@@ -18,6 +24,7 @@ export const TraitsHeader = (props: TraitsHeaderProps) => {
const [isOpen, setOpen] = useState(false)
const traitsOpen = useTraitsOpen((state) => state.traitsOpen)
const setTraitsOpen = useTraitsOpen((state) => state.setTraitsOpen)
const isMobile = useIsMobile()
const prevTraitIsOpen = index !== undefined ? traitsOpen[index - 1] : false
const showBorderTop = index !== TraitPosition.TRAIT_START_INDEX
......@@ -63,7 +70,7 @@ export const TraitsHeader = (props: TraitsHeaderProps) => {
</Box>
</Box>
</Box>
{children}
<ChildreMobileWrapper isMobile={isMobile}>{children}</ChildreMobileWrapper>
</Box>
</>
)
......
import { Box } from 'nft/components/Box'
import { FilterDropdown, FilterItem } from 'nft/components/collection/MarketplaceSelect'
import { useCollectionFilters } from 'nft/hooks'
import { DropDownOption } from 'nft/types'
import { useState } from 'react'
export const FilterSortDropdown = ({ sortDropDownOptions }: { sortDropDownOptions: DropDownOption[] }) => {
const [isOpen, setOpen] = useState(false)
const onClick: React.MouseEventHandler<HTMLElement> = (e) => {
e.preventDefault()
setOpen(!isOpen)
}
const sortItems = sortDropDownOptions.map((option) => (
<SortByItem dropDownOption={option} parentOnClick={onClick} key={option.displayText} />
))
return <FilterDropdown title={'Sort by'} items={sortItems} onClick={onClick} isOpen={isOpen} />
}
const SortByItem = ({
dropDownOption,
parentOnClick,
}: {
dropDownOption: DropDownOption
parentOnClick: React.MouseEventHandler<HTMLElement>
}) => {
const sortBy = useCollectionFilters((state) => state.sortBy)
const checkMark =
dropDownOption.sortBy !== undefined && sortBy === dropDownOption.sortBy ? (
<Box
as="img"
alt={dropDownOption.displayText}
width="20"
height="20"
objectFit="cover"
src="/nft/svgs/checkmark.svg"
/>
) : (
<></>
)
const onClick: React.MouseEventHandler<HTMLElement> = (e) => {
e.preventDefault()
parentOnClick(e)
dropDownOption.onClick()
}
return <FilterItem title={dropDownOption.displayText} element={checkMark} onClick={onClick} />
}
......@@ -5,8 +5,7 @@ import { Row } from 'nft/components/Flex'
import { ArrowsIcon, ChevronUpIcon, ReversedArrowsIcon } from 'nft/components/icons'
import { buttonTextMedium } from 'nft/css/common.css'
import { themeVars } from 'nft/css/sprinkles.css'
import { useIsCollectionLoading } from 'nft/hooks'
import { useCollectionFilters } from 'nft/hooks'
import { useCollectionFilters, useIsCollectionLoading } from 'nft/hooks'
import { DropDownOption } from 'nft/types'
import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from 'react'
......@@ -217,11 +216,6 @@ const DropDownItem = ({
onClick={onClick}
cursor="pointer"
>
{option.icon && (
<Box width="20" height="20">
{option.icon}
</Box>
)}
<Box marginLeft="8" className={buttonTextMedium}>
{option.displayText}
</Box>
......
export * from './FilterSortDropdown'
export * from './SortDropdown'
......@@ -35,6 +35,7 @@ export type Trait = {
interface State {
traits: Trait[]
hasRarity: boolean
markets: string[]
minPrice: string
maxPrice: string
......@@ -48,6 +49,7 @@ interface State {
}
type Actions = {
setHasRarity: (hasRarity: boolean) => void
setMarketCount: (_: Record<string, number>) => void
addMarket: (market: string) => void
removeMarket: (market: string) => void
......@@ -72,6 +74,7 @@ export const initialCollectionFilterState: State = {
minRarity: '',
maxRarity: '',
traits: [],
hasRarity: false,
markets: [],
marketCount: {},
buyNow: false,
......@@ -84,6 +87,7 @@ export const useCollectionFilters = create<CollectionFilters>()(
devtools(
(set) => ({
...initialCollectionFilterState,
setHasRarity: (hasRarity) => set({ hasRarity }),
setSortBy: (sortBy) => set({ sortBy }),
setSearch: (search) => set({ search }),
setBuyNow: (buyNow) => set({ buyNow }),
......
......@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'
const isClient = typeof window !== 'undefined'
function getIsMobile() {
export function getIsMobile() {
return isClient ? window.innerWidth < breakpoints.sm : false
}
......
......@@ -9,6 +9,7 @@ import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters }
import { CollectionNftsAndMenuLoading } from 'nft/components/collection/CollectionNfts'
import { CollectionPageSkeleton } from 'nft/components/collection/CollectionPageSkeleton'
import { Column, Row } from 'nft/components/Flex'
import { BagCloseIcon } from 'nft/components/icons'
import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks'
import * as styles from 'nft/pages/collection/index.css'
import { GenieCollection } from 'nft/types'
......@@ -16,6 +17,7 @@ import { Suspense, useEffect } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { useSpring } from 'react-spring'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
const FILTER_WIDTH = 332
const BAG_WIDTH = 324
......@@ -26,12 +28,41 @@ const CollectionDescriptionSection = styled(Column)`
${styles.ScreenBreakpointsPaddings}
`
const MobileFilterHeader = styled(Row)`
padding: 20px 16px;
justify-content: space-between;
`
const CollectionDisplaySection = styled(Row)`
${styles.ScreenBreakpointsPaddings}
align-items: flex-start;
position: relative;
`
const IconWrapper = styled.button`
background-color: transparent;
border-radius: 8px;
border: none;
color: ${({ theme }) => theme.textPrimary};
cursor: pointer;
display: flex;
padding: 2px 0px;
opacity: 1;
&:hover {
opacity: ${({ theme }) => theme.opacity.hover};
}
&:active {
opacity: ${({ theme }) => theme.opacity.click};
}
transition: ${({
theme: {
transition: { duration, timing },
},
}) => `opacity ${duration.medium} ${timing.ease}`};
`
const Collection = () => {
const { contractAddress } = useParams()
const isMobile = useIsMobile()
......@@ -46,14 +77,15 @@ const Collection = () => {
const collectionStats = useCollectionQuery(contractAddress as string)
const { gridX, gridWidthOffset } = useSpring({
gridX: isFiltersExpanded ? FILTER_WIDTH : 0,
gridWidthOffset: isFiltersExpanded
? isBagExpanded
? BAG_WIDTH + FILTER_WIDTH
: FILTER_WIDTH
: isBagExpanded
? BAG_WIDTH
: 0,
gridX: isFiltersExpanded && !isMobile ? FILTER_WIDTH : 0,
gridWidthOffset:
isFiltersExpanded && !isMobile
? isBagExpanded
? BAG_WIDTH + FILTER_WIDTH
: FILTER_WIDTH
: isBagExpanded
? BAG_WIDTH
: 0,
})
useEffect(() => {
......@@ -111,12 +143,34 @@ const Collection = () => {
/>
</CollectionDescriptionSection>
<CollectionDisplaySection>
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && <Filters traitsByGroup={collectionStats?.traits ?? {}} />}
<Box
position={isMobile ? 'fixed' : 'sticky'}
top="0"
left="0"
width={isMobile ? 'full' : '0'}
height={isMobile && isFiltersExpanded ? 'full' : undefined}
background={isMobile ? 'backgroundBackdrop' : undefined}
zIndex={isMobile ? 'modalBackdrop' : undefined}
overflowY={isMobile ? 'scroll' : undefined}
>
{isFiltersExpanded && (
<>
{isMobile && (
<MobileFilterHeader>
<ThemedText.HeadlineSmall>Filter</ThemedText.HeadlineSmall>
<IconWrapper onClick={() => setFiltersExpanded(false)}>
<BagCloseIcon />
</IconWrapper>
</MobileFilterHeader>
)}
<Filters traitsByGroup={collectionStats?.traits ?? {}} />
</>
)}
</Box>
{/* @ts-ignore: https://github.com/microsoft/TypeScript/issues/34933 */}
<AnimatedBox
position={isMobile && isFiltersExpanded ? 'fixed' : 'static'}
style={{
transform: gridX.to((x) => `translate(${x as number}px)`),
width: gridWidthOffset.to((x) => `calc(100% - ${x as number}px)`),
......
import { SortBy } from 'nft/hooks'
import { SellOrder } from '../sell'
export interface OpenSeaCollection {
......@@ -160,10 +162,10 @@ export enum ToolTipType {
// index starts at 1 for boolean reasons
export interface DropDownOption {
displayText: string
icon?: JSX.Element
onClick: () => void
reverseIndex?: number
reverseOnClick?: () => void
sortBy?: SortBy
}
export enum DetailsOrigin {
......
import { getIsMobile } from 'nft/hooks'
export const scrollToTop = () => {
window.document.getElementById('nft-anchor')?.scrollIntoView({
const isMobile = getIsMobile()
const anchorElement = isMobile ? 'nft-anchor-mobile' : 'nft-anchor'
window.document.getElementById(anchorElement)?.scrollIntoView({
behavior: 'smooth',
})
}
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