From 607202b4e9eb23ac3b12df777e8b707753fe59c1 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Tue, 1 May 2018 17:25:48 +0800 Subject: [PATCH 1/4] implement rich cell editor --- .../react-bootstrap-table2-editor/index.js | 3 + .../src/checkbox-editor.js | 61 ++++++++++++++ .../src/const.js | 8 ++ .../src/date-editor.js | 42 ++++++++++ .../src/dropdown-editor.js | 61 ++++++++++++++ .../src/editing-cell.js | 81 ++++++++++++------- .../src/text-editor.js | 4 + .../src/textarea-editor.js | 60 ++++++++++++++ .../react-bootstrap-table2/src/header-cell.js | 2 + 9 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 packages/react-bootstrap-table2-editor/src/checkbox-editor.js create mode 100644 packages/react-bootstrap-table2-editor/src/date-editor.js create mode 100644 packages/react-bootstrap-table2-editor/src/dropdown-editor.js create mode 100644 packages/react-bootstrap-table2-editor/src/textarea-editor.js diff --git a/packages/react-bootstrap-table2-editor/index.js b/packages/react-bootstrap-table2-editor/index.js index df715d7..3d07c2c 100644 --- a/packages/react-bootstrap-table2-editor/index.js +++ b/packages/react-bootstrap-table2-editor/index.js @@ -1,6 +1,7 @@ import wrapperFactory from './src/wrapper'; import editingCellFactory from './src/editing-cell'; import { + EDITTYPE, CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT, DELAY_FOR_DBCLICK @@ -14,3 +15,5 @@ export default (options = {}) => ({ DELAY_FOR_DBCLICK, options }); + +export const Type = EDITTYPE; diff --git a/packages/react-bootstrap-table2-editor/src/checkbox-editor.js b/packages/react-bootstrap-table2-editor/src/checkbox-editor.js new file mode 100644 index 0000000..561f25c --- /dev/null +++ b/packages/react-bootstrap-table2-editor/src/checkbox-editor.js @@ -0,0 +1,61 @@ +/* eslint no-return-assign: 0 */ +import React, { Component } from 'react'; +import cs from 'classnames'; +import PropTypes from 'prop-types'; + +class CheckBoxEditor extends Component { + constructor(props) { + super(props); + this.state = { + checked: props.defaultValue.toString() === props.value.split(':')[0] + }; + this.handleChange = this.handleChange.bind(this); + } + + componentDidMount() { + this.checkbox.focus(); + } + + getValue() { + const [positive, negative] = this.props.value.split(':'); + return this.checkbox.checked ? positive : negative; + } + + handleChange(e) { + if (this.props.onChange) this.props.onChange(e); + const { target } = e; + this.setState(() => ({ checked: target.checked })); + } + + render() { + const { defaultValue, className, ...rest } = this.props; + const editorClass = cs('editor edit-chseckbox checkbox', className); + return ( + this.checkbox = node } + type="checkbox" + className={ editorClass } + { ...rest } + checked={ this.state.checked } + onChange={ this.handleChange } + /> + ); + } +} + +CheckBoxEditor.propTypes = { + className: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object + ]), + value: PropTypes.string, + defaultValue: PropTypes.any, + onChange: PropTypes.func +}; +CheckBoxEditor.defaultProps = { + className: '', + value: 'on:off', + defaultValue: false, + onChange: undefined +}; +export default CheckBoxEditor; diff --git a/packages/react-bootstrap-table2-editor/src/const.js b/packages/react-bootstrap-table2-editor/src/const.js index acbef9d..4455ad7 100644 --- a/packages/react-bootstrap-table2-editor/src/const.js +++ b/packages/react-bootstrap-table2-editor/src/const.js @@ -2,3 +2,11 @@ export const TIME_TO_CLOSE_MESSAGE = 3000; export const DELAY_FOR_DBCLICK = 200; export const CLICK_TO_CELL_EDIT = 'click'; export const DBCLICK_TO_CELL_EDIT = 'dbclick'; + +export const EDITTYPE = { + TEXT: 'text', + SELECT: 'select', + TEXTAREA: 'textarea', + CHECKBOX: 'checkbox', + DATE: 'date' +}; diff --git a/packages/react-bootstrap-table2-editor/src/date-editor.js b/packages/react-bootstrap-table2-editor/src/date-editor.js new file mode 100644 index 0000000..b712fe2 --- /dev/null +++ b/packages/react-bootstrap-table2-editor/src/date-editor.js @@ -0,0 +1,42 @@ +/* eslint no-return-assign: 0 */ +import React, { Component } from 'react'; +import cs from 'classnames'; +import PropTypes from 'prop-types'; + +class DateEditor extends Component { + componentDidMount() { + const { defaultValue } = this.props; + this.date.valueAsDate = new Date(defaultValue); + this.date.focus(); + } + + getValue() { + return this.date.value; + } + + render() { + const { defaultValue, className, ...rest } = this.props; + const editorClass = cs('form-control editor edit-date', className); + return ( + this.date = node } + type="date" + className={ editorClass } + { ...rest } + /> + ); + } +} + +DateEditor.propTypes = { + className: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object + ]), + defaultValue: PropTypes.string +}; +DateEditor.defaultProps = { + className: '', + defaultValue: '' +}; +export default DateEditor; diff --git a/packages/react-bootstrap-table2-editor/src/dropdown-editor.js b/packages/react-bootstrap-table2-editor/src/dropdown-editor.js new file mode 100644 index 0000000..cf6a2a6 --- /dev/null +++ b/packages/react-bootstrap-table2-editor/src/dropdown-editor.js @@ -0,0 +1,61 @@ +/* eslint no-return-assign: 0 */ +import React, { Component } from 'react'; +import cs from 'classnames'; +import PropTypes from 'prop-types'; + +class DropDownEditor extends Component { + componentDidMount() { + const { defaultValue } = this.props; + this.select.value = defaultValue; + this.select.focus(); + } + + getValue() { + return this.select.value; + } + + render() { + const { defaultValue, className, options, ...rest } = this.props; + const editorClass = cs('form-control editor edit-select', className); + + const attr = { + ...rest, + className: editorClass + }; + + return ( + + ); + } +} + +DropDownEditor.propTypes = { + defaultValue: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]), + className: PropTypes.string, + style: PropTypes.object, + options: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.any + })) + ]).isRequired +}; +DropDownEditor.defaultProps = { + className: '', + defaultValue: '', + style: {} +}; +export default DropDownEditor; diff --git a/packages/react-bootstrap-table2-editor/src/editing-cell.js b/packages/react-bootstrap-table2-editor/src/editing-cell.js index 2c732de..2acda72 100644 --- a/packages/react-bootstrap-table2-editor/src/editing-cell.js +++ b/packages/react-bootstrap-table2-editor/src/editing-cell.js @@ -6,9 +6,13 @@ import React, { Component } from 'react'; import cs from 'classnames'; import PropTypes from 'prop-types'; +import DropdownEditor from './dropdown-editor'; +import TextAreaEditor from './textarea-editor'; +import CheckBoxEditor from './checkbox-editor'; +import DateEditor from './date-editor'; import TextEditor from './text-editor'; import EditorIndicator from './editor-indicator'; -import { TIME_TO_CLOSE_MESSAGE } from './const'; +import { TIME_TO_CLOSE_MESSAGE, EDITTYPE } from './const'; export default _ => class EditingCell extends Component { @@ -73,8 +77,8 @@ export default _ => }, timeToCloseMessage); } - beforeComplete(row, column, newValue) { - const { onUpdate } = this.props; + beforeComplete(newValue) { + const { onUpdate, row, column } = this.props; if (_.isFunction(column.validator)) { const validateForm = column.validator(newValue, row, column); if (_.isObject(validateForm) && !validateForm.valid) { @@ -89,28 +93,20 @@ export default _ => } handleBlur() { - const { onEscape, blurToSave, row, column } = this.props; + const { onEscape, blurToSave } = this.props; if (blurToSave) { - const value = this.editor.text.value; - if (!_.isDefined(value)) { - // TODO: for other custom or embed editor - } - this.beforeComplete(row, column, value); + this.beforeComplete(this.editor.getValue()); } else { onEscape(); } } handleKeyDown(e) { - const { onEscape, row, column } = this.props; + const { onEscape } = this.props; if (e.keyCode === 27) { // ESC onEscape(); } else if (e.keyCode === 13) { // ENTER - const value = e.currentTarget.value; - if (!_.isDefined(value)) { - // TODO: for other custom or embed editor - } - this.beforeComplete(row, column, value); + this.beforeComplete(this.editor.getValue()); } } @@ -124,17 +120,13 @@ export default _ => } render() { - const { invalidMessage } = this.state; + let editor; const { row, column, className, style, rowIndex, columnIndex } = this.props; const { dataField } = column; const value = _.get(row, dataField); - const editorAttrs = { - onKeyDown: this.handleKeyDown, - onBlur: this.handleBlur - }; + const hasError = _.isDefined(this.state.invalidMessage); - const hasError = _.isDefined(invalidMessage); let customEditorClass = column.editorClasses || ''; if (_.isFunction(column.editorClasses)) { customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex); @@ -150,20 +142,51 @@ export default _ => shake: hasError }, customEditorClass); + let editorProps = { + ref: node => this.editor = node, + defaultValue: value, + style: editorStyle, + className: editorClass, + onKeyDown: this.handleKeyDown, + onBlur: this.handleBlur + }; + + const isDefaultEditorDefined = _.isObject(column.editor); + + if (isDefaultEditorDefined) { + editorProps = { + ...editorProps, + ...column.editor + }; + } else if (_.isFunction(column.editorRenderer)) { + editorProps = { + ...editorProps, + onUpdate: this.beforeComplete + }; + } + + if (_.isFunction(column.editorRenderer)) { + editor = column.editorRenderer(editorProps, value, row, column, rowIndex, columnIndex); + } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.SELECT) { + editor = ; + } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.TEXTAREA) { + editor = ; + } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.CHECKBOX) { + editor = ; + } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.DATE) { + editor = ; + } else { + editor = ; + } + return ( - this.editor = node } - defaultValue={ value } - style={ editorStyle } - className={ editorClass } - { ...editorAttrs } - /> - { hasError ? : null } + { editor } + { hasError ? : null } ); } diff --git a/packages/react-bootstrap-table2-editor/src/text-editor.js b/packages/react-bootstrap-table2-editor/src/text-editor.js index 7937282..b44c2da 100644 --- a/packages/react-bootstrap-table2-editor/src/text-editor.js +++ b/packages/react-bootstrap-table2-editor/src/text-editor.js @@ -10,6 +10,10 @@ class TextEditor extends Component { this.text.focus(); } + getValue() { + return this.text.value; + } + render() { const { defaultValue, className, ...rest } = this.props; const editorClass = cs('form-control editor edit-text', className); diff --git a/packages/react-bootstrap-table2-editor/src/textarea-editor.js b/packages/react-bootstrap-table2-editor/src/textarea-editor.js new file mode 100644 index 0000000..e65b27e --- /dev/null +++ b/packages/react-bootstrap-table2-editor/src/textarea-editor.js @@ -0,0 +1,60 @@ +/* eslint no-return-assign: 0 */ +import React, { Component } from 'react'; +import cs from 'classnames'; +import PropTypes from 'prop-types'; + +class TextAreaEditor extends Component { + constructor(props) { + super(props); + this.handleKeyDown = this.handleKeyDown.bind(this); + } + + componentDidMount() { + const { defaultValue } = this.props; + this.text.value = defaultValue; + this.text.focus(); + } + + getValue() { + return this.text.value; + } + + handleKeyDown(e) { + if (e.keyCode === 13 && !e.shiftKey) return; + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + } + + render() { + const { defaultValue, className, ...rest } = this.props; + const editorClass = cs('form-control editor edit-textarea', className); + return ( +