Skip to content

Commit f1a0710

Browse files
committed
implement cell editor
1 parent 2e10cb1 commit f1a0710

File tree

11 files changed

+380
-79
lines changed

11 files changed

+380
-79
lines changed

packages/react-bootstrap-table2/src/body.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ const Body = (props) => {
1313
keyField,
1414
isEmpty,
1515
noDataIndication,
16-
visibleColumnSize
16+
visibleColumnSize,
17+
cellEdit
1718
} = props;
1819

1920
let content;
@@ -22,14 +23,20 @@ const Body = (props) => {
2223
const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication;
2324
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
2425
} else {
25-
content = data.map((row, index) => (
26-
<Row
27-
key={ _.get(row, keyField) }
28-
row={ row }
29-
rowIndex={ index }
30-
columns={ columns }
31-
/>
32-
));
26+
content = data.map((row, index) => {
27+
const key = _.get(row, keyField);
28+
const editable = !(cellEdit && cellEdit.nonEditableRows.indexOf(key) > -1);
29+
return (
30+
<Row
31+
key={ key }
32+
row={ row }
33+
rowIndex={ index }
34+
columns={ columns }
35+
cellEdit={ cellEdit }
36+
editable={ editable }
37+
/>
38+
);
39+
});
3340
}
3441

3542
return (

packages/react-bootstrap-table2/src/bootstrap-table.js

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import Header from './header';
77
import Body from './body';
88
import Store from './store/base';
99
import PropsBaseResolver from './props-resolver';
10+
import Const from './const';
11+
import _ from './utils';
1012

1113
class BootstrapTable extends PropsBaseResolver(Component) {
1214
constructor(props) {
@@ -16,8 +18,15 @@ class BootstrapTable extends PropsBaseResolver(Component) {
1618
this.store = !store ? new Store(props) : store;
1719

1820
this.handleSort = this.handleSort.bind(this);
21+
this.startEditing = this.startEditing.bind(this);
22+
this.escapeEditing = this.escapeEditing.bind(this);
23+
this.completeEditing = this.completeEditing.bind(this);
1924
this.state = {
20-
data: this.store.get()
25+
data: this.store.get(),
26+
currEditCell: {
27+
ridx: null,
28+
cidx: null
29+
}
2130
};
2231
}
2332

@@ -39,6 +48,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
3948
'table-condensed': condensed
4049
});
4150

51+
const cellEditInfo = this.resolveCellEditProps({
52+
onStart: this.startEditing,
53+
onEscape: this.escapeEditing,
54+
onComplete: this.completeEditing
55+
});
56+
4257
return (
4358
<div className="react-bootstrap-table-container">
4459
<table className={ tableClass }>
@@ -55,6 +70,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
5570
isEmpty={ this.isEmpty() }
5671
visibleColumnSize={ this.visibleColumnSize() }
5772
noDataIndication={ noDataIndication }
73+
cellEdit={ cellEditInfo }
5874
/>
5975
</table>
6076
</div>
@@ -70,6 +86,39 @@ class BootstrapTable extends PropsBaseResolver(Component) {
7086
};
7187
});
7288
}
89+
90+
completeEditing(row, column, newValue) {
91+
const { cellEdit, keyField } = this.props;
92+
const { beforeSaveCell, onEditing, afterSaveCell } = cellEdit;
93+
const oldValue = _.get(row, column.dataField);
94+
const rowId = _.get(row, keyField);
95+
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
96+
onEditing(rowId, column.dataField, newValue);
97+
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
98+
99+
this.setState(() => {
100+
return {
101+
data: this.store.get(),
102+
currEditCell: { ridx: null, cidx: null }
103+
};
104+
});
105+
}
106+
107+
startEditing(ridx, cidx) {
108+
this.setState(() => {
109+
return {
110+
currEditCell: { ridx, cidx }
111+
};
112+
});
113+
}
114+
115+
escapeEditing() {
116+
this.setState(() => {
117+
return {
118+
currEditCell: { ridx: null, cidx: null }
119+
};
120+
});
121+
}
73122
}
74123

75124
BootstrapTable.propTypes = {
@@ -81,7 +130,15 @@ BootstrapTable.propTypes = {
81130
striped: PropTypes.bool,
82131
bordered: PropTypes.bool,
83132
hover: PropTypes.bool,
84-
condensed: PropTypes.bool
133+
condensed: PropTypes.bool,
134+
cellEdit: PropTypes.shape({
135+
mode: PropTypes.oneOf([Const.CLICK_TO_CELL_EDIT, Const.DBCLICK_TO_CELL_EDIT]).isRequired,
136+
onEditing: PropTypes.func.isRequired,
137+
blurToSave: PropTypes.bool,
138+
beforeSaveCell: PropTypes.func,
139+
afterSaveCell: PropTypes.func,
140+
nonEditableRows: PropTypes.func
141+
})
85142
};
86143

87144
BootstrapTable.defaultProps = {

packages/react-bootstrap-table2/src/cell.js

Lines changed: 87 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,103 @@
1-
import React from 'react';
1+
/* eslint react/prop-types: 0 */
2+
import React, { Component } from 'react';
23
import PropTypes from 'prop-types';
34

5+
import Const from './const';
46
import _ from './utils';
57

6-
const Cell = ({ row, rowIndex, column, columnIndex }) => {
7-
const {
8-
dataField,
9-
hidden,
10-
formatter,
11-
formatExtraData,
12-
style,
13-
classes,
14-
title,
15-
events,
16-
align,
17-
attrs
18-
} = column;
19-
let cellTitle;
20-
let cellStyle = {};
21-
let content = _.get(row, dataField);
22-
23-
const cellAttrs = {
24-
..._.isFunction(attrs) ? attrs(content, row, rowIndex, columnIndex) : attrs,
25-
...events
26-
};
27-
28-
const cellClasses = _.isFunction(classes)
29-
? classes(content, row, rowIndex, columnIndex)
30-
: classes;
31-
32-
if (style) {
33-
cellStyle = _.isFunction(style) ? style(content, row, rowIndex, columnIndex) : style;
8+
class Cell extends Component {
9+
constructor(props) {
10+
super(props);
11+
this.handleEditingCell = this.handleEditingCell.bind(this);
3412
}
3513

36-
if (title) {
37-
cellTitle = _.isFunction(title) ? title(content, row, rowIndex, columnIndex) : content;
38-
cellAttrs.title = cellTitle;
14+
handleEditingCell(e) {
15+
const { editMode, column, onStart, rowIndex, columnIndex } = this.props;
16+
const { events } = column;
17+
if (events) {
18+
if (editMode === Const.CLICK_TO_CELL_EDIT) {
19+
const customClick = events.onClick;
20+
if (_.isFunction(customClick)) customClick(e);
21+
} else {
22+
const customDbClick = events.onDoubleClick;
23+
if (_.isFunction(customDbClick)) customDbClick(e);
24+
}
25+
}
26+
onStart(rowIndex, columnIndex);
3927
}
4028

41-
if (formatter) {
42-
content = column.formatter(content, row, rowIndex, formatExtraData);
43-
}
29+
render() {
30+
const {
31+
row,
32+
rowIndex,
33+
column,
34+
columnIndex,
35+
editMode,
36+
editable
37+
} = this.props;
38+
const {
39+
dataField,
40+
hidden,
41+
formatter,
42+
formatExtraData,
43+
style,
44+
classes,
45+
title,
46+
events,
47+
align,
48+
attrs
49+
} = column;
50+
let cellTitle;
51+
let cellStyle = {};
52+
let content = _.get(row, dataField);
4453

45-
if (align) {
46-
cellStyle.textAlign = _.isFunction(align) ? align(content, row, rowIndex, columnIndex) : align;
47-
}
54+
const cellAttrs = {
55+
..._.isFunction(attrs) ? attrs(content, row, rowIndex, columnIndex) : attrs,
56+
...events
57+
};
4858

49-
if (hidden) {
50-
cellStyle.display = 'none';
51-
}
59+
const cellClasses = _.isFunction(classes)
60+
? classes(content, row, rowIndex, columnIndex)
61+
: classes;
5262

53-
if (cellClasses) cellAttrs.className = cellClasses;
63+
if (style) {
64+
cellStyle = _.isFunction(style) ? style(content, row, rowIndex, columnIndex) : style;
65+
}
5466

55-
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
67+
if (title) {
68+
cellTitle = _.isFunction(title) ? title(content, row, rowIndex, columnIndex) : content;
69+
cellAttrs.title = cellTitle;
70+
}
5671

57-
return (
58-
<td { ...cellAttrs }>{ content }</td>
59-
);
60-
};
72+
if (formatter) {
73+
content = column.formatter(content, row, rowIndex, formatExtraData);
74+
}
75+
76+
if (align) {
77+
cellStyle.textAlign =
78+
_.isFunction(align) ? align(content, row, rowIndex, columnIndex) : align;
79+
}
80+
81+
if (hidden) {
82+
cellStyle.display = 'none';
83+
}
84+
85+
if (cellClasses) cellAttrs.className = cellClasses;
86+
87+
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
88+
89+
if (editable && editMode !== Const.UNABLE_TO_CELL_EDIT) {
90+
if (editMode === Const.CLICK_TO_CELL_EDIT) { // click to edit
91+
cellAttrs.onClick = this.handleEditingCell;
92+
} else { // dbclick to edit
93+
cellAttrs.onDoubleClick = this.handleEditingCell;
94+
}
95+
}
96+
return (
97+
<td { ...cellAttrs }>{ content }</td>
98+
);
99+
}
100+
}
61101

62102
Cell.propTypes = {
63103
row: PropTypes.object.isRequired,
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export default {
22
SORT_ASC: 'asc',
3-
SORT_DESC: 'desc'
3+
SORT_DESC: 'desc',
4+
UNABLE_TO_CELL_EDIT: 'none',
5+
CLICK_TO_CELL_EDIT: 'click',
6+
DBCLICK_TO_CELL_EDIT: 'dbclick'
47
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint react/prop-types: 0 */
2+
/* eslint no-return-assign: 0 */
3+
import React, { Component } from 'react';
4+
import PropTypes from 'prop-types';
5+
6+
import _ from './utils';
7+
import TextEditor from './text-editor';
8+
9+
class EditingCell extends Component {
10+
constructor(props) {
11+
super(props);
12+
this.handleBlur = this.handleBlur.bind(this);
13+
this.handleKeyDown = this.handleKeyDown.bind(this);
14+
}
15+
16+
handleBlur() {
17+
const { onEscape, onComplete, blurToSave, row, column } = this.props;
18+
if (blurToSave) {
19+
const value = this.editor.text.value;
20+
if (!_.isDefined(value)) {
21+
// TODO: for other custom or embed editor
22+
}
23+
onComplete(row, column, value);
24+
} else {
25+
onEscape();
26+
}
27+
}
28+
29+
handleKeyDown(e) {
30+
const { onEscape, onComplete, row, column } = this.props;
31+
if (e.keyCode === 27) { // ESC
32+
onEscape();
33+
} else if (e.keyCode === 13) { // ENTER
34+
const value = e.currentTarget.value;
35+
if (!_.isDefined(value)) {
36+
// TODO: for other custom or embed editor
37+
}
38+
onComplete(row, column, value);
39+
}
40+
}
41+
42+
render() {
43+
const { row, column } = this.props;
44+
const { dataField } = column;
45+
46+
const value = _.get(row, dataField);
47+
const editorAttrs = {
48+
onKeyDown: this.handleKeyDown,
49+
onBlur: this.handleBlur
50+
};
51+
return (
52+
<td>
53+
<TextEditor ref={ node => this.editor = node } defaultValue={ value } { ...editorAttrs } />
54+
</td>
55+
);
56+
}
57+
}
58+
59+
EditingCell.propTypes = {
60+
row: PropTypes.object.isRequired,
61+
column: PropTypes.object.isRequired,
62+
onComplete: PropTypes.func.isRequired,
63+
onEscape: PropTypes.func.isRequired
64+
};
65+
66+
export default EditingCell;

0 commit comments

Comments
 (0)