react-jsonschema-form-validation
Version:
Simple form validation using JSON Schema and AJV
217 lines (197 loc) • 7.39 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/esm/inherits";
/**
* @import {
* ReactNode,
* ElementType,
* Ref,
* FocusEvent,
* ForwardRefRenderFunction,
* ComponentProps,
* ComponentPropsWithRef,
* } from 'react'
* @import { FormChangeEvent, SafePropsOmit } from '../Form/helpers'
* @import { FormContextValue } from '../Form/Context.types'
*/
import classnames from 'classnames';
import memoize from 'memoize-one';
import React, { PureComponent } from 'react';
import { withFormContext } from '../Form/Context';
/**
* Signature of the user-supplied `onChange` handler. It receives the event
* and the form's internal handler so the user can decide whether to apply,
* transform or skip the update.
*
* @typedef {(
* event: FormChangeEvent,
* formHandleFieldChange: FormContextValue['handleFieldChange'],
* ) => void} FieldChangeHandler
*/
/**
* Base props of `<Field>` — the validation-specific props handled by the
* component itself. The public, polymorphic `FieldProps<C>` extends this
* with the props of the underlying component `C`.
*
* `forwardedRef` is an implementation detail (injected by the outer
* `React.forwardRef` wrapper) and is omitted from the public polymorphic
* type below.
*
* @typedef {{
* name: string,
* children?: ReactNode,
* className?: string,
* component?: ElementType,
* forwardedRef?: Ref<unknown> | null,
* onBlur?: ((event: FocusEvent) => void) | null,
* onChange?: FieldChangeHandler | null,
* }} FieldBaseProps
*/
/**
* Polymorphic props of `<Field>`. When `component={X}` is supplied, every
* prop accepted by `X` is also accepted here (with autocomplete and typo
* detection). The default `C = 'input'` matches the runtime default.
*
* - `name` — path within the form data this field reads/writes.
* - `component` — host element or component (default `'input'`).
* - `onChange` — user override; receives the raw event plus the form's
* internal change handler so the user can decide whether
* to apply, transform or skip the update.
* - `onBlur` — user override; always fires *after* `form.touch(name)`.
*
* @template {ElementType} [C = 'input']
* @typedef {(
* Omit<FieldBaseProps, 'component' | 'forwardedRef'>
* & { component?: C }
* & SafePropsOmit<ComponentProps<C>, keyof FieldBaseProps | 'ref'>
* )} FieldProps
*/
/** @extends {PureComponent<FieldBaseProps>} */
var Field =
/*#__PURE__*/
function (_PureComponent) {
_inherits(Field, _PureComponent);
function Field() {
var _getPrototypeOf2;
var _this;
_classCallCheck(this, Field);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(Field)).call.apply(_getPrototypeOf2, [this].concat(args)));
_this.memoGetClassnames = memoize(function (
/** @type {string | undefined} */
className,
/** @type {boolean} */
isInvalid,
/** @type {boolean} */
isSubmitted,
/** @type {boolean} */
isTouched) {
return classnames('Jfv_Field', className, {
isInvalid: isInvalid,
isSubmitted: isSubmitted,
isTouched: isTouched
});
});
_this.memoGetOnBlurHandler = memoize(function (
/** @type {FormContextValue['touch']} */
touch,
/** @type {string} */
name,
/** @type {((e: FocusEvent) => void) | null | undefined} */
onBlur) {
return (
/** @param {FocusEvent} event */
function (event) {
touch(name);
if (onBlur) onBlur(event);
}
);
});
_this.memoGetOnChangeHandler = memoize(function (
/** @type {FieldChangeHandler | null | undefined} */
onChange,
/** @type {FormContextValue['handleFieldChange']} */
handleFieldChange) {
return (
/** @param {FormChangeEvent} event */
function (event) {
if (onChange) {
// User-supplied onChange replaces the default behavior. We pass the
// form's own update handler so the user can still apply the change
// from inside their handler (e.g. after a transformation).
onChange(event, handleFieldChange);
return;
}
handleFieldChange(event);
}
);
});
_this.getClassnames = function (form) {
var _this$props = _this.props,
className = _this$props.className,
name = _this$props.name;
var isFieldInvalid = form.isFieldInvalid,
isFieldTouched = form.isFieldTouched,
isSubmitted = form.isSubmitted;
return _this.memoGetClassnames(className, isFieldInvalid(name), isSubmitted, isFieldTouched(name));
};
return _this;
}
_createClass(Field, [{
key: "render",
value: function render() {
var _this2 = this;
var _this$props2 = this.props,
children = _this$props2.children,
className = _this$props2.className,
_this$props2$componen = _this$props2.component,
Component = _this$props2$componen === void 0 ? 'input' : _this$props2$componen,
onBlur = _this$props2.onBlur,
onChange = _this$props2.onChange,
name = _this$props2.name,
forwardedRef = _this$props2.forwardedRef,
props = _objectWithoutProperties(_this$props2, ["children", "className", "component", "onBlur", "onChange", "name", "forwardedRef"]);
return withFormContext(function (form) {
return React.createElement(Component, Object.assign({
className: _this2.getClassnames(form),
name: name,
onBlur: _this2.memoGetOnBlurHandler(form.touch, name, onBlur),
onChange: _this2.memoGetOnChangeHandler(onChange, form.handleFieldChange),
ref: forwardedRef
}, props), children);
});
}
}]);
return Field;
}(PureComponent);
Field.defaultProps = {
children: null,
className: '',
component: 'input',
forwardedRef: null,
onBlur: null,
onChange: null
}; // `React.forwardRef` itself is not generic; the type annotation on the
// default export below reinstates the polymorphism so consumers get full
// autocomplete and type-checking for the props of the `component` they
// pass. `ref` is typed via `ComponentPropsWithRef<C>['ref']` so it matches
// the underlying component (e.g. `Ref<HTMLInputElement>` by default, or
// the custom handle type when a user component is supplied).
var FieldComponent = React.forwardRef(
/** @type {ForwardRefRenderFunction<unknown, FieldBaseProps>} */
function (props, ref) {
return React.createElement(Field, Object.assign({}, props, {
forwardedRef: ref
}));
});
export default
/** @type {<C extends ElementType = 'input'>(
props: FieldProps<C> & { ref?: ComponentPropsWithRef<C>['ref'] }
) => JSX.Element | null} */
/** @type {unknown} */
FieldComponent;