improved heading and images for markdown

This commit is contained in:
keyan 2022-07-13 18:00:48 -05:00
parent d7210662b3
commit 5457026bd3
8 changed files with 97 additions and 19 deletions

View File

@ -72,7 +72,7 @@ export function CommentFlat ({ item, ...props }) {
} }
export default function Comment ({ export default function Comment ({
item, children, replyOpen, includeParent, item, children, replyOpen, includeParent, topLevel,
rootText, noComments, noReply, truncate, depth rootText, noComments, noReply, truncate, depth
}) { }) {
const [edit, setEdit] = useState() const [edit, setEdit] = useState()
@ -173,7 +173,7 @@ export default function Comment ({
) )
: ( : (
<div className={styles.text}> <div className={styles.text}>
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}> <Text topLevel={topLevel} nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>
{truncate ? truncateString(item.text) : item.searchText || item.text} {truncate ? truncateString(item.text) : item.searchText || item.text}
</Text> </Text>
</div> </div>

View File

@ -62,6 +62,7 @@ export function DiscussionForm ({
autoFocus autoFocus
/> />
<MarkdownInput <MarkdownInput
topLevel
label={<>{textLabel} <small className='text-muted ml-2'>optional</small></>} label={<>{textLabel} <small className='text-muted ml-2'>optional</small></>}
name='text' name='text'
as={TextareaAutosize} as={TextareaAutosize}

View File

@ -71,7 +71,7 @@ export function InputSkeleton ({ label, hint }) {
) )
} }
export function MarkdownInput ({ label, groupClassName, ...props }) { export function MarkdownInput ({ label, topLevel, groupClassName, ...props }) {
const [tab, setTab] = useState('write') const [tab, setTab] = useState('write')
const [, meta] = useField(props) const [, meta] = useField(props)
@ -103,7 +103,7 @@ export function MarkdownInput ({ label, groupClassName, ...props }) {
</div> </div>
<div className={tab !== 'preview' ? 'd-none' : 'form-group'}> <div className={tab !== 'preview' ? 'd-none' : 'form-group'}>
<div className={`${styles.text} form-control`}> <div className={`${styles.text} form-control`}>
{tab === 'preview' && <Text>{meta.value}</Text>} {tab === 'preview' && <Text topLevel={topLevel}>{meta.value}</Text>}
</div> </div>
</div> </div>
</div> </div>

View File

@ -92,14 +92,14 @@ function TopLevelItem ({ item, noReply, ...props }) {
} }
function ItemText ({ item }) { function ItemText ({ item }) {
return <Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.searchText || item.text}</Text> return <Text topLevel nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.searchText || item.text}</Text>
} }
export default function ItemFull ({ item, bio, ...props }) { export default function ItemFull ({ item, bio, ...props }) {
return ( return (
<> <>
{item.parentId {item.parentId
? <Comment item={item} replyOpen includeParent noComments {...props} /> ? <Comment topLevel item={item} replyOpen includeParent noComments {...props} />
: ( : (
<div className='mt-1'>{ <div className='mt-1'>{
bio bio

View File

@ -160,6 +160,7 @@ export default function JobForm ({ item, sub }) {
/> />
</BForm.Row> </BForm.Row>
<MarkdownInput <MarkdownInput
topLevel
label='description' label='description'
name='text' name='text'
as={TextareaAutosize} as={TextareaAutosize}

View File

@ -9,6 +9,7 @@ import sub from '../lib/remark-sub'
import remarkDirective from 'remark-directive' import remarkDirective from 'remark-directive'
import { visit } from 'unist-util-visit' import { visit } from 'unist-util-visit'
import reactStringReplace from 'react-string-replace' import reactStringReplace from 'react-string-replace'
import { useEffect, useState } from 'react'
function myRemarkPlugin () { function myRemarkPlugin () {
return (tree) => { return (tree) => {
@ -27,17 +28,18 @@ function myRemarkPlugin () {
} }
} }
export default function Text ({ nofollow, children }) { export default function Text ({ topLevel, nofollow, children }) {
// all the reactStringReplace calls are to facilitate search highlighting // all the reactStringReplace calls are to facilitate search highlighting
return ( return (
<div className={styles.text}> <div className={styles.text}>
<ReactMarkdown <ReactMarkdown
components={{ components={{
h1: 'h6', h1: ({children, node, ...props}) => topLevel ? <h1 {...props}>{children}</h1> : <h3 {...props}>{children}</h3>,
h2: 'h6', h2: ({children, node, ...props}) => topLevel ? <h2 {...props}>{children}</h2> : <h4 {...props}>{children}</h4>,
h3: 'h6', h3: ({children, node, ...props}) => topLevel ? <h3 {...props}>{children}</h3> : <h5 {...props}>{children}</h5>,
h4: 'h6', h4: ({children, node, ...props}) => topLevel ? <h4 {...props}>{children}</h4> : <h6 {...props}>{children}</h6>,
h5: 'h6', h5: ({children, node, ...props}) => topLevel ? <h5 {...props}>{children}</h5> : <h6 {...props}>{children}</h6>,
h6: 'h6', h6: 'h6',
table: ({ node, ...props }) => table: ({ node, ...props }) =>
<div className='table-responsive'> <div className='table-responsive'>
@ -79,7 +81,8 @@ export default function Text ({ nofollow, children }) {
{children} {children}
</a> </a>
) )
} },
img: ({node, ...props}) => <ZoomableImage topLevel={topLevel} {...props}/>
}} }}
remarkPlugins={[gfm, mention, sub, remarkDirective, myRemarkPlugin]} remarkPlugins={[gfm, mention, sub, remarkDirective, myRemarkPlugin]}
> >
@ -88,3 +91,39 @@ export default function Text ({ nofollow, children }) {
</div> </div>
) )
} }
function ZoomableImage ({ src, topLevel, ...props }) {
if (!src) {
return null
}
const defaultMediaStyle = {
maxHeight: topLevel ? '75vh' : '25vh',
cursor: 'zoom-in'
}
// if image changes we need to update state
const [mediaStyle, setMediaStyle] = useState(defaultMediaStyle)
useEffect(() => {
setMediaStyle(defaultMediaStyle)
}, [src])
const handleClick = () => {
if (mediaStyle.cursor === 'zoom-in') {
setMediaStyle({
width: '100%',
cursor: 'zoom-out'
})
} else {
setMediaStyle(defaultMediaStyle)
}
}
return <img
className={topLevel ? styles.topLevel : undefined}
style={mediaStyle}
src={src}
onClick={handleClick}
{...props}
/>
}

View File

@ -7,9 +7,13 @@
@media screen and (min-width: 767px) { @media screen and (min-width: 767px) {
.text { .text {
line-height: 130%; line-height: 130%;
} }
} }
.text hr {
border-top: 1px solid var(--theme-clickToContextColor);
}
.text p { .text p {
margin-bottom: .5rem; margin-bottom: .5rem;
@ -21,21 +25,29 @@
margin-bottom: .5rem; margin-bottom: .5rem;
} }
.text pre > div { .text pre>div {
margin: 0 !important; margin: 0 !important;
} }
.text > *:last-child { .text>*:last-child {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.text blockquote > *:last-child { .text blockquote>*:last-child {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.text img { .text img {
display: block;
margin-top: .5rem;
border-radius: .4rem;
width: min-content;
max-width: 100%; max-width: 100%;
max-height: 300px; }
.text img.topLevel {
margin-top: .75rem;
margin-bottom: .75rem;
} }
.text table { .text table {
@ -46,4 +58,28 @@
border-left: 2px solid var(--theme-grey); border-left: 2px solid var(--theme-grey);
padding-left: 1rem; padding-left: 1rem;
margin: 0 0 0.5rem 0.5rem !important; margin: 0 0 0.5rem 0.5rem !important;
}
.text h1 {
font-size: 1.6rem;
}
.text h2 {
font-size: 1.45rem;
}
.text h3 {
font-size: 1.3rem;
}
.text h4 {
font-size: 1.15rem;
}
.text h5 {
font-size: 1rem;
}
.text h6 {
font-size: .85rem;
} }

View File

@ -64,6 +64,7 @@ export function BioForm ({ handleSuccess, bio }) {
}} }}
> >
<MarkdownInput <MarkdownInput
topLevel
name='bio' name='bio'
as={TextareaAutosize} as={TextareaAutosize}
minRows={6} minRows={6}