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' import React, { 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, value, onClick, ...props }) { const { isSubmitting, setFieldValue } = useFormikContext() return ( { setFieldValue('submit', value) onClick && onClick(e) } : onClick} {...props} > {children} ) } export function CopyInput (props) { const [copied, setCopied] = useState(false) const handleClick = () => { copy(props.placeholder) setCopied(true) setTimeout(() => setCopied(false), 1500) } return ( {copied ? : 'copy'} } {...props} /> ) } export function InputSkeleton ({ label, hint }) { return ( {label && {label}} {hint && {hint} } ) } export function MarkdownInput ({ label, groupClassName, ...props }) { const [tab, setTab] = useState('write') const [, meta] = useField(props) useEffect(() => { !meta.value && setTab('write') }, [meta.value]) return ( setTab(tab)}> write preview {tab === 'preview' && {meta.value}} ) } function FormGroup ({ className, label, children }) { return ( {label && {label}} {children} ) } function InputInner ({ prepend, append, hint, showValid, onChange, overrideValue, innerRef, storageKeyPrefix, ...props }) { const [field, meta, helpers] = props.readOnly ? [{}, {}, {}] : useField(props) const formik = props.readOnly ? null : useFormikContext() const storageKey = storageKeyPrefix ? storageKeyPrefix + '-' + props.name : undefined useEffect(() => { if (overrideValue) { helpers.setValue(overrideValue) if (storageKey) { localStorage.setItem(storageKey, overrideValue) } } else if (storageKey) { const draft = localStorage.getItem(storageKey) if (draft) { // for some reason we have to turn off validation to get formik to // not assume this is invalid helpers.setValue(draft, false) } } }, [overrideValue]) return ( <> {prepend && ( {prepend} )} { if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) { formik?.submitForm() } }} ref={innerRef} {...field} {...props} onChange={(e) => { field.onChange(e) if (storageKey) { localStorage.setItem(storageKey, e.target.value) } if (onChange) { onChange(formik, e) } }} isInvalid={meta.touched && meta.error} isValid={showValid && meta.touched && !meta.error} /> {append && ( {append} )} {meta.touched && meta.error} {hint && ( {hint} )} > ) } export function Input ({ label, groupClassName, ...props }) { return ( ) } export function Checkbox ({ children, label, extra, handleChange, inline, ...props }) { // React treats radios and checkbox inputs differently other input types, select, and textarea. // Formik does this too! When you specify `type` to useField(), it will // return the correct bag of props for you const [field] = useField({ ...props, type: 'checkbox' }) return ( { field.onChange(e) handleChange && handleChange(e.target.checked) }} /> {label} {extra && {extra} } ) } export function Form ({ initial, schema, onSubmit, children, initialError, validateImmediately, storageKeyPrefix, ...props }) { const [error, setError] = useState(initialError) return ( onSubmit && onSubmit(...args).then(() => { if (!storageKeyPrefix) return Object.keys(...args).forEach(v => localStorage.removeItem(storageKeyPrefix + '-' + v)) }).catch(e => setError(e.message || e))} > {error && setError(undefined)} dismissible>{error}} {storageKeyPrefix ? React.Children.map(children, (child) => { if (child) { return React.cloneElement(child, { storageKeyPrefix }) } }) : children} ) } export function SyncForm ({ initial, schema, children, action, ...props }) { const ref = useRef(null) return ( ref.current.submit()} > {props => ( {children} )} ) }