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,13 +1,10 @@
import { WebApplication } from '@/Application/Application'
import { ViewControllerManager } from '@/Services/ViewControllerManager'
import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants/Constants'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure'
import VisuallyHidden from '@reach/visually-hidden'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useRef, useState } from 'react'
import { FunctionComponent, useCallback, useRef, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import ChangeEditorMenu from './ChangeEditorMenu'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import Popover from '../Popover/Popover'
type Props = {
application: WebApplication
@@ -22,89 +19,38 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
}: Props) => {
const note = viewControllerManager.notesController.firstSelectedNote
const [isOpen, setIsOpen] = useState(false)
const [isVisible, setIsVisible] = useState(false)
const [position, setPosition] = useState({
top: 0,
right: 0,
})
const [maxHeight, setMaxHeight] = useState<number | 'auto'>('auto')
const buttonRef = useRef<HTMLButtonElement>(null)
const panelRef = useRef<HTMLDivElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
const [closeOnBlur] = useCloseOnBlur(containerRef, setIsOpen)
const toggleChangeEditorMenu = async () => {
const rect = buttonRef.current?.getBoundingClientRect()
if (rect) {
const { clientHeight } = document.documentElement
const footerElementRect = document.getElementById('footer-bar')?.getBoundingClientRect()
const footerHeightInPx = footerElementRect?.height
if (footerHeightInPx) {
setMaxHeight(clientHeight - rect.bottom - footerHeightInPx - MENU_MARGIN_FROM_APP_BORDER)
}
setPosition({
top: rect.bottom,
right: document.body.clientWidth - rect.right,
})
const newOpenState = !isOpen
if (newOpenState && onClickPreprocessing) {
await onClickPreprocessing()
}
setIsOpen(newOpenState)
setTimeout(() => {
setIsVisible(newOpenState)
})
const toggleMenu = useCallback(async () => {
const willMenuOpen = !isOpen
if (willMenuOpen && onClickPreprocessing) {
await onClickPreprocessing()
}
}
setIsOpen(willMenuOpen)
}, [onClickPreprocessing, isOpen])
return (
<div ref={containerRef}>
<Disclosure open={isOpen} onChange={toggleChangeEditorMenu}>
<DisclosureButton
onKeyDown={(event) => {
if (event.key === 'Escape') {
setIsOpen(false)
}
<button
className="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"
title="Change note type"
aria-label="Change note type"
onClick={toggleMenu}
ref={buttonRef}
>
<Icon type="dashboard" />
</button>
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="pt-2 md:pt-0">
<ChangeEditorMenu
application={application}
isVisible={isOpen}
note={note}
closeMenu={() => {
setIsOpen(false)
}}
onBlur={closeOnBlur}
ref={buttonRef}
className="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"
>
<VisuallyHidden>Change note type</VisuallyHidden>
<Icon type="dashboard" className="block" />
</DisclosureButton>
<DisclosurePanel
onKeyDown={(event) => {
if (event.key === 'Escape') {
setIsOpen(false)
buttonRef.current?.focus()
}
}}
ref={panelRef}
style={{
...position,
maxHeight,
}}
className="slide-down-animation max-h-120 fixed flex min-w-68 max-w-xs flex-col overflow-y-auto rounded bg-default shadow-main transition-transform duration-150"
onBlur={closeOnBlur}
>
{isOpen && (
<ChangeEditorMenu
closeOnBlur={closeOnBlur}
application={application}
isVisible={isVisible}
note={note}
closeMenu={() => {
setIsOpen(false)
}}
/>
)}
</DisclosurePanel>
</Disclosure>
/>
</Popover>
</div>
)
}

View File

@@ -14,7 +14,7 @@ import {
SNNote,
TransactionalMutation,
} from '@standardnotes/snjs'
import { Fragment, FunctionComponent, useCallback, useEffect, useState } from 'react'
import { Fragment, FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react'
import { EditorMenuGroup } from '@/Components/NotesOptions/EditorMenuGroup'
import { EditorMenuItem } from '@/Components/NotesOptions/EditorMenuItem'
import { createEditorMenuGroups } from './createEditorMenuGroups'
@@ -28,7 +28,6 @@ import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/Premium
type ChangeEditorMenuProps = {
application: WebApplication
closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void
closeMenu: () => void
isVisible: boolean
note: SNNote | undefined
@@ -36,25 +35,17 @@ type ChangeEditorMenuProps = {
const getGroupId = (group: EditorMenuGroup) => group.title.toLowerCase().replace(/\s/, '-')
const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
application,
closeOnBlur,
closeMenu,
isVisible,
note,
}) => {
const [editors] = useState<SNComponent[]>(() =>
application.componentManager.componentsForArea(ComponentArea.Editor).sort((a, b) => {
return a.displayName.toLowerCase() < b.displayName.toLowerCase() ? -1 : 1
}),
const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({ application, closeMenu, isVisible, note }) => {
const editors = useMemo(
() =>
application.componentManager.componentsForArea(ComponentArea.Editor).sort((a, b) => {
return a.displayName.toLowerCase() < b.displayName.toLowerCase() ? -1 : 1
}),
[application.componentManager],
)
const [groups, setGroups] = useState<EditorMenuGroup[]>([])
const groups = useMemo(() => createEditorMenuGroups(application, editors), [application, editors])
const [currentEditor, setCurrentEditor] = useState<SNComponent>()
useEffect(() => {
setGroups(createEditorMenuGroups(application, editors))
}, [application, editors])
useEffect(() => {
if (note) {
setCurrentEditor(application.componentManager.editorForNote(note))
@@ -195,7 +186,6 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
type={MenuItemType.RadioButton}
onClick={onClickEditorItem}
className={'flex-row-reverse py-2'}
onBlur={closeOnBlur}
checked={item.isEntitled ? isSelectedEditor(item) : undefined}
>
<div className="flex flex-grow items-center justify-between">