UNPKG

admin-on-rest-fr05t1k

Version:

A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI

157 lines (138 loc) 5.39 kB
import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { Card, CardTitle, CardText } from 'material-ui/Card'; import compose from 'recompose/compose'; import inflection from 'inflection'; import ViewTitle from '../layout/ViewTitle'; import Title from '../layout/Title'; import { crudGetOne as crudGetOneAction, crudUpdate as crudUpdateAction } from '../../actions/dataActions'; import DefaultActions from './EditActions'; import translate from '../../i18n/translate'; /** * Turns a children data structure (either single child or array of children) into an array. * We can't use React.Children.toArray as it loses references. */ const arrayizeChildren = children => (Array.isArray(children) ? children : [children]); export class Edit extends Component { constructor(props) { super(props); this.state = { key: 0, record: props.data, }; this.previousKey = 0; this.handleSubmit = this.handleSubmit.bind(this); } componentDidMount() { this.updateData(); } componentWillReceiveProps(nextProps) { if (this.props.data !== nextProps.data) { this.setState({ record: nextProps.data }); // FIXME: erases user entry when fetch response arrives late if (this.fullRefresh) { this.fullRefresh = false; this.setState({ key: this.state.key + 1 }); } } if (this.props.id !== nextProps.id) { this.updateData(nextProps.resource, nextProps.id); } } // FIXME Seems that the cloneElement in CrudRoute slices the children array, which makes this necessary to avoid rerenders shouldComponentUpdate(nextProps) { if (nextProps.isLoading !== this.props.isLoading) { return true; } const currentChildren = arrayizeChildren(this.props.children); const newChildren = arrayizeChildren(nextProps.children); return newChildren.every((child, index) => child === currentChildren[index]); } getBasePath() { const { location } = this.props; return location.pathname.split('/').slice(0, -1).join('/'); } updateData(resource = this.props.resource, id = this.props.id) { this.props.crudGetOne(resource, id, this.getBasePath()); } refresh = (event) => { event.stopPropagation(); this.fullRefresh = true; this.updateData(); } handleSubmit(record) { this.props.crudUpdate(this.props.resource, this.props.id, record, this.props.data, this.getBasePath()); } render() { const { actions = <DefaultActions />, children, data, hasDelete, hasShow, id, isLoading, resource, title, translate } = this.props; const { key } = this.state; const basePath = this.getBasePath(); const resourceName = translate(`resources.${resource}.name`, { smart_count: 1, _: inflection.humanize(inflection.singularize(resource)), }); const defaultTitle = translate('aor.page.edit', { name: `${resourceName}`, id, data, }); const titleElement = data ? <Title title={title} record={data} defaultTitle={defaultTitle} /> : ''; // using this.previousKey instead of this.fullRefresh makes // the new form mount, the old form unmount, and the new form update appear in the same frame // so the form doesn't disappear while refreshing const isRefreshing = key !== this.previousKey; this.previousKey = key; return ( <div> <Card style={{ opacity: isLoading ? 0.8 : 1 }} key={key}> {actions && React.cloneElement(actions, { basePath, data, hasDelete, hasShow, refresh: this.refresh, resource, })} <ViewTitle title={titleElement} /> {data && !isRefreshing && React.cloneElement(children, { onSubmit: this.handleSubmit, resource, basePath, record: data, })} {!data && <CardText>&nbsp;</CardText>} </Card> </div> ); } } Edit.propTypes = { actions: PropTypes.element, children: PropTypes.element.isRequired, crudGetOne: PropTypes.func.isRequired, crudUpdate: PropTypes.func.isRequired, data: PropTypes.object, hasDelete: PropTypes.bool, hasShow: PropTypes.bool, id: PropTypes.string.isRequired, isLoading: PropTypes.bool.isRequired, location: PropTypes.object.isRequired, params: PropTypes.object.isRequired, resource: PropTypes.string.isRequired, title: PropTypes.any, translate: PropTypes.func, }; function mapStateToProps(state, props) { return { id: props.params.id, data: state.admin[props.resource].data[props.params.id], isLoading: state.admin.loading > 0, }; } const enhance = compose( connect( mapStateToProps, { crudGetOne: crudGetOneAction, crudUpdate: crudUpdateAction }, ), translate, ); export default enhance(Edit);