refactor: mobile popover UX (#2140)

This commit is contained in:
Aman Harwara
2023-01-18 01:00:23 +05:30
committed by GitHub
parent 7af4ecbc3d
commit baf77516fe
33 changed files with 237 additions and 117 deletions

View File

@@ -0,0 +1,92 @@
import { useDisableBodyScrollOnMobile } from '@/Hooks/useDisableBodyScrollOnMobile'
import { useLifecycleAnimation } from '@/Hooks/useLifecycleAnimation'
import { classNames } from '@standardnotes/snjs'
import { ReactNode } from 'react'
import Portal from '../Portal/Portal'
const MobilePopoverContent = ({
open,
requestClose,
children,
title,
className,
}: {
open: boolean
requestClose: () => void
children: ReactNode
title: string
className?: string
}) => {
const [isMounted, setPopoverElement] = useLifecycleAnimation({
open,
enter: {
keyframes: [
{
opacity: 0.25,
transform: 'translateY(1rem)',
},
{
opacity: 1,
transform: 'translateY(0)',
},
],
options: {
easing: 'cubic-bezier(.36,.66,.04,1)',
duration: 150,
fill: 'forwards',
},
initialStyle: {
transformOrigin: 'bottom',
},
},
enterCallback: (element) => {
element.scrollTop = 0
},
exit: {
keyframes: [
{
opacity: 1,
transform: 'translateY(0)',
},
{
opacity: 0,
transform: 'translateY(1rem)',
},
],
options: {
easing: 'cubic-bezier(.36,.66,.04,1)',
duration: 150,
fill: 'forwards',
},
initialStyle: {
transformOrigin: 'bottom',
},
},
})
useDisableBodyScrollOnMobile()
if (!isMounted) {
return null
}
return (
<Portal>
<div
ref={setPopoverElement}
className="absolute top-0 left-0 z-modal flex h-full w-full origin-bottom flex-col bg-default pt-safe-top pb-safe-bottom opacity-0"
>
<div className="flex items-center justify-between border-b border-border py-2.5 px-3 text-base">
<div />
<div className="font-semibold">{title}</div>
<button className="font-semibold active:shadow-none active:outline-none" onClick={requestClose}>
Done
</button>
</div>
<div className={classNames('h-full overflow-y-auto', className)}>{children}</div>
</div>
</Portal>
)
}
export default MobilePopoverContent

View File

@@ -1,6 +1,8 @@
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { useAndroidBackHandler } from '@/NativeMobileWeb/useAndroidBackHandler'
import { UuidGenerator } from '@standardnotes/snjs'
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
import MobilePopoverContent from './MobilePopoverContent'
import PositionedPopoverContent from './PositionedPopoverContent'
import { PopoverProps } from './Types'
@@ -38,6 +40,7 @@ const Popover = ({
open,
overrideZIndex,
side,
title,
togglePopover,
disableClickOutside,
disableMobileFullscreenTakeover,
@@ -87,6 +90,23 @@ const Popover = ({
}
}, [addAndroidBackHandler, open, togglePopover])
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
if (isMobileScreen && !disableMobileFullscreenTakeover) {
return (
<MobilePopoverContent
open={open}
requestClose={() => {
togglePopover?.()
}}
title={title}
className={className}
>
{children}
</MobilePopoverContent>
)
}
return open ? (
<PopoverContext.Provider value={contextValue}>
<PositionedPopoverContent
@@ -95,13 +115,14 @@ const Popover = ({
anchorPoint={anchorPoint}
childPopovers={childPopovers}
className={`popover-content-container ${className ?? ''}`}
id={popoverId.current}
overrideZIndex={overrideZIndex}
side={side}
togglePopover={togglePopover}
disableClickOutside={disableClickOutside}
disableMobileFullscreenTakeover={disableMobileFullscreenTakeover}
id={popoverId.current}
maxHeight={maxHeight}
overrideZIndex={overrideZIndex}
side={side}
title={title}
togglePopover={togglePopover}
>
{children}
</PositionedPopoverContent>

View File

@@ -30,25 +30,36 @@ type PopoverAnchorPointProps = {
anchorElement?: never
}
type PopoverMutuallyExclusiveProps =
| {
togglePopover: () => void
disableMobileFullscreenTakeover?: never
}
| {
togglePopover?: never
disableMobileFullscreenTakeover: boolean
}
type CommonPopoverProps = {
align?: PopoverAlignment
children: ReactNode
side?: PopoverSide
overrideZIndex?: string
togglePopover?: () => void
className?: string
disableClickOutside?: boolean
disableMobileFullscreenTakeover?: boolean
maxHeight?: (calculatedMaxHeight: number) => number
title: string
}
export type PopoverContentProps = CommonPopoverProps & {
anchorElement?: HTMLElement | null
anchorPoint?: Point
childPopovers: Set<string>
togglePopover?: () => void
disableMobileFullscreenTakeover?: boolean
id: string
}
export type PopoverProps =
| (CommonPopoverProps & PopoverAnchorElementProps)
| (CommonPopoverProps & PopoverAnchorPointProps)
| (CommonPopoverProps & PopoverMutuallyExclusiveProps & PopoverAnchorElementProps)
| (CommonPopoverProps & PopoverMutuallyExclusiveProps & PopoverAnchorPointProps)