chore: fix note options menu visibility in plain editor on mobile [skip e2e]

This commit is contained in:
Aman Harwara
2023-10-29 14:00:45 +05:30
parent 12ccd22d78
commit 0f73ea8c4e
4 changed files with 66 additions and 38 deletions

View File

@@ -29,7 +29,7 @@ import {
VaultUserServiceEvent, VaultUserServiceEvent,
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services' import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services'
import { ChangeEventHandler, createRef, CSSProperties, KeyboardEventHandler, RefObject } from 'react' import { ChangeEventHandler, createRef, CSSProperties, FocusEvent, KeyboardEventHandler, RefObject } from 'react'
import { SuperEditor } from '../SuperEditor/SuperEditor' import { SuperEditor } from '../SuperEditor/SuperEditor'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle' import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer' import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
@@ -826,7 +826,10 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
this.setState({ plainEditorFocused: true }) this.setState({ plainEditorFocused: true })
} }
onPlainBlur = () => { onPlainBlur = (event: FocusEvent) => {
if (event.relatedTarget?.id === ElementIds.NoteOptionsButton) {
return
}
this.setState({ plainEditorFocused: false }) this.setState({ plainEditorFocused: false })
} }
@@ -937,30 +940,33 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
{pluralize(this.state.conflictedNotes.length, 'conflict', 'conflicts')} {pluralize(this.state.conflictedNotes.length, 'conflict', 'conflicts')}
</Button> </Button>
)} )}
{renderHeaderOptions && ( <div className="note-view-options-buttons flex items-center gap-3">
<div className="note-view-options-buttons flex items-center gap-3"> {!this.state.readonly && renderHeaderOptions && (
{!this.state.readonly && ( <>
<> <LinkedItemsButton
<LinkedItemsButton linkingController={this.application.linkingController}
linkingController={this.application.linkingController} onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction} />
/> <ChangeEditorButton
<ChangeEditorButton noteViewController={this.controller}
noteViewController={this.controller} onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction} />
/> <PinNoteButton
<PinNoteButton notesController={this.application.notesController}
notesController={this.application.notesController} onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction} />
/> </>
</> )}
)} <NotesOptionsPanel
<NotesOptionsPanel notesController={this.application.notesController}
notesController={this.application.notesController} onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction} onButtonBlur={() => {
/> this.setState({
</div> plainEditorFocused: false,
)} })
}}
/>
</div>
</div> </div>
<div className="mb-1 mt-2.5 md:hidden"> <div className="mb-1 mt-2.5 md:hidden">
<CollaborationInfoHUD item={this.note} /> <CollaborationInfoHUD item={this.note} />

View File

@@ -16,7 +16,16 @@ import {
PrefDefaults, PrefDefaults,
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { TAB_COMMAND } from '@standardnotes/ui-services' import { TAB_COMMAND } from '@standardnotes/ui-services'
import { ChangeEventHandler, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react' import {
ChangeEventHandler,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
FocusEvent,
} from 'react'
import { NoteViewController } from '../Controller/NoteViewController' import { NoteViewController } from '../Controller/NoteViewController'
type Props = { type Props = {
@@ -25,7 +34,7 @@ type Props = {
controller: NoteViewController controller: NoteViewController
locked: boolean locked: boolean
onFocus: () => void onFocus: () => void
onBlur: () => void onBlur: (event: FocusEvent) => void
} }
export type PlainEditorInterface = { export type PlainEditorInterface = {
@@ -106,13 +115,16 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
onFocus() onFocus()
}, [application, isAdjustingMobileCursor, lastEditorFocusEventSource, onFocus]) }, [application, isAdjustingMobileCursor, lastEditorFocusEventSource, onFocus])
const onContentBlur = useCallback(() => { const onContentBlur = useCallback(
if (lastEditorFocusEventSource.current) { (event: FocusEvent) => {
application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: lastEditorFocusEventSource }) if (lastEditorFocusEventSource.current) {
} application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: lastEditorFocusEventSource })
lastEditorFocusEventSource.current = undefined }
onBlur() lastEditorFocusEventSource.current = undefined
}, [application, lastEditorFocusEventSource, onBlur]) onBlur(event)
},
[application, lastEditorFocusEventSource, onBlur],
)
const scrollMobileCursorIntoViewAfterWebviewResize = useCallback(() => { const scrollMobileCursorIntoViewAfterWebviewResize = useCallback(() => {
if (needsAdjustMobileCursor.current) { if (needsAdjustMobileCursor.current) {

View File

@@ -1,17 +1,19 @@
import { useCallback, useRef, useState } from 'react' import { FocusEvent, useCallback, useRef, useState } from 'react'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import NotesOptions from './NotesOptions' import NotesOptions from './NotesOptions'
import { NotesController } from '@/Controllers/NotesController/NotesController' import { NotesController } from '@/Controllers/NotesController/NotesController'
import Popover from '../Popover/Popover' import Popover from '../Popover/Popover'
import RoundIconButton from '../Button/RoundIconButton' import RoundIconButton from '../Button/RoundIconButton'
import Menu from '../Menu/Menu' import Menu from '../Menu/Menu'
import { ElementIds } from '@/Constants/ElementIDs'
type Props = { type Props = {
notesController: NotesController notesController: NotesController
onClickPreprocessing?: () => Promise<void> onClickPreprocessing?: () => Promise<void>
onButtonBlur?: (event: FocusEvent) => void
} }
const NotesOptionsPanel = ({ notesController, onClickPreprocessing }: Props) => { const NotesOptionsPanel = ({ notesController, onClickPreprocessing, onButtonBlur }: Props) => {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const buttonRef = useRef<HTMLButtonElement>(null) const buttonRef = useRef<HTMLButtonElement>(null)
@@ -30,7 +32,14 @@ const NotesOptionsPanel = ({ notesController, onClickPreprocessing }: Props) =>
return ( return (
<> <>
<RoundIconButton label="Note options menu" onClick={toggleMenu} ref={buttonRef} icon="more" /> <RoundIconButton
id={ElementIds.NoteOptionsButton}
label="Note options menu"
onClick={toggleMenu}
onBlur={onButtonBlur}
ref={buttonRef}
icon="more"
/>
<Popover <Popover
title="Note options" title="Note options"
disableClickOutside={disableClickOutside} disableClickOutside={disableClickOutside}

View File

@@ -8,6 +8,7 @@ export const ElementIds = {
NavigationColumn: 'navigation', NavigationColumn: 'navigation',
NoteTextEditor: 'note-text-editor', NoteTextEditor: 'note-text-editor',
NoteTitleEditor: 'note-title-editor', NoteTitleEditor: 'note-title-editor',
NoteOptionsButton: 'note-options-button',
RootId: 'app-group-root', RootId: 'app-group-root',
NoteStatusTooltip: 'note-status-tooltip', NoteStatusTooltip: 'note-status-tooltip',
ItemLinkAutocompleteInput: 'item-link-autocomplete-input', ItemLinkAutocompleteInput: 'item-link-autocomplete-input',