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';
|
|
|
|
|
2018-10-25 12:07:50 +02:00
|
|
|
// 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
|
|
|
|
});
|
|
|
|
|
2018-07-26 17:07:48 +02:00
|
|
|
class FormNestedArray extends PureComponent {
|
2018-07-24 18:42:59 +02:00
|
|
|
getCurrentValue() {
|
2018-10-23 15:02:57 +02:00
|
|
|
return this.props.value || [];
|
2018-07-24 18:42:59 +02:00
|
|
|
}
|
|
|
|
|
2018-03-23 15:46:31 +09:00
|
|
|
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-23 08:51:24 +09:00
|
|
|
};
|
2018-03-22 19:22:54 +09:00
|
|
|
|
2018-03-23 15:46:31 +09:00
|
|
|
removeItem = index => {
|
2018-03-25 10:54:45 +09:00
|
|
|
this.props.updateCurrentValues({ [`${this.props.path}.${index}`]: null });
|
2018-03-23 08:51:24 +09:00
|
|
|
};
|
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 => {
|
2018-05-23 22:04:32 +09:00
|
|
|
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'
|
|
|
|
);
|
2018-12-07 02:37:36 -06:00
|
|
|
const { errors, path, label, formComponents, minCount, maxCount } = this.props;
|
2018-09-23 08:13:09 +09:00
|
|
|
const FormComponents = formComponents;
|
2018-12-07 02:37:36 -06:00
|
|
|
|
|
|
|
//filter out null values to calculate array length
|
|
|
|
let arrayLength = value.filter(singleValue => {
|
|
|
|
return !!singleValue;
|
|
|
|
}).length;
|
2018-12-07 05:31:56 -06:00
|
|
|
|
2018-08-07 16:05:24 +09:00
|
|
|
// 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
|
|
|
|
);
|
2018-08-07 16:05:24 +09:00
|
|
|
const hasErrors = nestedArrayErrors && nestedArrayErrors.length;
|
2018-12-07 05:31:56 -06:00
|
|
|
|
2018-03-22 19:22:54 +09:00
|
|
|
return (
|
2018-10-25 12:07:50 +02:00
|
|
|
<FormComponents.FormNestedArrayLayout
|
|
|
|
label={label}
|
|
|
|
content={[
|
|
|
|
value.map(
|
2018-07-24 18:42:59 +02:00
|
|
|
(subDocument, i) =>
|
|
|
|
!this.isDeleted(i) && (
|
2018-09-19 13:16:48 -04:00
|
|
|
<React.Fragment key={i}>
|
2018-09-23 08:16:48 +09:00
|
|
|
<FormComponents.FormNestedItem
|
2018-09-19 13:16:48 -04:00
|
|
|
{...properties}
|
|
|
|
itemIndex={i}
|
|
|
|
path={`${this.props.path}.${i}`}
|
|
|
|
removeItem={() => {
|
|
|
|
this.removeItem(i);
|
|
|
|
}}
|
2018-12-07 02:37:36 -06:00
|
|
|
hideRemove={minCount && arrayLength <= minCount}
|
2018-09-19 13:16:48 -04:00
|
|
|
/>
|
2018-10-23 15:02:57 +02:00
|
|
|
<FormComponents.FormNestedDivider
|
|
|
|
label={this.props.label}
|
|
|
|
addItem={this.addItem}
|
|
|
|
/>
|
2018-09-19 13:16:48 -04:00
|
|
|
</React.Fragment>
|
2018-07-24 18:42:59 +02:00
|
|
|
)
|
2018-10-25 12:07:50 +02:00
|
|
|
),
|
2018-12-07 05:31:56 -06:00
|
|
|
(!maxCount || arrayLength < maxCount) && (
|
2019-01-02 18:35:36 +01:00
|
|
|
<Components.FormNestedFoot
|
2018-12-07 02:37:36 -06:00
|
|
|
key="add-button"
|
2019-01-02 18:35:36 +01:00
|
|
|
addItem={this.addItem}
|
|
|
|
label={this.props.label}
|
|
|
|
className="form-nested-foot"
|
|
|
|
/>
|
2018-12-07 02:37:36 -06:00
|
|
|
),
|
2018-10-25 12:07:50 +02:00
|
|
|
hasErrors ? (
|
|
|
|
<FormComponents.FieldErrors
|
|
|
|
key="form-nested-errors"
|
|
|
|
errors={nestedArrayErrors}
|
|
|
|
/>
|
|
|
|
) : null
|
|
|
|
]}
|
|
|
|
/>
|
2018-03-23 08:51:24 +09:00
|
|
|
);
|
2018-03-22 19:22:54 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-26 17:07:48 +02:00
|
|
|
FormNestedArray.propTypes = {
|
2018-07-24 18:42:59 +02:00
|
|
|
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-07-24 18:42:59 +02:00
|
|
|
};
|
|
|
|
|
2018-10-23 15:02:57 +02:00
|
|
|
module.exports = FormNestedArray;
|
2018-07-24 18:42:59 +02:00
|
|
|
|
2018-07-26 17:07:48 +02:00
|
|
|
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);
|