Commit 2b279e00 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

feat: virtual containers for collection pages (#5125)

* use fixedsizeList only

* add autosizer comments

* undo asset testing change

* init

* cleanup

* scrollbar styles

* scrollbar styles
Co-authored-by: default avatarLynn Yu <lynn.yu@uniswap.org>
parent 9f5c588b
...@@ -76,13 +76,16 @@ export const detailsOpen = style([ ...@@ -76,13 +76,16 @@ export const detailsOpen = style([
}), }),
]) ])
export const MAX_FILTER_DROPDOWN_HEIGHT = 302
export const filterDropDowns = style([ export const filterDropDowns = style([
borderBottom, borderBottom,
sprinkles({ sprinkles({
overflowY: 'scroll', paddingLeft: '0',
paddingBottom: '8',
}), }),
{ {
maxHeight: '302px', maxHeight: `${MAX_FILTER_DROPDOWN_HEIGHT}px`,
'::-webkit-scrollbar': { display: 'none' }, '::-webkit-scrollbar': { display: 'none' },
scrollbarWidth: 'none', scrollbarWidth: 'none',
}, },
......
...@@ -8,22 +8,28 @@ import { subheadSmall } from 'nft/css/common.css' ...@@ -8,22 +8,28 @@ import { subheadSmall } from 'nft/css/common.css'
import { Trait, useCollectionFilters } from 'nft/hooks/useCollectionFilters' import { Trait, useCollectionFilters } from 'nft/hooks/useCollectionFilters'
import { pluralize } from 'nft/utils/roundAndPluralize' import { pluralize } from 'nft/utils/roundAndPluralize'
import { scrollToTop } from 'nft/utils/scrollToTop' import { scrollToTop } from 'nft/utils/scrollToTop'
import { FormEvent, MouseEvent, useEffect, useMemo, useState } from 'react' import { CSSProperties, FormEvent, MouseEvent, useCallback, useEffect, useMemo, useState } from 'react'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList } from 'react-window'
import { Input } from '../layout/Input' import { Input } from '../layout/Input'
import * as styles from './Filters.css' import * as styles from './Filters.css'
import { TraitsHeader } from './TraitsHeader' import { TraitsHeader } from './TraitsHeader'
const TRAIT_ROW_HEIGHT = 44
const TraitItem = ({ const TraitItem = ({
trait, trait,
addTrait, addTrait,
removeTrait, removeTrait,
isTraitSelected, isTraitSelected,
style,
}: { }: {
trait: Trait trait: Trait
addTrait: (trait: Trait) => void addTrait: (trait: Trait) => void
removeTrait: (trait: Trait) => void removeTrait: (trait: Trait) => void
isTraitSelected: boolean isTraitSelected: boolean
style?: CSSProperties
}) => { }) => {
const [isCheckboxSelected, setCheckboxSelected] = useState(false) const [isCheckboxSelected, setCheckboxSelected] = useState(false)
const [hovered, setHovered] = useState(false) const [hovered, setHovered] = useState(false)
...@@ -76,6 +82,7 @@ const TraitItem = ({ ...@@ -76,6 +82,7 @@ const TraitItem = ({
style={{ style={{
paddingBottom: '22px', paddingBottom: '22px',
paddingTop: '22px', paddingTop: '22px',
...style,
}} }}
maxHeight="44" maxHeight="44"
onMouseEnter={handleHover} onMouseEnter={handleHover}
...@@ -105,6 +112,12 @@ const TraitItem = ({ ...@@ -105,6 +112,12 @@ const TraitItem = ({
) )
} }
interface TraitRowProps {
data: Array<Trait>
index: number
style: CSSProperties
}
export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: string; index: number }) => { export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: string; index: number }) => {
const addTrait = useCollectionFilters((state) => state.addTrait) const addTrait = useCollectionFilters((state) => state.addTrait)
const removeTrait = useCollectionFilters((state) => state.removeTrait) const removeTrait = useCollectionFilters((state) => state.removeTrait)
...@@ -117,6 +130,24 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st ...@@ -117,6 +130,24 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st
[debouncedSearch, traits] [debouncedSearch, traits]
) )
const Row = useCallback(
function TraitRow({ data, index, style }: TraitRowProps) {
const trait: Trait = data[index]
const isTraitSelected = selectedTraits.find(
({ trait_type, trait_value }) =>
trait_type === trait.trait_type && String(trait_value) === String(trait.trait_value)
)
return <TraitItem style={style} isTraitSelected={!!isTraitSelected} {...{ trait, addTrait, removeTrait }} />
},
[selectedTraits, addTrait, removeTrait]
)
const itemKey = useCallback((index: number, data: Trait[]) => {
const trait = data[index]
return `${trait.trait_type}_${trait.trait_value}_${index}`
}, [])
return traits.length ? ( return traits.length ? (
<TraitsHeader index={index} numTraits={traits.length} title={type}> <TraitsHeader index={index} numTraits={traits.length} title={type}>
<Input <Input
...@@ -129,21 +160,24 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st ...@@ -129,21 +160,24 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st
position="static" position="static"
width="full" width="full"
/> />
<Column className={styles.filterDropDowns} paddingLeft="0" paddingBottom="8"> <Column
{searchedTraits.map((trait, index) => { className={styles.filterDropDowns}
const isTraitSelected = selectedTraits.find( style={{ height: `${Math.min(TRAIT_ROW_HEIGHT * searchedTraits.length, styles.MAX_FILTER_DROPDOWN_HEIGHT)}px` }}
({ trait_type, trait_value }) => >
trait_type === trait.trait_type && String(trait_value) === String(trait.trait_value) <AutoSizer disableWidth>
) {({ height }) => (
<FixedSizeList
return ( height={height}
<TraitItem width="100%"
isTraitSelected={!!isTraitSelected} itemData={searchedTraits}
key={trait.trait_value} itemCount={searchedTraits.length}
{...{ trait, addTrait, removeTrait }} itemSize={TRAIT_ROW_HEIGHT}
/> itemKey={itemKey}
) >
})} {({ index, style, data }) => <Row style={style} key={itemKey(index, data)} data={data} index={index} />}
</FixedSizeList>
)}
</AutoSizer>
</Column> </Column>
</TraitsHeader> </TraitsHeader>
) : null ) : null
......
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