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_7: 'var(--darkest-gray)',
|
||||
accents_8: 'var(--darkest-gray)',
|
||||
border: 'var(--light-gray)',
|
||||
border: 'var(--lightest-gray)',
|
||||
},
|
||||
expressiveness: {
|
||||
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 type { Document } from '@lib/types'
|
||||
import generateUUID from '@lib/generate-uuid'
|
||||
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',
|
||||
]
|
||||
import { allowedFileTypes, allowedFileNames, allowedFileExtensions } from '@lib/constants'
|
||||
|
||||
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) }) {
|
||||
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 { useMemo, useState } from "react"
|
||||
import timeAgo from "@lib/time-ago"
|
||||
import FileTree from "@components/file-tree"
|
||||
|
||||
type Props = {
|
||||
post: Post
|
||||
|
@ -61,6 +62,7 @@ const PostPage = ({ post }: Props) => {
|
|||
Download as ZIP archive
|
||||
</Button>
|
||||
</div>
|
||||
{post.files.length > 1 && <FileTree files={post.files} />}
|
||||
{post.files.map(({ id, content, title }: File) => (
|
||||
<DocumentComponent
|
||||
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 {
|
||||
height: 100% !important;
|
||||
padding-bottom: var(--small-gap) !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
padding-bottom: var(--small-gap) !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: var(--main-content) !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 0 !important;
|
||||
max-width: var(--main-content) !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 0 !important;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue