@fluent-windows/core
Version:
React components that inspired by Microsoft's Fluent Design System.
104 lines (98 loc) • 3.46 kB
JavaScript
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;