feat: Add Select Page Modal (#4643)

* add select nfts shopping bag modal

* Add shopping bag to top lovel modal wrapper

* addressing comments

* rename to listingTag

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
This commit is contained in:
Charles Bachmeier 2022-09-16 11:31:25 -07:00 committed by GitHub
parent 49c5cbbf3b
commit 8c947a0e0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 301 additions and 0 deletions

@ -2,9 +2,12 @@ import { useWeb3React } from '@web3-react/core'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import { lazy } from 'react'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
const Cart = lazy(() => import('nft/components/sell/modal/ListingTag'))
export default function TopLevelModals() {
const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM)
const addressClaimToggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
@ -18,6 +21,7 @@ export default function TopLevelModals() {
<>
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
<ConnectedAccountBlocked account={account} isOpen={open} />
<Cart />
</>
)
}

@ -0,0 +1,52 @@
import { style } from '@vanilla-extract/css'
import { breakpoints, sprinkles } from 'nft/css/sprinkles.css'
export const tagContainer = style([
sprinkles({
borderRadius: '20',
borderWidth: '1px',
borderStyle: 'solid',
borderColor: 'medGray',
}),
{
'@media': {
[`screen and (max-width: ${breakpoints.md}px)`]: {
borderRadius: '0',
},
},
},
])
export const tagAssets = style([
sprinkles({
maxHeight: 'inherit',
}),
{
'@media': {
[`screen and (min-width: ${breakpoints.md}px)`]: {
maxHeight: '55vh',
},
},
'::-webkit-scrollbar': { display: 'none' },
scrollbarWidth: 'none',
},
])
export const closeIcon = style({
transform: 'rotate(90deg)',
float: 'right',
paddingTop: '1px',
})
export const orderButton = style([
sprinkles({
width: 'full',
paddingY: '12',
paddingX: '0',
}),
{
':hover': {
boxShadow: 'none',
},
},
])

@ -0,0 +1,120 @@
import Loader from 'components/Loader'
import { Box } from 'nft/components/Box'
import { Column } from 'nft/components/Flex'
import { CloseDropDownIcon } from 'nft/components/icons'
import { bodySmall, buttonMedium, headlineSmall } from 'nft/css/common.css'
import { useBag, useIsMobile, useSellAsset, useSellPageState } from 'nft/hooks'
import { SellPageStateType } from 'nft/types'
import { lazy, Suspense } from 'react'
import { useLocation } from 'react-router-dom'
import * as styles from './ListingTag.css'
const CartSellAssetRow = lazy(() => import('./TagAssetRow'))
const Cart = () => {
const { pathname } = useLocation()
const isNFTSellPage = pathname.startsWith('/nfts/sell')
const sellAssets = useSellAsset((state) => state.sellAssets)
const setSellPageState = useSellPageState((state) => state.setSellPageState)
const sellPageState = useSellPageState((state) => state.state)
const toggleCart = useBag((state) => state.toggleBag)
const isMobile = useIsMobile()
const bagExpanded = useBag((s) => s.bagExpanded)
return (
<Box
zIndex={{ sm: '3', md: '2' }}
width={{ sm: 'full', md: 'auto' }}
height={{ sm: 'full', md: 'auto' }}
position="fixed"
left={{ sm: '0', md: 'unset' }}
right={{ sm: 'unset', md: '0' }}
top={{ sm: '0', md: 'unset' }}
display={bagExpanded && isNFTSellPage ? 'flex' : 'none'}
>
<Suspense fallback={<Loader />}>
<Column
marginTop={{ sm: '0', md: '4' }}
marginRight={{ sm: '0', md: '20' }}
className={styles.tagContainer}
width={{ sm: 'full', md: '288' }}
height={{ sm: 'full', md: 'auto' }}
backgroundColor="white"
marginLeft="0"
justifyContent="flex-start"
>
{sellPageState === SellPageStateType.LISTING ? null : (
<>
<BagHeader bagQuantity={sellAssets.length} />
<Column
overflowX="hidden"
overflowY="auto"
position="relative"
paddingTop="6"
paddingBottom="6"
height="full"
className={styles.tagAssets}
>
{sellAssets.length
? sellAssets.map((asset, index) => <CartSellAssetRow asset={asset} key={index} />)
: null}
</Column>
<Box padding="12">
<Box
as="button"
className={`${buttonMedium} ${styles.orderButton}`}
disabled={sellAssets.length === 0}
onClick={() => {
isMobile && toggleCart()
setSellPageState(SellPageStateType.LISTING)
}}
>
Continue
</Box>
</Box>
</>
)}
</Column>
</Suspense>
</Box>
)
}
const BagHeader = ({ bagQuantity }: { bagQuantity: number }) => {
const toggleCart = useBag((state) => state.toggleBag)
const resetSellAssets = useSellAsset((state) => state.reset)
const isMobile = useIsMobile()
return (
<Box position="relative" zIndex="2" paddingTop="20" paddingLeft="12" paddingRight="12">
{isMobile ? (
<Box
as="button"
border="none"
color="darkGray"
background="black"
className={styles.closeIcon}
onClick={toggleCart}
>
<CloseDropDownIcon />
</Box>
) : null}
<Box className={headlineSmall} paddingTop="0" paddingBottom="8" paddingX="0" margin="0">
{'Selected items'}
</Box>
{bagQuantity > 0 ? (
<Box className={bodySmall} paddingTop="0" paddingBottom="8" paddingX="0" marginY="0" marginX="auto">
{bagQuantity} {bagQuantity === 1 ? 'NFT' : 'NFTs'}
<Box as="span" position="relative" paddingRight="2" paddingLeft="4" style={{ fontSize: '8px', top: '-2px' }}>
&#x2022;
</Box>
<Box as="span" color="blue400" onClick={resetSellAssets} cursor="pointer" paddingLeft="2">
Remove all
</Box>
</Box>
) : null}
</Box>
)
}
export default Cart

