stacker.news/components/form.js

133 lines
3.4 KiB
JavaScript
Raw Normal View History

2021-04-14 00:57:32 +00:00
import Button from 'react-bootstrap/Button'
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'
2021-04-24 21:05:07 +00:00
import { useRef, useState } from 'react'
2021-05-13 13:28:38 +00:00
import copy from 'clipboard-copy'
import Thumb from '../svgs/thumb-up-fill.svg'
2021-04-14 00:57:32 +00:00
export function SubmitButton ({ children, variant, ...props }) {
const { isSubmitting } = useFormikContext()
return (
<Button
variant={variant || 'main'}
type='submit'
disabled={isSubmitting}
{...props}
>
{children}
</Button>
)
}
2021-05-13 13:28:38 +00:00
export function CopyInput (props) {
const [copied, setCopied] = useState(false)
const handleClick = () => {
copy(props.placeholder)
setCopied(true)
setTimeout(() => setCopied(false), 1500)
}
return (
<Input
onClick={handleClick}
append={<Button onClick={handleClick}>{copied ? <Thumb width={18} height={18} /> : 'copy'}</Button>}
{...props}
/>
)
}
export function InputSkeleton ({ label }) {
return (
<BootstrapForm.Group>
{label && <BootstrapForm.Label>{label}</BootstrapForm.Label>}
<div className='clouds form-control' />
</BootstrapForm.Group>
)
}
2021-05-21 22:32:21 +00:00
export function Input ({ label, prepend, append, hint, showValid, noBottomMargin, ...props }) {
2021-05-13 13:28:38 +00:00
const [field, meta] = props.readOnly ? [{}, {}] : useField(props)
2021-04-14 00:57:32 +00:00
return (
2021-05-21 22:32:21 +00:00
<BootstrapForm.Group className={noBottomMargin ? 'mb-0' : ''}>
2021-04-14 00:57:32 +00:00
{label && <BootstrapForm.Label>{label}</BootstrapForm.Label>}
<InputGroup hasValidation>
{prepend && (
<InputGroup.Prepend>
2021-05-13 13:28:38 +00:00
{prepend}
2021-04-14 00:57:32 +00:00
</InputGroup.Prepend>
)}
<BootstrapForm.Control
{...field} {...props}
isInvalid={meta.touched && meta.error}
2021-05-21 22:32:21 +00:00
isValid={showValid && meta.touched && !meta.error}
2021-04-14 00:57:32 +00:00
/>
{append && (
<InputGroup.Append>
2021-05-13 13:28:38 +00:00
{append}
2021-04-14 00:57:32 +00:00
</InputGroup.Append>
)}
<BootstrapForm.Control.Feedback type='invalid'>
{meta.touched && meta.error}
</BootstrapForm.Control.Feedback>
{hint && (
<BootstrapForm.Text>
{hint}
</BootstrapForm.Text>
)}
</InputGroup>
</BootstrapForm.Group>
)
}
export function Form ({
2021-05-13 21:19:51 +00:00
initial, schema, onSubmit, children, initialError, ...props
2021-04-14 00:57:32 +00:00
}) {
2021-05-13 21:19:51 +00:00
const [error, setError] = useState(initialError)
2021-04-14 00:57:32 +00:00
return (
<Formik
initialValues={initial}
validationSchema={schema}
2021-04-14 23:56:29 +00:00
validateOnBlur={false}
2021-04-24 21:05:07 +00:00
onSubmit={async (...args) =>
2021-05-20 01:09:32 +00:00
onSubmit && onSubmit(...args).catch(e => setError(e.message || e))}
2021-04-14 00:57:32 +00:00
>
<FormikForm {...props} noValidate>
{error && <Alert variant='danger' onClose={() => setError(undefined)} dismissible>{error}</Alert>}
{children}
</FormikForm>
</Formik>
)
}
2021-04-24 21:05:07 +00:00
export function SyncForm ({
initial, schema, children, action, ...props
}) {
const ref = useRef(null)
return (
<Formik
initialValues={initial}
validationSchema={schema}
validateOnBlur={false}
onSubmit={() => ref.current.submit()}
>
{props => (
<form
ref={ref}
onSubmit={props.handleSubmit}
onReset={props.handleReset}
action={action}
method='POST'
noValidate
>
{children}
</form>
)}
</Formik>
)
}