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 39be018

Browse files
move cell edit logic to react-bootstrap-table2-editor
1 parent 6913434 commit 39be018

File tree

17 files changed

+267
-246
lines changed

17 files changed

+267
-246
lines changed
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+
};
File renamed without changes.
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+
});
File renamed without changes.

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
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';
54

6-
export default Base =>
5+
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
6+
7+
export default (
8+
Base,
9+
{ _, remoteResolver }
10+
) =>
711
class CellEditWrapper extends remoteResolver(Component) {
12+
static propTypes = {
13+
options: PropTypes.shape({
14+
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
15+
onErrorMessageDisappear: PropTypes.func,
16+
blurToSave: PropTypes.bool,
17+
beforeSaveCell: PropTypes.func,
18+
afterSaveCell: PropTypes.func,
19+
nonEditableRows: PropTypes.func,
20+
timeToCloseMessage: PropTypes.number,
21+
errorMessage: PropTypes.string
22+
})
23+
}
24+
825
constructor(props) {
926
super(props);
1027
this.startEditing = this.startEditing.bind(this);
@@ -21,10 +38,10 @@ export default Base =>
2138

2239
componentWillReceiveProps(nextProps) {
2340
if (nextProps.cellEdit && this.isRemoteCellEdit()) {
24-
if (nextProps.cellEdit.errorMessage) {
41+
if (nextProps.cellEdit.options.errorMessage) {
2542
this.setState(() => ({
2643
isDataChanged: false,
27-
message: nextProps.cellEdit.errorMessage
44+
message: nextProps.cellEdit.options.errorMessage
2845
}));
2946
} else {
3047
this.setState(() => ({
@@ -41,7 +58,7 @@ export default Base =>
4158

4259
handleCellUpdate(row, column, newValue) {
4360
const { keyField, cellEdit, store } = this.props;
44-
const { beforeSaveCell, afterSaveCell } = cellEdit;
61+
const { beforeSaveCell, afterSaveCell } = cellEdit.options;
4562
const oldValue = _.get(row, column.dataField);
4663
const rowId = _.get(row, keyField);
4764
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
@@ -84,16 +101,31 @@ export default Base =>
84101
}
85102

86103
render() {
87-
const { isDataChanged, ...rest } = this.state;
104+
const { isDataChanged, ...stateRest } = this.state;
105+
const {
106+
cellEdit: {
107+
options: { nonEditableRows, ...optionsRest },
108+
editingCellFactory,
109+
...cellEditRest
110+
}
111+
} = this.props;
112+
const newCellEdit = {
113+
...optionsRest,
114+
...cellEditRest,
115+
...stateRest,
116+
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
117+
EditingCell: editingCellFactory(_),
118+
onStart: this.startEditing,
119+
onEscape: this.escapeEditing,
120+
onUpdate: this.handleCellUpdate
121+
};
122+
88123
return (
89124
<Base
90125
{ ...this.props }
91-
isDataChanged={ isDataChanged }
92126
data={ this.props.store.data }
93-
onCellUpdate={ this.handleCellUpdate }
94-
onStartEditing={ this.startEditing }
95-
onEscapeEditing={ this.escapeEditing }
96-
currEditCell={ { ...rest } }
127+
isDataChanged={ isDataChanged }
128+
cellEdit={ newCellEdit }
97129
/>
98130
);
99131
}

‎packages/react-bootstrap-table2-example/.storybook/webpack.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const sourcePath = path.join(__dirname, '../../react-bootstrap-table2/src');
44
const paginationSourcePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/src');
55
const overlaySourcePath = path.join(__dirname, '../../react-bootstrap-table2-overlay/src');
66
const filterSourcePath = path.join(__dirname, '../../react-bootstrap-table2-filter/src');
7+
const editorSourcePath = path.join(__dirname, '../../react-bootstrap-table2-editor/src');
78
const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style');
89
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
910
const storyPath = path.join(__dirname, '../stories');
@@ -27,7 +28,7 @@ const loaders = [{
2728
test: /\.js?$/,
2829
use: ['babel-loader'],
2930
exclude: /node_modules/,
30-
include: [sourcePath, paginationSourcePath, overlaySourcePath, filterSourcePath, storyPath],
31+
include: [sourcePath, paginationSourcePath, overlaySourcePath, filterSourcePath, editorSourcePath,storyPath],
3132
}, {
3233
test: /\.css$/,
3334
use: ['style-loader', 'css-loader'],

‎packages/react-bootstrap-table2-example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"dependencies": {
1919
"bootstrap": "^3.3.7",
2020
"react-bootstrap-table2": "0.0.1",
21+
"react-bootstrap-table2-editor": "0.0.1",
2122
"react-bootstrap-table2-paginator": "0.0.1",
2223
"react-bootstrap-table2-overlay": "0.0.1",
2324
"react-bootstrap-table2-filter": "0.0.1"

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ const Body = (props) => {
3737
const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication;
3838
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
3939
} else {
40+
const nonEditableRows = cellEdit.nonEditableRows || [];
4041
content = data.map((row, index) => {
4142
const key = _.get(row, keyField);
42-
const editable = !(cellEdit.mode !== Const.UNABLE_TO_CELL_EDIT &&
43-
cellEdit.nonEditableRows.indexOf(key) > -1);
43+
const editable = !(nonEditableRows.length > 0 && nonEditableRows.indexOf(key) > -1);
4444

4545
const selected = selectRow.mode !== Const.ROW_SELECT_DISABLED
4646
? selectedRowKeys.includes(key)

0 commit comments

Comments
(0)

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