{children
? children({ index: i, readOnly: i < readOnlyLen, placeholder: i >= min ? 'optional' : undefined })
: = min ? 'optional' : undefined} />}
{options.length - 1 === i && options.length !== max
? fieldArrayHelpers.push(emptyItem)} />
// filler div for col alignment across rows
: }
{options.length - 1 === i &&
<>
{hint && {hint}}
{form.touched[name] && typeof form.errors[name] === 'string' &&
{form.errors[name]}
}
>}
))}
>
)
}}
)
}
export function Checkbox ({ children, label, groupClassName, hiddenLabel, extra, handleChange, inline, disabled, ...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,, helpers] = useField({ ...props, type: 'checkbox' })
return (
{hiddenLabel && {label}}
{
field.onChange(e)
handleChange && handleChange(e.target.checked, helpers.setValue)
}}
/>
{label}
{extra &&
{extra}
}
)
}
const StorageKeyPrefixContext = createContext()
export function Form ({
initial, schema, onSubmit, children, initialError, validateImmediately, storageKeyPrefix, validateOnChange = true, invoiceable, innerRef, ...props
}) {
const toaster = useToast()
const initialErrorToasted = useRef(false)
useEffect(() => {
if (initialError && !initialErrorToasted.current) {
toaster.danger(initialError.message || initialError.toString?.())
initialErrorToasted.current = true
}
}, [])
const clearLocalStorage = useCallback((values) => {
Object.keys(values).forEach(v => {
window.localStorage.removeItem(storageKeyPrefix + '-' + v)
if (Array.isArray(values[v])) {
values[v].forEach(
(iv, i) => {
Object.keys(iv).forEach(k => {
window.localStorage.removeItem(`${storageKeyPrefix}-${v}[${i}].${k}`)
})
window.localStorage.removeItem(`${storageKeyPrefix}-${v}[${i}]`)
})
}
})
}, [storageKeyPrefix])
// if `invoiceable` is set,
// support for payment per invoice if they are lurking or don't have enough balance
// is added to submit handlers.
// submit handlers need to accept { satsReceived, hash, hmac } in their first argument
// and use them as variables in their GraphQL mutation
if (invoiceable && onSubmit) {
const options = typeof invoiceable === 'object' ? invoiceable : undefined
onSubmit = useInvoiceable(onSubmit, { callback: clearLocalStorage, ...options })
}
const onSubmitInner = useCallback(async (values, ...args) => {
try {
if (onSubmit) {
// extract cost from formik fields
// (cost may also be set in a formik field named 'amount')
let cost = values?.cost || values?.amount
// add potential image fees which are set in a different field
// to differentiate between fees (in receipts for example)
cost += (values?.imageFeesInfo?.totalFees || 0)
const options = await onSubmit(cost, values, ...args)
if (!storageKeyPrefix || options?.keepLocalStorage) return
clearLocalStorage(values)
}
} catch (err) {
console.log(err)
toaster.danger(err.message || err.toString?.())
}
}, [onSubmit, toaster, clearLocalStorage, storageKeyPrefix])
return (
{children}
)
}
export function Select ({ label, items, groupClassName, onChange, noForm, overrideValue, ...props }) {
const [field, meta, helpers] = noForm ? [{}, {}, {}] : useField(props)
const formik = noForm ? null : useFormikContext()
const invalid = meta.touched && meta.error
useEffect(() => {
if (overrideValue) {
helpers.setValue(overrideValue)
}
}, [overrideValue])
return (
{
if (field?.onChange) {
field.onChange(e)
}
if (onChange) {
onChange(formik, e)
}
}}
isInvalid={invalid}
>
{items.map(item => )}
{meta.touched && meta.error}
)
}
export function DatePicker ({ fromName, toName, noForm, onMount, ...props }) {
const formik = noForm ? null : useFormikContext()
const onChangeHandler = props.onChange
const [,, fromHelpers] = noForm ? [{}, {}, {}] : useField({ ...props, name: fromName })
const [,, toHelpers] = noForm ? [{}, {}, {}] : useField({ ...props, name: toName })
useEffect(() => {
if (onMount) {
const [from, to] = onMount()
fromHelpers.setValue(from)
toHelpers.setValue(to)
}
}, [])
return (
{
fromHelpers.setValue(from?.toISOString())
toHelpers.setValue(to?.toISOString())
onChangeHandler(formik, [from, to], e)
}}
/>
)
}