in public/js/components/FormFields/FormFieldArrayWrapper.js [34:140]
renderValue(value, i) {
const updateFn = (newValue) => {
if (value === undefined) {
//It's the first update to a new item - add it to props and remove from newItems
this.setState({
newItems: this.state.newItems.length > 1 ? this.state.newItems.slice(1) : []
});
const update = this.props.fieldValue ? this.props.fieldValue.concat([newValue]) : [newValue];
this.props.onUpdateField(update);
} else {
//Find the value in the array to change
const newFieldValue = this.props.fieldValue.map((oldValue) => {
return value === oldValue ? newValue : oldValue;
});
this.props.onUpdateField(newFieldValue);
}
};
const removeFn = (removeIndex) => {
if (this.props.fieldValue && this.props.fieldValue.length > removeIndex) {
const newFieldValue = this.props.fieldValue.filter((value, currentIndex) => {
return currentIndex !== removeIndex;
});
this.props.onUpdateField(newFieldValue);
} else {
//It must be a new item
this.setState({
newItems: this.state.newItems.length > 1 ? this.state.newItems.slice(1) : []
});
}
};
const moveInArrayFn = (arr, fromIndex, toIndex) => {
arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);
};
const moveFn = (currentIndex, newIndex) => {
this.setState({
childrenVisible: false
}, () => {
/* we only allow to move if item has been updated and indices are within array bounds */
if (value !== undefined && this.props.fieldValue && this.props.fieldValue.length > currentIndex && currentIndex >= 0 && this.props.fieldValue.length > newIndex && newIndex >= 0) {
const arr = this.props.fieldValue.slice();
moveInArrayFn(arr, currentIndex, newIndex);
this.props.onUpdateField(arr);
}
// Atom Workshop used to use Scribe for its rich text editors, but we've now moved to ProseMirror. The app was built in
// the context described in the following paragraph. Our ProseMirror implementation should work without this workaround,
// but the logic doesn't degrade the experience in the application, so I'm leaving that logic undisturbed, along with
// this explanation:
//
// When reordering array elements & the child is a scribe editor, we have no way of passing information
// down to scribe to force a re-render, as scribe cannot handle update changes once it has been
// initialised. To trigger the visual change without a browser refresh, we need to unmount
// and remount the child scribe component. However as we only render the root React component
// with the DOM, and every other downstream component is rendered via React, there is no way to
// remove the children components. Therefore we use this flag in state to identify whether
// they should be visible or not. We hide the component until the updates have passed downstream,
// and then the further change in state below will trigger a re-render & the lifecycle methods of any children.
this.setState({
childrenVisible: true
});
});
};
const hydratedChildren = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
key: `${this.props.fieldName}-${i}`,
fieldName: `${this.props.fieldName}-${i}`,
fieldValue: value,
fieldErrors: this.props.fieldErrors,
formRowClass: 'form__row form__row--flex',
onUpdateField: updateFn
});
});
const renderMoveBtns = () => {
if (i !== 0 && (i+1) < this.props.fieldValue.length) {
return <div>
<button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i-1) ) } >Move up</button>
<button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i+1) ) } >Move down </button>
</div>;
} else if (i === 0) {
return <div><button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i+1) ) } >Move down </button></div>;
} else {
return <div><button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i-1) ) } >Move up</button></div>;
}
};
return (
<div className={this.props.fieldClass ? this.props.fieldClass : null}>
{this.props.numbered ? <span className="form__field-number">{`${i + 1}. `}</span> : false }
{hydratedChildren}
{renderMoveBtns()}
<button className="btn form__field-btn btn--red" type="button" onClick={removeFn.bind(this, i)}>Delete</button>
</div>
);
}