client: improve rendered post styling; length is 100% if uneditable

This commit is contained in:
Max Leiter 2022-03-08 16:39:24 -08:00
parent 5b71fc6b27
commit 844ccded3c
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
7 changed files with 113 additions and 82 deletions

View file

@ -24,3 +24,7 @@
/* Override geist-ui styling */ /* Override geist-ui styling */
margin: 0 !important; margin: 0 !important;
} }
.textarea {
height: 100%;
}

View file

@ -17,6 +17,7 @@ type Props = {
const Document = ({ remove, editable, title, content, setTitle, setContent, initialTab = 'edit' }: Props) => { const Document = ({ remove, editable, title, content, setTitle, setContent, initialTab = 'edit' }: Props) => {
const codeEditorRef = useRef<HTMLTextAreaElement>(null) const codeEditorRef = useRef<HTMLTextAreaElement>(null)
const [tab, setTab] = useState(initialTab) const [tab, setTab] = useState(initialTab)
const height = editable ? "500px" : '100%'
const handleTabChange = (newTab: string) => { const handleTabChange = (newTab: string) => {
if (newTab === 'edit') { if (newTab === 'edit') {
@ -59,20 +60,23 @@ const Document = ({ remove, editable, title, content, setTitle, setContent, init
<Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}> <Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}>
<Tabs.Item label={editable ? "Edit" : "Raw"} value="edit"> <Tabs.Item label={editable ? "Edit" : "Raw"} value="edit">
{/* <textarea className={styles.lineCounter} wrap='off' readOnly ref={lineNumberRef}>1.</textarea> */} {/* <textarea className={styles.lineCounter} wrap='off' readOnly ref={lineNumberRef}>1.</textarea> */}
<Textarea <div style={{ display: 'flex', flexDirection: 'column' }}>
ref={codeEditorRef} <Textarea
placeholder="Type some contents..." ref={codeEditorRef}
value={content} placeholder="Type some contents..."
onChange={(event) => setContent ? setContent(event.target.value) : null} value={content}
width="100%" onChange={(event) => setContent ? setContent(event.target.value) : null}
height="500px" width="100%"
disabled={!editable} disabled={!editable}
resize="vertical" // TODO: Textarea should grow to fill parent if height == 100%
className={styles.textarea} style={{ flex: 1, minHeight: 350 }}
/> resize="vertical"
className={styles.textarea}
/>
</div>
</Tabs.Item> </Tabs.Item>
<Tabs.Item label="Preview" value="preview"> <Tabs.Item label="Preview" value="preview">
<MarkdownPreview height={500} content={content} /> <MarkdownPreview height={height} content={content} />
</Tabs.Item> </Tabs.Item>
</Tabs> </Tabs>

View file

@ -13,28 +13,27 @@
.mobile { .mobile {
position: relative; position: relative;
z-index: 1; z-index: 2;
}
.controls {
display: none !important;
} }
@media only screen and (max-width: 650px) { @media only screen and (max-width: 650px) {
.tabs { .tabs {
display: none; display: none;
} }
.controls {
display: block !important;
}
} }
.controls { .controls {
flex: 1 1; flex: 1 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end;
}
.controls :global(.menu-toggle) {
display: flex;
align-items: center;
min-width: 40px;
height: 40px;
padding: 0;
} }
.wrapper { .wrapper {

View file

@ -72,12 +72,12 @@ const Header = ({ changeTheme, theme }: DriftProps) => {
condition: !isSignedIn condition: !isSignedIn
}, },
{ {
name: "", name: isMobile ? "GitHub" : "",
href: "https://github.com/maxleiter/drift", href: "https://github.com/maxleiter/drift",
icon: <GitHubIcon />, icon: <GitHubIcon />,
condition: true condition: true
} }
], [isSignedIn, router]) ], [isMobile, isSignedIn, router])
useEffect(() => { useEffect(() => {
setSelectedTab(pages.find((page) => { setSelectedTab(pages.find((page) => {
@ -89,7 +89,7 @@ const Header = ({ changeTheme, theme }: DriftProps) => {
return ( return (
<Page.Header height={'var(--page-nav-height)'} margin={0} paddingBottom={0} paddingTop={"var(--gap)"}> <Page.Header height={'var(--page-nav-height)'} margin={0} paddingBottom={0} paddingTop={"var(--gap)"}>
{!isMobile && <div className={styles.tabs}> <div className={styles.tabs}>
<Tabs <Tabs
value={selectedTab} value={selectedTab}
leftSpace={0} leftSpace={0}
@ -116,17 +116,14 @@ const Header = ({ changeTheme, theme }: DriftProps) => {
else return null else return null
})} })}
</Tabs> </Tabs>
</div>} </div>
<div className="controls"> <div className={styles.controls}>
{isMobile && ( <Button
<Button auto
className="menu-toggle" type="abort"
auto onClick={() => setExpanded(!expanded)}>
type="abort" <MenuIcon size="1.125rem" />
onClick={() => setExpanded(!expanded)}> </Button>
<MenuIcon size="1.125rem" />
</Button>
)}
</div> </div>
{isMobile && expanded && (<div className={styles.mobile}> {isMobile && expanded && (<div className={styles.mobile}>
<ButtonGroup vertical> <ButtonGroup vertical>

View file

@ -1,15 +1,8 @@
import { memo } from "react" import { memo } from "react"
import ReactMarkdown from "react-markdown" import ReactMarkdownPreview from "./react-markdown-preview"
import remarkGfm from "remark-gfm"
// @ts-ignore because of no types in remark-a11y-emoji
import a11yEmoji from '@fec/remark-a11y-emoji';
import styles from './preview.module.css'
const MarkdownPreview = ({ content, height }: { content?: string, height?: number | string }) => { const MarkdownPreview = ({ content = '', height = 500 }: { content?: string, height?: number | string }) => {
{/* remarkGfm is github flavored markdown support, a11yEmoji wraps emojis in accessible spans for screen readers */ } return (<ReactMarkdownPreview height={height} content={content} />)
return (<div style={{ height }}><ReactMarkdown className={styles.markdownPreview} remarkPlugins={[remarkGfm, a11yEmoji]} >
{content || ""}
</ReactMarkdown></div>)
} }
export default memo(MarkdownPreview) export default memo(MarkdownPreview)

View file

@ -0,0 +1,19 @@
import ReactMarkdown from "react-markdown"
import remarkGfm from "remark-gfm"
// @ts-ignore because of no types in remark-a11y-emoji
import a11yEmoji from '@fec/remark-a11y-emoji';
import styles from './preview.module.css'
type Props = {
content: string | undefined
height: number | string
}
const ReactMarkdownPreview = ({ content, height }: Props) => {
return (<div style={{ height }}><ReactMarkdown className={styles.markdownPreview} remarkPlugins={[remarkGfm, a11yEmoji]} >
{content || ""}
</ReactMarkdown></div>)
}
export default ReactMarkdownPreview

View file

@ -5,7 +5,54 @@ import { Page, Spacer } from '@geist-ui/core'
import Header from '../components/header' import Header from '../components/header'
import { ThemeProps } from './_app' import { ThemeProps } from './_app'
import Document from '../components/document' import Document from '../components/document'
const Home = ({ theme, changeTheme }: ThemeProps) => {
export function getStaticProps() {
const introDoc = `# Welcome to Drift
### Drift is a self-hostable clone of GitHub Gist.
#### It is a simple way to share code and text snippets with your friends, with support for the following:
- Render GitHub Extended Markdown (including images)
- User authentication
- Private, public, and secret posts
If you want to signup, you can join at [/signup](/signup) as long as you have a passcode provided by the administrator (which you don't need for this demo).
**This demo is on a memory-only database, so accounts and pastes can be deleted at any time.**
You can find the source code on [GitHub](https://github.com/MaxLeiter/drift).
Drift was inspired by [this tweet](https://twitter.com/emilyst/status/1499858264346935297):
> What is the absolute closest thing to GitHub Gist that can be self-hosted?
In terms of design and functionality. Hosts images and markdown, rendered. Creates links that can be private or public. Uses/requires registration.
I have looked at dozens of pastebin-like things.
`
const todoDoc =
`#### In no particular order:
- Less JavaScript usage (it's currently required)
- A non-Node backend
- Hosting images
- Password-protected posts
- Administrator panel
- Meta tags
- User settings
- Search
- "Forking"
- LaTeX
- Syntax highlighting based on filename ending"`
return {
props: {
introContent: introDoc,
todoContent: todoDoc,
}
}
}
type Props = ThemeProps & {
introContent: string
todoContent: string
}
const Home = ({ theme, changeTheme, introContent, todoContent }: Props) => {
return ( return (
<Page className={styles.container} width="100%"> <Page className={styles.container} width="100%">
<Head> <Head>
@ -16,49 +63,17 @@ const Home = ({ theme, changeTheme }: ThemeProps) => {
<Page.Header> <Page.Header>
<Header theme={theme} changeTheme={changeTheme} /> <Header theme={theme} changeTheme={changeTheme} />
</Page.Header> </Page.Header>
<Page.Content width={"var(--main-content-width)"} margin="auto"> <Page.Content width={"var(--main-content-width)"} margin="auto" paddingTop={"var(--gap)"}>
<Document <Document
editable={false} editable={false}
content={ content={introContent}
`# Welcome to Drift
### Drift is a self-hostable clone of GitHub Gist.
#### It is a simple way to share code and text snippets with your friends, with support for the following:
- Render GitHub Extended Markdown (including images)
- User authentication
- Private, public, and secret posts
If you want to signup, you can join at [/signup](/signup) as long as you have a passcode provided by the administrator (which you don't need for this demo).
**This demo is on a memory-only database, so accounts and pastes can be deleted at any time.**
You can find the source code on [GitHub](https://github.com/MaxLeiter/drift).
Drift was inspired by [this tweet](https://twitter.com/emilyst/status/1499858264346935297):
> What is the absolute closest thing to GitHub Gist that can be self-hosted?
In terms of design and functionality. Hosts images and markdown, rendered. Creates links that can be private or public. Uses/requires registration.
I have looked at dozens of pastebin-like things.
`}
title={`Welcome to Drift.md`} title={`Welcome to Drift.md`}
initialTab={`preview`} initialTab={`preview`}
/> />
<Spacer height={1} /> <Spacer height={1} />
<Document <Document
editable={false} editable={false}
content={ content={todoContent}
`#### In no particular order:
- [ ] Less JavaScript usage (it's currently required)
- [ ] A non-Node backend
- [ ] Hosting images
- [ ] Password-protected posts
- [ ] Administrator panel
- [ ] Meta tags
- [ ] User settings
- [ ] Search
- [ ] "Forking
- [ ] LaTeX
- [ ] Syntax highlighting based on filename ending"`}
title={`TODO.md`} title={`TODO.md`}
initialTab={`preview`} initialTab={`preview`}
/> />