Skip to content

Commit

Permalink
feat: useExpandableModal, abstract away components
Browse files Browse the repository at this point in the history
  • Loading branch information
thechefpenguin committed Nov 12, 2024
1 parent e8c820f commit 5469e60
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 86 deletions.
37 changes: 37 additions & 0 deletions apps/web/src/components/AdPanel/Expandable/ExpandableContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Box, Text } from '@pancakeswap/uikit'
import { PropsWithChildren, ReactNode } from 'react'
import { Divider } from './styles'

interface ExpandableContentProps extends PropsWithChildren {
title: string
isExpanded: boolean

extendedContentRef: React.RefObject<HTMLDivElement>

expandableContent?: ReactNode
defaultContent?: ReactNode
}

export const ExpandableContent = ({
title,
isExpanded,
extendedContentRef,
expandableContent,
defaultContent,
}: ExpandableContentProps) => {
return (
<>
{isExpanded ? (
<Box ref={extendedContentRef} overflow="hidden">
<Text bold as="h1" textAlign="center" p="16px">
{title}
</Text>
<Divider />
<Box p="16px">{expandableContent}</Box>
</Box>
) : (
defaultContent
)}
</>
)
}
22 changes: 22 additions & 0 deletions apps/web/src/components/AdPanel/Expandable/ExpandableModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Modal, ModalV2, useMatchBreakpoints } from '@pancakeswap/uikit'
import { ReactNode } from 'react'

interface ExpandableModalProps {
title: string
isOpen: boolean
onDismiss: () => void
expandableContent?: ReactNode
}

export const ExpandableModal = ({ title, isOpen, expandableContent, onDismiss }: ExpandableModalProps) => {
const { isDesktop } = useMatchBreakpoints()
return (
<>
<ModalV2 isOpen={isOpen} onDismiss={onDismiss} closeOnOverlayClick>
<Modal title={title} onDismiss={onDismiss} maxWidth={isDesktop ? '438px' : 'unset'}>
{expandableContent}
</Modal>
</ModalV2>
</>
)
}
17 changes: 17 additions & 0 deletions apps/web/src/components/AdPanel/Expandable/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Box, Flex } from '@pancakeswap/uikit'
import styled from 'styled-components'

export const Divider = styled(Box)`
width: 100%;
background-color: ${({ theme }) => theme.colors.cardBorder};
height: 1px;
`

export const ActionContainer = styled(Flex)<{ $isExpanded?: boolean }>`
${({ theme, $isExpanded }) =>
$isExpanded &&
`
border-top: 1px solid ${theme.colors.cardBorder};
`}
`
66 changes: 66 additions & 0 deletions apps/web/src/components/AdPanel/Expandable/useExpandableCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useMatchBreakpoints, useModalV2 } from '@pancakeswap/uikit'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useIsSlideExpanded } from '../useIsSlideExpanded'

interface UseExpandableCardProps {
/** Id unique to this expandable card */
adId: string
forceMobile?: boolean
}

export const useExpandableCard = ({ adId, forceMobile }: UseExpandableCardProps) => {
const { slideExpanded, setSlideExpanded } = useIsSlideExpanded()
const { isDesktop } = useMatchBreakpoints()
const { isOpen, onDismiss, setIsOpen } = useModalV2()

const extendedContentRef = useRef<HTMLDivElement>(null)
const adCardRef = useRef<HTMLDivElement>(null)
const actionPanelRef = useRef<HTMLDivElement>(null)

const isMobile = useMemo(() => forceMobile || !isDesktop, [forceMobile, isDesktop])

const isExpanded = useMemo(() => {
return !isMobile ? slideExpanded[adId] || false : false
}, [adId, isMobile, slideExpanded])

const handleExpand = useCallback(() => {
setSlideExpanded(adId, true)
if (isMobile) setIsOpen(true)
}, [adId, isMobile, setIsOpen, setSlideExpanded])

const handleDismiss = useCallback(() => {
setSlideExpanded(adId, false)
onDismiss()
}, [adId, onDismiss, setSlideExpanded])

const toggleHeight = useCallback((isAuto: boolean, isExpend: boolean) => {
const targetCard = adCardRef.current
if (!targetCard || !isExpend) return
targetCard.style.height = isAuto ? 'auto' : `${targetCard.offsetHeight}px`
}, [])

useEffect(() => {
if (isExpanded && extendedContentRef.current && adCardRef.current && actionPanelRef.current) {
const targetCard = adCardRef.current

const contentPanelHeight = extendedContentRef.current.scrollHeight - 13
const actionPanelHeight = actionPanelRef.current.scrollHeight || 64

targetCard.style.height = `${contentPanelHeight + actionPanelHeight}px`
} else if (!isExpanded && adCardRef.current) {
const targetCard = adCardRef.current
targetCard.style.height = `164px`
}
}, [isExpanded])

return {
isExpanded,
isOpen,
extendedContentRef,
adCardRef,
actionPanelRef,
handleExpand,
handleDismiss,
toggleHeight,
}
}
126 changes: 40 additions & 86 deletions apps/web/src/components/AdPanel/Variations/ExpandableAd.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,53 @@
import { Box, Flex, Modal, ModalV2, Text, useMatchBreakpoints, useModalV2 } from '@pancakeswap/uikit'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { styled } from 'styled-components'
import { useTranslation } from '@pancakeswap/localization'
import { Flex } from '@pancakeswap/uikit'
import { BodyText } from '../BodyText'
import { ExpandButton } from '../Button'
import { AdCard } from '../Card'
import { ExpandableContent } from '../Expandable/ExpandableContent'
import { ExpandableModal } from '../Expandable/ExpandableModal'
import { ActionContainer } from '../Expandable/styles'
import { useExpandableCard } from '../Expandable/useExpandableCard'
import { FAQ } from '../FAQ'
import { Title } from '../Title'
import { AdPlayerProps } from '../types'
import { useIsSlideExpanded } from '../useIsSlideExpanded'

