diff --git a/app/assets/javascripts/components/NotesOptions/ChangeEditorOption.tsx b/app/assets/javascripts/components/NotesOptions/ChangeEditorOption.tsx index d95ffa3ca..115d46be4 100644 --- a/app/assets/javascripts/components/NotesOptions/ChangeEditorOption.tsx +++ b/app/assets/javascripts/components/NotesOptions/ChangeEditorOption.tsx @@ -27,7 +27,7 @@ import { TransactionalMutation, } from '@standardnotes/snjs'; import { FunctionComponent } from 'preact'; -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; import { Icon } from '../Icon'; import { createEditorMenuGroups } from './changeEditor/createEditorMenuGroups'; import { EditorAccordionMenu } from './changeEditor/EditorAccordionMenu'; @@ -54,6 +54,87 @@ export type EditorMenuItem = { export type EditorMenuGroup = AccordionMenuGroup; +type MenuPositionStyle = { + top?: number | 'auto'; + right?: number | 'auto'; + bottom: number | 'auto'; + left?: number | 'auto'; +}; + +const calculateMenuPosition = ( + button: HTMLButtonElement | null, + menu?: HTMLDivElement | null +): MenuPositionStyle | undefined => { + const defaultFontSize = window.getComputedStyle( + document.documentElement + ).fontSize; + + const maxChangeEditorMenuSize = + parseFloat(defaultFontSize) * MAX_MENU_SIZE_MULTIPLIER; + + const { clientWidth, clientHeight } = document.documentElement; + + const buttonRect = button?.getBoundingClientRect(); + + const buttonParentRect = button?.parentElement?.getBoundingClientRect(); + + const menuBoundingRect = menu?.getBoundingClientRect(); + + const footerElementRect = document + .getElementById('footer-bar') + ?.getBoundingClientRect(); + + const footerHeightInPx = footerElementRect?.height ?? 0; + + let position: MenuPositionStyle = { + bottom: 'auto', + }; + + if (buttonRect && buttonParentRect) { + let positionBottom = + clientHeight - buttonRect.bottom - buttonRect.height / 2; + + if (positionBottom < footerHeightInPx) { + positionBottom = footerHeightInPx + MENU_MARGIN_FROM_APP_BORDER; + } + + if (buttonRect.right + maxChangeEditorMenuSize > clientWidth) { + position = { + bottom: positionBottom, + right: clientWidth - buttonRect.left, + }; + } else { + position = { + bottom: positionBottom, + left: buttonRect.right, + }; + } + } + + if (menuBoundingRect && buttonRect) { + if (menuBoundingRect.y < MENU_MARGIN_FROM_APP_BORDER) { + if ( + buttonRect.right + maxChangeEditorMenuSize > + document.documentElement.clientWidth + ) { + return { + ...position, + top: MENU_MARGIN_FROM_APP_BORDER + buttonRect.top - buttonRect.height, + bottom: 'auto', + }; + } else { + return { + ...position, + top: MENU_MARGIN_FROM_APP_BORDER, + bottom: 'auto', + }; + } + } + } else { + return position; + } +}; + export const ChangeEditorOption: FunctionComponent = ({ application, appState, @@ -61,15 +142,11 @@ export const ChangeEditorOption: FunctionComponent = ({ note, }) => { const [changeEditorMenuOpen, setChangeEditorMenuOpen] = useState(false); - const [changeEditorMenuPosition, setChangeEditorMenuPosition] = useState<{ - top?: number | 'auto'; - right?: number | 'auto'; - bottom: number | 'auto'; - left?: number | 'auto'; - }>({ - right: 0, - bottom: 0, - }); + const [changeEditorMenuPosition, setChangeEditorMenuPosition] = + useState({ + right: 0, + bottom: 0, + }); const changeEditorMenuRef = useRef(null); const changeEditorButtonRef = useRef(null); const [editors] = useState(() => @@ -95,78 +172,28 @@ export const ChangeEditorOption: FunctionComponent = ({ }, [application, note]); const toggleChangeEditorMenu = () => { - const defaultFontSize = window.getComputedStyle( - document.documentElement - ).fontSize; - const maxChangeEditorMenuSize = - parseFloat(defaultFontSize) * MAX_MENU_SIZE_MULTIPLIER; - const { clientWidth, clientHeight } = document.documentElement; - const buttonRect = changeEditorButtonRef.current?.getBoundingClientRect(); - const buttonParentRect = - changeEditorButtonRef.current?.parentElement?.getBoundingClientRect(); - const footerElementRect = document - .getElementById('footer-bar') - ?.getBoundingClientRect(); - const footerHeightInPx = footerElementRect?.height; - - if (buttonRect && buttonParentRect && footerHeightInPx) { - let positionBottom = - clientHeight - buttonRect.bottom - buttonRect.height / 2; - - if (positionBottom < footerHeightInPx) { - positionBottom = footerHeightInPx + MENU_MARGIN_FROM_APP_BORDER; - } - - if (buttonRect.right + maxChangeEditorMenuSize > clientWidth) { - setChangeEditorMenuPosition({ - top: positionBottom - buttonParentRect.height / 2, - right: clientWidth - buttonRect.left, - bottom: 'auto', - }); - } else { - setChangeEditorMenuPosition({ - bottom: positionBottom, - left: buttonRect.right, - }); + if (!changeEditorMenuOpen) { + const menuPosition = calculateMenuPosition(changeEditorButtonRef.current); + if (menuPosition) { + setChangeEditorMenuPosition(menuPosition); } } setChangeEditorMenuOpen(!changeEditorMenuOpen); }; - useEffect(() => { + useLayoutEffect(() => { if (changeEditorMenuOpen) { - const defaultFontSize = window.getComputedStyle( - document.documentElement - ).fontSize; - const maxChangeEditorMenuSize = - parseFloat(defaultFontSize) * MAX_MENU_SIZE_MULTIPLIER; - const changeEditorMenuBoundingRect = - changeEditorMenuRef.current?.getBoundingClientRect(); - const buttonRect = changeEditorButtonRef.current?.getBoundingClientRect(); + const newMenuPosition = calculateMenuPosition( + changeEditorButtonRef.current, + changeEditorMenuRef.current + ); - if (changeEditorMenuBoundingRect && buttonRect) { - if (changeEditorMenuBoundingRect.y < MENU_MARGIN_FROM_APP_BORDER) { - if ( - buttonRect.right + maxChangeEditorMenuSize > - document.documentElement.clientWidth - ) { - setChangeEditorMenuPosition({ - ...changeEditorMenuPosition, - top: MENU_MARGIN_FROM_APP_BORDER + buttonRect.height, - bottom: 'auto', - }); - } else { - setChangeEditorMenuPosition({ - ...changeEditorMenuPosition, - top: MENU_MARGIN_FROM_APP_BORDER, - bottom: 'auto', - }); - } - } + if (newMenuPosition) { + setChangeEditorMenuPosition(newMenuPosition); } } - }, [changeEditorMenuOpen, changeEditorMenuPosition]); + }, [changeEditorMenuOpen]); const selectComponent = async (component: SNComponent | null) => { if (component) {