client: add file dropdown to post view

This commit is contained in:
Max Leiter 2022-03-25 13:01:46 -07:00
parent dfea957046
commit 1ace04985c
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
5 changed files with 182 additions and 20 deletions

View file

@ -0,0 +1,69 @@
.wrapper {
display: flex;
flex-direction: row;
align-items: center;
}
.dropdownButton {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
}
.content {
list-style: none;
width: 100%;
padding: 0 var(--gap);
margin: 0;
}
.content li {
transition: var(--transition);
border-radius: var(--radius);
margin: 0;
padding: 2px var(--gap);
}
.content li:hover,
.content li:focus {
background-color: var(--lighter-gray);
}
.content li a {
display: block;
height: 100%;
width: min-content;
}
.button {
border-radius: none !important;
}
.content li .fileIcon {
display: inline-block;
margin-right: var(--gap-half);
}
.content li .fileTitle {
font-size: 1rem;
}
.content li::before {
content: "";
padding: 0;
margin: 0;
}
.cardContent {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: var(--gap-half);
padding-top: 200px;
}
@media screen and (max-width: 82rem) {
}

View file

@ -0,0 +1,64 @@
import { Button, Link, Note, Popover, Spacer } from '@geist-ui/core'
import FileIcon from '@geist-ui/icons/fileText'
import CodeIcon from '@geist-ui/icons/fileLambda'
import styles from './dropdown.module.css'
import { useCallback, useEffect, useState } from "react"
import { codeFileExtensions } from "@lib/constants"
import ChevronDown from '@geist-ui/icons/chevronDown'
import ShiftBy from "@components/shift-by"
import type { File } from '@lib/types'
import FileTree from '@geist-ui/icons/list'
type Item = File & {
icon: JSX.Element
}
const FileDropdown = ({
files
}: {
files: File[]
}) => {
const [expanded, setExpanded] = useState(false)
const [items, setItems] = useState<Item[]>([])
useEffect(() => {
const newItems = files.map(file => {
const extension = file.title.split('.').pop()
if (codeFileExtensions.includes(extension || '')) {
return {
...file,
icon: <CodeIcon />
}
} else {
return {
...file,
icon: <FileIcon />
}
}
})
setItems(newItems)
}, [files])
const content = useCallback(() => (<ul className={styles.content}>
{items.map(item => (
<li key={item.id} onClick={() => setExpanded(false)}>
<Link color={false} href={`#${item.title}`}>
<ShiftBy y={5}><span className={styles.fileIcon}>
{item.icon}</span></ShiftBy>
<span className={styles.fileTitle}>{item.title}</span>
</Link>
</li>
))}
</ul>
), [items])
// a list of files with an icon and a title
return (
<Button auto onClick={() => setExpanded(!expanded)} className={styles.button} iconRight={<ChevronDown />}>
<Popover content={content} visible={expanded} trigger="click" hideArrow={true}>
{files.length} files
</Popover>
</Button >
)
}
export default FileDropdown

View file

@ -17,11 +17,21 @@
.fileTree li { .fileTree li {
transition: var(--transition); transition: var(--transition);
border-radius: var(--radius); border-radius: var(--radius);
padding: var(--gap-half) 0;
margin: 0; margin: 0;
padding: var(--gap-half) 0;
} }
.fileTree li:hover { .fileTree li a {
margin: 0px;
display: block;
width: 100%;
height: 100%;
padding: var(--gap-half);
}
.fileTree li:hover,
.fileTree li:focus,
.fileTree li:active {
background: var(--lighter-gray); background: var(--lighter-gray);
} }

View file

@ -6,11 +6,12 @@ import styles from './post-page.module.css'
import homeStyles from '@styles/Home.module.css' import homeStyles from '@styles/Home.module.css'
import type { File, Post } from "@lib/types" import type { File, Post } from "@lib/types"
import { Page, Button, Text, Badge, Tooltip, Spacer } from "@geist-ui/core" import { Page, Button, Text, Badge, Tooltip, Spacer, ButtonDropdown, ButtonGroup } from "@geist-ui/core"
import ShiftBy from "@components/shift-by" import ShiftBy from "@components/shift-by"
import { useMemo, useState } from "react" import { useMemo, useState } from "react"
import timeAgo from "@lib/time-ago" import timeAgo from "@lib/time-ago"
import FileTree from "@components/file-tree" import Archive from '@geist-ui/icons/archive'
import FileDropdown from "@components/file-dropdown"
type Props = { type Props = {
post: Post post: Post
@ -51,18 +52,24 @@ const PostPage = ({ post }: Props) => {
<Page.Content className={homeStyles.main}> <Page.Content className={homeStyles.main}>
{/* {!isLoading && <PostFileExplorer files={post.files} />} */} {/* {!isLoading && <PostFileExplorer files={post.files} />} */}
<div className={styles.header}> <div className={styles.header}>
<div className={styles.titleAndBadge}> <span className={styles.title}>
<Text h2>{post.title}</Text> <Text h2>{post.title}</Text>
<span> <div className={styles.badges}>
<VisibilityBadge visibility={post.visibility} /> <VisibilityBadge visibility={post.visibility} />
<Badge type="secondary"><Tooltip text={formattedTime}>{time}</Tooltip></Badge> <Badge type="secondary"><Tooltip text={formattedTime}>{time}</Tooltip></Badge>
</span>
</div> </div>
<Button auto onClick={download}> </span>
<span className={styles.buttons}>
<ButtonGroup>
<Button auto onClick={download} icon={<Archive />}>
Download as ZIP archive Download as ZIP archive
</Button> </Button>
<FileDropdown files={post.files} />
</ButtonGroup>
</span>
</div> </div>
{post.files.length > 1 && <FileTree files={post.files} />} {/* {post.files.length > 1 && <FileTree files={post.files} />} */}
{post.files.map(({ id, content, title }: File) => ( {post.files.map(({ id, content, title }: File) => (
<DocumentComponent <DocumentComponent
key={id} key={id}

View file

@ -4,32 +4,44 @@
align-items: center; align-items: center;
} }
.header .titleAndBadge { .header .title {
display: flex; display: flex;
text-align: center; text-align: center;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.header .titleAndBadge span { .header .title .badges > * {
margin-left: var(--gap); margin-left: var(--gap);
margin-bottom: var(--gap-quarter);
} }
@media screen and (max-width: 680px) { .header .buttons {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
@media screen and (max-width: 900px) {
.header { .header {
flex-direction: column; flex-direction: column;
} }
}
.header .titleAndBadge { @media screen and (max-width: 700px) {
.header .title {
flex-direction: column; flex-direction: column;
padding-bottom: var(--gap-double); padding-bottom: var(--gap-double);
} }
.header .titleAndBadge span { .header .title .badges > * {
margin-left: 0; margin-left: 0;
margin-bottom: var(--gap); }
.header .title .badges {
display: flex; display: flex;
justify-content: space-around; flex-direction: row;
width: 100%; justify-content: space-between;
width: 80%;
} }
} }