export const Divider = styled(Box)`
width: 100%;
background-color: ${({ theme }) => theme.colors.cardBorder};
height: 1px;
`

const ActionContainer = styled(Flex)<{ $isExpanded?: boolean }>`
${({ theme, $isExpanded }) =>
$isExpanded &&
`
border-top: 1px solid ${theme.colors.cardBorder};
`}
`

// Unique id for this expandable slide
const adId = 'expandable-ad'
const title = 'Quick Start on How to Swap'

const ExpandedContent = () => {
return <FAQ type="swap" />
}

export const ExpandableAd = (props: AdPlayerProps) => {
const { isOpen, onDismiss, setIsOpen } = useModalV2()
const { isDesktop } = useMatchBreakpoints()
const { slideExpanded, setSlideExpanded } = useIsSlideExpanded()
const extendedRef = useRef<HTMLDivElement>(null)
const adCardRef = useRef<HTMLDivElement>(null)
const actionPanelRef = useRef<HTMLDivElement>(null)

const isMobile = useMemo(() => props.forceMobile || !isDesktop, [props.forceMobile, isDesktop])

const isExpanded = useMemo(() => {
return !isMobile ? slideExpanded[adId] || false : false
}, [isMobile, slideExpanded])

const handleExpand = () => {
setSlideExpanded(adId, true)
if (isMobile) setIsOpen(true)
}

const handleDismiss = () => {
setSlideExpanded(adId, false)
onDismiss()
}

useEffect(() => {
if (isExpanded && extendedRef.current && adCardRef.current && actionPanelRef.current) {
const targetCard = adCardRef.current

const contentPanelHeight = extendedRef.current.scrollHeight - 13
const actionPanelHeight = actionPanelRef.current.scrollHeight || 64

targetCard.style.height = `${contentPanelHeight + actionPanelHeight}px`
} else if (!isExpanded && adCardRef.current) {
const targetCard = adCardRef.current
targetCard.style.height = `164px`
}
}, [isExpanded])

const toggleHeight = useCallback((isAuto: boolean, isExpend: boolean) => {
const targetCard = adCardRef.current
if (!targetCard || !isExpend) return
targetCard.style.height = isAuto ? 'auto' : `${targetCard.offsetHeight}px`
}, [])
const { t } = useTranslation()
const {
actionPanelRef,
adCardRef,
extendedContentRef,
handleDismiss,
handleExpand,
isOpen,
isExpanded,
toggleHeight,
} = useExpandableCard({
adId: 'expandable-ad',
forceMobile: props.forceMobile,
})

const title = t('Quick Start on How to Swap')

return (
<AdCard imageUrl="/images/adpanel-test/bannerImg1.png" isExpanded={isExpanded} {...props} ref={adCardRef}>
<Flex flexDirection="column" justifyContent="space-between" height="100%">
{isExpanded ? (
<Box ref={extendedRef} overflow="hidden">
<Text bold as="h1" textAlign="center" p="16px">
{title}
</Text>
<Divider />
<Box p="16px">
<ExpandedContent />
</Box>
</Box>
) : (
<>
<Title>Need Help?</Title>
<BodyText>Quick start now on How to Swap!</BodyText>
</>
)}
<ExpandableContent
title={title}
extendedContentRef={extendedContentRef}
isExpanded={isExpanded}
expandableContent={<ExpandedContent />}
defaultContent={
<>
<Title>{t('Need Help?')}</Title>
<BodyText>{t('Quick start now on How to Swap!')}</BodyText>
</>
}
/>

<ActionContainer ref={actionPanelRef} p={isExpanded ? '16px' : '0'} $isExpanded={isExpanded}>
<ExpandButton
Expand All @@ -108,11 +61,12 @@ export const ExpandableAd = (props: AdPlayerProps) => {
</Flex>

{/* On Non-Desktop devices, show expanded content in modal */}
<ModalV2 isOpen={isOpen} onDismiss={handleDismiss} closeOnOverlayClick>
<Modal title={title} onDismiss={handleDismiss} maxWidth={isDesktop ? '438px' : 'unset'}>
<ExpandedContent />
</Modal>
</ModalV2>
<ExpandableModal
title={title}
isOpen={isOpen}
onDismiss={handleDismiss}
expandableContent={<ExpandedContent />}
/>
</AdCard>
)
}

0 comments on commit 5469e60

Please sign in to comment.