client: improve rendered post styling; length is 100% if uneditable
This commit is contained in:
parent
5b71fc6b27
commit
844ccded3c
7 changed files with 113 additions and 82 deletions
|
@ -24,3 +24,7 @@
|
||||||
/* Override geist-ui styling */
|
/* Override geist-ui styling */
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
19
client/components/preview/react-markdown-preview.tsx
Normal file
19
client/components/preview/react-markdown-preview.tsx
Normal 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
|
|
@ -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`}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in a new issue