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

feat: [ListV2] Setup styles for Listing Modal (#5870)

* init modal title

* active and closed section headers

* added left border to section

* add tooltip

* complete collection row

* enforce max height

* add sign listings section

* fix top margin for section

* file re-org

* make modal section re-useable and move to its own file

* format

* define section props

* use accentAction for icons

* improved index

---------
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 13221e69
...@@ -163,6 +163,7 @@ export const getListings = (sellAssets: WalletAsset[]): [CollectionRow[], Listin ...@@ -163,6 +163,7 @@ export const getListings = (sellAssets: WalletAsset[]): [CollectionRow[], Listin
name: asset.asset_contract.name, name: asset.asset_contract.name,
status: ListingStatus.DEFINED, status: ListingStatus.DEFINED,
collectionAddress: asset.asset_contract.address, collectionAddress: asset.asset_contract.address,
isVerified: asset.collectionIsVerified,
marketplace, marketplace,
} }
newCollectionsToApprove.push(newCollectionRow) newCollectionsToApprove.push(newCollectionRow)
......
...@@ -24,12 +24,12 @@ export const ChevronUpIcon = ({ ...@@ -24,12 +24,12 @@ export const ChevronUpIcon = ({
...props ...props
}: SVGProps & { secondaryWidth?: string; secondaryHeight?: string; secondaryColor?: string }) => ( }: SVGProps & { secondaryWidth?: string; secondaryHeight?: string; secondaryColor?: string }) => (
<svg <svg
{...props}
width={secondaryWidth || '29'} width={secondaryWidth || '29'}
height={secondaryHeight || '28'} height={secondaryHeight || '28'}
viewBox="0 0 29 28" viewBox="0 0 29 28"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
{...props}
> >
<g clipPath="url(#clip0_564_11230)"> <g clipPath="url(#clip0_564_11230)">
<path <path
...@@ -757,3 +757,16 @@ export const CancelListingIcon = (props: SVGProps) => ( ...@@ -757,3 +757,16 @@ export const CancelListingIcon = (props: SVGProps) => (
/> />
</svg> </svg>
) )
export const ListingModalWindowActive = (props: SVGProps) => (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<circle cx="8" cy="8" r="8" fill={themeVars.colors.accentAction} fillOpacity="0.24" />
<circle cx="8" cy="8" r="5" fill={themeVars.colors.accentAction} />
</svg>
)
export const ListingModalWindowClosed = (props: SVGProps) => (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<circle cx="8" cy="8" r="7" stroke="#333D59" strokeWidth="2" />
</svg>
)
import ListingModal from 'nft/components/bag/profile/ListingModal'
import { Portal } from 'nft/components/common/Portal'
import { Overlay } from 'nft/components/modals/Overlay'
import styled from 'styled-components/macro'
import { Z_INDEX } from 'theme/zIndex'
const ListModalWrapper = styled.div`
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 420px;
z-index: ${Z_INDEX.modalOverTooltip};
background: ${({ theme }) => theme.backgroundSurface};
border-radius: 20px;
border: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
box-shadow: ${({ theme }) => theme.deepShadow};
padding: 0px 12px 4px;
`
export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
return (
<Portal>
<ListModalWrapper>
<ListingModal />
</ListModalWrapper>
<Overlay onClick={overlayClick} />
</Portal>
)
}
...@@ -18,7 +18,7 @@ import styled, { css } from 'styled-components/macro' ...@@ -18,7 +18,7 @@ import styled, { css } from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex' import { Z_INDEX } from 'theme/zIndex'
import { ListModal } from './ListModal' import { ListModal } from './Modal/ListModal'
import { NFTListingsGrid } from './NFTListingsGrid' import { NFTListingsGrid } from './NFTListingsGrid'
import { SelectMarketplacesDropdown } from './SelectMarketplacesDropdown' import { SelectMarketplacesDropdown } from './SelectMarketplacesDropdown'
import { SetDurationModal } from './SetDurationModal' import { SetDurationModal } from './SetDurationModal'
......
import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics'
import { InterfaceModalName } from '@uniswap/analytics-events'
import Row from 'components/Row'
import { Portal } from 'nft/components/common/Portal'
import { Overlay } from 'nft/components/modals/Overlay'
import { useNFTList } from 'nft/hooks'
import { useReducer } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { Z_INDEX } from 'theme/zIndex'
import { ListModalSection, Section } from './ListModalSection'
const ListModalWrapper = styled.div`
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 420px;
z-index: ${Z_INDEX.modal};
background: ${({ theme }) => theme.backgroundSurface};
border-radius: 20px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
box-shadow: ${({ theme }) => theme.deepShadow};
padding: 20px 24px 24px;
display: flex;
flex-direction: column;
gap: 16px;
`
const TitleRow = styled(Row)`
justify-content: space-between;
margin-bottom: 8px;
`
export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => {
const listings = useNFTList((state) => state.listings)
const collectionsRequiringApproval = useNFTList((state) => state.collectionsRequiringApproval)
const [openSection, toggleOpenSection] = useReducer(
(s) => (s === Section.APPROVE ? Section.SIGN : Section.APPROVE),
Section.APPROVE
)
return (
<Portal>
<Trace modal={InterfaceModalName.NFT_LISTING}>
<ListModalWrapper>
<TitleRow>
<ThemedText.HeadlineSmall lineHeight="28px">
<Trans>List NFTs</Trans>
</ThemedText.HeadlineSmall>
<X size={24} cursor="pointer" onClick={overlayClick} />
</TitleRow>
<ListModalSection
sectionType={Section.APPROVE}
active={openSection === Section.APPROVE}
content={collectionsRequiringApproval}
toggleSection={toggleOpenSection}
/>
<ListModalSection
sectionType={Section.SIGN}
active={openSection === Section.SIGN}
content={listings}
toggleSection={toggleOpenSection}
/>
</ListModalWrapper>
</Trace>
<Overlay onClick={overlayClick} />
</Portal>
)
}
import { Plural, Trans } from '@lingui/macro'
import Column from 'components/Column'
import { ScrollBarStyles } from 'components/Common'
import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip'
import {
ChevronUpIcon,
ListingModalWindowActive,
ListingModalWindowClosed,
LoadingIcon,
VerifiedIcon,
} from 'nft/components/icons'
import { AssetRow, CollectionRow } from 'nft/types'
import { Info } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { colors } from 'theme/colors'
import { TRANSITION_DURATIONS } from 'theme/styles'
const SectionHeader = styled(Row)`
justify-content: space-between;
`
const SectionTitle = styled(ThemedText.SubHeader)<{ active: boolean }>`
line-height: 24px;
color: ${({ theme, active }) => (active ? theme.textPrimary : theme.textSecondary)};
`
const SectionArrow = styled(ChevronUpIcon)<{ active: boolean }>`
height: 24px;
width: 24px;
cursor: pointer;
transition: ${TRANSITION_DURATIONS.medium}ms;
transform: rotate(${({ active }) => (active ? 0 : 180)}deg);
`
const SectionBody = styled(Column)`
border-left: 1.5px solid ${colors.gray650};
margin-top: 4px;
margin-left: 7px;
padding-top: 4px;
padding-left: 20px;
max-height: 394px;
overflow-y: auto;
${ScrollBarStyles}
`
const StyledInfoIcon = styled(Info)`
height: 16px;
width: 16px;
margin-left: 4px;
color: ${({ theme }) => theme.textSecondary};
`
const ContentRowContainer = styled(Column)`
gap: 8px;
`
const ContentRow = styled(Row)`
padding: 16px;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
border-radius: 12px;
opacity: 0.6;
`
const CollectionIcon = styled.img`
border-radius: 100px;
height: 24px;
width: 24px;
z-index: 1;
`
const AssetIcon = styled.img`
border-radius: 4px;
height: 24px;
width: 24px;
z-index: 1;
`
const MarketplaceIcon = styled.img`
border-radius: 4px;
height: 24px;
width: 24px;
margin-left: -4px;
margin-right: 12px;
`
const ContentName = styled(ThemedText.SubHeaderSmall)`
color: ${({ theme }) => theme.textPrimary};
line-height: 20px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 50%;
`
const StyledVerifiedIcon = styled(VerifiedIcon)`
height: 16px;
width: 16px;
margin-left: 4px;
`
const StyledLoadingIconBackground = styled(LoadingIcon)`
height: 14px;
width: 14px;
stroke: ${({ theme }) => theme.textTertiary};
margin-left: auto;
margin-right: 0px;
`
export const enum Section {
APPROVE,
SIGN,
}
interface ListModalSectionProps {
sectionType: Section
active: boolean
content: AssetRow[]
toggleSection: React.DispatchWithoutAction
}
export const ListModalSection = ({ sectionType, active, content, toggleSection }: ListModalSectionProps) => {
const theme = useTheme()
const isCollectionApprovalSection = sectionType === Section.APPROVE
return (
<Column>
<SectionHeader>
<Row>
{active ? <ListingModalWindowActive /> : <ListingModalWindowClosed />}
<SectionTitle active={active} marginLeft="12px">
{isCollectionApprovalSection ? (
<>
<Trans>Approve</Trans>&nbsp;{content.length}&nbsp;
<Plural value={content.length} _1="Collection" other="Collections" />
</>
) : (
<>
<Trans>Sign</Trans> &nbsp;{content.length}&nbsp;{' '}
<Plural value={content.length} _1="Listing" other="Listings" />
</>
)}
</SectionTitle>
</Row>
<SectionArrow
active={active}
secondaryColor={active ? theme.textPrimary : theme.textSecondary}
onClick={toggleSection}
/>
</SectionHeader>
{active && (
<SectionBody>
{isCollectionApprovalSection && (
<Row height="16px" marginBottom="16px">
<ThemedText.Caption lineHeight="16px" color="textSecondary">
<Trans>Why is a transaction required?</Trans>
</ThemedText.Caption>
<MouseoverTooltip
text={<Trans>Listing an NFT requires a one-time marketplace approval for each NFT collection.</Trans>}
>
<StyledInfoIcon />
</MouseoverTooltip>
</Row>
)}
<ContentRowContainer>
{content.map((row) => {
return (
<ContentRow key={row.name}>
{isCollectionApprovalSection ? (
<CollectionIcon src={row.images[0]} />
) : (
<AssetIcon src={row.images[0]} />
)}
<MarketplaceIcon src={row.images[1]} />
<ContentName>{row.name}</ContentName>
{isCollectionApprovalSection && (row as CollectionRow).isVerified && <StyledVerifiedIcon />}
<StyledLoadingIconBackground />
</ContentRow>
)
})}
</ContentRowContainer>
</SectionBody>
)}
</Column>
)
}
...@@ -109,6 +109,7 @@ export interface ListingRow extends AssetRow { ...@@ -109,6 +109,7 @@ export interface ListingRow extends AssetRow {
export interface CollectionRow extends AssetRow { export interface CollectionRow extends AssetRow {
collectionAddress?: string collectionAddress?: string
isVerified?: boolean
marketplace: ListingMarket marketplace: ListingMarket
} }
......
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