client: set post title if text pasted w/o title set
This commit is contained in:
parent
c55ca681b4
commit
7ca0cbac4c
8 changed files with 61 additions and 27 deletions
|
@ -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)}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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">
|
||||||
<Preview height={height} title={title} content={content} />
|
<div style={{ marginTop: 'var(--gap-half)', }}>
|
||||||
|
<Preview height={height} title={title} content={content} />
|
||||||
|
</div>
|
||||||
</Tabs.Item>
|
</Tabs.Item>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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[],
|
||||||
|
|
|
@ -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">
|
||||||
<HtmlPreview height={height} fileId={id} />
|
<div style={{ marginTop: 'var(--gap-half)', }}>
|
||||||
|
|
||||||
|
<HtmlPreview height={height} fileId={id} />
|
||||||
|
</div>
|
||||||
</Tabs.Item>
|
</Tabs.Item>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue