Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 55fe007

Browse files
Merge pull request #165 from react-bootstrap-table/enhance/cell-edit-as-package
react-bootstrap-table2-editor
2 parents 6913434 + 618346b commit 55fe007

39 files changed

+831
-843
lines changed

‎docs/cell-edit.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Cell Editing
2+
Before start to use cell edit, please remember to install `react-bootstrap-table2-editor`
3+
4+
```sh
5+
$ npm install react-bootstrap-table2-editor --save
6+
```
7+
18
# Properties on cellEdit prop
29
* [mode (**required**)](#mode)
310
* [blurToSave](#blurToSave)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "react-bootstrap-table2-editor",
3+
"version": "0.0.1",
4+
"description": "it's the editor addon for react-bootstrap-table2",
5+
"main": "src/index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "",
10+
"license": "ISC"
11+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const TIME_TO_CLOSE_MESSAGE = 3000;
2+
export const DELAY_FOR_DBCLICK = 200;
3+
export const CLICK_TO_CELL_EDIT = 'click';
4+
export const DBCLICK_TO_CELL_EDIT = 'dbclick';
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/* eslint react/prop-types: 0 */
2+
/* eslint no-return-assign: 0 */
3+
/* eslint class-methods-use-this: 0 */
4+
/* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */
5+
import React, { Component } from 'react';
6+
import cs from 'classnames';
7+
import PropTypes from 'prop-types';
8+
9+
import TextEditor from './text-editor';
10+
import EditorIndicator from './editor-indicator';
11+
import { TIME_TO_CLOSE_MESSAGE } from './const';
12+
13+
export default _ =>
14+
class EditingCell extends Component {
15+
static propTypes = {
16+
row: PropTypes.object.isRequired,
17+
column: PropTypes.object.isRequired,
18+
onUpdate: PropTypes.func.isRequired,
19+
onEscape: PropTypes.func.isRequired,
20+
timeToCloseMessage: PropTypes.number,
21+
className: PropTypes.string,
22+
style: PropTypes.object
23+
}
24+
25+
static defaultProps = {
26+
timeToCloseMessage: TIME_TO_CLOSE_MESSAGE,
27+
className: null,
28+
style: {}
29+
}
30+
31+
constructor(props) {
32+
super(props);
33+
this.indicatorTimer = null;
34+
this.clearTimer = this.clearTimer.bind(this);
35+
this.handleBlur = this.handleBlur.bind(this);
36+
this.handleClick = this.handleClick.bind(this);
37+
this.handleKeyDown = this.handleKeyDown.bind(this);
38+
this.beforeComplete = this.beforeComplete.bind(this);
39+
this.state = {
40+
invalidMessage: null
41+
};
42+
}
43+
44+
componentWillReceiveProps({ message }) {
45+
if (_.isDefined(message)) {
46+
this.createTimer();
47+
this.setState(() => ({
48+
invalidMessage: message
49+
}));
50+
}
51+
}
52+
53+
componentWillUnmount() {
54+
this.clearTimer();
55+
}
56+
57+
clearTimer() {
58+
if (this.indicatorTimer) {
59+
clearTimeout(this.indicatorTimer);
60+
}
61+
}
62+
63+
createTimer() {
64+
this.clearTimer();
65+
const { timeToCloseMessage, onErrorMessageDisappear } = this.props;
66+
this.indicatorTimer = _.sleep(() => {
67+
this.setState(() => ({
68+
invalidMessage: null
69+
}));
70+
if (_.isFunction(onErrorMessageDisappear)) onErrorMessageDisappear();
71+
}, timeToCloseMessage);
72+
}
73+
74+
beforeComplete(row, column, newValue) {
75+
const { onUpdate } = this.props;
76+
if (_.isFunction(column.validator)) {
77+
const validateForm = column.validator(newValue, row, column);
78+
if (_.isObject(validateForm) && !validateForm.valid) {
79+
this.setState(() => ({
80+
invalidMessage: validateForm.message
81+
}));
82+
this.createTimer();
83+
return;
84+
}
85+
}
86+
onUpdate(row, column, newValue);
87+
}
88+
89+
handleBlur() {
90+
const { onEscape, blurToSave, row, column } = this.props;
91+
if (blurToSave) {
92+
const value = this.editor.text.value;
93+
if (!_.isDefined(value)) {
94+
// TODO: for other custom or embed editor
95+
}
96+
this.beforeComplete(row, column, value);
97+
} else {
98+
onEscape();
99+
}
100+
}
101+
102+
handleKeyDown(e) {
103+
const { onEscape, row, column } = this.props;
104+
if (e.keyCode === 27) { // ESC
105+
onEscape();
106+
} else if (e.keyCode === 13) { // ENTER
107+
const value = e.currentTarget.value;
108+
if (!_.isDefined(value)) {
109+
// TODO: for other custom or embed editor
110+
}
111+
this.beforeComplete(row, column, value);
112+
}
113+
}
114+
115+
handleClick(e) {
116+
if (e.target.tagName !== 'TD') {
117+
// To avoid the row selection event be triggered,
118+
// When user define selectRow.clickToSelect and selectRow.clickToEdit
119+
// We shouldn't trigger selection event even if user click on the cell editor(input)
120+
e.stopPropagation();
121+
}
122+
}
123+
124+
render() {
125+
const { invalidMessage } = this.state;
126+
const { row, column, className, style } = this.props;
127+
const { dataField } = column;
128+
129+
const value = _.get(row, dataField);
130+
const editorAttrs = {
131+
onKeyDown: this.handleKeyDown,
132+
onBlur: this.handleBlur
133+
};
134+
135+
const hasError = _.isDefined(invalidMessage);
136+
const editorClass = hasError ? cs('animated', 'shake') : null;
137+
return (
138+
<td
139+
className={ cs('react-bootstrap-table-editing-cell', className) }
140+
style={ style }
141+
onClick={ this.handleClick }
142+
>
143+
<TextEditor
144+
ref={ node => this.editor = node }
145+
defaultValue={ value }
146+
className={ editorClass }
147+
{ ...editorAttrs }
148+
/>
149+
{ hasError ? <EditorIndicator invalidMessage={ invalidMessage } /> : null }
150+
</td>
151+
);
152+
}
153+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import wrapperFactory from './wrapper';
2+
import editingCellFactory from './editing-cell';
3+
import {
4+
CLICK_TO_CELL_EDIT,
5+
DBCLICK_TO_CELL_EDIT,
6+
DELAY_FOR_DBCLICK
7+
} from './const';
8+
9+
export default (options = {}) => ({
10+
wrapperFactory,
11+
editingCellFactory,
12+
CLICK_TO_CELL_EDIT,
13+
DBCLICK_TO_CELL_EDIT,
14+
DELAY_FOR_DBCLICK,
15+
options
16+
});

‎packages/react-bootstrap-table2/src/cell-edit/wrapper.js renamed to ‎packages/react-bootstrap-table2-editor/src/wrapper.js

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
/* eslint react/prop-types: 0 */
22
import React, { Component } from 'react';
3-
import _ from '../utils';
4-
import remoteResolver from '../props-resolver/remote-resolver';
3+
import PropTypes from 'prop-types';
4+
5+
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
6+
7+
export default (
8+
Base,
9+
{ _, remoteResolver }
10+
) => {
11+
let EditingCell;
12+
return class CellEditWrapper extends remoteResolver(Component) {
13+
static propTypes = {
14+
options: PropTypes.shape({
15+
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
16+
onErrorMessageDisappear: PropTypes.func,
17+
blurToSave: PropTypes.bool,
18+
beforeSaveCell: PropTypes.func,
19+
afterSaveCell: PropTypes.func,
20+
nonEditableRows: PropTypes.func,
21+
timeToCloseMessage: PropTypes.number,
22+
errorMessage: PropTypes.string
23+
})
24+
}
525

6-
export default Base =>
7-
class CellEditWrapper extends remoteResolver(Component) {
826
constructor(props) {
927
super(props);
28+
EditingCell = props.cellEdit.editingCellFactory(_);
1029
this.startEditing = this.startEditing.bind(this);
1130
this.escapeEditing = this.escapeEditing.bind(this);
1231
this.completeEditing = this.completeEditing.bind(this);
@@ -21,10 +40,10 @@ export default Base =>
2140

2241
componentWillReceiveProps(nextProps) {
2342
if (nextProps.cellEdit && this.isRemoteCellEdit()) {
24-
if (nextProps.cellEdit.errorMessage) {
43+
if (nextProps.cellEdit.options.errorMessage) {
2544
this.setState(() => ({
2645
isDataChanged: false,
27-
message: nextProps.cellEdit.errorMessage
46+
message: nextProps.cellEdit.options.errorMessage
2847
}));
2948
} else {
3049
this.setState(() => ({
@@ -41,7 +60,7 @@ export default Base =>
4160

4261
handleCellUpdate(row, column, newValue) {
4362
const { keyField, cellEdit, store } = this.props;
44-
const { beforeSaveCell, afterSaveCell } = cellEdit;
63+
const { beforeSaveCell, afterSaveCell } = cellEdit.options;
4564
const oldValue = _.get(row, column.dataField);
4665
const rowId = _.get(row, keyField);
4766
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
@@ -84,17 +103,33 @@ export default Base =>
84103
}
85104

86105
render() {
87-
const { isDataChanged, ...rest } = this.state;
106+
const { isDataChanged, ...stateRest } = this.state;
107+
const {
108+
cellEdit: {
109+
options: { nonEditableRows, errorMessage, ...optionsRest },
110+
editingCellFactory,
111+
...cellEditRest
112+
}
113+
} = this.props;
114+
const newCellEdit = {
115+
...optionsRest,
116+
...cellEditRest,
117+
...stateRest,
118+
EditingCell,
119+
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
120+
onStart: this.startEditing,
121+
onEscape: this.escapeEditing,
122+
onUpdate: this.handleCellUpdate
123+
};
124+
88125
return (
89126
<Base
90127
{ ...this.props }
91-
isDataChanged={ isDataChanged }
92128
data={ this.props.store.data }
93-
onCellUpdate={ this.handleCellUpdate }
94-
onStartEditing={ this.startEditing }
95-
onEscapeEditing={ this.escapeEditing }
96-
currEditCell={ { ...rest } }
129+
isDataChanged={ isDataChanged }
130+
cellEdit={ newCellEdit }
97131
/>
98132
);
99133
}
100134
};
135+
};

‎packages/react-bootstrap-table2/test/cell-edit/editing-cell.test.js renamed to ‎packages/react-bootstrap-table2-editor/test/editing-cell.test.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
1+
/* eslint react/prop-types: 0 */
12
import 'jsdom-global/register';
23
import React from 'react';
34
import sinon from 'sinon';
45
import { shallow, mount } from 'enzyme';
56

6-
import { TableRowWrapper } from '../test-helpers/table-wrapper';
7-
import EditingCell from '../../src/cell-edit/editing-cell';
8-
import TextEditor from '../../src/cell-edit/text-editor';
9-
import EditorIndicator from '../../src/cell-edit/editor-indicator';
7+
import _ from 'react-bootstrap-table2/src/utils';
8+
import editingCellFactory from '../src/editing-cell';
9+
import TextEditor from '../src/text-editor';
10+
import EditorIndicator from '../src/editor-indicator';
11+
12+
const EditingCell = editingCellFactory(_);
13+
const TableRowWrapper = props => (
14+
<table>
15+
<tbody>
16+
<tr>{ props.children }</tr>
17+
</tbody>
18+
</table>
19+
);
20+
1021

1122
describe('EditingCell', () => {
1223
let wrapper;

‎packages/react-bootstrap-table2/test/cell-edit/text-editor.test.js renamed to ‎packages/react-bootstrap-table2-editor/test/text-editor.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'jsdom-global/register';
22
import React from 'react';
33
import { mount } from 'enzyme';
44

5-
import TextEditor from '../../src/cell-edit/text-editor';
5+
import TextEditor from '../src/text-editor';
66

77
describe('TextEditor', () => {
88
let wrapper;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /