feat: responsive popovers & menus (#1323)

This commit is contained in:
Aman Harwara
2022-07-21 02:20:14 +05:30
committed by GitHub
parent baf7fb0019
commit 2573407851
44 changed files with 1308 additions and 1415 deletions

View File

@@ -1,10 +1,9 @@
import { WebApplication } from '@/Application/Application'
import { Disclosure, DisclosurePanel } from '@reach/disclosure'
import { memo, useCallback, useRef, useState } from 'react'
import Icon from '../../Icon/Icon'
import { DisplayOptionsMenuPositionProps } from './DisplayOptionsMenuProps'
import DisplayOptionsMenuPortal from './DisplayOptionsMenuPortal'
import StyledDisplayOptionsButton from './StyledDisplayOptionsButton'
import { classNames } from '@/Utils/ConcatenateClassNames'
import Popover from '@/Components/Popover/Popover'
import DisplayOptionsMenu from './DisplayOptionsMenu'
type Props = {
application: {
@@ -26,21 +25,12 @@ const ContentListHeader = ({
isFilesSmartView,
optionsSubtitle,
}: Props) => {
const [displayOptionsMenuPosition, setDisplayOptionsMenuPosition] = useState<DisplayOptionsMenuPositionProps>()
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false)
const toggleDisplayOptionsMenu = useCallback(() => {
if (displayOptionsButtonRef.current) {
const buttonBoundingRect = displayOptionsButtonRef.current.getBoundingClientRect()
setDisplayOptionsMenuPosition({
top: buttonBoundingRect.bottom,
left: buttonBoundingRect.right - buttonBoundingRect.width,
})
}
setShowDisplayOptionsMenu((show) => !show)
}, [])
@@ -52,24 +42,30 @@ const ContentListHeader = ({
</div>
<div className="flex">
<div className="relative" ref={displayOptionsContainerRef}>
<Disclosure open={showDisplayOptionsMenu} onChange={toggleDisplayOptionsMenu}>
<StyledDisplayOptionsButton $pressed={showDisplayOptionsMenu} ref={displayOptionsButtonRef}>
<Icon type="sort-descending" />
</StyledDisplayOptionsButton>
<DisclosurePanel>
{showDisplayOptionsMenu && displayOptionsMenuPosition && (
<DisplayOptionsMenuPortal
application={application}
closeDisplayOptionsMenu={toggleDisplayOptionsMenu}
containerRef={displayOptionsContainerRef}
isOpen={showDisplayOptionsMenu}
isFilesSmartView={isFilesSmartView}
top={displayOptionsMenuPosition.top}
left={displayOptionsMenuPosition.left}
/>
)}
</DisclosurePanel>
</Disclosure>
<button
className={classNames(
'bg-text-padding flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border text-neutral hover:bg-contrast focus:bg-contrast',
showDisplayOptionsMenu && 'bg-contrast',
)}
onClick={toggleDisplayOptionsMenu}
ref={displayOptionsButtonRef}
>
<Icon type="sort-descending" />
</button>
<Popover
open={showDisplayOptionsMenu}
anchorElement={displayOptionsButtonRef.current}
togglePopover={toggleDisplayOptionsMenu}
align="start"
className="py-2"
>
<DisplayOptionsMenu
application={application}
closeDisplayOptionsMenu={toggleDisplayOptionsMenu}
isFilesSmartView={isFilesSmartView}
isOpen={showDisplayOptionsMenu}
/>
</Popover>
</div>
<button
className="ml-3 flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-transparent bg-info text-info-contrast hover:brightness-125"

View File

@@ -97,14 +97,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
}, [application, hideEditorIcon])
return (
<Menu
className={
'slide-down-animation z-index-dropdown-menu flex min-w-70 flex-col overflow-y-auto rounded border border-solid border-border bg-default py-1 text-sm shadow-main transition-transform duration-150'
}
a11yLabel="Notes list options menu"
closeMenu={closeDisplayOptionsMenu}
isOpen={isOpen}
>
<Menu className="text-sm" a11yLabel="Notes list options menu" closeMenu={closeDisplayOptionsMenu} isOpen={isOpen}>
<div className="my-1 px-3 text-xs font-semibold uppercase text-text">Sort by</div>
<MenuItem
className="py-2"

View File

@@ -1,63 +0,0 @@
import { createPortal } from 'react-dom'
import styled from 'styled-components'
import { DisplayOptionsMenuPositionProps, DisplayOptionsMenuProps } from './DisplayOptionsMenuProps'
import DisplayOptionsMenu from './DisplayOptionsMenu'
import { useRef, useEffect, RefObject } from 'react'
type Props = DisplayOptionsMenuProps &
DisplayOptionsMenuPositionProps & {
containerRef: RefObject<HTMLDivElement>
}
const PositionedOptionsMenu = styled.div<DisplayOptionsMenuPositionProps>`
position: absolute;
top: ${(props) => props.top}px;
left: ${(props) => props.left}px;
z-index: var(--z-index-dropdown-menu);
`
const DisplayOptionsMenuPortal = ({
application,
closeDisplayOptionsMenu,
containerRef,
isFilesSmartView,
isOpen,
top,
left,
}: Props) => {
const menuRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const closeIfClickedOutside = (event: MouseEvent) => {
const isDescendantOfMenu = menuRef.current?.contains(event.target as Node)
const isDescendantOfContainer = containerRef.current?.contains(event.target as Node)
if (!isDescendantOfMenu && !isDescendantOfContainer) {
closeDisplayOptionsMenu()
}
}
document.addEventListener('click', closeIfClickedOutside, { capture: true })
return () => {
document.removeEventListener('click', closeIfClickedOutside, {
capture: true,
})
}
}, [closeDisplayOptionsMenu, containerRef])
return createPortal(
<PositionedOptionsMenu top={top} left={left} ref={menuRef}>
<div className="sn-component">
<DisplayOptionsMenu
application={application}
closeDisplayOptionsMenu={closeDisplayOptionsMenu}
isFilesSmartView={isFilesSmartView}
isOpen={isOpen}
/>
</div>
</PositionedOptionsMenu>,
document.body,
)
}
export default DisplayOptionsMenuPortal