@ -0,0 +1,82 @@
import { style } from '@vanilla-extract/css'
import { sprinkles } from 'nft/css/sprinkles.css'
export const tagAssetImage = style([
sprinkles({
borderRadius: '8',
height: '52',
width: '52',
marginRight: '12',
marginLeft: '8',
cursor: 'pointer',
}),
{
boxSizing: 'border-box',
},
])
export const tagAssetName = style([
sprinkles({
fontWeight: 'medium',
overflow: 'hidden',
marginRight: 'auto',
marginTop: '4',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}),
])
export const tagAssetCollectionName = style([
sprinkles({
fontWeight: 'normal',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}),
{
maxWidth: '65%',
},
])
export const tagAssetRowBottom = style([
sprinkles({
width: 'full',
display: 'flex',
flexWrap: 'nowrap',
marginRight: '4',
}),
{
marginTop: '-10px',
},
])
export const removeAsset = style([
sprinkles({
position: 'absolute',
cursor: 'pointer',
}),
{
bottom: '-12px',
left: '22px',
},
])
export const removeIcon = style([
sprinkles({
width: '32',
}),
])
export const tagAssetInfo = style([
sprinkles({
fontSize: '14',
color: 'blackBlue',
display: 'flex',
flexWrap: 'wrap',
width: 'full',
overflowX: 'hidden',
}),
{
lineHeight: '17px',
},
])

@ -0,0 +1,43 @@
import { Box } from 'nft/components/Box'
import { bodySmall, subheadSmall } from 'nft/css/common.css'
import { useSellAsset } from 'nft/hooks'
import { WalletAsset } from 'nft/types'
import { useEffect, useRef, useState } from 'react'
import * as styles from './TagAssetRow.css'
const CartSellAssetRow = ({ asset }: { asset: WalletAsset }) => {
const removeAsset = useSellAsset((state) => state.removeSellAsset)
const [hovered, setHovered] = useState(false)
const handleHover = () => setHovered(!hovered)
const assetRowRef = useRef<HTMLDivElement>()
useEffect(() => {
if (hovered && assetRowRef.current && assetRowRef.current.matches(':hover') === false) setHovered(false)
}, [hovered])
return (
<Box display="flex" padding="4" marginBottom="4" borderRadius="8" position="relative">
<Box
onMouseEnter={handleHover}
onMouseLeave={handleHover}
onClick={() => {
removeAsset(asset)
}}
>
<Box visibility={hovered ? 'visible' : 'hidden'} className={styles.removeAsset}>
<img className={styles.removeIcon} src={'/nft/svgs/minusCircle.svg'} alt="Remove item" />
</Box>
<img className={styles.tagAssetImage} src={asset.image_url} alt={asset.name} />
</Box>
<Box className={styles.tagAssetInfo}>
<Box className={`${subheadSmall} ${styles.tagAssetName}`}>{asset.name || `#${asset.tokenId}`}</Box>
<Box className={styles.tagAssetRowBottom}>
<Box className={`${bodySmall} ${styles.tagAssetCollectionName}`}>{asset.collection?.name}</Box>
</Box>
</Box>
</Box>
)
}
export default CartSellAssetRow