diff --git a/app/assets/javascripts/components/NotesContextMenu.tsx b/app/assets/javascripts/components/NotesContextMenu.tsx index abd4fa354..3afd7df89 100644 --- a/app/assets/javascripts/components/NotesContextMenu.tsx +++ b/app/assets/javascripts/components/NotesContextMenu.tsx @@ -2,13 +2,19 @@ import { AppState } from '@/ui_models/app_state'; import { toDirective, useCloseOnBlur, useCloseOnClickOutside } from './utils'; import { observer } from 'mobx-react-lite'; import { NotesOptions } from './NotesOptions'; -import { useRef } from 'preact/hooks'; +import { useCallback, useEffect, useRef } from 'preact/hooks'; type Props = { appState: AppState; }; const NotesContextMenu = observer(({ appState }: Props) => { + const { + contextMenuOpen, + contextMenuPosition, + contextMenuMaxHeight, + } = appState.notes; + const contextMenuRef = useRef(); const [closeOnBlur] = useCloseOnBlur( contextMenuRef, @@ -20,13 +26,24 @@ const NotesContextMenu = observer(({ appState }: Props) => { (open: boolean) => appState.notes.setContextMenuOpen(open) ); - return appState.notes.contextMenuOpen ? ( + const reloadContextMenuLayout = useCallback(() => { + appState.notes.reloadContextMenuLayout(); + }, [appState.notes]); + + useEffect(() => { + window.addEventListener('resize', reloadContextMenuLayout); + return () => { + window.removeEventListener('resize', reloadContextMenuLayout); + }; + }, [reloadContextMenuLayout]); + + return contextMenuOpen ? (
diff --git a/app/assets/javascripts/components/SearchOptions.tsx b/app/assets/javascripts/components/SearchOptions.tsx index 8315c3a91..ddd320d93 100644 --- a/app/assets/javascripts/components/SearchOptions.tsx +++ b/app/assets/javascripts/components/SearchOptions.tsx @@ -11,6 +11,7 @@ import { } from '@reach/disclosure'; import { Switch } from './Switch'; import { observer } from 'mobx-react-lite'; +import { useEffect } from 'react'; type Props = { appState: AppState; @@ -45,16 +46,27 @@ const SearchOptions = observer(({ appState }: Props) => { } } + const updateWidthAndPosition = () => { + const rect = buttonRef.current.getBoundingClientRect(); + setMaxWidth(rect.right - 16); + setPosition({ + top: rect.bottom, + right: document.body.clientWidth - rect.right, + }); + }; + + useEffect(() => { + window.addEventListener('resize', updateWidthAndPosition); + return () => { + window.removeEventListener('resize', updateWidthAndPosition); + }; + }, []); + return ( { - const rect = buttonRef.current.getBoundingClientRect(); - setMaxWidth(rect.right - 16); - setPosition({ - top: rect.bottom, - right: document.body.clientWidth - rect.right, - }); + updateWidthAndPosition(); setOpen(!open); }} > diff --git a/app/assets/javascripts/ui_models/app_state/notes_state.ts b/app/assets/javascripts/ui_models/app_state/notes_state.ts index 66d1ad024..9b59fc6c9 100644 --- a/app/assets/javascripts/ui_models/app_state/notes_state.ts +++ b/app/assets/javascripts/ui_models/app_state/notes_state.ts @@ -28,6 +28,7 @@ export class NotesState { top: 0, left: 0, }; + contextMenuClickLocation: { x: number, y: number } = { x: 0, y: 0 }; contextMenuMaxHeight: number | 'auto' = 'auto'; showProtectedWarning = false; @@ -47,6 +48,7 @@ export class NotesState { trashedNotesCount: computed, setContextMenuOpen: action, + setContextMenuClickLocation: action, setContextMenuPosition: action, setContextMenuMaxHeight: action, setShowProtectedWarning: action, @@ -183,6 +185,10 @@ export class NotesState { this.contextMenuOpen = open; } + setContextMenuClickLocation(location: { x: number, y: number }): void { + this.contextMenuClickLocation = location; + } + setContextMenuPosition(position: { top?: number; left: number; @@ -195,6 +201,60 @@ export class NotesState { this.contextMenuMaxHeight = maxHeight; } + reloadContextMenuLayout(): void { + const { clientHeight } = document.documentElement; + const defaultFontSize = window.getComputedStyle( + document.documentElement + ).fontSize; + const maxContextMenuHeight = parseFloat(defaultFontSize) * 30; + const footerHeight = 32; + + // Open up-bottom is default behavior + let openUpBottom = true; + + const bottomSpace = clientHeight - footerHeight - this.contextMenuClickLocation.y; + const upSpace = this.contextMenuClickLocation.y; + + // If not enough space to open up-bottom + if (maxContextMenuHeight > bottomSpace) { + // If there's enough space, open bottom-up + if (upSpace > maxContextMenuHeight) { + openUpBottom = false; + this.setContextMenuMaxHeight( + 'auto' + ); + // Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space + } else { + if (upSpace > bottomSpace) { + this.setContextMenuMaxHeight( + upSpace - 2 + ); + openUpBottom = false; + } else { + this.setContextMenuMaxHeight( + bottomSpace - 2 + ); + } + } + } else { + this.setContextMenuMaxHeight( + 'auto' + ); + } + + if (openUpBottom) { + this.setContextMenuPosition({ + top: this.contextMenuClickLocation.y, + left: this.contextMenuClickLocation.x, + }); + } else { + this.setContextMenuPosition({ + bottom: clientHeight - this.contextMenuClickLocation.y, + left: this.contextMenuClickLocation.x, + }); + } + } + async changeSelectedNotes( mutate: (mutator: NoteMutator) => void ): Promise { diff --git a/app/assets/javascripts/views/notes/notes_view.ts b/app/assets/javascripts/views/notes/notes_view.ts index 6ed9e41c1..5ad8a32c8 100644 --- a/app/assets/javascripts/views/notes/notes_view.ts +++ b/app/assets/javascripts/views/notes/notes_view.ts @@ -310,58 +310,11 @@ class NotesViewCtrl extends PureViewCtrl { await this.selectNote(note, true); } if (this.state.selectedNotes[note.uuid]) { - const { clientHeight } = document.documentElement; - const defaultFontSize = window.getComputedStyle( - document.documentElement - ).fontSize; - const maxContextMenuHeight = parseFloat(defaultFontSize) * 30; - const footerHeight = 32; - - // Open up-bottom is default behavior - let openUpBottom = true; - - const bottomSpace = clientHeight - footerHeight - e.clientY; - const upSpace = e.clientY; - - // If not enough space to open up-bottom - if (maxContextMenuHeight > bottomSpace) { - // If there's enough space, open bottom-up - if (upSpace > maxContextMenuHeight) { - openUpBottom = false; - this.appState.notes.setContextMenuMaxHeight( - 'auto' - ); - // Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space - } else { - if (upSpace > bottomSpace) { - this.appState.notes.setContextMenuMaxHeight( - upSpace - 2 - ); - openUpBottom = false; - } else { - this.appState.notes.setContextMenuMaxHeight( - bottomSpace - 2 - ); - } - } - } else { - this.appState.notes.setContextMenuMaxHeight( - 'auto' - ); - } - - if (openUpBottom) { - this.appState.notes.setContextMenuPosition({ - top: e.clientY, - left: e.clientX, - }); - } else { - this.appState.notes.setContextMenuPosition({ - bottom: clientHeight - e.clientY, - left: e.clientX, - }); - } - + this.appState.notes.setContextMenuClickLocation({ + x: e.clientX, + y: e.clientY, + }); + this.appState.notes.reloadContextMenuLayout(); this.appState.notes.setContextMenuOpen(true); } }