From baf77516feafcd5b6378d8c3d824727e8879a628 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Wed, 18 Jan 2023 01:00:23 +0530 Subject: [PATCH] refactor: mobile popover UX (#2140) --- .../AccountMenu/GeneralAccountMenu.tsx | 2 +- .../WorkspaceSwitcherOption.tsx | 1 + .../LockscreenWorkspaceSwitcher.tsx | 1 + .../ChangeEditor/ChangeEditorButton.tsx | 1 + .../Header/AddItemMenuButton.tsx | 1 + .../Header/ContentListHeader.tsx | 1 + .../ContentTableView/ContentTableView.tsx | 3 + .../FileContextMenu/FileContextMenu.tsx | 1 + .../FileContextMenu/FileOptionsPanel.tsx | 8 +- .../FilePreview/FilePreviewModal.tsx | 3 +- .../FileView/FileViewWithoutProtection.tsx | 5 +- .../Components/Footer/AccountMenuButton.tsx | 1 + .../Components/Footer/QuickSettingsButton.tsx | 1 + .../LinkedItems/LinkedFileMenuOptions.tsx | 27 ++-- .../LinkedItemBubblesContainer.tsx | 2 +- .../LinkedItems/LinkedItemsButton.tsx | 8 +- .../LinkedItems/LinkedItemsSectionItem.tsx | 1 + .../Components/NoteView/NoteView.tsx | 10 +- .../BlockPickerPlugin/BlockPickerPlugin.tsx | 1 + .../ItemSelectionPlugin.tsx | 1 + .../NotesContextMenu/NotesContextMenu.tsx | 1 + .../Components/NotesOptions/AddTagOption.tsx | 1 + .../NotesOptions/ChangeEditorOption.tsx | 1 + .../Listed/ListedActionsOption.tsx | 1 + .../NotesOptions/NotesOptionsPanel.tsx | 1 + .../NotesOptions/SuperNoteOptions.tsx | 1 + .../Popover/MobilePopoverContent.tsx | 92 +++++++++++++ .../Components/Popover/Popover.tsx | 29 +++- .../javascripts/Components/Popover/Types.ts | 19 ++- .../General/SmartViews/EditSmartViewModal.tsx | 1 + .../SmartViewBuilder/AddSmartViewModal.tsx | 1 + .../Components/Tags/TagContextMenu.tsx | 1 + .../javascripts/Constants/AnimationConfigs.ts | 126 ++++++------------ 33 files changed, 237 insertions(+), 117 deletions(-) create mode 100644 packages/web/src/javascripts/Components/Popover/MobilePopoverContent.tsx diff --git a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx index 17e1b01cd..0404629b8 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx @@ -89,7 +89,7 @@ const GeneralAccountMenu: FunctionComponent = ({ return ( <> -
+
Account
diff --git a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx index f2cccb304..44fc47645 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx @@ -32,6 +32,7 @@ const WorkspaceSwitcherOption: FunctionComponent = ({ mainApplicationGrou = ({ mainApplication Switch workspace = ({ iconClassName={`text-accessory-tint-${selectedEditorIconTint}`} /> { diff --git a/packages/web/src/javascripts/Components/ContentListView/Header/ContentListHeader.tsx b/packages/web/src/javascripts/Components/ContentListView/Header/ContentListHeader.tsx index 4c964cc04..05cb7fbc4 100644 --- a/packages/web/src/javascripts/Components/ContentListView/Header/ContentListHeader.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/Header/ContentListHeader.tsx @@ -76,6 +76,7 @@ const ContentListHeader = ({ togglePopover={toggleDisplayOptionsMenu} align="start" className="py-2" + title="Display options" > { @@ -153,6 +154,7 @@ const ItemLinksCell = ({ { @@ -425,6 +427,7 @@ const ContentTableView = ({ {contextMenuPosition && contextMenuItem && ( { diff --git a/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx b/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx index 2510b9eb2..905c4d78e 100644 --- a/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx +++ b/packages/web/src/javascripts/Components/FileContextMenu/FileContextMenu.tsx @@ -22,6 +22,7 @@ const FileContextMenu: FunctionComponent = observer( return ( setShowFileContextMenu(!showFileContextMenu)} diff --git a/packages/web/src/javascripts/Components/FileContextMenu/FileOptionsPanel.tsx b/packages/web/src/javascripts/Components/FileContextMenu/FileOptionsPanel.tsx index 7148a7c3c..71ce62ed6 100644 --- a/packages/web/src/javascripts/Components/FileContextMenu/FileOptionsPanel.tsx +++ b/packages/web/src/javascripts/Components/FileContextMenu/FileOptionsPanel.tsx @@ -30,7 +30,13 @@ const FilesOptionsPanel = ({ return ( <> - + = observer(({ application, view { @@ -153,7 +154,7 @@ const FilePreviewModal: FunctionComponent = observer(({ application, view {showLinkedBubblesContainer && ( -
+
- +
+ +
diff --git a/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx b/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx index 67a1f8a0a..09c71a204 100644 --- a/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx +++ b/packages/web/src/javascripts/Components/Footer/AccountMenuButton.tsx @@ -41,6 +41,7 @@ const AccountMenuButton = ({ - + - + - - - + diff --git a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx index 55e975578..a3fcf2ee9 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx @@ -101,7 +101,7 @@ const LinkedItemBubblesContainer = ({ item, linkingController }: Props) => { return ( )}
- +
+ +
)} diff --git a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx index 3ca2968ee..eead04620 100644 --- a/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx +++ b/packages/web/src/javascripts/Components/NoteView/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx @@ -109,6 +109,7 @@ export default function BlockPickerMenuPlugin(): JSX.Element { return ( = ({ currentNote }) = return ( = ({ = ({ applic = ({ application, note, icon void + children: ReactNode + title: string + className?: string +}) => { + const [isMounted, setPopoverElement] = useLifecycleAnimation({ + open, + enter: { + keyframes: [ + { + opacity: 0.25, + transform: 'translateY(1rem)', + }, + { + opacity: 1, + transform: 'translateY(0)', + }, + ], + options: { + easing: 'cubic-bezier(.36,.66,.04,1)', + duration: 150, + fill: 'forwards', + }, + initialStyle: { + transformOrigin: 'bottom', + }, + }, + enterCallback: (element) => { + element.scrollTop = 0 + }, + exit: { + keyframes: [ + { + opacity: 1, + transform: 'translateY(0)', + }, + { + opacity: 0, + transform: 'translateY(1rem)', + }, + ], + options: { + easing: 'cubic-bezier(.36,.66,.04,1)', + duration: 150, + fill: 'forwards', + }, + initialStyle: { + transformOrigin: 'bottom', + }, + }, + }) + + useDisableBodyScrollOnMobile() + + if (!isMounted) { + return null + } + + return ( + +
+
+
+
{title}
+ +
+
{children}
+
+ + ) +} + +export default MobilePopoverContent diff --git a/packages/web/src/javascripts/Components/Popover/Popover.tsx b/packages/web/src/javascripts/Components/Popover/Popover.tsx index 61e26ad3a..a8cd4dec7 100644 --- a/packages/web/src/javascripts/Components/Popover/Popover.tsx +++ b/packages/web/src/javascripts/Components/Popover/Popover.tsx @@ -1,6 +1,8 @@ +import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { useAndroidBackHandler } from '@/NativeMobileWeb/useAndroidBackHandler' import { UuidGenerator } from '@standardnotes/snjs' import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' +import MobilePopoverContent from './MobilePopoverContent' import PositionedPopoverContent from './PositionedPopoverContent' import { PopoverProps } from './Types' @@ -38,6 +40,7 @@ const Popover = ({ open, overrideZIndex, side, + title, togglePopover, disableClickOutside, disableMobileFullscreenTakeover, @@ -87,6 +90,23 @@ const Popover = ({ } }, [addAndroidBackHandler, open, togglePopover]) + const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) + + if (isMobileScreen && !disableMobileFullscreenTakeover) { + return ( + { + togglePopover?.() + }} + title={title} + className={className} + > + {children} + + ) + } + return open ? ( {children} diff --git a/packages/web/src/javascripts/Components/Popover/Types.ts b/packages/web/src/javascripts/Components/Popover/Types.ts index 994d78d8b..25d2870fb 100644 --- a/packages/web/src/javascripts/Components/Popover/Types.ts +++ b/packages/web/src/javascripts/Components/Popover/Types.ts @@ -30,25 +30,36 @@ type PopoverAnchorPointProps = { anchorElement?: never } +type PopoverMutuallyExclusiveProps = + | { + togglePopover: () => void + disableMobileFullscreenTakeover?: never + } + | { + togglePopover?: never + disableMobileFullscreenTakeover: boolean + } + type CommonPopoverProps = { align?: PopoverAlignment children: ReactNode side?: PopoverSide overrideZIndex?: string - togglePopover?: () => void className?: string disableClickOutside?: boolean - disableMobileFullscreenTakeover?: boolean maxHeight?: (calculatedMaxHeight: number) => number + title: string } export type PopoverContentProps = CommonPopoverProps & { anchorElement?: HTMLElement | null anchorPoint?: Point childPopovers: Set + togglePopover?: () => void + disableMobileFullscreenTakeover?: boolean id: string } export type PopoverProps = - | (CommonPopoverProps & PopoverAnchorElementProps) - | (CommonPopoverProps & PopoverAnchorPointProps) + | (CommonPopoverProps & PopoverMutuallyExclusiveProps & PopoverAnchorElementProps) + | (CommonPopoverProps & PopoverMutuallyExclusiveProps & PopoverAnchorPointProps) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/General/SmartViews/EditSmartViewModal.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/General/SmartViews/EditSmartViewModal.tsx index 91f278294..821db3e73 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/General/SmartViews/EditSmartViewModal.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/General/SmartViews/EditSmartViewModal.tsx @@ -94,6 +94,7 @@ const EditSmartViewModal = ({ controller, platform }: Props) => { { navigationController.setContextMenuOpen(!contextMenuOpen)} diff --git a/packages/web/src/javascripts/Constants/AnimationConfigs.ts b/packages/web/src/javascripts/Constants/AnimationConfigs.ts index d7122675b..8254b591d 100644 --- a/packages/web/src/javascripts/Constants/AnimationConfigs.ts +++ b/packages/web/src/javascripts/Constants/AnimationConfigs.ts @@ -4,90 +4,6 @@ export type AnimationConfig = { initialStyle?: Partial } -export const EnterFromTopAnimation: AnimationConfig = { - keyframes: [ - { - opacity: 0, - transform: 'scaleY(0)', - }, - { - opacity: 1, - transform: 'scaleY(1)', - }, - ], - options: { - easing: 'ease-in-out', - duration: 150, - fill: 'forwards', - }, - initialStyle: { - transformOrigin: 'top', - }, -} - -export const EnterFromBelowAnimation: AnimationConfig = { - keyframes: [ - { - opacity: 0, - transform: 'scaleY(0)', - }, - { - opacity: 1, - transform: 'scaleY(1)', - }, - ], - options: { - easing: 'ease-in-out', - duration: 150, - fill: 'forwards', - }, - initialStyle: { - transformOrigin: 'bottom', - }, -} - -export const ExitToTopAnimation: AnimationConfig = { - keyframes: [ - { - opacity: 1, - transform: 'scaleY(1)', - }, - { - opacity: 0, - transform: 'scaleY(0)', - }, - ], - options: { - easing: 'ease-in-out', - duration: 150, - fill: 'forwards', - }, - initialStyle: { - transformOrigin: 'top', - }, -} - -export const ExitToBelowAnimation: AnimationConfig = { - keyframes: [ - { - opacity: 1, - transform: 'scaleY(1)', - }, - { - opacity: 0, - transform: 'scaleY(0)', - }, - ], - options: { - easing: 'ease-in-out', - duration: 150, - fill: 'forwards', - }, - initialStyle: { - transformOrigin: 'bottom', - }, -} - export const TranslateFromTopAnimation: AnimationConfig = { keyframes: [ { @@ -129,3 +45,45 @@ export const TranslateToTopAnimation: AnimationConfig = { transformOrigin: 'top', }, } + +export const TranslateFromBelowAnimation: AnimationConfig = { + keyframes: [ + { + opacity: 0, + transform: 'translateY(100%)', + }, + { + opacity: 1, + transform: 'translateY(0)', + }, + ], + options: { + easing: 'ease-in-out', + duration: 150, + fill: 'forwards', + }, + initialStyle: { + transformOrigin: 'bottom', + }, +} + +export const TranslateToBelowAnimation: AnimationConfig = { + keyframes: [ + { + opacity: 1, + transform: 'translateY(0)', + }, + { + opacity: 0, + transform: 'translateY(100%)', + }, + ], + options: { + easing: 'ease-in-out', + duration: 150, + fill: 'forwards', + }, + initialStyle: { + transformOrigin: 'bottom', + }, +}