Skip to content

Commit

Permalink
Toggle button for file previews (#3290)
Browse files Browse the repository at this point in the history
  • Loading branch information
fiskus authored Jan 26, 2023
1 parent d91e53b commit d425970
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 67 deletions.
44 changes: 44 additions & 0 deletions catalog/app/components/Preview/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import cx from 'classnames'
import * as React from 'react'
import * as M from '@material-ui/core'

import * as AWS from 'utils/AWS'
import * as LogicalKeyResolver from 'utils/LogicalKeyResolver'

const useStyles = M.makeStyles((t) => ({
root: {
alignItems: 'center',
display: 'flex',
height: t.spacing(4),
justifyContent: 'center',
width: t.spacing(3),
},
}))

interface MenuProps {
className?: string
handle: LogicalKeyResolver.S3SummarizeHandle
}

export default function Menu({ className, handle }: MenuProps) {
const classes = useStyles()
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
const downloadUrl = AWS.Signer.useDownloadUrl(handle)
const handleClose = React.useCallback(() => setAnchorEl(null), [])
const handleOpen = React.useCallback((e) => setAnchorEl(e.currentTarget), [])
return (
<div className={cx(classes.root, className)}>
<M.IconButton onClick={handleOpen}>
<M.Icon>more_vert</M.Icon>
</M.IconButton>
<M.Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
<M.MenuItem button component="a" href={downloadUrl} onClick={handleClose}>
<M.ListItemIcon>
<M.Icon>arrow_downward</M.Icon>
</M.ListItemIcon>
<M.ListItemText>Download</M.ListItemText>
</M.MenuItem>
</M.Menu>
</div>
)
}
49 changes: 49 additions & 0 deletions catalog/app/components/Preview/ToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import cx from 'classnames'
import * as React from 'react'
import * as M from '@material-ui/core'

const useStyles = M.makeStyles((t) => ({
root: {
alignItems: 'center',
display: 'flex',
height: t.spacing(4),
justifyContent: 'center',
},
icon: {
transition: 'ease transform .15s',
},
iconExpanded: {
transform: `rotate(180deg)`,
},
}))

interface ToggleButtonProps extends M.BoxProps {
className?: string
expanded?: boolean
onToggle?: () => void
}

export default function ToggleButton({
className,
expanded,
onToggle,
...props
}: ToggleButtonProps) {
const classes = useStyles()
return (
<M.Box className={cx(classes.root, className)} {...props}>
<M.Button
onClick={onToggle}
startIcon={
<M.Icon className={cx(classes.icon, { [classes.iconExpanded]: expanded })}>
{expanded ? 'unfold_less' : 'unfold_more'}
</M.Icon>
}
variant="outlined"
size="small"
>
{expanded ? 'Collapse' : 'Expand'}
</M.Button>
</M.Box>
)
}
2 changes: 2 additions & 0 deletions catalog/app/components/Preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export { default as Display, bind as display } from './Display'
export { default as render } from './render'
export { default as load, getRenderProps } from './load'
export { PreviewData, PreviewError, CONTEXT } from './types'
export { default as Menu } from './Menu'
export { default as ToggleButton } from './ToggleButton'
53 changes: 24 additions & 29 deletions catalog/app/components/SearchResults/SearchResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,15 @@ function HeaderIcon(props) {
)
}

function ObjectHeader({ handle, showBucket, downloadable = false }) {
function ObjectHeader({ handle, showBucket, downloadable = false, expanded, onToggle }) {
return (
<Heading display="flex" alignItems="center" mb="0 !important">
<ObjectCrumbs {...{ handle, showBucket }} />
<M.Box flexGrow={1} />
{!!downloadable &&
AWS.Signer.withDownloadUrl(handle, (url) => (
<M.Box
alignItems="center"
display="flex"
height={32}
justifyContent="center"
width={24}
my={{ xs: -0.25, md: 0 }}
>
<M.IconButton href={url} title="Download" download>
<M.Icon>arrow_downward</M.Icon>
</M.IconButton>
</M.Box>
))}
<Preview.ToggleButton expanded={expanded} onToggle={onToggle} mr={1} />
{!!downloadable && (
<Preview.Menu handle={handle} expanded={expanded} onToggle={onToggle} />
)}
</Heading>
)
}
Expand Down Expand Up @@ -300,7 +289,7 @@ const usePreviewBoxStyles = M.makeStyles((t) => ({
},
}))

