From 8c8f045b9a53d910bcbf1dc5ee19cf92addf252e Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 22 Nov 2022 19:16:59 +0530 Subject: [PATCH] fix: super editor popover menus (#2041) --- .../FileContextMenu/FileContextMenu.tsx | 1 - .../Components/Footer/AccountMenuButton.tsx | 9 ++- .../BlockPickerPlugin/BlockPickerPlugin.tsx | 6 +- .../SuperEditor/Plugins/ClassNames.ts | 2 +- .../ItemSelectionPlugin.tsx | 6 +- .../NotesContextMenu/NotesContextMenu.tsx | 1 - .../Popover/GetPositionedPopoverStyles.ts | 60 ++++++++++++++----- .../Components/Popover/Popover.tsx | 2 + .../Popover/PositionedPopoverContent.tsx | 17 ++---- .../javascripts/Components/Popover/Types.ts | 1 + .../Components/Popover/Utils/Collisions.ts | 11 ++++ .../Components/Popover/Utils/Rect.ts | 9 --- 12 files changed, 75 insertions(+), 50 deletions(-) diff --git a/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx b/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx index 885ff14dd..192b72e5d 100644 --- a/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx +++ b/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx @@ -20,7 +20,6 @@ const FileContextMenu: FunctionComponent = observer(({ filesController, s open={showFileContextMenu} anchorPoint={fileContextMenuLocation} togglePopover={() => setShowFileContextMenu(!showFileContextMenu)} - side="right" align="start" className="py-2" > diff --git a/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx b/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx index c991edf78..24b8c5b9d 100644 --- a/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx +++ b/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx @@ -40,7 +40,14 @@ const AccountMenuButton = ({ - + { setPopoverOpen((prevValue) => !prevValue) }} disableMobileFullscreenTakeover={true} side={isMobileScreen() ? 'top' : 'bottom'} + maxHeight={(mh) => mh / 2} >
    diff --git a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ClassNames.ts b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ClassNames.ts index 3dd199e15..422e3684d 100644 --- a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ClassNames.ts +++ b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ClassNames.ts @@ -2,7 +2,7 @@ import { classNames } from '@/Utils/ConcatenateClassNames' export const PopoverClassNames = classNames( 'z-dropdown-menu w-full', - 'cursor-auto flex-col overflow-y-auto rounded bg-default md:h-auto h-auto overflow-y-scroll', + 'cursor-auto flex-col overflow-y-auto rounded bg-default h-auto', ) export const PopoverItemClassNames = classNames( diff --git a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ItemSelectionPlugin/ItemSelectionPlugin.tsx b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ItemSelectionPlugin/ItemSelectionPlugin.tsx index 0b9055005..9c34c6cd9 100644 --- a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ItemSelectionPlugin/ItemSelectionPlugin.tsx +++ b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/ItemSelectionPlugin/ItemSelectionPlugin.tsx @@ -105,16 +105,14 @@ export const ItemSelectionPlugin: FunctionComponent = ({ currentNote }) = return ( { setPopoverOpen((prevValue) => !prevValue) }} disableMobileFullscreenTakeover={true} side={isMobileScreen() ? 'top' : 'bottom'} + maxHeight={(mh) => mh / 2} >
      diff --git a/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx b/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx index 1e607ed3e..92e869264 100644 --- a/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx +++ b/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx @@ -38,7 +38,6 @@ const NotesContextMenu = ({ }} className="py-2" open={contextMenuOpen} - side="right" togglePopover={closeMenu} >
      diff --git a/packages/web/src/javascripts/Components/Popover/GetPositionedPopoverStyles.ts b/packages/web/src/javascripts/Components/Popover/GetPositionedPopoverStyles.ts index f66a74c47..ebf938808 100644 --- a/packages/web/src/javascripts/Components/Popover/GetPositionedPopoverStyles.ts +++ b/packages/web/src/javascripts/Components/Popover/GetPositionedPopoverStyles.ts @@ -1,18 +1,31 @@ import { MediaQueryBreakpoints } from '@/Hooks/useMediaQuery' +import { isMobileScreen } from '@/Utils' import { CSSProperties } from 'react' import { PopoverAlignment, PopoverSide } from './Types' -import { OppositeSide, checkCollisions, getNonCollidingSide, getNonCollidingAlignment } from './Utils/Collisions' -import { getPositionedPopoverRect } from './Utils/Rect' +import { OppositeSide, checkCollisions, getNonCollidingAlignment, getOverflows } from './Utils/Collisions' +import { getAppRect, getPopoverMaxHeight, getPositionedPopoverRect } from './Utils/Rect' + +const getStylesFromRect = ( + rect: DOMRect, + options: { + disableMobileFullscreenTakeover?: boolean + maxHeight?: number | 'none' + }, +): CSSProperties => { + const { disableMobileFullscreenTakeover = false, maxHeight = 'none' } = options + + const canApplyMaxHeight = maxHeight !== 'none' && (!isMobileScreen() || disableMobileFullscreenTakeover) -const getStylesFromRect = (rect: DOMRect, disableMobileFullscreenTakeover?: boolean): CSSProperties => { return { willChange: 'transform', transform: `translate(${rect.x}px, ${rect.y}px)`, - ...(disableMobileFullscreenTakeover - ? { - maxWidth: `${window.innerWidth - rect.x * 2}px`, - } - : {}), + visibility: 'visible', + ...(canApplyMaxHeight && { + maxHeight: `${maxHeight}px`, + }), + ...(disableMobileFullscreenTakeover && { + maxWidth: `${window.innerWidth - rect.x * 2}px`, + }), } } @@ -23,6 +36,7 @@ type Options = { popoverRect?: DOMRect side: PopoverSide disableMobileFullscreenTakeover?: boolean + maxHeightFunction?: (calculatedMaxHeight: number) => number } export const getPositionedPopoverStyles = ({ @@ -32,31 +46,45 @@ export const getPositionedPopoverStyles = ({ popoverRect, side, disableMobileFullscreenTakeover, -}: Options): [CSSProperties | null, PopoverSide, PopoverAlignment] => { + maxHeightFunction, +}: Options): CSSProperties | null => { if (!popoverRect || !anchorRect) { - return [null, side, align] + return null } const matchesMediumBreakpoint = matchMedia(MediaQueryBreakpoints.md).matches if (!matchesMediumBreakpoint && !disableMobileFullscreenTakeover) { - return [null, side, align] + return null } const rectForPreferredSide = getPositionedPopoverRect(popoverRect, anchorRect, side, align) const preferredSideRectCollisions = checkCollisions(rectForPreferredSide, documentRect) + const preferredSideOverflows = getOverflows(rectForPreferredSide, documentRect) const oppositeSide = OppositeSide[side] const rectForOppositeSide = getPositionedPopoverRect(popoverRect, anchorRect, oppositeSide, align) - const oppositeSideRectCollisions = checkCollisions(rectForOppositeSide, documentRect) + const oppositeSideOverflows = getOverflows(rectForOppositeSide, documentRect) - const finalSide = getNonCollidingSide(side, preferredSideRectCollisions, oppositeSideRectCollisions) - const finalAlignment = getNonCollidingAlignment(finalSide, align, preferredSideRectCollisions, { + const sideWithLessOverflows = preferredSideOverflows[side] < oppositeSideOverflows[oppositeSide] ? side : oppositeSide + const finalAlignment = getNonCollidingAlignment(sideWithLessOverflows, align, preferredSideRectCollisions, { popoverRect, buttonRect: anchorRect, documentRect, }) - const finalPositionedRect = getPositionedPopoverRect(popoverRect, anchorRect, finalSide, finalAlignment) + const finalPositionedRect = getPositionedPopoverRect(popoverRect, anchorRect, sideWithLessOverflows, finalAlignment) - return [getStylesFromRect(finalPositionedRect, disableMobileFullscreenTakeover), finalSide, finalAlignment] + let maxHeight = getPopoverMaxHeight( + getAppRect(), + anchorRect, + sideWithLessOverflows, + finalAlignment, + disableMobileFullscreenTakeover, + ) + + if (maxHeightFunction && typeof maxHeight === 'number') { + maxHeight = maxHeightFunction(maxHeight) + } + + return getStylesFromRect(finalPositionedRect, { disableMobileFullscreenTakeover, maxHeight }) } diff --git a/packages/web/src/javascripts/Components/Popover/Popover.tsx b/packages/web/src/javascripts/Components/Popover/Popover.tsx index 69ff3223d..5508ca521 100644 --- a/packages/web/src/javascripts/Components/Popover/Popover.tsx +++ b/packages/web/src/javascripts/Components/Popover/Popover.tsx @@ -41,6 +41,7 @@ const Popover = ({ togglePopover, disableClickOutside, disableMobileFullscreenTakeover, + maxHeight, }: Props) => { const popoverId = useRef(UuidGenerator.GenerateUuid()) @@ -100,6 +101,7 @@ const Popover = ({ togglePopover={togglePopover} disableClickOutside={disableClickOutside} disableMobileFullscreenTakeover={disableMobileFullscreenTakeover} + maxHeight={maxHeight} > {children} diff --git a/packages/web/src/javascripts/Components/Popover/PositionedPopoverContent.tsx b/packages/web/src/javascripts/Components/Popover/PositionedPopoverContent.tsx index 0f4769245..01c0b8a85 100644 --- a/packages/web/src/javascripts/Components/Popover/PositionedPopoverContent.tsx +++ b/packages/web/src/javascripts/Components/Popover/PositionedPopoverContent.tsx @@ -7,7 +7,6 @@ import Portal from '../Portal/Portal' import HorizontalSeparator from '../Shared/HorizontalSeparator' import { getPositionedPopoverStyles } from './GetPositionedPopoverStyles' import { PopoverContentProps } from './Types' -import { getPopoverMaxHeight, getAppRect } from './Utils/Rect' import { usePopoverCloseOnClickOutside } from './Utils/usePopoverCloseOnClickOutside' import { useDisableBodyScrollOnMobile } from '@/Hooks/useDisableBodyScrollOnMobile' import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' @@ -25,6 +24,7 @@ const PositionedPopoverContent = ({ togglePopover, disableClickOutside, disableMobileFullscreenTakeover, + maxHeight, }: PopoverContentProps) => { const [popoverElement, setPopoverElement] = useState(null) const popoverRect = useAutoElementRect(popoverElement) @@ -39,13 +39,14 @@ const PositionedPopoverContent = ({ const documentRect = useDocumentRect() const isDesktopScreen = useMediaQuery(MediaQueryBreakpoints.md) - const [styles, positionedSide, positionedAlignment] = getPositionedPopoverStyles({ + const styles = getPositionedPopoverStyles({ align, anchorRect, documentRect, popoverRect: popoverRect ?? popoverElement?.getBoundingClientRect(), side, disableMobileFullscreenTakeover: disableMobileFullscreenTakeover, + maxHeightFunction: maxHeight, }) usePopoverCloseOnClickOutside({ @@ -79,20 +80,10 @@ const PositionedPopoverContent = ({ !disableMobileFullscreenTakeover && 'h-full', overrideZIndex ? overrideZIndex : 'z-dropdown-menu', !isDesktopScreen && !disableMobileFullscreenTakeover ? 'pt-safe-top pb-safe-bottom' : '', - !styles && 'md:invisible', + isDesktopScreen || disableMobileFullscreenTakeover ? 'invisible' : '', )} style={{ ...styles, - maxHeight: styles - ? getPopoverMaxHeight( - getAppRect(documentRect), - anchorRect, - positionedSide, - positionedAlignment, - disableMobileFullscreenTakeover, - ) - : '', - top: !isDesktopScreen ? `${document.documentElement.scrollTop}px` : '', }} ref={setPopoverElement} data-popover={id} diff --git a/packages/web/src/javascripts/Components/Popover/Types.ts b/packages/web/src/javascripts/Components/Popover/Types.ts index d1611e5b2..6e1ecf2c8 100644 --- a/packages/web/src/javascripts/Components/Popover/Types.ts +++ b/packages/web/src/javascripts/Components/Popover/Types.ts @@ -39,6 +39,7 @@ type CommonPopoverProps = { className?: string disableClickOutside?: boolean disableMobileFullscreenTakeover?: boolean + maxHeight?: (calculatedMaxHeight: number) => number } export type PopoverContentProps = CommonPopoverProps & { diff --git a/packages/web/src/javascripts/Components/Popover/Utils/Collisions.ts b/packages/web/src/javascripts/Components/Popover/Utils/Collisions.ts index 4b28bcdcf..0a743aa02 100644 --- a/packages/web/src/javascripts/Components/Popover/Utils/Collisions.ts +++ b/packages/web/src/javascripts/Components/Popover/Utils/Collisions.ts @@ -8,6 +8,17 @@ export const OppositeSide: Record = { right: 'left', } +export const getOverflows = (popoverRect: DOMRect, documentRect: DOMRect): Record => { + const overflows = { + top: documentRect.top - popoverRect.top, + bottom: popoverRect.height - (documentRect.bottom - popoverRect.top), + left: documentRect.left - popoverRect.left, + right: popoverRect.right - documentRect.right, + } + + return overflows +} + export const checkCollisions = (popoverRect: DOMRect, containerRect: DOMRect): RectCollisions => { const appRect = getAppRect(containerRect) diff --git a/packages/web/src/javascripts/Components/Popover/Utils/Rect.ts b/packages/web/src/javascripts/Components/Popover/Utils/Rect.ts index 9dfc84bde..f12559c19 100644 --- a/packages/web/src/javascripts/Components/Popover/Utils/Rect.ts +++ b/packages/web/src/javascripts/Components/Popover/Utils/Rect.ts @@ -49,15 +49,6 @@ export const getPopoverMaxHeight = ( return appRect.height - constraint - MarginFromAppBorderInPX } -export const getMaxHeightAdjustedRect = (rect: DOMRect, maxHeight: number) => { - return DOMRect.fromRect({ - width: rect.width, - height: rect.height < maxHeight ? rect.height : maxHeight, - x: rect.x, - y: rect.y, - }) -} - export const getAppRect = (updatedDocumentRect?: DOMRect) => { const footerRect = document.querySelector('footer')?.getBoundingClientRect() const documentRect = updatedDocumentRect ? updatedDocumentRect : document.documentElement.getBoundingClientRect()