fix: ipad web view ui improvements (#1664)

This commit is contained in:
Aman Harwara
2022-09-29 13:47:00 +05:30
committed by GitHub
parent c3d6a91730
commit 20e420820d
10 changed files with 79 additions and 36 deletions

View File

@@ -41,6 +41,13 @@ const getKey = () => {
return keyCount++ return keyCount++
} }
const setViewportHeight = () => {
document.documentElement.style.setProperty(
'--viewport-height',
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
)
}
const startApplication: StartApplication = async function startApplication( const startApplication: StartApplication = async function startApplication(
defaultSyncServerHost: string, defaultSyncServerHost: string,
device: WebOrDesktopDevice, device: WebOrDesktopDevice,
@@ -53,6 +60,7 @@ const startApplication: StartApplication = async function startApplication(
let root: Root let root: Root
const onDestroy = () => { const onDestroy = () => {
window.removeEventListener('orientationchange', setViewportHeight)
const rootElement = document.getElementById(ElementIds.RootId) as HTMLElement const rootElement = document.getElementById(ElementIds.RootId) as HTMLElement
root.unmount() root.unmount()
rootElement.remove() rootElement.remove()
@@ -66,10 +74,8 @@ const startApplication: StartApplication = async function startApplication(
root = createRoot(appendedRootNode) root = createRoot(appendedRootNode)
disableIosTextFieldZoom() disableIosTextFieldZoom()
document.documentElement.style.setProperty( setViewportHeight()
'--viewport-height', window.addEventListener('orientationchange', setViewportHeight)
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
)
root.render( root.render(
<ApplicationGroupView <ApplicationGroupView

View File

@@ -69,7 +69,7 @@ const ContentList: FunctionComponent<Props> = ({
<div <div
className={classNames( className={classNames(
'infinite-scroll overflow-y-auto overflow-x-hidden focus:shadow-none focus:outline-none', 'infinite-scroll overflow-y-auto overflow-x-hidden focus:shadow-none focus:outline-none',
'md:max-h-full md:overflow-y-hidden md:hover:overflow-y-auto', 'md:max-h-full md:overflow-y-hidden md:hover:overflow-y-auto pointer-coarse:md:overflow-y-auto',
'md:hover:[overflow-y:_overlay]', 'md:hover:[overflow-y:_overlay]',
)} )}
id={ElementIds.ContentList} id={ElementIds.ContentList}

View File

@@ -24,6 +24,7 @@ import { StreamingFileReader } from '@standardnotes/filepicker'
import SearchBar from '../SearchBar/SearchBar' import SearchBar from '../SearchBar/SearchBar'
import { SearchOptionsController } from '@/Controllers/SearchOptionsController' import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
import { classNames } from '@/Utils/ConcatenateClassNames' import { classNames } from '@/Utils/ConcatenateClassNames'
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
type Props = { type Props = {
accountMenuController: AccountMenuController accountMenuController: AccountMenuController
@@ -50,7 +51,7 @@ const ContentListView: FunctionComponent<Props> = ({
selectionController, selectionController,
searchOptionsController, searchOptionsController,
}) => { }) => {
const { toggleAppPane } = useResponsiveAppPane() const { isNotesListVisibleOnTablets, toggleAppPane } = useResponsiveAppPane()
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const itemsViewPanelRef = useRef<HTMLDivElement>(null) const itemsViewPanelRef = useRef<HTMLDivElement>(null)
@@ -181,12 +182,19 @@ const ContentListView: FunctionComponent<Props> = ({
[isFilesSmartView], [isFilesSmartView],
) )
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
return ( return (
<div <div
id="items-column" id="items-column"
className={classNames( className={classNames(
'sn-component section app-column flex h-screen flex-col pt-safe-top md:h-full', 'sn-component section app-column flex h-screen flex-col pt-safe-top md:h-full',
'xl:w-87.5 xsm-only:!w-full sm-only:!w-full pointer-coarse:md-only:!w-52 pointer-coarse:lg-only:!w-52', 'xl:w-87.5 xsm-only:!w-full sm-only:!w-full',
isTabletScreenSize && !isNotesListVisibleOnTablets
? 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0'
: 'pointer-coarse:md-only:!w-60 pointer-coarse:lg-only:!w-60',
)} )}
aria-label={'Notes & Files'} aria-label={'Notes & Files'}
ref={itemsViewPanelRef} ref={itemsViewPanelRef}

View File

@@ -22,7 +22,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application]) const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application])
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const [panelWidth, setPanelWidth] = useState<number>(0) const [panelWidth, setPanelWidth] = useState<number>(0)
const { toggleAppPane } = useResponsiveAppPane() const { selectedPane, toggleAppPane } = useResponsiveAppPane()
const [hasPasscode, setHasPasscode] = useState(() => application.hasPasscode()) const [hasPasscode, setHasPasscode] = useState(() => application.hasPasscode())
useEffect(() => { useEffect(() => {
@@ -63,7 +63,11 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<div <div
id="navigation" id="navigation"
className={classNames( className={classNames(
'sn-component section app-column h-screen max-h-screen w-[220px] overflow-hidden pt-safe-top md:h-full md:max-h-full md:min-h-0 md:py-0 xsm-only:!w-full sm-only:!w-full', 'sn-component section app-column h-screen max-h-screen overflow-hidden pt-safe-top md:h-full md:max-h-full md:min-h-0 md:pb-0',
'w-[220px] xl:w-87.5 xsm-only:!w-full sm-only:!w-full',
selectedPane === AppPaneId.Navigation
? 'pointer-coarse:md-only:!w-48 pointer-coarse:lg-only:!w-48'
: 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0',
isIOS() ? 'pb-safe-bottom' : 'pb-2.5', isIOS() ? 'pb-safe-bottom' : 'pb-2.5',
)} )}
ref={ref} ref={ref}
@@ -71,7 +75,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content"> <ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content">
<div <div
className={classNames( className={classNames(
'flex-grow overflow-y-auto overflow-x-hidden md:overflow-y-hidden md:hover:overflow-y-auto', 'flex-grow overflow-y-auto overflow-x-hidden md:overflow-y-hidden md:hover:overflow-y-auto pointer-coarse:md:overflow-y-auto',
'md:hover:[overflow-y:_overlay]', 'md:hover:[overflow-y:_overlay]',
)} )}
> >

View File

@@ -3,13 +3,17 @@ import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
export const NavigationMenuButton = () => { export const NavigationMenuButton = () => {
const { toggleAppPane } = useResponsiveAppPane() const { selectedPane, toggleAppPane } = useResponsiveAppPane()
return ( return (
<button <button
className="bg-text-padding mr-3 inline-flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border align-middle text-neutral hover:bg-contrast focus:bg-contrast md:hidden" className="bg-text-padding mr-3 inline-flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border align-middle text-neutral hover:bg-contrast focus:bg-contrast md:hidden pointer-coarse:md-only:inline-flex pointer-coarse:lg-only:inline-flex"
onClick={() => { onClick={() => {
toggleAppPane(AppPaneId.Navigation) if (selectedPane === AppPaneId.Items || selectedPane === AppPaneId.Editor) {
toggleAppPane(AppPaneId.Navigation)
} else {
toggleAppPane(AppPaneId.Items)
}
}} }}
title="Navigation menu" title="Navigation menu"
aria-label="Navigation menu" aria-label="Navigation menu"

View File

@@ -1,20 +1,36 @@
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata' import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import Icon from '../Icon/Icon' import Icon from '../Icon/Icon'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider' import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { useMediaQuery, MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import { IconType } from '@standardnotes/snjs'
const MobileItemsListButton = () => { const MobileItemsListButton = () => {
const { toggleAppPane } = useResponsiveAppPane() const { toggleAppPane, isNotesListVisibleOnTablets, toggleNotesListOnTablets } = useResponsiveAppPane()
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
const iconType: IconType = isTabletScreenSize && !isNotesListVisibleOnTablets ? 'chevron-right' : 'chevron-left'
const label = isTabletScreenSize
? isNotesListVisibleOnTablets
? 'Hide items list'
: 'Show items list'
: 'Go to items list'
return ( return (
<button <button
className="bg-text-padding mr-3 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 md:hidden" className="bg-text-padding mr-3 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 md:hidden pointer-coarse:md-only:flex pointer-coarse:lg-only:flex"
onClick={() => { onClick={() => {
toggleAppPane(AppPaneId.Items) if (isTabletScreenSize) {
toggleNotesListOnTablets()
} else {
toggleAppPane(AppPaneId.Items)
}
}} }}
title="Go to items list" title={label}
aria-label="Go to items list" aria-label={label}
> >
<Icon type="chevron-left" /> <Icon type={iconType} />
</button> </button>
) )
} }

View File

@@ -58,8 +58,9 @@ const PositionedPopoverContent = ({
<Portal> <Portal>
<div <div
className={classNames( className={classNames(
'safe-area-padding absolute top-0 left-0 flex h-full w-full min-w-80 cursor-auto flex-col overflow-y-auto rounded bg-default shadow-main md:h-auto md:max-w-xs', 'absolute top-0 left-0 flex h-full w-full min-w-80 cursor-auto flex-col overflow-y-auto rounded bg-default shadow-main md:h-auto md:max-w-xs',
overrideZIndex ? overrideZIndex : 'z-dropdown-menu', overrideZIndex ? overrideZIndex : 'z-dropdown-menu',
!isDesktopScreen ? 'pt-safe-top pb-safe-bottom' : '',
)} )}
style={{ style={{
...styles, ...styles,

View File

@@ -53,7 +53,7 @@ const PanelSettingsSection = ({ application }: Props) => {
}, [application]) }, [application])
return ( return (
<div className="hidden text-sm md:block"> <div className="hidden text-sm md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden">
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<div className="my-1 px-3 text-sm font-semibold uppercase text-text">Panel Settings</div> <div className="my-1 px-3 text-sm font-semibold uppercase text-text">Panel Settings</div>
<MenuItem <MenuItem

View File

@@ -19,6 +19,8 @@ import { AppPaneId } from './AppPaneMetadata'
type ResponsivePaneData = { type ResponsivePaneData = {
selectedPane: AppPaneId selectedPane: AppPaneId
toggleAppPane: (paneId: AppPaneId) => void toggleAppPane: (paneId: AppPaneId) => void
isNotesListVisibleOnTablets: boolean
toggleNotesListOnTablets: () => void
} }
const ResponsivePaneContext = createContext<ResponsivePaneData | undefined>(undefined) const ResponsivePaneContext = createContext<ResponsivePaneData | undefined>(undefined)
@@ -60,15 +62,10 @@ const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
const toggleAppPane = useCallback( const toggleAppPane = useCallback(
(paneId: AppPaneId) => { (paneId: AppPaneId) => {
if (paneId === currentSelectedPane) { setPreviousSelectedPane(currentSelectedPane)
setCurrentSelectedPane(previousSelectedPane ? previousSelectedPane : AppPaneId.Editor) setCurrentSelectedPane(paneId)
setPreviousSelectedPane(paneId)
} else {
setPreviousSelectedPane(currentSelectedPane)
setCurrentSelectedPane(paneId)
}
}, },
[currentSelectedPane, previousSelectedPane], [currentSelectedPane],
) )
useEffect(() => { useEffect(() => {
@@ -102,12 +99,20 @@ const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
} }
}, [addAndroidBackHandler, currentSelectedPaneRef, toggleAppPane]) }, [addAndroidBackHandler, currentSelectedPaneRef, toggleAppPane])
const [isNotesListVisibleOnTablets, setNotesListVisibleOnTablets] = useState(true)
const toggleNotesListOnTablets = useCallback(() => {
setNotesListVisibleOnTablets((visible) => !visible)
}, [])
const contextValue = useMemo( const contextValue = useMemo(
() => ({ () => ({
selectedPane: currentSelectedPane, selectedPane: currentSelectedPane,
toggleAppPane, toggleAppPane,
isNotesListVisibleOnTablets,
toggleNotesListOnTablets,
}), }),
[currentSelectedPane, toggleAppPane], [currentSelectedPane, isNotesListVisibleOnTablets, toggleAppPane, toggleNotesListOnTablets],
) )
return ( return (

View File

@@ -40,11 +40,6 @@ body {
color: var(--sn-stylekit-foreground-color); color: var(--sn-stylekit-foreground-color);
} }
.safe-area-padding {
padding: var(--safe-area-inset-top) var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left);
}
html, html,
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
@@ -154,12 +149,16 @@ $footer-height: 2rem;
} }
.app { .app {
height: calc(100% - #{$footer-height}); height: 100%;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
vertical-align: top; vertical-align: top;
width: 100%; width: 100%;
@media screen and (min-width: 768px) {
height: calc(var(--viewport-height) - #{$footer-height});
}
.section { .section {
position: relative; position: relative;
overflow: hidden; overflow: hidden;