Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Unified management for form sub-components #1110

Merged
merged 15 commits into from
Apr 25, 2019
4 changes: 2 additions & 2 deletions src/components/Base/Checkbox/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
padding-left: 22px;
color: $N300;
transition: all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6);
cursor: pointer;

input[type='checkbox'] {
width: 0;
Expand Down Expand Up @@ -50,7 +51,6 @@
box-shadow: 0 2px 4px 0 rgba(48, 14, 86, 0.04);
box-sizing: border-box;
transform: translate(0, -50%);
cursor: pointer;
}
&:hover {
&::before {
Expand Down Expand Up @@ -93,9 +93,9 @@
}
.checkbox[disabled] {
opacity: 0.5;
cursor: not-allowed;
&::before {
opacity: 0.5;
cursor: not-allowed;
}
&:hover {
&::before {
Expand Down
37 changes: 37 additions & 0 deletions src/components/Base/Form/Field/FieldGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import classnames from 'classnames';
import _ from 'lodash';

import styles from '../index.scss';

const FieldGroup = props => {
const { layout, labelType, children } = props;
const childNodes = React.Children.map(children, child => {
const isField = _.invoke(child, 'type.displayName.includes', 'Field');
const className = _.get(child, 'props.className');
if (!child) {
return null;
}

const childProps = {
...child.props,
className: classnames(styles.formItem, className)
};
if (isField) {
childProps.layout = childProps.layout || layout;
childProps.labelType = childProps.labelType || labelType;
}

return React.cloneElement(child, childProps);
});

return (
<div className={classnames(styles.fieldGroup, props.className)}>
{childNodes}
</div>
);
};

FieldGroup.displayName = 'FieldGroup';

export default FieldGroup;
17 changes: 17 additions & 0 deletions src/components/Base/Form/Field/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import {
Checkbox, Button, Input, Select
} from 'components/Base';
import wrapField from './wrapField';
import FieldGroup from './FieldGroup';

const TextArea = props => <textarea {...props} />;

export default {
FieldGroup,
ButtonField: wrapField(Button),
CheckboxField: wrapField(Checkbox),
TextField: wrapField(Input),
TextareaField: wrapField(TextArea),
SelectField: wrapField(Select)
};
102 changes: 102 additions & 0 deletions src/components/Base/Form/Field/wrapField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import styles from '../index.scss';

export default function warpField(WrappedComponent) {
if (!WrappedComponent) return null;

const displayName = `Field-${WrappedComponent.displayName
|| WrappedComponent.name
|| 'unkown'}`;

class Field extends PureComponent {
static displayName = displayName;

static propTypes = {
className: PropTypes.string,
label: PropTypes.string,
labelType: PropTypes.oneOf(['normal', 'title']),
layout: PropTypes.oneOf(['horizon', 'vertical', 'inline'])
};

get isTextArea() {
return displayName.includes('TextArea');
}

get isCheckbox() {
return displayName.includes('Checkbox');
}

get layout() {
return this.props.layout || 'horizon';
liiil825 marked this conversation as resolved.
Show resolved Hide resolved
}

get labelType() {
return this.props.labelType || 'normal';
}

renderLabel() {
const { label, name } = this.props;
if (!label) {
return null;
}

return (
<label htmlFor={name} className={styles[this.labelType]}>
{label}
</label>
);
}

renderContent() {
const {
name,
label,
labelType,
iconLeft,
iconRight,
...restProps
} = this.props;

return (
<div className={styles.control}>
<WrappedComponent {...restProps} name={name} id={name} />
</div>
);
}

renderHelp() {
const { help } = this.props;
if (!help) {
return null;
}
return <div className={styles.help}>{help}</div>;
}

render() {
const { className } = this.props;

return (
<div
className={classnames(
styles.field,
styles[this.layout],
{
[styles.checkboxField]: this.isCheckbox,
[styles.textareaField]: this.isTextArea
},
className
)}
>
{this.renderLabel()}
{this.renderContent()}
{this.renderHelp()}
</div>
);
}
}

return Field;
}
64 changes: 42 additions & 22 deletions src/components/Base/Form/form.jsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,74 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import _ from 'lodash';

import { getFormData } from 'utils';

import styles from './index.scss';

export default class Form extends React.Component {
static propTypes = {
className: PropTypes.string,
data: PropTypes.object,
labelType: PropTypes.oneOf(['normal', 'title']),
layout: PropTypes.oneOf(['horizon', 'vertical', 'inline']),
onSubmit: PropTypes.func
};

static defaultProps = {
className: ''
className: '',
labelType: 'normal',
layout: 'horizon'
};

constructor(props) {
super(props);
this._formData = props.data || {};
}

componentWillReceiveProps(nextProps) {
if ('data' in nextProps) {
this._formData = nextProps.data || {};
}
this.formRef = React.createRef();
}

handleSubmit = e => {
e.preventDefault();
// validator

this.props.onSubmit(this._formData);
this.props.onSubmit(e, getFormData(this.formRef.current));
};

getData() {
return this._formData;
}

render() {
const { className, children } = this.props;
const {
children, layout, labelType, noPadding, ...restProps
} = this.props;

const classNames = classnames(styles.form, className);
const formClass = classnames(
styles.form,
{
[styles.noPaddingForm]: noPadding
},
this.props.className
);

const childNodes = React.Children.map(children, child => React.cloneElement(child, {
...child.props,
formData: this._formData
}));
const childNodes = React.Children.map(children, (child = {}) => {
const isField = _.invoke(child, 'type.displayName.includes', 'Field');
const className = classnames(
styles.formItem,
_.get(child, 'props.className')
);
const props = {
...child.props,
className
};
if (isField) {
props.layout = props.layout || layout;
props.labelType = props.labelType || labelType;
}
return React.cloneElement(child, props);
});

return (
<form className={classNames} onSubmit={this.handleSubmit}>
<form
{...restProps}
className={formClass}
ref={this.formRef}
onSubmit={this.handleSubmit}
>
{childNodes}
</form>
);
Expand Down
8 changes: 2 additions & 6 deletions src/components/Base/Form/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import Form from './form';
import Section from './section';
import Item from './item';
import Field from './Field';

Form.Section = Section;
Form.Item = Item;

export default Form;
export default Object.assign(Form, Field);
liiil825 marked this conversation as resolved.
Show resolved Hide resolved
Loading