"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useForm = useForm; exports.useFormState = useFormState; var _lodash = require("lodash"); var _react = require("react"); var _useAsyncFn = _interopRequireDefault(require("react-use/lib/useAsyncFn")); var _saferLodashSet = require("@kbn/safer-lodash-set"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ /** * Returns state and {@link HTMLFormElement} event handlers useful for creating * forms with inline validation. * * @see {@link useFormState} if you don't want to use {@link HTMLFormElement}. * * @example * ```typescript * const [form, eventHandlers] = useForm({ * onSubmit: (values) => apiClient.create(values), * validate: (values) => !values.email ? { email: 'Required' } : {} * }); * * * * Submit * * ``` */ function useForm(options) { const form = useFormState(options); const eventHandlers = { onSubmit: event => { event.preventDefault(); form.submit(); }, onChange: event => { const { name, type, checked, value } = event.target; if (name) { form.setValue(name, type === 'checkbox' ? checked : value); } }, onBlur: event => { const { name } = event.target; if (name) { form.setTouched(event.target.name); } } }; return [form, eventHandlers]; } /** * Returns state useful for creating forms with inline validation. * * @example * ```typescript * const form = useFormState({ * onSubmit: (values) => apiClient.create(values), * validate: (values) => !values.toggle ? { toggle: 'Required' } : {} * }); * * form.setValue('toggle', e.target.checked)} * onBlur={() => form.setTouched('toggle')} * isInvalid={!!form.errors.toggle} * /> * * Submit * * ``` */ function useFormState({ onSubmit, validate, defaultValues }) { const [values, setValues] = (0, _react.useState)(defaultValues); const [errors, setErrors] = (0, _react.useState)({}); const [touched, setTouched] = (0, _react.useState)({}); const [submitCount, setSubmitCount] = (0, _react.useState)(0); async function validateFormFn(formValues) { // Allows resetting `useAsyncFn` state if (!formValues) { return Promise.resolve(undefined); } const nextErrors = await validate(formValues); setErrors(nextErrors); if (Object.keys(nextErrors).length === 0) { setSubmitCount(0); } return nextErrors; } async function submitFormFn(formValues) { // Allows resetting `useAsyncFn` state if (!formValues) { return Promise.resolve(undefined); } const nextErrors = await validateForm(formValues); setTouched(mapDeep(formValues, true)); setSubmitCount(submitCount + 1); if (Object.keys(nextErrors).length === 0) { return onSubmit(formValues); } } const [validationState, validateForm] = (0, _useAsyncFn.default)(validateFormFn, [validate]); const [submitState, submitForm] = (0, _useAsyncFn.default)(submitFormFn, [validateForm, onSubmit]); return { setValue: async (name, value) => { const nextValues = setDeep(values, name, value); setValues(nextValues); await validateForm(nextValues); }, setTouched: async (name, value = true) => { setTouched(setDeep(touched, name, value)); await validateForm(values); }, setError: (name, message) => { setErrors(setDeep(errors, name, message)); setTouched(setDeep(touched, name, true)); }, reset: (nextValues = defaultValues) => { setValues(nextValues); setErrors({}); setTouched({}); setSubmitCount(0); validateForm(undefined); // Resets `validationState` submitForm(undefined); // Resets `submitState` }, submit: () => submitForm(values), values, errors, touched, isValidating: validationState.loading, isSubmitting: submitState.loading, submitError: submitState.error, isInvalid: Object.keys(errors).length > 0, isSubmitted: submitCount > 0 }; } function mapDeep(values, value) { return (0, _lodash.cloneDeepWith)(values, v => { if (typeof v !== 'object' && v !== null) { return value; } }); } function setDeep(values, name, value) { if ((0, _lodash.get)(values, name) !== value) { return (0, _saferLodashSet.set)((0, _lodash.cloneDeep)(values), name, value); } return values; }