function PreviewBox({ children, title, expanded, onExpand }) {
function PreviewBox({ children, title, expanded, onToggle }) {
const classes = usePreviewBoxStyles()
return (
<SmallerSection>
Expand All @@ -310,9 +299,7 @@ function PreviewBox({ children, title, expanded, onExpand }) {
{children}

{!expanded && (
<div className={classes.fade} onClick={onExpand}>
<M.Button variant="outlined">Expand</M.Button>
</div>
<div className={classes.fade} onClick={onToggle} title="Click to expand" />
)}
</div>
</SmallerSection>
Expand All @@ -321,12 +308,16 @@ function PreviewBox({ children, title, expanded, onExpand }) {

const previewOptions = { context: Preview.CONTEXT.LISTING }

function PreviewDisplay({ handle, bucketExistenceData, versionExistenceData }) {
const [expanded, setExpanded] = React.useState(false)
const onExpand = React.useCallback(() => setExpanded(true), [setExpanded])
function PreviewDisplay({
handle,
bucketExistenceData,
versionExistenceData,
expanded,
onToggle,
}) {
const renderContents = React.useCallback(
(children) => <PreviewBox {...{ children, expanded, onExpand }} />,
[expanded, onExpand],
(children) => <PreviewBox {...{ children, expanded, onToggle }} />,
[expanded, onToggle],
)
const withData = (callback) =>
bucketExistenceData.case({
Expand Down Expand Up @@ -369,11 +360,11 @@ function PreviewDisplay({ handle, bucketExistenceData, versionExistenceData }) {

function Meta({ meta }) {
const [expanded, setExpanded] = React.useState(false)
const onExpand = React.useCallback(() => setExpanded(true), [setExpanded])
const onToggle = React.useCallback(() => setExpanded((e) => !e), [])
if (!meta || R.isEmpty(meta)) return null

return (
<PreviewBox expanded={expanded} onExpand={onExpand}>
<PreviewBox expanded={expanded} onToggle={onToggle}>
<JsonDisplay defaultExpanded={1} name="User metadata" value={meta} />
</PreviewBox>
)
Expand Down Expand Up @@ -457,6 +448,8 @@ function FileHit({ showBucket, hit: { path, versions, bucket } }) {
}),
}),
})
const [expanded, setExpanded] = React.useState(false)
const onToggle = React.useCallback(() => setExpanded((e) => !e), [])

return (
<Section
Expand All @@ -465,10 +458,12 @@ function FileHit({ showBucket, hit: { path, versions, bucket } }) {
data-search-hit-bucket={bucket}
data-search-hit-path={path}
>
<ObjectHeader {...{ handle, showBucket, downloadable }} />
<ObjectHeader {...{ handle, showBucket, downloadable, expanded, onToggle }} />
<VersionInfo bucket={bucket} path={path} version={v} versions={versions} />
<Meta meta={v.meta} />
<PreviewDisplay {...{ handle, bucketExistenceData, versionExistenceData }} />
<PreviewDisplay
{...{ handle, bucketExistenceData, versionExistenceData, expanded, onToggle }}
/>
</Section>
)
}
Expand Down
71 changes: 33 additions & 38 deletions catalog/app/containers/Bucket/Summarize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,6 @@ interface SummarizeFile {

type MakeURL = (h: S3Handle) => LocationDescriptor

const useDownloadButtonStyles = M.makeStyles((t) => ({
root: {
alignItems: 'center',
display: 'flex',
height: t.spacing(4),
justifyContent: 'center',
width: t.spacing(3),
},
}))

interface DownloadButtonProps {
className?: string
handle: S3Handle
}

function DownloadButton({ className, handle }: DownloadButtonProps) {
const classes = useDownloadButtonStyles()
return AWS.Signer.withDownloadUrl(handle, (url: string) => (
<div className={cx(classes.root, className)}>
<M.IconButton href={url} title="Download" download>
<M.Icon>arrow_downward</M.Icon>
</M.IconButton>
</div>
))
}

enum FileThemes {
Overview = 'overview',
Nested = 'nested',
Expand Down Expand Up @@ -118,7 +92,10 @@ const useSectionStyles = M.makeStyles((t) => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
},
headingAction: {
menu: {
marginLeft: t.spacing(1),
},
toggle: {
marginLeft: 'auto',
},
}))
Expand All @@ -127,13 +104,17 @@ interface SectionProps extends M.PaperProps {
description?: React.ReactNode
handle?: S3Handle
heading?: React.ReactNode
expanded?: boolean
onToggle?: () => void
}

export function Section({
handle,
heading,
description,
children,
expanded,
onToggle,
...props
}: SectionProps) {
const ft = React.useContext(FileThemeContext)
Expand All @@ -143,7 +124,14 @@ export function Section({
{!!heading && (
<div className={classes.heading}>
<div className={classes.headingText}>{heading}</div>
{handle && <DownloadButton className={classes.headingAction} handle={handle} />}
{onToggle && (
<Preview.ToggleButton
className={classes.toggle}
expanded={expanded}
onToggle={onToggle}
/>
)}
{handle && <Preview.Menu className={classes.menu} handle={handle} />}
</div>
)}
{!!description && <div className={classes.description}>{description}</div>}
Expand All @@ -155,7 +143,7 @@ export function Section({
interface PreviewBoxProps {
children: React.ReactNode
expanded?: boolean
onExpand: () => void
onToggle: () => void
}

const usePreviewBoxStyles = M.makeStyles((t) => ({
Expand Down Expand Up @@ -201,15 +189,16 @@ const usePreviewBoxStyles = M.makeStyles((t) => ({
},
}))

function PreviewBox({ children, expanded, onExpand }: PreviewBoxProps) {
function PreviewBox({ children, expanded, onToggle }: PreviewBoxProps) {
const classes = usePreviewBoxStyles()
// TODO: Move expandable block to ExpandableBox and re-use for SearchResults
// TODO: Listen firstElementNode ({children}) for resize
// if children height is smaller than box -> onToggle(force)
return (
<div className={cx(classes.root, { [classes.expanded]: expanded })}>
{children}
{!expanded && (
<div className={classes.fade} onClick={onExpand} title="Click to expand">
<M.Button variant="outlined">Expand</M.Button>
</div>
<div className={classes.fade} onClick={onToggle} title="Click to expand" />
)}
</div>
)
Expand Down Expand Up @@ -264,7 +253,7 @@ export function FilePreview({
headingOverride,
packageHandle,
}: FilePreviewProps) {
const description = file ? <Markdown data={file.description} /> : null
const description = file?.description ? <Markdown data={file.description} /> : null
const heading = headingOverride != null ? headingOverride : <Crumbs handle={handle} />

const key = handle.logicalKey || handle.key
Expand All @@ -283,15 +272,21 @@ export function FilePreview({
)

const [expanded, setExpanded] = React.useState(defaultExpanded)
const onExpand = React.useCallback(() => setExpanded(true), [setExpanded])
const onToggle = React.useCallback(() => setExpanded((e) => !e), [])
const renderContents = React.useCallback(
(children) => <PreviewBox {...{ children, expanded, onExpand }} />,
[expanded, onExpand],
(children) => <PreviewBox {...{ children, expanded, onToggle }} />,
[expanded, onToggle],
)

// TODO: check for glacier and hide items
return (
<Section description={description} heading={heading} handle={handle}>
<Section
description={description}
heading={heading}
handle={handle}
expanded={expanded}
onToggle={onToggle}
>
{Preview.load(
previewHandle,
Preview.display({
Expand Down
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Entries inside each section should be ordered by type:
* [Added] Add link to package revisions from package list ([#3256](https://github.com/quiltdata/quilt/pull/3256))
* [Added] WebP support in thumbnail lambda ([#3275](https://github.com/quiltdata/quilt/pull/3275))
* [Added] Set default search mode in Admin Settings ([#3270](https://github.com/quiltdata/quilt/pull/3270))
* [Added] Togggle buttons for file previews ([#3290](https://github.com/quiltdata/quilt/pull/3290))
* [Fixed] Fix performance issue (missing memoization) in search results ([#3257](https://github.com/quiltdata/quilt/pull/3257))
* [Fixed] Fix fetching and writing settings in Admin/Settings section ([#3276](https://github.com/quiltdata/quilt/pull/3276))
* [Fixed] Fix iframe preview width ([#3279](https://github.com/quiltdata/quilt/pull/3279))
Expand Down

0 comments on commit d425970

Please sign in to comment.