UNPKG

react-x-editable

Version:
271 lines (253 loc) 8.48 kB
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import _ from 'lodash'; import { Button, Overlay, Popover, FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap'; import Text from './Text'; import Textarea from './Textarea'; import Select from './Select'; import Checklist from './Checklist'; import Date from './Date'; //import Radio from './Radio'; export default class Editable extends Component { constructor(props){ super(props); this.state = { dataType : props.dataType ? props.dataType : "text", name : props.name, mode : props.mode ? props.mode : "inline", disabled : props.disabled ? props.disabled : false, showButtons : props.showButtons != undefined ? props.showButtons : true, validate : props.validate ? props.validate : undefined, display : props.display ? props.display : undefined, // only used when mode is popup title : props.title ? props.title : null, placement : props.placement ? props.placement : "right", //Input bsInputClass :props.bsInputClass ? props.bsInputClass : "", bsInputSize :props.bsInputSize ? props.bsInputSize : "sm", //Select & checklist options : props.options ? props.options : null, //checklist optionsInline : props.inline ? props.inline : false, //Required for customize input customComponent :props.customComponent ? props.customComponent : null, onInputChange : props.onInputChange ? props.onInputChange : null, //handle callback if provided handleSubmit : props.handleSubmit ? props.handleSubmit : null, //for internal use editable: false, valueUpdated : false, }; this.setInitialValue(this.props); } componentWillReceiveProps(nextProps){ if(nextProps.value != this.value){ this.setInitialValue(nextProps); } } setInitialValue = (newProps) => { const { dataType, options, value } = newProps; if(dataType == "select" || dataType == "checklist"){ if( options == null ) { throw("Please specify options for "+dataType+" data type"); } if(value && _.isEmpty(value)){ const option = _.find(options, {'value' : value}); if(option && option.text) { this.value = option.text; this.newValue = option.text;; }else { throw("No option found for specified value:"+ value) } }else{ this.value = value; this.newValue = value; } }else { this.value = value; this.newValue = value; } this.validation = {}; } setEditable = (editable) => { if(!this.state.disabled) this.setState({editable}); } onSubmit = () => { this.validation = this.getValidationState(); if(this.validation.type === "error"){ this.setState({ valueUpdated : false}); }else { this.value = this.newValue; this.setEditable(false); this.setState({ valueUpdated : true}, this.state.handleSubmit ? () => this.state.handleSubmit(this) : null); } } onCancel = () => { this.setEditable(false); //reset validation this.validation = {}; } setValueToAnchor(value, event){ this.newValue = value; //To trigger onInputChange event:user defined if(this.props.onInputChange){ this.props.onInputChange(event); } } getValueForAnchor(){ if(this.value){ if(this.props.display){ return this.props.display(this.value); } else if(this.props.seperator && _.isArray(this.value)){ return _.join(this.value, this.props.seperator); }else if(_.isArray(this.value)){ return _.join(this.value, ','); }else if(_.isObject(this.value)){ let tmp = ''; _.forOwn(this.value, function(value, key) { tmp += key +":"+ value +" "; } ); return tmp; } } return this.value; } getValidationState(){ if(this.props.validate){ const validate = this.props.validate(this.newValue) if(validate){ return {type : 'error', msg : validate }; } } return {type : undefined, msg : undefined }; } getButtons(){ if(this.state.showButtons){ return ( <div className="editable-btn" key={this.props.name+"editable-btn"}> <Button bsStyle="success" bsSize="xsmall" onClick={this.onSubmit.bind(this)} key={"btn-success"+this.props.name}> <i className="fa fa-check" key={"icon-fa-check"+this.props.name}></i></Button> <Button bsStyle="danger" bsSize="xsmall" onClick={this.onCancel.bind(this)} key={"btn-danger"+this.props.name}> <i className="fa fa-times" key={"icon-fa-times"+this.props.name}></i> </Button> </div> ) } return null; } getContent(){ const { editable, title, validate, showButtons, defaultValue, dataType, placement, mode, name } = this.state; const componetProps = { key: "editable-name-"+this.state.name, setValueToAnchor: this.setValueToAnchor.bind(this), value: this.value || defaultValue , onSubmit: this.onSubmit.bind(this), setEditable: this.setEditable.bind(this), validation: this.validation }; const content = []; if (editable) { switch (dataType) { case 'text': content.push(<Text {...this.props} {...componetProps} {...this.state} />); break; case 'textarea': content.push(<Textarea {...this.props} {...componetProps} {...this.state} />); break; case 'select': content.push(<Select {...this.props} {...componetProps} {...this.state} />); break; case 'checklist': content.push(<Checklist {...componetProps} {...this.state} />); break; case 'date': content.push(<Date {...componetProps} {...this.state} />); break; // case 'radio': // content.push(<Radio {...componetProps} {...this.state} />); // break; case 'custom': const customComponentContent = this.state.customComponent(componetProps, this.state) content.push(customComponentContent); break; default: throw('Please set valid dataType:'+dataType) } content.push(this.getButtons()); if(mode == 'popup'){ return ( <div> <Overlay rootClose={true} onHide={() => { this.setEditable(false) } } show={editable} target={() => ReactDOM.findDOMNode(this.editableAnchor)} {...this.props}> <Popover id={"popover-positioned-"+placement} title={title} key={this.props.name}> {content} </Popover> </Overlay> </div> ); } return content; } } render() { const { editable, title, validate, showButtons, defaultValue, dataType, mode, disabled } = this.state; const editableContainerClass = (disabled) ? "editable-disabled" : "editable-container"; return ( <div className={editableContainerClass} key={this.props.name} > { !(mode == 'inline' && editable) ? (<a ref={ref => this.editableAnchor = ref} onClick={this.setEditable.bind(this, true)} href="javascript:;" > { this.getValueForAnchor() || this.props.emptyValueText } </a> ) : null } {this.getContent()} </div> ); } } Editable.defaultProps = { showButtons : true, dataType : "text", mode : "inline", disabled : false, emptyValueText : "empty", //depend on mode placement : "right", }; Editable.propTypes = { dataType : PropTypes.string.isRequired, name : PropTypes.string.isRequired, mode : PropTypes.string, showButtons : PropTypes.bool, disabled : PropTypes.bool, validate : PropTypes.func, display: PropTypes.func, onInputChange : PropTypes.func, //handle callback if provided handleSubmit : PropTypes.func, // only used when mode is popup title : PropTypes.string, placement : PropTypes.string, // for input type text bsInputClass :PropTypes.string, bsInputSize :PropTypes.string, };