UNPKG

@atlaskit/form

Version:

A form allows people to input information.

207 lines (202 loc) 6.95 kB
import _extends from "@babel/runtime/helpers/extends"; import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import React, { createContext, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createForm } from 'final-form'; import createDecorator from 'final-form-focus'; import set from 'lodash/set'; import forwardRefWithGeneric from '@atlaskit/ds-lib/forward-ref-with-generic'; import mergeRefs from '@atlaskit/ds-lib/merge-refs'; import { getFirstErrorField } from './utils'; /** * __Form context__ * * A form context creates a context for the field values and allows them to be accessed by the children. */ export var FormContext = /*#__PURE__*/createContext({ registerField: function registerField() { return function () {}; }, getCurrentValue: function getCurrentValue() { return undefined; }, subscribe: function subscribe() { return function () {}; } }); /** * __Is disabled context__ * * An is disabled context creates the context for when a value is disabled. */ export var IsDisabledContext = /*#__PURE__*/createContext(false); var FormBase = function FormBase(props, ref) { var autocomplete = props.autocomplete, userProvidedFormProps = props.formProps, id = props.id, label = props.label, labelId = props.labelId, name = props.name, noValidate = props.noValidate, onSubmit = props.onSubmit, testId = props.testId, xcss = props.xcss; var formRef = useRef(null); var onSubmitRef = useRef(onSubmit); onSubmitRef.current = onSubmit; var _useState = useState(function () { // Types here would break the existing API var finalForm = createForm({ onSubmit: function onSubmit() { return onSubmitRef.current.apply(onSubmitRef, arguments); }, destroyOnUnregister: true, initialValues: {}, mutators: { setDefaultValue: function setDefaultValue(_ref, state) { var _ref2 = _slicedToArray(_ref, 2), name = _ref2[0], defaultValue = _ref2[1]; if (state.formState.initialValues) { var initialValues = state.formState.initialValues; var values = state.formState.values; var _value = name && typeof defaultValue === 'function' ? defaultValue(initialValues[name]) : defaultValue; set(initialValues, name, _value); set(values, name, _value); } } } }); createDecorator(function () { return formRef.current ? Array.from(formRef.current.querySelectorAll('input')) : []; }, getFirstErrorField)(finalForm); return finalForm; }), _useState2 = _slicedToArray(_useState, 1), form = _useState2[0]; var _useState3 = useState({ dirty: false, submitting: false }), _useState4 = _slicedToArray(_useState3, 2), state = _useState4[0], setState = _useState4[1]; useEffect(function () { var unsubscribe = form.subscribe(function (_ref3) { var dirty = _ref3.dirty, submitting = _ref3.submitting; setState({ dirty: dirty, submitting: submitting }); }, { dirty: true, submitting: true }); return unsubscribe; }, [form]); var registerField = useCallback(function (name, defaultValue, subscriber, subscription, config) { form.pauseValidation(); var unsubscribe = form.registerField(name, subscriber, subscription, config); form.mutators.setDefaultValue(name, defaultValue); form.resumeValidation(); return unsubscribe; }, [form]); var handleSubmit = function handleSubmit(e) { if (e) { e.preventDefault(); } form.submit(); }; var handleReset = function handleReset(initialValues) { form.reset(initialValues); }; var handleKeyDown = function handleKeyDown(e) { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey) && formRef.current) { var submitButton = formRef.current.querySelector('button:not([type]), button[type="submit"], input[type="submit"]'); if (submitButton) { submitButton.click(); } e.preventDefault(); } }; var _props$isDisabled = props.isDisabled, isDisabled = _props$isDisabled === void 0 ? false : _props$isDisabled, children = props.children; var dirty = state.dirty, submitting = state.submitting; /** * This method is needed in FormContext to use it on the field level * to check the current value of the field in case of the component re-mounting. */ var getCurrentValue = useCallback(function (name) { var formState = form.getState(); return (formState === null || formState === void 0 ? void 0 : formState.values[name]) || undefined; }, [form]); var FormContextValue = useMemo(function () { return { registerField: registerField, getCurrentValue: getCurrentValue, subscribe: form.subscribe }; }, [registerField, getCurrentValue, form.subscribe]); var conditionalFormProps = { autocomplete: autocomplete, className: xcss, id: id, 'aria-label': label, 'aria-labelledby': labelId, name: name, noValidate: noValidate, 'data-testid': testId }; // Abstracting so we can use the same for both rendering patterns var formProps = { onKeyDown: handleKeyDown, onSubmit: handleSubmit, ref: ref ? mergeRefs([ref, formRef]) : formRef }; // We don't want to add undefined values to the component Object.keys(conditionalFormProps).forEach(function (attr) { if (conditionalFormProps[attr] !== undefined) { formProps[attr] = conditionalFormProps[attr]; } }); var childrenContent = function () { if (typeof children === 'function') { var result = children.length > 0 ? children({ formProps: formProps, dirty: dirty, reset: handleReset, submitting: submitting, disabled: isDisabled, getState: function getState() { return form.getState(); }, getValues: function getValues() { return form.getState().values; }, setFieldValue: form.change, resetFieldState: form.resetFieldState }) : children(); return result === undefined ? null : result; } else { return /*#__PURE__*/React.createElement("form", _extends({}, formProps, userProvidedFormProps), children); } }(); return /*#__PURE__*/React.createElement(FormContext.Provider, { value: FormContextValue }, /*#__PURE__*/React.createElement(IsDisabledContext.Provider, { value: isDisabled }, childrenContent)); }; /** * __Form__ * * A form allows users to input information. * * - [Examples](https://atlassian.design/components/form/examples) * - [Code](https://atlassian.design/components/form/code) * - [Usage](https://atlassian.design/components/form/usage) */ var Form = forwardRefWithGeneric(FormBase); export default Form;