Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toggle button for file previews #3290

Merged
merged 19 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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