client: set post title if text pasted w/o title set

This commit is contained in:
Max Leiter 2022-03-23 16:42:56 -07:00
parent c55ca681b4
commit 7ca0cbac4c
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
8 changed files with 61 additions and 27 deletions

View file

@ -2,11 +2,12 @@ import type { Document } from "@lib/types"
import DocumentComponent from "@components/edit-document" import DocumentComponent from "@components/edit-document"
import { ChangeEvent, memo, useCallback } from "react" import { ChangeEvent, memo, useCallback } from "react"
const DocumentList = ({ docs, removeDoc, updateDocContent, updateDocTitle }: { const DocumentList = ({ docs, removeDoc, updateDocContent, updateDocTitle, onPaste }: {
docs: Document[], docs: Document[],
updateDocTitle: (i: number) => (title: string) => void updateDocTitle: (i: number) => (title: string) => void
updateDocContent: (i: number) => (content: string) => void updateDocContent: (i: number) => (content: string) => void
removeDoc: (i: number) => () => void removeDoc: (i: number) => () => void
onPaste: (e: any) => void
}) => { }) => {
const handleOnChange = useCallback((i) => (e: ChangeEvent<HTMLTextAreaElement>) => { const handleOnChange = useCallback((i) => (e: ChangeEvent<HTMLTextAreaElement>) => {
updateDocContent(i)(e.target.value) updateDocContent(i)(e.target.value)
@ -16,6 +17,7 @@ const DocumentList = ({ docs, removeDoc, updateDocContent, updateDocTitle }: {
docs.map(({ content, id, title }, i) => { docs.map(({ content, id, title }, i) => {
return ( return (
<DocumentComponent <DocumentComponent
onPaste={onPaste}
key={id} key={id}
remove={removeDoc(i)} remove={removeDoc(i)}
setContent={updateDocContent(i)} setContent={updateDocContent(i)}

View file

@ -1,8 +1,8 @@
.card { .card {
max-width: var(--main-content);
margin: var(--gap) auto; margin: var(--gap) auto;
padding: 2; padding: var(--gap);
border: 1px solid var(--light-gray); border: 1px solid var(--light-gray);
border-radius: var(--radius);
} }
.input { .input {

View file

@ -21,9 +21,10 @@ type Props = {
initialTab?: "edit" | "preview" initialTab?: "edit" | "preview"
skeleton?: boolean skeleton?: boolean
remove?: () => void remove?: () => void
onPaste?: (e: any) => void
} }
const Document = ({ remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: Props) => { const Document = ({ onPaste, remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: 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 height = editable ? "500px" : '100%'
@ -54,7 +55,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
if (skeleton) { if (skeleton) {
return <> return <>
<Spacer height={1} /> <Spacer height={1} />
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 'var(--main-content)', margin: "0 auto" }}> <div className={styles.card}>
<div className={styles.fileNameContainer}> <div className={styles.fileNameContainer}>
<Skeleton width={275} height={36} /> <Skeleton width={275} height={36} />
{remove && <Skeleton width={36} height={36} />} {remove && <Skeleton width={36} height={36} />}
@ -63,7 +64,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
<div style={{ flexDirection: 'row', display: 'flex' }}><Skeleton width={125} height={36} /></div> <div style={{ flexDirection: 'row', display: 'flex' }}><Skeleton width={125} height={36} /></div>
<Skeleton width={'100%'} height={350} /> <Skeleton width={'100%'} height={350} />
</div > </div >
</Card> </div>
</> </>
} }
@ -90,8 +91,9 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
<Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}> <Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}>
<Tabs.Item label={"Edit"} value="edit"> <Tabs.Item label={"Edit"} value="edit">
{/* <textarea className={styles.lineCounter} wrap='off' readOnly ref={lineNumberRef}>1.</textarea> */} {/* <textarea className={styles.lineCounter} wrap='off' readOnly ref={lineNumberRef}>1.</textarea> */}
<div style={{ marginTop: 'var(--gap)', display: 'flex', flexDirection: 'column' }}> <div style={{ marginTop: 'var(--gap-half)', display: 'flex', flexDirection: 'column' }}>
<Textarea <Textarea
onPaste={onPaste ? onPaste : undefined}
ref={codeEditorRef} ref={codeEditorRef}
placeholder="" placeholder=""
value={content} value={content}
@ -105,7 +107,9 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
</div> </div>
</Tabs.Item> </Tabs.Item>
<Tabs.Item label="Preview" value="preview"> <Tabs.Item label="Preview" value="preview">
<div style={{ marginTop: 'var(--gap-half)', }}>
<Preview height={height} title={title} content={content} /> <Preview height={height} title={title} content={content} />
</div>
</Tabs.Item> </Tabs.Item>
</Tabs> </Tabs>

View file

@ -9,7 +9,7 @@ import Cookies from 'js-cookie'
import type { PostVisibility, Document as DocumentType } from '@lib/types'; import type { PostVisibility, Document as DocumentType } from '@lib/types';
import PasswordModal from './password'; import PasswordModal from './password';
import getPostPath from '@lib/get-post-path'; import getPostPath from '@lib/get-post-path';
import DocumentList from '@components/edit-document-list'; import EditDocumentList from '@components/edit-document-list';
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
const Post = () => { const Post = () => {
@ -109,11 +109,31 @@ const Post = () => {
else setDocs((docs) => [...docs, ...files]) else setDocs((docs) => [...docs, ...files])
}, [docs, title]) }, [docs, title])
// pasted files
// const files = e.clipboardData.files as File[]
// if (files.length) {
// const docs = Array.from(files).map((file) => ({
// title: file.name,
// content: '',
// id: generateUUID()
// }))
// }
const onPaste = useCallback((e: any) => {
const pastedText = (e.clipboardData).getData('text')
if (pastedText) {
if (!title) {
setTitle("Pasted text")
}
}
}, [title])
return ( return (
<div style={{ marginBottom: 150 }}> <div style={{ marginBottom: 150 }}>
<Title title={title} onChange={onChangeTitle} /> <Title title={title} onChange={onChangeTitle} />
<FileDropzone setDocs={uploadDocs} /> <FileDropzone setDocs={uploadDocs} />
<DocumentList docs={docs} updateDocTitle={updateDocTitle} updateDocContent={updateDocContent} removeDoc={removeDoc} /> <EditDocumentList onPaste={onPaste} docs={docs} updateDocTitle={updateDocTitle} updateDocContent={updateDocContent} removeDoc={removeDoc} />
<div className={styles.buttons}> <div className={styles.buttons}>
<Button <Button
className={styles.button} className={styles.button}

View file

@ -1,6 +1,6 @@
import type { Document } from "@lib/types" import type { Document } from "@lib/types"
import DocumentComponent from "@components/edit-document" import DocumentComponent from "@components/edit-document"
import { ChangeEvent, memo, useCallback } from "react" import { memo, } from "react"
const DocumentList = ({ docs }: { const DocumentList = ({ docs }: {
docs: Document[], docs: Document[],

View file

@ -68,7 +68,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
if (skeleton) { if (skeleton) {
return <> return <>
<Spacer height={1} /> <Spacer height={1} />
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 980, margin: "0 auto" }}> <div className={styles.card}>
<div className={styles.fileNameContainer}> <div className={styles.fileNameContainer}>
<Skeleton width={275} height={36} /> <Skeleton width={275} height={36} />
</div> </div>
@ -76,7 +76,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
<div style={{ flexDirection: 'row', display: 'flex' }}><Skeleton width={125} height={36} /></div> <div style={{ flexDirection: 'row', display: 'flex' }}><Skeleton width={125} height={36} /></div>
<Skeleton width={'100%'} height={350} /> <Skeleton width={'100%'} height={350} />
</div > </div >
</Card> </div>
</> </>
} }
@ -102,7 +102,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
<Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}> <Tabs onChange={handleTabChange} initialValue={initialTab} hideDivider leftSpace={0}>
<Tabs.Item label={"Raw"} value="edit"> <Tabs.Item label={"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> */}
<div style={{ marginTop: 'var(--gap)', display: 'flex', flexDirection: 'column' }}> <div style={{ marginTop: 'var(--gap-half)', display: 'flex', flexDirection: 'column' }}>
<Textarea <Textarea
readOnly readOnly
ref={codeEditorRef} ref={codeEditorRef}
@ -116,7 +116,10 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
</div> </div>
</Tabs.Item> </Tabs.Item>
<Tabs.Item label="Preview" value="preview"> <Tabs.Item label="Preview" value="preview">
<div style={{ marginTop: 'var(--gap-half)', }}>
<HtmlPreview height={height} fileId={id} /> <HtmlPreview height={height} fileId={id} />
</div>
</Tabs.Item> </Tabs.Item>
</Tabs> </Tabs>

View file

@ -2,16 +2,14 @@ import { marked, Lexer } from 'marked'
import Highlight, { defaultProps, Language, } from 'prism-react-renderer' import Highlight, { defaultProps, Language, } from 'prism-react-renderer'
import { renderToStaticMarkup } from 'react-dom/server' import { renderToStaticMarkup } from 'react-dom/server'
// // image sizes. DDoS Safe?
// const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
// image sizes. DDoS Safe? // //@ts-ignore
const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/; // Lexer.rules.inline.normal.link = imageSizeLink;
//@ts-ignore // //@ts-ignore
Lexer.rules.inline.normal.link = imageSizeLink; // Lexer.rules.inline.gfm.link = imageSizeLink;
//@ts-ignore // //@ts-ignore
Lexer.rules.inline.gfm.link = imageSizeLink; // Lexer.rules.inline.breaks.link = imageSizeLink;
//@ts-ignore
Lexer.rules.inline.breaks.link = imageSizeLink;
//@ts-ignore //@ts-ignore
delete defaultProps.theme delete defaultProps.theme

View file

@ -2,6 +2,15 @@ import { marked } from 'marked'
import Highlight, { defaultProps, Language, } from 'prism-react-renderer' import Highlight, { defaultProps, Language, } from 'prism-react-renderer'
import { renderToStaticMarkup } from 'react-dom/server' import { renderToStaticMarkup } from 'react-dom/server'
// // image sizes. DDoS Safe?
// const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
// //@ts-ignore
// Lexer.rules.inline.normal.link = imageSizeLink;
// //@ts-ignore
// Lexer.rules.inline.gfm.link = imageSizeLink;
// //@ts-ignore
// Lexer.rules.inline.breaks.link = imageSizeLink;
//@ts-ignore //@ts-ignore
delete defaultProps.theme delete defaultProps.theme
// import linkStyles from '../components/link/link.module.css' // import linkStyles from '../components/link/link.module.css'
@ -12,8 +21,6 @@ renderer.heading = (text, level, _, slugger) => {
const id = slugger.slug(text) const id = slugger.slug(text)
const Component = `h${level}` const Component = `h${level}`
return renderToStaticMarkup( return renderToStaticMarkup(
//@ts-ignore //@ts-ignore
<Component> <Component>