Vulcan/packages/vulcan-forms/lib/components/FormNestedArray.jsx

157 lines
4.6 KiB
React
Raw Normal View History

2018-03-22 19:22:54 +09:00
import React, { PureComponent } from 'react';
2018-03-24 11:16:11 +09:00
import PropTypes from 'prop-types';
2018-03-22 19:22:54 +09:00
import { Components, registerComponent } from 'meteor/vulcan:core';
// Replaceable layout
const FormNestedArrayLayout = ({ hasErrors, label, content }) => (
<div
className={`form-group row form-nested ${hasErrors ? 'input-error' : ''}`}
>
<label className="control-label col-sm-3">{label}</label>
<div className="col-sm-9">{content}</div>
</div>
);
FormNestedArrayLayout.propTypes = {
hasErrors: PropTypes.bool,
label: PropTypes.node,
content: PropTypes.node
};
registerComponent({
name: 'FormNestedArrayLayout',
component: FormNestedArrayLayout
});
class FormNestedArray extends PureComponent {
getCurrentValue() {
2018-10-23 15:02:57 +02:00
return this.props.value || [];
}
addItem = () => {
2018-10-23 15:02:57 +02:00
const value = this.getCurrentValue();
this.props.updateCurrentValues(
{ [`${this.props.path}.${value.length}`]: {} },
{ mode: 'merge' }
);
};
2018-03-22 19:22:54 +09:00
removeItem = index => {
2018-03-25 10:54:45 +09:00
this.props.updateCurrentValues({ [`${this.props.path}.${index}`]: null });
};
2018-03-22 19:22:54 +09:00
2018-03-24 11:16:11 +09:00
/*
Go through this.context.deletedValues and see if any value matches both the current field
and the given index (ex: if we want to know if the second address is deleted, we
look for the presence of 'addresses.1')
*/
isDeleted = index => {
return this.props.deletedValues.includes(`${this.props.path}.${index}`);
2018-03-24 11:16:11 +09:00
};
2018-03-22 19:22:54 +09:00
render() {
2018-10-23 15:02:57 +02:00
const value = this.getCurrentValue();
2018-04-21 18:24:54 +09:00
// do not pass FormNested's own value, input and inputProperties props down
2018-10-23 15:02:57 +02:00
const properties = _.omit(
this.props,
'value',
'input',
'inputProperties',
'nestedInput'
);
const { errors, path, label, formComponents, minCount, maxCount } = this.props;
const FormComponents = formComponents;
//filter out null values to calculate array length
let arrayLength = value.filter(singleValue => {
return !!singleValue;
}).length;
// only keep errors specific to the nested array (and not its subfields)
2018-10-23 15:02:57 +02:00
const nestedArrayErrors = errors.filter(
error => error.path && error.path === path
);
const hasErrors = nestedArrayErrors && nestedArrayErrors.length;
2018-03-22 19:22:54 +09:00
return (
<FormComponents.FormNestedArrayLayout
label={label}
content={[
value.map(
(subDocument, i) =>
!this.isDeleted(i) && (
<React.Fragment key={i}>
<FormComponents.FormNestedItem
{...properties}
itemIndex={i}
path={`${this.props.path}.${i}`}
removeItem={() => {
this.removeItem(i);
}}
hideRemove={minCount && arrayLength <= minCount}
/>
2018-10-23 15:02:57 +02:00
<FormComponents.FormNestedDivider
label={this.props.label}
addItem={this.addItem}
/>
</React.Fragment>
)
),
(!maxCount || arrayLength < maxCount) && (
<Components.FormNestedFoot
key="add-button"
addItem={this.addItem}
label={this.props.label}
className="form-nested-foot"
/>
),
hasErrors ? (
<FormComponents.FieldErrors
key="form-nested-errors"
errors={nestedArrayErrors}
/>
) : null
]}
/>
);
2018-03-22 19:22:54 +09:00
}
}
FormNestedArray.propTypes = {
currentValues: PropTypes.object,
path: PropTypes.string,
2018-10-23 15:02:57 +02:00
label: PropTypes.string,
errors: PropTypes.array.isRequired,
deletedValues: PropTypes.array.isRequired,
2018-10-25 10:26:54 +02:00
formComponents: PropTypes.object.isRequired
};
2018-10-23 15:02:57 +02:00
module.exports = FormNestedArray;
registerComponent('FormNestedArray', FormNestedArray);
2018-03-28 16:47:10 +09:00
const IconAdd = ({ width = 24, height = 24 }) => (
2018-10-23 15:02:57 +02:00
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
2018-03-28 16:47:10 +09:00
<path d="M448 294.2v-76.4c0-13.3-10.7-24-24-24H286.2V56c0-13.3-10.7-24-24-24h-76.4c-13.3 0-24 10.7-24 24v137.8H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h137.8V456c0 13.3 10.7 24 24 24h76.4c13.3 0 24-10.7 24-24V318.2H424c13.3 0 24-10.7 24-24z" />
</svg>
);
registerComponent('IconAdd', IconAdd);
const IconRemove = ({ width = 24, height = 24 }) => (
2018-10-23 15:02:57 +02:00
<svg
width={width}
height={height}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
2018-03-28 16:47:10 +09:00
<path d="M424 318.2c13.3 0 24-10.7 24-24v-76.4c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h400z" />
</svg>
);
registerComponent('IconRemove', IconRemove);