client: add file dropdown to post view
This commit is contained in:
parent
dfea957046
commit
1ace04985c
5 changed files with 182 additions and 20 deletions
69
client/components/file-dropdown/dropdown.module.css
Normal file
69
client/components/file-dropdown/dropdown.module.css
Normal 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) {
|
||||
}
|
64
client/components/file-dropdown/index.tsx
Normal file
64
client/components/file-dropdown/index.tsx
Normal 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
|
|
@ -17,11 +17,21 @@
|
|||
.fileTree li {
|
||||
transition: var(--transition);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--gap-half) 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,12 @@ import styles from './post-page.module.css'
|
|||
import homeStyles from '@styles/Home.module.css'
|
||||
|
||||
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 { useMemo, useState } from "react"
|
||||
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 = {
|
||||
post: Post
|
||||
|
@ -51,18 +52,24 @@ const PostPage = ({ post }: Props) => {
|
|||
<Page.Content className={homeStyles.main}>
|
||||
{/* {!isLoading && <PostFileExplorer files={post.files} />} */}
|
||||
<div className={styles.header}>
|
||||
<div className={styles.titleAndBadge}>
|
||||
<span className={styles.title}>
|
||||
<Text h2>{post.title}</Text>
|
||||
<span>
|
||||
<div className={styles.badges}>
|
||||
<VisibilityBadge visibility={post.visibility} />
|
||||
<Badge type="secondary"><Tooltip text={formattedTime}>{time}</Tooltip></Badge>
|
||||
</span>
|
||||
</div>
|
||||
<Button auto onClick={download}>
|
||||
</span>
|
||||
<span className={styles.buttons}>
|
||||
<ButtonGroup>
|
||||
<Button auto onClick={download} icon={<Archive />}>
|
||||
Download as ZIP archive
|
||||
</Button>
|
||||
<FileDropdown files={post.files} />
|
||||
</ButtonGroup>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
{post.files.length > 1 && <FileTree files={post.files} />}
|
||||
{/* {post.files.length > 1 && <FileTree files={post.files} />} */}
|
||||
{post.files.map(({ id, content, title }: File) => (
|
||||
<DocumentComponent
|
||||
key={id}
|
||||
|
|
|
@ -4,32 +4,44 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.header .titleAndBadge {
|
||||
.header .title {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header .titleAndBadge span {
|
||||
.header .title .badges > * {
|
||||
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 {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.header .titleAndBadge {
|
||||
@media screen and (max-width: 700px) {
|
||||
.header .title {
|
||||
flex-direction: column;
|
||||
padding-bottom: var(--gap-double);
|
||||
}
|
||||
|
||||
.header .titleAndBadge span {
|
||||
.header .title .badges > * {
|
||||
margin-left: 0;
|
||||
margin-bottom: var(--gap);
|
||||
}
|
||||
|
||||
.header .title .badges {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue