UNPKG

@fluent-windows/core

Version:

React components that inspired by Microsoft's Fluent Design System.

104 lines (98 loc) 3.46 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import * as React from 'react'; import { createUseStyles } from '@fluent-windows/styles'; import { styles } from './Form.styled'; import { FormPropTypes } from './Form.type'; import Field from './components/Field'; import { FormContext } from './Form.context'; import { createValidator } from './Form.validator'; import classNames from 'classnames'; export const name = 'Form'; const useStyles = createUseStyles(styles, { name }); const Form = React.forwardRef((props, ref) => { const { className: classNameProp, children, prefix, suffix, initialState, onSubmit, ...rest } = props; const childrenArray = React.useMemo(() => React.Children.toArray(children).map(child => !!child.props.rules && child).filter(v => v), [children]); const fields = React.useMemo(() => initialState || childrenArray.reduce((acc, cur) => ({ ...acc, [cur.props.name]: '' }), {}), [childrenArray, initialState]); const reducer = React.useCallback((state, action) => { switch (action.type) { case 'reset': return { values: fields, errors: {} }; case 'onChange': return { values: { ...state.values, ...action.payload }, errors: state.errors }; case 'onError': // eslint-disable-next-line const errors = Array.isArray(action.payload) ? action.payload.reduce((acc, cur) => ({ ...acc, [cur.field]: cur.message }), {}) : {}; return { values: state.values, errors }; default: throw new Error('The `type` provided by `dispatch` does not conform to the specification'); } }, [fields]); const [state, dispatch] = React.useReducer(reducer, { values: fields, errors: {} }); const descriptor = React.useMemo(() => childrenArray.reduce((acc, cur) => ({ ...acc, [cur.props.name]: cur.props.rules }), {}), [childrenArray]); const contextValue = { state, dispatch, descriptor }; // Used to control the content triggered by the submit event const handleSubmit = React.useCallback(e => { e.preventDefault(); createValidator(descriptor, state.values, errors => { dispatch({ type: 'onError', payload: errors }); onSubmit && onSubmit(state.values, errors); }); }, [state, onSubmit, descriptor]); const classes = useStyles(props); const className = classNames(classes.root, classNameProp); return React.createElement("form", _extends({ className: className, ref: ref, onSubmit: handleSubmit }, rest), React.createElement(FormContext.Provider, { value: contextValue }, React.createElement("table", { className: classes.table }, React.createElement("caption", null, prefix), React.createElement("tbody", null, React.Children.map(children, child => child), React.createElement("tr", null, React.createElement("td", { colSpan: 2 }, suffix)))))); }); Object.defineProperty(Form, 'Field', { get() { return Field; } }); Form.displayName = `F${name}`; Form.propTypes = FormPropTypes; export default Form;