fix: ipad web view ui improvements (#1664)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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]',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user