refactor: file preview modal header on mobile
This commit is contained in:
@@ -9,7 +9,7 @@ type Props = {
|
|||||||
|
|
||||||
const FilePreviewInfoPanel: FunctionComponent<Props> = ({ file }) => {
|
const FilePreviewInfoPanel: FunctionComponent<Props> = ({ file }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-w-70 flex-col border-0 border-l border-solid border-border p-4">
|
<div className="flex min-w-70 flex-col p-4">
|
||||||
<div className="mb-4 flex items-center">
|
<div className="mb-4 flex items-center">
|
||||||
<Icon type="info" className="mr-2" />
|
<Icon type="info" className="mr-2" />
|
||||||
<div className="font-semibold">File information</div>
|
<div className="font-semibold">File information</div>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { DialogContent } from '@reach/dialog'
|
|
||||||
import { FunctionComponent, KeyboardEventHandler, useCallback, useMemo, useRef, useState } from 'react'
|
import { FunctionComponent, KeyboardEventHandler, useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import { getFileIconComponent } from './getFileIconComponent'
|
import { getFileIconComponent } from './getFileIconComponent'
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
@@ -20,6 +19,8 @@ import { mergeRefs } from '@/Hooks/mergeRefs'
|
|||||||
import { classNames } from '@standardnotes/snjs'
|
import { classNames } from '@standardnotes/snjs'
|
||||||
import { isIOS } from '@/Utils'
|
import { isIOS } from '@/Utils'
|
||||||
import ModalOverlay from '../Modal/ModalOverlay'
|
import ModalOverlay from '../Modal/ModalOverlay'
|
||||||
|
import Modal from '../Modal/Modal'
|
||||||
|
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -104,23 +105,63 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
}
|
}
|
||||||
}, [application.items, currentFile, setCurrentFile])
|
}, [application.items, currentFile, setCurrentFile])
|
||||||
|
|
||||||
|
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||||
|
|
||||||
|
const toggleOptionsMenu = () => setShowOptionsMenu((show) => !show)
|
||||||
|
const closeOptionsMenu = () => setShowOptionsMenu(false)
|
||||||
|
const toggleInfoPanel = () => setShowFileInfoPanel((show) => !show)
|
||||||
|
const toggleLinkedBubblesContainer = () => setShowLinkedBubblesContainer((show) => !show)
|
||||||
|
|
||||||
if (!currentFile) {
|
if (!currentFile) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogContent
|
<Modal
|
||||||
className={classNames(
|
title={currentFile.name}
|
||||||
'm-0 flex h-full w-full flex-col rounded bg-[color:var(--modal-background-color)] p-0 shadow-main md:max-h-[90%] md:max-w-[90%]',
|
close={dismiss}
|
||||||
isIOS() && 'pt-safe-top',
|
className={{
|
||||||
)}
|
content: classNames(
|
||||||
|
'm-0 flex h-full w-full flex-col rounded bg-[color:var(--modal-background-color)] p-0 shadow-main md:!h-full md:max-h-[90%] md:!w-full md:max-w-[90%]',
|
||||||
|
isIOS() && 'pt-safe-top',
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
label: 'Done',
|
||||||
|
type: 'primary',
|
||||||
|
onClick: dismiss,
|
||||||
|
hidden: !isMobileScreen,
|
||||||
|
mobileSlot: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Show file options',
|
||||||
|
type: 'secondary',
|
||||||
|
onClick: toggleOptionsMenu,
|
||||||
|
hidden: !isMobileScreen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${showFileInfoPanel ? 'Hide' : 'Show'} file info`,
|
||||||
|
type: 'secondary',
|
||||||
|
onClick: toggleInfoPanel,
|
||||||
|
hidden: !isMobileScreen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${showLinkedBubblesContainer ? 'Hide' : 'Show'} links section`,
|
||||||
|
type: 'secondary',
|
||||||
|
onClick: toggleLinkedBubblesContainer,
|
||||||
|
hidden: !isMobileScreen,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
customHeader={<></>}
|
||||||
|
disableCustomHeader={isMobileScreen}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="flex h-full w-full flex-col focus:shadow-none focus:outline-none"
|
className="flex h-full w-full flex-col focus:shadow-none focus:outline-none"
|
||||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
onKeyDown={keyDownHandler}
|
onKeyDown={keyDownHandler}
|
||||||
>
|
>
|
||||||
<div className="min-h-6 flex flex-shrink-0 flex-wrap items-center justify-between gap-2 border-0 border-b border-solid border-border px-4 py-3 focus:shadow-none">
|
<div className="min-h-6 hidden flex-shrink-0 flex-wrap items-center justify-between gap-2 border-0 border-b border-solid border-border px-4 py-3 focus:shadow-none md:flex">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="h-6 w-6">{IconComponent}</div>
|
<div className="h-6 w-6">{IconComponent}</div>
|
||||||
{isRenaming ? (
|
{isRenaming ? (
|
||||||
@@ -160,7 +201,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
<StyledTooltip label="Show linked items" className="!z-modal">
|
<StyledTooltip label="Show linked items" className="!z-modal">
|
||||||
<button
|
<button
|
||||||
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
||||||
onClick={() => setShowLinkedBubblesContainer((show) => !show)}
|
onClick={toggleLinkedBubblesContainer}
|
||||||
aria-label="Show linked items"
|
aria-label="Show linked items"
|
||||||
>
|
>
|
||||||
<Icon type="link" className="text-neutral" />
|
<Icon type="link" className="text-neutral" />
|
||||||
@@ -169,7 +210,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
<StyledTooltip label="Show file options" className="!z-modal">
|
<StyledTooltip label="Show file options" className="!z-modal">
|
||||||
<button
|
<button
|
||||||
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
||||||
onClick={() => setShowOptionsMenu((show) => !show)}
|
onClick={toggleOptionsMenu}
|
||||||
ref={menuButtonRef}
|
ref={menuButtonRef}
|
||||||
aria-label="Show file options"
|
aria-label="Show file options"
|
||||||
>
|
>
|
||||||
@@ -180,9 +221,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
title="File options"
|
title="File options"
|
||||||
open={showOptionsMenu}
|
open={showOptionsMenu}
|
||||||
anchorElement={menuButtonRef.current}
|
anchorElement={menuButtonRef.current}
|
||||||
togglePopover={() => {
|
togglePopover={closeOptionsMenu}
|
||||||
setShowOptionsMenu(false)
|
|
||||||
}}
|
|
||||||
side="bottom"
|
side="bottom"
|
||||||
align="start"
|
align="start"
|
||||||
className="py-2"
|
className="py-2"
|
||||||
@@ -194,9 +233,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
linkingController={viewControllerManager.linkingController}
|
linkingController={viewControllerManager.linkingController}
|
||||||
navigationController={viewControllerManager.navigationController}
|
navigationController={viewControllerManager.navigationController}
|
||||||
selectedFiles={[currentFile]}
|
selectedFiles={[currentFile]}
|
||||||
closeMenu={() => {
|
closeMenu={closeOptionsMenu}
|
||||||
setShowOptionsMenu(false)
|
|
||||||
}}
|
|
||||||
shouldShowRenameOption={false}
|
shouldShowRenameOption={false}
|
||||||
shouldShowAttachOption={false}
|
shouldShowAttachOption={false}
|
||||||
/>
|
/>
|
||||||
@@ -205,7 +242,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
<StyledTooltip label="Show file info" className="!z-modal">
|
<StyledTooltip label="Show file info" className="!z-modal">
|
||||||
<button
|
<button
|
||||||
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
className="mr-4 flex cursor-pointer rounded border border-solid border-border bg-transparent p-1.5 hover:bg-contrast"
|
||||||
onClick={() => setShowFileInfoPanel((show) => !show)}
|
onClick={toggleInfoPanel}
|
||||||
aria-label="Show file info"
|
aria-label="Show file info"
|
||||||
>
|
>
|
||||||
<Icon type="info" className="text-neutral" />
|
<Icon type="info" className="text-neutral" />
|
||||||
@@ -229,14 +266,19 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex min-h-0 flex-grow">
|
<div className="flex min-h-0 flex-grow flex-col-reverse md:flex-row">
|
||||||
<div className="relative flex max-w-full flex-grow items-center justify-center">
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'relative flex max-w-full flex-grow items-center justify-center',
|
||||||
|
showFileInfoPanel && 'border-t border-border md:border-b-0 md:border-r',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<FilePreview file={currentFile} application={application} key={currentFile.uuid} />
|
<FilePreview file={currentFile} application={application} key={currentFile.uuid} />
|
||||||
</div>
|
</div>
|
||||||
{showFileInfoPanel && <FilePreviewInfoPanel file={currentFile} />}
|
{showFileInfoPanel && <FilePreviewInfoPanel file={currentFile} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</Modal>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ const Modal = ({
|
|||||||
togglePopover={() => setShowAdvanced((show) => !show)}
|
togglePopover={() => setShowAdvanced((show) => !show)}
|
||||||
align="start"
|
align="start"
|
||||||
portal={false}
|
portal={false}
|
||||||
className="!fixed w-1/2 !min-w-0 divide-y divide-border border border-border"
|
className="!fixed divide-y divide-border border border-border"
|
||||||
>
|
>
|
||||||
{extraActions
|
{extraActions
|
||||||
.filter((action) => action.type !== 'cancel')
|
.filter((action) => action.type !== 'cancel')
|
||||||
@@ -157,7 +157,10 @@ const Modal = ({
|
|||||||
action.type === 'destructive' && 'text-danger',
|
action.type === 'destructive' && 'text-danger',
|
||||||
)}
|
)}
|
||||||
key={index}
|
key={index}
|
||||||
onClick={action.onClick}
|
onClick={() => {
|
||||||
|
action.onClick()
|
||||||
|
setShowAdvanced(false)
|
||||||
|
}}
|
||||||
disabled={action.disabled}
|
disabled={action.disabled}
|
||||||
>
|
>
|
||||||
{action.label}
|
{action.label}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { PopoverAlignment, PopoverSide } from './Types'
|
|||||||
import { OppositeSide, checkCollisions, getNonCollidingAlignment, getOverflows } from './Utils/Collisions'
|
import { OppositeSide, checkCollisions, getNonCollidingAlignment, getOverflows } from './Utils/Collisions'
|
||||||
import { getAppRect, getPopoverMaxHeight, getPositionedPopoverRect } from './Utils/Rect'
|
import { getAppRect, getPopoverMaxHeight, getPositionedPopoverRect } from './Utils/Rect'
|
||||||
|
|
||||||
|
const percentOf = (percent: number, value: number) => (percent / 100) * value
|
||||||
|
|
||||||
const getStylesFromRect = (
|
const getStylesFromRect = (
|
||||||
rect: DOMRect,
|
rect: DOMRect,
|
||||||
options: {
|
options: {
|
||||||
@@ -15,16 +17,18 @@ const getStylesFromRect = (
|
|||||||
const { disableMobileFullscreenTakeover = false, maxHeight = 'none' } = options
|
const { disableMobileFullscreenTakeover = false, maxHeight = 'none' } = options
|
||||||
|
|
||||||
const canApplyMaxHeight = maxHeight !== 'none' && (!isMobileScreen() || disableMobileFullscreenTakeover)
|
const canApplyMaxHeight = maxHeight !== 'none' && (!isMobileScreen() || disableMobileFullscreenTakeover)
|
||||||
|
const shouldApplyMobileWidth = isMobileScreen() && disableMobileFullscreenTakeover
|
||||||
|
const marginForMobile = percentOf(10, window.innerWidth)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
willChange: 'transform',
|
willChange: 'transform',
|
||||||
transform: `translate(${rect.x}px, ${rect.y}px)`,
|
transform: `translate(${shouldApplyMobileWidth ? marginForMobile / 2 : rect.x}px, ${rect.y}px)`,
|
||||||
visibility: 'visible',
|
visibility: 'visible',
|
||||||
...(canApplyMaxHeight && {
|
...(canApplyMaxHeight && {
|
||||||
maxHeight: `${maxHeight}px`,
|
maxHeight: `${maxHeight}px`,
|
||||||
}),
|
}),
|
||||||
...(disableMobileFullscreenTakeover && {
|
...(shouldApplyMobileWidth && {
|
||||||
maxWidth: `${window.innerWidth - rect.x * 2}px`,
|
width: `${window.innerWidth - marginForMobile}px`,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user