markdown previews
This commit is contained in:
parent
05bd31fe7a
commit
605aac97ab
|
@ -104,13 +104,12 @@ export default function Comment ({ item, children, replyOpen, includeParent, cac
|
|||
>
|
||||
{reply ? 'cancel' : 'reply'}
|
||||
</div>}
|
||||
{reply &&
|
||||
<div className={styles.replyWrapper}>
|
||||
<Reply
|
||||
parentId={item.id} autoFocus={!replyOpen}
|
||||
onSuccess={() => setReply(replyOpen || false)} cacheId={cacheId}
|
||||
/>
|
||||
</div>}
|
||||
<div className={reply ? styles.replyWrapper : 'd-none'}>
|
||||
<Reply
|
||||
parentId={item.id} autoFocus={!replyOpen}
|
||||
onSuccess={() => setReply(replyOpen || false)} cacheId={cacheId}
|
||||
/>
|
||||
</div>
|
||||
{children}
|
||||
<div className={`${styles.comments} ml-sm-1 ml-md-3`}>
|
||||
{item.comments && !noComments
|
||||
|
|
|
@ -3,9 +3,13 @@ import InputGroup from 'react-bootstrap/InputGroup'
|
|||
import BootstrapForm from 'react-bootstrap/Form'
|
||||
import Alert from 'react-bootstrap/Alert'
|
||||
import { Formik, Form as FormikForm, useFormikContext, useField } from 'formik'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import copy from 'clipboard-copy'
|
||||
import Thumb from '../svgs/thumb-up-fill.svg'
|
||||
import { Nav } from 'react-bootstrap'
|
||||
import Markdown from '../svgs/markdown-line.svg'
|
||||
import styles from './form.module.css'
|
||||
import Text from '../components/text'
|
||||
|
||||
export function SubmitButton ({ children, variant, ...props }) {
|
||||
const { isSubmitting } = useFormikContext()
|
||||
|
@ -55,11 +59,60 @@ export function InputSkeleton ({ label }) {
|
|||
)
|
||||
}
|
||||
|
||||
export function Input ({ label, prepend, append, hint, showValid, groupClassName, ...props }) {
|
||||
const [field, meta] = props.readOnly ? [{}, {}] : useField(props)
|
||||
export function MarkdownInput ({ label, groupClassName, ...props }) {
|
||||
const [tab, setTab] = useState('write')
|
||||
const [, meta] = useField(props)
|
||||
|
||||
useEffect(() => {
|
||||
!meta.value && setTab('write')
|
||||
}, [meta.value])
|
||||
|
||||
return (
|
||||
<BootstrapForm.Group className={groupClassName}>
|
||||
<FormGroup label={label} className={groupClassName}>
|
||||
<div className={`${styles.markdownInput} ${tab === 'write' ? styles.noTopLeftRadius : ''}`}>
|
||||
<Nav variant='tabs' defaultActiveKey='write' activeKey={tab} onSelect={tab => setTab(tab)}>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='write'>write</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='preview' disabled={!meta.value}>preview</Nav.Link>
|
||||
</Nav.Item>
|
||||
<a
|
||||
className='ml-auto text-muted d-flex align-items-center'
|
||||
href='https://guides.github.com/features/mastering-markdown/' target='_blank' rel='noreferrer'
|
||||
>
|
||||
<Markdown width={18} height={18} />
|
||||
</a>
|
||||
</Nav>
|
||||
<div className={tab !== 'write' ? 'd-none' : ''}>
|
||||
<InputInner
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<div className={tab !== 'preview' ? 'd-none' : `${styles.text} form-control`}>
|
||||
<Text>{meta.value}</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
|
||||
function FormGroup ({ className, label, children }) {
|
||||
return (
|
||||
<BootstrapForm.Group className={className}>
|
||||
{label && <BootstrapForm.Label>{label}</BootstrapForm.Label>}
|
||||
{children}
|
||||
</BootstrapForm.Group>
|
||||
)
|
||||
}
|
||||
|
||||
function InputInner ({ prepend, append, hint, showValid, ...props }) {
|
||||
const [field, meta] = props.readOnly ? [{}, {}] : useField(props)
|
||||
|
||||
return (
|
||||
<>
|
||||
<InputGroup hasValidation>
|
||||
{prepend && (
|
||||
<InputGroup.Prepend>
|
||||
|
@ -85,7 +138,15 @@ export function Input ({ label, prepend, append, hint, showValid, groupClassName
|
|||
{hint}
|
||||
</BootstrapForm.Text>
|
||||
)}
|
||||
</BootstrapForm.Group>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Input ({ label, groupClassName, ...props }) {
|
||||
return (
|
||||
<FormGroup label={label} className={groupClassName}>
|
||||
<InputInner {...props} />
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
.noTopLeftRadius textarea {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.markdownInput textarea {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.markdownInput .text {
|
||||
margin-top: -1px;
|
||||
height: auto;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Form, Input, SubmitButton } from '../components/form'
|
||||
import { Form, MarkdownInput, SubmitButton } from '../components/form'
|
||||
import * as Yup from 'yup'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import styles from './reply.module.css'
|
||||
|
@ -62,7 +62,7 @@ export default function Reply ({ parentId, onSuccess, autoFocus }) {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
<MarkdownInput
|
||||
name='text'
|
||||
as='textarea'
|
||||
rows={4}
|
||||
|
|
|
@ -16,4 +16,17 @@
|
|||
margin-top: .25rem;
|
||||
width: 71px;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.noTopLeftRadius textarea {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.reply textarea {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.reply .text {
|
||||
margin-top: -1px;
|
||||
height: auto;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import Button from 'react-bootstrap/Button'
|
||||
import { Form, Input, SubmitButton } from '../components/form'
|
||||
import { Form, Input, MarkdownInput, SubmitButton } from '../components/form'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import * as Yup from 'yup'
|
||||
|
@ -44,7 +44,7 @@ export function DiscussionForm () {
|
|||
required
|
||||
autoFocus
|
||||
/>
|
||||
<Input
|
||||
<MarkdownInput
|
||||
label={<>text <small className='text-muted ml-2'>optional</small></>}
|
||||
name='text'
|
||||
as='textarea'
|
||||
|
|
|
@ -44,6 +44,10 @@ $container-max-widths: (
|
|||
md: 720px,
|
||||
lg: 900px,
|
||||
) !default;
|
||||
$nav-link-padding-y: .1rem;
|
||||
$nav-tabs-link-active-bg: #fff;
|
||||
$nav-tabs-link-hover-border-color: transparent;
|
||||
$nav-tabs-link-active-border-color: #ced4da #ced4da $nav-tabs-link-active-bg;
|
||||
|
||||
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
|
@ -90,6 +94,21 @@ footer {
|
|||
}
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
font-size: 80%;
|
||||
color: #6c757d;
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 92%;
|
||||
font-weight: bold;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm4 12.5v-4l2 2 2-2v4h2v-7h-2l-2 2-2-2H5v7h2zm11-3v-4h-2v4h-2l3 3 3-3h-2z"/></svg>
|
After Width: | Height: | Size: 281 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm3 10.5H5v-7h2l2 2 2-2h2v7h-2v-4l-2 2-2-2v4zm11-3h2l-3 3-3-3h2v-4h2v4z"/></svg>
|
After Width: | Height: | Size: 294 B |
Loading…
Reference in New Issue