client: add file tree to post view
This commit is contained in:
parent
448c443e2e
commit
dfea957046
7 changed files with 311 additions and 103 deletions
|
@ -26,7 +26,7 @@ const App = ({
|
||||||
accents_6: 'var(--darker-gray)',
|
accents_6: 'var(--darker-gray)',
|
||||||
accents_7: 'var(--darkest-gray)',
|
accents_7: 'var(--darkest-gray)',
|
||||||
accents_8: 'var(--darkest-gray)',
|
accents_8: 'var(--darkest-gray)',
|
||||||
border: 'var(--light-gray)',
|
border: 'var(--lightest-gray)',
|
||||||
},
|
},
|
||||||
expressiveness: {
|
expressiveness: {
|
||||||
dropdownBoxShadow: '0 0 0 1px var(--light-gray)',
|
dropdownBoxShadow: '0 0 0 1px var(--light-gray)',
|
||||||
|
|
99
client/components/file-tree/file-tree.module.css
Normal file
99
client/components/file-tree/file-tree.module.css
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
.fileTreeWrapper {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTreeWrapper h5 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree {
|
||||||
|
list-style: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li {
|
||||||
|
transition: var(--transition);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: var(--gap-half) 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li:hover {
|
||||||
|
background: var(--lighter-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li .fileTreeIcon {
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: var(--gap-half);
|
||||||
|
padding-left: var(--gap-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li .fileTreeTitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li::before {
|
||||||
|
content: "";
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
top: 0;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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) {
|
||||||
|
.fileTreeWrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-top: var(--gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardContent {
|
||||||
|
margin: var(--gap);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li .fileTreeIcon {
|
||||||
|
margin-right: var(--gap-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li .fileTreeTitle {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree li::before {
|
||||||
|
content: "";
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
61
client/components/file-tree/index.tsx
Normal file
61
client/components/file-tree/index.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { File } from "@lib/types"
|
||||||
|
import { Card, Link, Text } from '@geist-ui/core'
|
||||||
|
import FileIcon from '@geist-ui/icons/fileText'
|
||||||
|
import CodeIcon from '@geist-ui/icons/fileLambda'
|
||||||
|
import styles from './file-tree.module.css'
|
||||||
|
import ShiftBy from "@components/shift-by"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { codeFileExtensions } from "@lib/constants"
|
||||||
|
|
||||||
|
type Item = File & {
|
||||||
|
icon: JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileTree = ({
|
||||||
|
files
|
||||||
|
}: {
|
||||||
|
files: File[]
|
||||||
|
}) => {
|
||||||
|
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])
|
||||||
|
|
||||||
|
// a list of files with an icon and a title
|
||||||
|
return (
|
||||||
|
<div className={styles.fileTreeWrapper}>
|
||||||
|
<Card height={'100%'} className={styles.card}>
|
||||||
|
<div className={styles.cardContent}>
|
||||||
|
<Text h4>Files</Text>
|
||||||
|
<ul className={styles.fileTree}>
|
||||||
|
{items.map(({ id, title, icon }) => (
|
||||||
|
<li key={id}>
|
||||||
|
<Link color={false} href={`#${title}`}>
|
||||||
|
<ShiftBy y={5}><span className={styles.fileTreeIcon}>
|
||||||
|
{icon}</span></ShiftBy>
|
||||||
|
<span className={styles.fileTreeTitle}>{title}</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileTree
|
|
@ -4,103 +4,8 @@ import { useDropzone } from 'react-dropzone'
|
||||||
import styles from './drag-and-drop.module.css'
|
import styles from './drag-and-drop.module.css'
|
||||||
import type { Document } from '@lib/types'
|
import type { Document } from '@lib/types'
|
||||||
import generateUUID from '@lib/generate-uuid'
|
import generateUUID from '@lib/generate-uuid'
|
||||||
const allowedFileTypes = [
|
import { allowedFileTypes, allowedFileNames, allowedFileExtensions } from '@lib/constants'
|
||||||
'application/json',
|
|
||||||
'application/x-javascript',
|
|
||||||
'application/xhtml+xml',
|
|
||||||
'application/xml',
|
|
||||||
'text/xml',
|
|
||||||
'text/plain',
|
|
||||||
'text/html',
|
|
||||||
'text/csv',
|
|
||||||
'text/tab-separated-values',
|
|
||||||
'text/x-c',
|
|
||||||
'text/x-c++',
|
|
||||||
'text/x-csharp',
|
|
||||||
'text/x-java',
|
|
||||||
'text/x-javascript',
|
|
||||||
'text/x-php',
|
|
||||||
'text/x-python',
|
|
||||||
'text/x-ruby',
|
|
||||||
'text/x-scala',
|
|
||||||
'text/x-swift',
|
|
||||||
'text/x-typescript',
|
|
||||||
'text/x-vb',
|
|
||||||
'text/x-vbscript',
|
|
||||||
'text/x-yaml',
|
|
||||||
'text/x-c++',
|
|
||||||
'text/x-c#',
|
|
||||||
'text/mathml',
|
|
||||||
'text/x-markdown',
|
|
||||||
'text/markdown',
|
|
||||||
]
|
|
||||||
|
|
||||||
const allowedFileNames = [
|
|
||||||
'Makefile',
|
|
||||||
'README',
|
|
||||||
'Dockerfile',
|
|
||||||
'Jenkinsfile',
|
|
||||||
'LICENSE',
|
|
||||||
'.env',
|
|
||||||
'.gitignore',
|
|
||||||
'.gitattributes',
|
|
||||||
'.env.example',
|
|
||||||
'.env.development',
|
|
||||||
'.env.production',
|
|
||||||
'.env.test',
|
|
||||||
'.env.staging',
|
|
||||||
'.env.development.local',
|
|
||||||
'yarn.lock',
|
|
||||||
'.bash',
|
|
||||||
'.bashrc',
|
|
||||||
'.bash_profile',
|
|
||||||
'.bash_logout',
|
|
||||||
'.profile',
|
|
||||||
'.fish_prompt',
|
|
||||||
'.zshrc',
|
|
||||||
'.zsh',
|
|
||||||
'.zprofile',
|
|
||||||
]
|
|
||||||
|
|
||||||
const allowedFileExtensions = [
|
|
||||||
'json',
|
|
||||||
'js',
|
|
||||||
'jsx',
|
|
||||||
'ts',
|
|
||||||
'tsx',
|
|
||||||
'c',
|
|
||||||
'cpp',
|
|
||||||
'c++',
|
|
||||||
'c#',
|
|
||||||
'java',
|
|
||||||
'php',
|
|
||||||
'py',
|
|
||||||
'rb',
|
|
||||||
'scala',
|
|
||||||
'swift',
|
|
||||||
'vb',
|
|
||||||
'vbscript',
|
|
||||||
'yaml',
|
|
||||||
'less',
|
|
||||||
'stylus',
|
|
||||||
'styl',
|
|
||||||
'sass',
|
|
||||||
'scss',
|
|
||||||
'lock',
|
|
||||||
'md',
|
|
||||||
'markdown',
|
|
||||||
'txt',
|
|
||||||
'html',
|
|
||||||
'htm',
|
|
||||||
'css',
|
|
||||||
'csv',
|
|
||||||
'log',
|
|
||||||
'sql',
|
|
||||||
'xml',
|
|
||||||
'webmanifest',
|
|
||||||
'vue',
|
|
||||||
'vuex',
|
|
||||||
]
|
|
||||||
|
|
||||||
function FileDropzone({ setDocs }: { setDocs: ((docs: Document[]) => void) }) {
|
function FileDropzone({ setDocs }: { setDocs: ((docs: Document[]) => void) }) {
|
||||||
const { palette } = useTheme()
|
const { palette } = useTheme()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { Page, Button, Text, Badge, Tooltip, Spacer } 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"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
post: Post
|
post: Post
|
||||||
|
@ -61,6 +62,7 @@ const PostPage = ({ post }: Props) => {
|
||||||
Download as ZIP archive
|
Download as ZIP archive
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{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}
|
||||||
|
|
141
client/lib/constants.ts
Normal file
141
client/lib/constants.ts
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
export const allowedFileTypes = [
|
||||||
|
'application/json',
|
||||||
|
'application/x-javascript',
|
||||||
|
'application/xhtml+xml',
|
||||||
|
'application/xml',
|
||||||
|
'text/xml',
|
||||||
|
'text/plain',
|
||||||
|
'text/html',
|
||||||
|
'text/csv',
|
||||||
|
'text/tab-separated-values',
|
||||||
|
'text/x-c',
|
||||||
|
'text/x-c++',
|
||||||
|
'text/x-csharp',
|
||||||
|
'text/x-java',
|
||||||
|
'text/x-javascript',
|
||||||
|
'text/x-php',
|
||||||
|
'text/x-python',
|
||||||
|
'text/x-ruby',
|
||||||
|
'text/x-scala',
|
||||||
|
'text/x-swift',
|
||||||
|
'text/x-typescript',
|
||||||
|
'text/x-vb',
|
||||||
|
'text/x-vbscript',
|
||||||
|
'text/x-yaml',
|
||||||
|
'text/x-c++',
|
||||||
|
'text/x-c#',
|
||||||
|
'text/mathml',
|
||||||
|
'text/x-markdown',
|
||||||
|
'text/markdown',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const allowedFileNames = [
|
||||||
|
'Makefile',
|
||||||
|
'README',
|
||||||
|
'Dockerfile',
|
||||||
|
'Jenkinsfile',
|
||||||
|
'LICENSE',
|
||||||
|
'.env',
|
||||||
|
'.gitignore',
|
||||||
|
'.gitattributes',
|
||||||
|
'.env.example',
|
||||||
|
'.env.development',
|
||||||
|
'.env.production',
|
||||||
|
'.env.test',
|
||||||
|
'.env.staging',
|
||||||
|
'.env.development.local',
|
||||||
|
'yarn.lock',
|
||||||
|
'.bash',
|
||||||
|
'.bashrc',
|
||||||
|
'.bash_profile',
|
||||||
|
'.bash_logout',
|
||||||
|
'.profile',
|
||||||
|
'.fish_prompt',
|
||||||
|
'.zshrc',
|
||||||
|
'.zsh',
|
||||||
|
'.zprofile',
|
||||||
|
'go',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const allowedFileExtensions = [
|
||||||
|
'json',
|
||||||
|
'js',
|
||||||
|
'jsx',
|
||||||
|
'ts',
|
||||||
|
'tsx',
|
||||||
|
'c',
|
||||||
|
'cpp',
|
||||||
|
'c++',
|
||||||
|
'c#',
|
||||||
|
'java',
|
||||||
|
'php',
|
||||||
|
'py',
|
||||||
|
'rb',
|
||||||
|
'scala',
|
||||||
|
'swift',
|
||||||
|
'vb',
|
||||||
|
'vbscript',
|
||||||
|
'yaml',
|
||||||
|
'less',
|
||||||
|
'stylus',
|
||||||
|
'styl',
|
||||||
|
'sass',
|
||||||
|
'scss',
|
||||||
|
'lock',
|
||||||
|
'md',
|
||||||
|
'markdown',
|
||||||
|
'txt',
|
||||||
|
'html',
|
||||||
|
'htm',
|
||||||
|
'css',
|
||||||
|
'csv',
|
||||||
|
'log',
|
||||||
|
'sql',
|
||||||
|
'xml',
|
||||||
|
'webmanifest',
|
||||||
|
'vue',
|
||||||
|
'vuex',
|
||||||
|
'rs',
|
||||||
|
'zig',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const codeFileExtensions = [
|
||||||
|
'json',
|
||||||
|
'js',
|
||||||
|
'jsx',
|
||||||
|
'ts',
|
||||||
|
'tsx',
|
||||||
|
'c',
|
||||||
|
'cpp',
|
||||||
|
'c++',
|
||||||
|
'c#',
|
||||||
|
'java',
|
||||||
|
'php',
|
||||||
|
'py',
|
||||||
|
'rb',
|
||||||
|
'scala',
|
||||||
|
'swift',
|
||||||
|
'vb',
|
||||||
|
'vbscript',
|
||||||
|
'yaml',
|
||||||
|
'less',
|
||||||
|
'stylus',
|
||||||
|
'styl',
|
||||||
|
'sass',
|
||||||
|
'scss',
|
||||||
|
'html',
|
||||||
|
'htm',
|
||||||
|
'css',
|
||||||
|
'asm',
|
||||||
|
's',
|
||||||
|
'sh',
|
||||||
|
'bat',
|
||||||
|
'cmd',
|
||||||
|
'sql',
|
||||||
|
'xml',
|
||||||
|
'rust',
|
||||||
|
'h',
|
||||||
|
'zig',
|
||||||
|
'rs',
|
||||||
|
'go'
|
||||||
|
]
|
|
@ -1,11 +1,11 @@
|
||||||
.wrapper {
|
.wrapper {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
padding-bottom: var(--small-gap) !important;
|
padding-bottom: var(--small-gap) !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
max-width: var(--main-content) !important;
|
max-width: var(--main-content) !important;
|
||||||
margin: 0 auto !important;
|
margin: 0 auto !important;
|
||||||
padding: 0 0 !important;
|
padding: 0 0 !important;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue