diff --git a/packages/react-bootstrap-table2-example/public/favicon.ico b/packages/react-bootstrap-table2-example/public/favicon.ico
new file mode 100644
index 0000000..8fc5cec
Binary files /dev/null and b/packages/react-bootstrap-table2-example/public/favicon.ico differ
diff --git a/packages/react-bootstrap-table2-example/public/images/logo-color-square.svg b/packages/react-bootstrap-table2-example/public/images/logo-color-square.svg
new file mode 100644
index 0000000..bc897b9
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/public/images/logo-color-square.svg
@@ -0,0 +1 @@
+logo square
\ No newline at end of file
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index 2941f37..519542e 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -14,6 +14,7 @@ import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
import CaptionTable from 'examples/basic/caption-table';
import LargeTable from 'examples/basic/large-table';
import ExposedAPITable from 'examples/basic/exposed-function';
+import TabIndexCellTable from 'examples/basic/tabindex-column';
// bootstrap 4
import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort';
@@ -102,6 +103,7 @@ import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import AutoSelectTextInput from 'examples/cell-edit/auto-select-text-input-table';
import EditorStyleTable from 'examples/cell-edit/editor-style-table';
import EditorClassTable from 'examples/cell-edit/editor-class-table';
+import DBClickEditWithSelection from 'examples/cell-edit/dbclick-to-edit-with-selection-table';
import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table';
import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
@@ -115,6 +117,7 @@ import ClickToSelectTable from 'examples/row-selection/click-to-select';
import DefaultSelectTable from 'examples/row-selection/default-select';
import SelectionManagement from 'examples/row-selection/selection-management';
import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit';
+import SelectionWithExpansionTable from 'examples/row-selection/selection-with-expansion';
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
import SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class';
@@ -130,6 +133,7 @@ import BasicRowExpand from 'examples/row-expand';
import RowExpandManagement from 'examples/row-expand/expand-management';
import NonExpandableRows from 'examples/row-expand/non-expandable-rows';
import ExpandColumn from 'examples/row-expand/expand-column';
+import OnlyExpandByColumn from 'examples/row-expand/expand-by-column-only.js';
import ExpandOnlyOne from 'examples/row-expand/expand-only-one';
import CustomExpandColumn from 'examples/row-expand/custom-expand-column';
import ExpandHooks from 'examples/row-expand/expand-hooks';
@@ -192,7 +196,8 @@ storiesOf('Basic Table', module)
.add('Customized id and class table', () => )
.add('Table with caption', () => )
.add('Large Table', () => )
- .add('Exposed API', () => );
+ .add('Exposed API', () => )
+ .add('Enable tabIndex on Cell', () => );
storiesOf('Bootstrap 4', module)
.addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR))
@@ -289,6 +294,7 @@ storiesOf('Cell Editing', module)
.add('Custom Cell Classes', () => )
.add('Custom Editor Classes', () => )
.add('Custom Editor Style', () => )
+ .add('DoubleClick to Edit with Selection', () => )
.add('Dropdown Editor', () => )
.add('Textarea Editor', () => )
.add('Checkbox Editor', () => )
@@ -303,6 +309,7 @@ storiesOf('Row Selection', module)
.add('Default Select', () => )
.add('Selection Management', () => )
.add('Click to Select and Edit Cell', () => )
+ .add('Row Select and Expand', () => )
.add('Selection without Data', () => )
.add('Selection Style', () => )
.add('Selection Class', () => )
@@ -319,6 +326,7 @@ storiesOf('Row Expand', module)
.add('Expand Management', () => )
.add('Non Expandabled Rows', () => )
.add('Expand Indicator', () => )
+ .add('Only Expand by Indicator', () => )
.add('Expand Only One Row at The Same Time', () => )
.add('Custom Expand Indicator', () => )
.add('Expand Hooks', () => );
diff --git a/packages/react-bootstrap-table2-example/stories/stylesheet/welcome/_index.scss b/packages/react-bootstrap-table2-example/stories/stylesheet/welcome/_index.scss
index 1d7949c..360ded8 100644
--- a/packages/react-bootstrap-table2-example/stories/stylesheet/welcome/_index.scss
+++ b/packages/react-bootstrap-table2-example/stories/stylesheet/welcome/_index.scss
@@ -1,3 +1,5 @@
+$logo-size: 96px;
+
.welcome {
margin-top: 70px;
text-align: center;
@@ -5,7 +7,22 @@
&-title {
color: $grey-900;
+ width: 100%;
+
+ display: inline-flex;
+ justify-content: center;
+ align-content: center;
+
+ &-logo {
+ position: relative;
+ top: -8px;
+ right: -12px;
+
+ width: $logo-size;
+ height: $logo-size;
+ }
}
+
&-sub-title {
font-size: 30px;
color: $grey-500;
diff --git a/packages/react-bootstrap-table2-filter/README.md b/packages/react-bootstrap-table2-filter/README.md
index 05c7c9e..8369efd 100644
--- a/packages/react-bootstrap-table2-filter/README.md
+++ b/packages/react-bootstrap-table2-filter/README.md
@@ -288,3 +288,4 @@ Following properties is valid in `FILTER_TYPES`:
* SELECT
* NUMBER
* DATE
+* MULTISELECT
diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js
index 7b29989..a32a82e 100644
--- a/packages/react-bootstrap-table2/src/body.js
+++ b/packages/react-bootstrap-table2/src/body.js
@@ -3,129 +3,106 @@
import React from 'react';
import PropTypes from 'prop-types';
-import cs from 'classnames';
import _ from './utils';
-import Row from './row';
-import ExpandRow from './row-expand/expand-row';
-import RowSection from './row-section';
+import SimpleRow from './row/simple-row';
+import RowAggregator from './row/aggregate-row';
+import RowSection from './row/row-section';
import Const from './const';
+import withRowSelection from './row-selection/row-consumer';
+import withRowExpansion from './row-expand/row-consumer';
-const Body = (props) => {
- const {
- columns,
- data,
- keyField,
- isEmpty,
- noDataIndication,
- visibleColumnSize,
- cellEdit,
- selectRow,
- selectedRowKeys,
- rowStyle,
- rowClasses,
- rowEvents,
- expandRow
- } = props;
-
- const {
- bgColor,
- nonSelectable
- } = selectRow;
-
- let content;
-
- if (isEmpty) {
- const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication;
- if (!indication) {
- return null;
+class Body extends React.Component {
+ constructor(props) {
+ super(props);
+ if (props.cellEdit.createContext) {
+ this.EditingCell = props.cellEdit.createEditingCell(_, props.cellEdit.options.onStartEdit);
}
- content = ;
- } else {
- const nonEditableRows = cellEdit.nonEditableRows || [];
- content = data.map((row, index) => {
- const key = _.get(row, keyField);
- const editable = !(nonEditableRows.length > 0 && nonEditableRows.indexOf(key) > -1);
-
- const selected = selectRow.mode !== Const.ROW_SELECT_DISABLED
- ? selectedRowKeys.includes(key)
- : null;
-
- const attrs = rowEvents || {};
- let style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle;
- let classes = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses);
- if (selected) {
- const selectedStyle = _.isFunction(selectRow.style)
- ? selectRow.style(row, index)
- : selectRow.style;
-
- const selectedClasses = _.isFunction(selectRow.classes)
- ? selectRow.classes(row, index)
- : selectRow.classes;
-
- style = {
- ...style,
- ...selectedStyle
- };
- classes = cs(classes, selectedClasses);
-
- if (bgColor) {
- style = style || {};
- style.backgroundColor = _.isFunction(bgColor) ? bgColor(row, index) : bgColor;
- }
- }
-
- const selectable = !nonSelectable || !nonSelectable.includes(key);
- const expandable = expandRow && !expandRow.nonExpandable.includes(key);
- const expanded = expandRow && expandRow.expanded.includes(key);
-
- const result = [
-
- ];
-
- if (expanded) {
- result.push((
-
- { expandRow.renderer(row) }
-
- ));
- }
-
- return result;
- });
}
- return (
- { content }
- );
-};
+ render() {
+ const {
+ columns,
+ data,
+ tabIndexCell,
+ keyField,
+ isEmpty,
+ noDataIndication,
+ visibleColumnSize,
+ cellEdit,
+ selectRow,
+ rowStyle,
+ rowClasses,
+ rowEvents,
+ expandRow
+ } = this.props;
+
+ let content;
+
+ if (isEmpty) {
+ const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication;
+ if (!indication) {
+ return null;
+ }
+ content = ;
+ } else {
+ let RowComponent = SimpleRow;
+ const selectRowEnabled = selectRow.mode !== Const.ROW_SELECT_DISABLED;
+ const expandRowEnabled = !!expandRow.renderer;
+
+ const additionalRowProps = {};
+ if (expandRowEnabled) {
+ RowComponent = withRowExpansion(RowAggregator, visibleColumnSize);
+ }
+
+ if (selectRowEnabled) {
+ RowComponent = withRowSelection(expandRowEnabled ? RowComponent : RowAggregator);
+ }
+
+ if (cellEdit.createContext) {
+ RowComponent = cellEdit.withRowLevelCellEdit(RowComponent, selectRowEnabled, keyField, _);
+ additionalRowProps.EditingCellComponent = this.EditingCell;
+ }
+
+ if (selectRowEnabled || expandRowEnabled) {
+ additionalRowProps.expandRow = expandRow;
+ additionalRowProps.selectRow = selectRow;
+ }
+
+ content = data.map((row, index) => {
+ const key = _.get(row, keyField);
+ const baseRowProps = {
+ key,
+ row,
+ tabIndexCell,
+ columns,
+ keyField,
+ cellEdit,
+ value: key,
+ rowIndex: index,
+ visibleColumnSize,
+ attrs: rowEvents || {},
+ ...additionalRowProps
+ };
+
+ baseRowProps.style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle;
+ baseRowProps.className = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses);
+
+ return ;
+ });
+ }
+
+ return (
+ { content }
+ );
+ }
+}
Body.propTypes = {
keyField: PropTypes.string.isRequired,
data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired,
- selectRow: PropTypes.object,
- selectedRowKeys: PropTypes.array
+ selectRow: PropTypes.object
};
export default Body;
diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js
index b0d4779..2bf37a3 100644
--- a/packages/react-bootstrap-table2/src/bootstrap-table.js
+++ b/packages/react-bootstrap-table2/src/bootstrap-table.js
@@ -9,7 +9,6 @@ import Caption from './caption';
import Body from './body';
import PropsBaseResolver from './props-resolver';
import Const from './const';
-import { getSelectionSummary } from './store/selection';
class BootstrapTable extends PropsBaseResolver(Component) {
constructor(props) {
@@ -44,6 +43,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
data,
columns,
keyField,
+ tabIndexCell,
id,
classes,
striped,
@@ -56,7 +56,9 @@ class BootstrapTable extends PropsBaseResolver(Component) {
rowClasses,
wrapperClasses,
rowEvents,
- selected
+ selectRow,
+ expandRow,
+ cellEdit
} = this.props;
const tableWrapperClass = cs('react-bootstrap-table', wrapperClasses);
@@ -68,20 +70,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
'table-condensed': condensed
}, classes);
- const cellSelectionInfo = this.resolveSelectRowProps({
- onRowSelect: this.props.onRowSelect
- });
-
- const { allRowsSelected, allRowsNotSelected } = getSelectionSummary(data, keyField, selected);
- const headerCellSelectionInfo = this.resolveSelectRowPropsForHeader({
- onAllRowsSelect: this.props.onAllRowsSelect,
- selected,
- allRowsSelected,
- allRowsNotSelected
- });
-
const tableCaption = (caption && { caption } );
- const expandRow = this.resolveExpandRowProps();
return (
@@ -95,19 +84,19 @@ class BootstrapTable extends PropsBaseResolver(Component) {
onSort={ this.props.onSort }
onFilter={ this.props.onFilter }
onExternalFilter={ this.props.onExternalFilter }
- selectRow={ headerCellSelectionInfo }
+ selectRow={ selectRow }
expandRow={ expandRow }
/>
}
if (props.selectRow) {
- this.SelectionContext = createSelectionContext(dataOperator);
+ this.SelectionContext = SelectionContext;
}
if (props.expandRow) {
- this.RowExpandContext = createRowExpandContext(dataOperator);
+ this.RowExpandContext = RowExpandContext;
}
if (props.cellEdit && props.cellEdit.createContext) {
@@ -54,27 +54,31 @@ const withContext = Base =>
}
}
+ componentWillReceiveProps(nextProps) {
+ if (!nextProps.pagination && this.props.pagination) {
+ this.PaginationContext = null;
+ }
+ if (nextProps.pagination && !this.props.pagination) {
+ this.PaginationContext = nextProps.pagination.createContext(
+ this.isRemotePagination, this.handleRemotePageChange);
+ }
+ }
+
renderBase() {
return (
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps,
paginationProps,
- expandProps,
- selectionProps
) => (
this.table = n }
{ ...this.props }
- { ...selectionProps }
{ ...sortProps }
- { ...cellEditProps }
{ ...filterProps }
{ ...searchProps }
{ ...paginationProps }
- { ...expandProps }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
/>
);
@@ -83,12 +87,10 @@ const withContext = Base =>
renderWithSelectionCtx(base, baseProps) {
return (
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps,
- paginationProps,
- expandProps
+ paginationProps
) => (
selectRow={ this.props.selectRow }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
>
-
- {
- selectionProps => base(
- rootProps,
- cellEditProps,
- filterProps,
- searchProps,
- sortProps,
- paginationProps,
- expandProps,
- selectionProps
- )
- }
-
+ {
+ base(
+ rootProps,
+ filterProps,
+ searchProps,
+ sortProps,
+ paginationProps
+ )
+ }
);
}
@@ -117,7 +114,6 @@ const withContext = Base =>
renderWithRowExpandCtx(base, baseProps) {
return (
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps,
@@ -129,19 +125,15 @@ const withContext = Base =>
expandRow={ this.props.expandRow }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
>
-
- {
- expandProps => base(
- rootProps,
- cellEditProps,
- filterProps,
- searchProps,
- sortProps,
- paginationProps,
- expandProps
- )
- }
-
+ {
+ base(
+ rootProps,
+ filterProps,
+ searchProps,
+ sortProps,
+ paginationProps
+ )
+ }
);
}
@@ -149,7 +141,6 @@ const withContext = Base =>
renderWithPaginationCtx(base) {
return (
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps
@@ -164,7 +155,6 @@ const withContext = Base =>
{
paginationProps => base(
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps,
@@ -179,7 +169,6 @@ const withContext = Base =>
renderWithSortCtx(base, baseProps) {
return (
rootProps,
- cellEditProps,
filterProps,
searchProps
) => (
@@ -194,7 +183,6 @@ const withContext = Base =>
{
sortProps => base(
rootProps,
- cellEditProps,
filterProps,
searchProps,
sortProps,
@@ -208,7 +196,6 @@ const withContext = Base =>
renderWithSearchCtx(base, baseProps) {
return (
rootProps,
- cellEditProps,
filterProps
) => (
{
searchProps => base(
rootProps,
- cellEditProps,
filterProps,
searchProps
)
@@ -232,10 +218,7 @@ const withContext = Base =>
}
renderWithFilterCtx(base, baseProps) {
- return (
- rootProps,
- cellEditProps
- ) => (
+ return rootProps => (
this.filterContext = n }
@@ -245,7 +228,6 @@ const withContext = Base =>
{
filterProps => base(
rootProps,
- cellEditProps,
filterProps
)
}
@@ -262,11 +244,7 @@ const withContext = Base =>
cellEdit={ this.props.cellEdit }
data={ rootProps.getData() }
>
-
- {
- cellEditProps => base(rootProps, cellEditProps)
- }
-
+ { base(rootProps) }
);
}
diff --git a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
index 3e882c6..5a3728b 100644
--- a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
+++ b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
@@ -1,92 +1,91 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import PropTypes from 'prop-types';
+import dataOperator from '../store/operators';
-export default (
- dataOperator
-) => {
- const RowExpandContext = React.createContext();
+const RowExpandContext = React.createContext();
- class RowExpandProvider extends React.Component {
- static propTypes = {
- children: PropTypes.node.isRequired,
- data: PropTypes.array.isRequired,
- keyField: PropTypes.string.isRequired
- }
+class RowExpandProvider extends React.Component {
+ static propTypes = {
+ children: PropTypes.node.isRequired,
+ data: PropTypes.array.isRequired,
+ keyField: PropTypes.string.isRequired
+ }
- state = { expanded: this.props.expandRow.expanded || [] };
+ state = { expanded: this.props.expandRow.expanded || [] };
- componentWillReceiveProps(nextProps) {
- if (nextProps.expandRow) {
- this.setState(() => ({
- expanded: nextProps.expandRow.expanded || this.state.expanded
- }));
- }
- }
-
- handleRowExpand = (rowKey, expanded, rowIndex, e) => {
- const { data, keyField, expandRow: { onExpand, onlyOneExpanding } } = this.props;
-
- let currExpanded = [...this.state.expanded];
-
- if (expanded) {
- if (onlyOneExpanding) currExpanded = [rowKey];
- else currExpanded.push(rowKey);
- } else {
- currExpanded = currExpanded.filter(value => value !== rowKey);
- }
-
- if (onExpand) {
- const row = dataOperator.getRowByRowId(data, keyField, rowKey);
- onExpand(row, expanded, rowIndex, e);
- }
- this.setState(() => ({ expanded: currExpanded }));
- }
-
- handleAllRowExpand = (e, expandAll) => {
- const {
- data,
- keyField,
- expandRow: {
- onExpandAll,
- nonExpandable
- }
- } = this.props;
- const { expanded } = this.state;
-
- let currExpanded;
-
- if (expandAll) {
- currExpanded = expanded.concat(dataOperator.expandableKeys(data, keyField, nonExpandable));
- } else {
- currExpanded = expanded.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined');
- }
-
- if (onExpandAll) {
- onExpandAll(expandAll, dataOperator.getExpandedRows(data, keyField, currExpanded), e);
- }
-
- this.setState(() => ({ expanded: currExpanded }));
- }
-
- render() {
- const { data, keyField } = this.props;
- return (
-
- { this.props.children }
-
- );
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.expandRow) {
+ this.setState(() => ({
+ expanded: nextProps.expandRow.expanded || this.state.expanded
+ }));
}
}
- return {
- Provider: RowExpandProvider,
- Consumer: RowExpandContext.Consumer
- };
+
+ handleRowExpand = (rowKey, expanded, rowIndex, e) => {
+ const { data, keyField, expandRow: { onExpand, onlyOneExpanding } } = this.props;
+
+ let currExpanded = [...this.state.expanded];
+
+ if (expanded) {
+ if (onlyOneExpanding) currExpanded = [rowKey];
+ else currExpanded.push(rowKey);
+ } else {
+ currExpanded = currExpanded.filter(value => value !== rowKey);
+ }
+
+ if (onExpand) {
+ const row = dataOperator.getRowByRowId(data, keyField, rowKey);
+ onExpand(row, expanded, rowIndex, e);
+ }
+ this.setState(() => ({ expanded: currExpanded }));
+ }
+
+ handleAllRowExpand = (e, expandAll) => {
+ const {
+ data,
+ keyField,
+ expandRow: {
+ onExpandAll,
+ nonExpandable
+ }
+ } = this.props;
+ const { expanded } = this.state;
+
+ let currExpanded;
+
+ if (expandAll) {
+ currExpanded = expanded.concat(dataOperator.expandableKeys(data, keyField, nonExpandable));
+ } else {
+ currExpanded = expanded.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined');
+ }
+
+ if (onExpandAll) {
+ onExpandAll(expandAll, dataOperator.getExpandedRows(data, keyField, currExpanded), e);
+ }
+
+ this.setState(() => ({ expanded: currExpanded }));
+ }
+
+ render() {
+ const { data, keyField } = this.props;
+ return (
+
+ { this.props.children }
+
+ );
+ }
+}
+
+export default {
+ Provider: RowExpandProvider,
+ Consumer: RowExpandContext.Consumer
};
diff --git a/packages/react-bootstrap-table2/src/contexts/selection-context.js b/packages/react-bootstrap-table2/src/contexts/selection-context.js
index 12a7af8..ce8d354 100644
--- a/packages/react-bootstrap-table2/src/contexts/selection-context.js
+++ b/packages/react-bootstrap-table2/src/contexts/selection-context.js
@@ -3,113 +3,132 @@ import React from 'react';
import PropTypes from 'prop-types';
import Const from '../const';
-export default (
- dataOperator
-) => {
- const SelectionContext = React.createContext();
+import dataOperator from '../store/operators';
+import { getSelectionSummary } from '../store/selection';
- class SelectionProvider extends React.Component {
- static propTypes = {
- children: PropTypes.node.isRequired,
- data: PropTypes.array.isRequired,
- keyField: PropTypes.string.isRequired
- }
+const SelectionContext = React.createContext();
+class SelectionProvider extends React.Component {
+ static propTypes = {
+ children: PropTypes.node.isRequired,
+ data: PropTypes.array.isRequired,
+ keyField: PropTypes.string.isRequired
+ }
- constructor(props) {
- super(props);
- if (props.registerExposedAPI) {
- const getSelected = () => this.getSelected();
- props.registerExposedAPI(getSelected);
- }
- }
-
- state = { selected: (this.props.selectRow && this.props.selectRow.selected) || [] };
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.selectRow) {
- this.setState(() => ({
- selected: nextProps.selectRow.selected || this.state.selected
- }));
- }
- }
-
- // exposed API
- getSelected() {
- return this.state.selected;
- }
-
- handleRowSelect = (rowKey, checked, rowIndex, e) => {
- const { data, keyField, selectRow: { mode, onSelect } } = this.props;
- const { ROW_SELECT_SINGLE } = Const;
-
- let currSelected = [...this.state.selected];
-
- if (mode === ROW_SELECT_SINGLE) { // when select mode is radio
- currSelected = [rowKey];
- } else if (checked) { // when select mode is checkbox
- currSelected.push(rowKey);
- } else {
- currSelected = currSelected.filter(value => value !== rowKey);
- }
-
- if (onSelect) {
- const row = dataOperator.getRowByRowId(data, keyField, rowKey);
- onSelect(row, checked, rowIndex, e);
- }
-
- this.setState(() => ({ selected: currSelected }));
- }
-
- handleAllRowsSelect = (e, isUnSelect) => {
- const {
- data,
- keyField,
- selectRow: {
- onSelectAll,
- nonSelectable
- }
- } = this.props;
- const { selected } = this.state;
-
- let currSelected;
-
- if (!isUnSelect) {
- currSelected = selected.concat(dataOperator.selectableKeys(data, keyField, nonSelectable));
- } else {
- currSelected = selected.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined');
- }
-
- if (onSelectAll) {
- onSelectAll(
- !isUnSelect,
- dataOperator.getSelectedRows(
- data,
- keyField,
- isUnSelect ? this.state.selected : currSelected
- ),
- e
- );
- }
-
- this.setState(() => ({ selected: currSelected }));
- }
-
- render() {
- return (
-
- { this.props.children }
-
- );
+ constructor(props) {
+ super(props);
+ if (props.registerExposedAPI) {
+ const getSelected = () => this.getSelected();
+ props.registerExposedAPI(getSelected);
}
}
- return {
- Provider: SelectionProvider,
- Consumer: SelectionContext.Consumer
- };
+
+ state = { selected: this.props.selectRow.selected || [] };
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.selectRow) {
+ this.setState(() => ({
+ selected: nextProps.selectRow.selected || this.state.selected
+ }));
+ }
+ }
+
+ // exposed API
+ getSelected() {
+ return this.state.selected;
+ }
+
+ handleRowSelect = (rowKey, checked, rowIndex, e) => {
+ const { data, keyField, selectRow: { mode, onSelect } } = this.props;
+ const { ROW_SELECT_SINGLE } = Const;
+
+ let currSelected = [...this.state.selected];
+
+ if (mode === ROW_SELECT_SINGLE) { // when select mode is radio
+ currSelected = [rowKey];
+ } else if (checked) { // when select mode is checkbox
+ currSelected.push(rowKey);
+ } else {
+ currSelected = currSelected.filter(value => value !== rowKey);
+ }
+
+ if (onSelect) {
+ const row = dataOperator.getRowByRowId(data, keyField, rowKey);
+ onSelect(row, checked, rowIndex, e);
+ }
+
+ this.setState(() => ({ selected: currSelected }));
+ }
+
+ handleAllRowsSelect = (e, isUnSelect) => {
+ const {
+ data,
+ keyField,
+ selectRow: {
+ onSelectAll,
+ nonSelectable
+ }
+ } = this.props;
+ const { selected } = this.state;
+
+ let currSelected;
+
+ if (!isUnSelect) {
+ currSelected = selected.concat(dataOperator.selectableKeys(data, keyField, nonSelectable));
+ } else {
+ currSelected = selected.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined');
+ }
+
+ if (onSelectAll) {
+ onSelectAll(
+ !isUnSelect,
+ dataOperator.getSelectedRows(
+ data,
+ keyField,
+ isUnSelect ? this.state.selected : currSelected
+ ),
+ e
+ );
+ }
+
+ this.setState(() => ({ selected: currSelected }));
+ }
+
+ render() {
+ const {
+ allRowsSelected,
+ allRowsNotSelected
+ } = getSelectionSummary(
+ this.props.data,
+ this.props.keyField,
+ this.state.selected
+ );
+
+ let checkedStatus;
+
+ // checkbox status depending on selected rows counts
+ if (allRowsSelected) checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
+ else if (allRowsNotSelected) checkedStatus = Const.CHECKBOX_STATUS_UNCHECKED;
+ else checkedStatus = Const.CHECKBOX_STATUS_INDETERMINATE;
+
+ return (
+
+ { this.props.children }
+
+ );
+ }
+}
+
+export default {
+ Provider: SelectionProvider,
+ Consumer: SelectionContext.Consumer
};
diff --git a/packages/react-bootstrap-table2/src/header.js b/packages/react-bootstrap-table2/src/header.js
index 4549343..3adf213 100644
--- a/packages/react-bootstrap-table2/src/header.js
+++ b/packages/react-bootstrap-table2/src/header.js
@@ -1,15 +1,14 @@
/* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types';
-import Const from './const';
import HeaderCell from './header-cell';
import SelectionHeaderCell from './row-selection/selection-header-cell';
import ExpandHeaderCell from './row-expand/expand-header-cell';
+import withHeaderSelection from './row-selection/selection-header-cell-consumer';
+import withHeaderExpansion from './row-expand/expand-header-cell-consumer';
const Header = (props) => {
- const { ROW_SELECT_DISABLED } = Const;
-
const {
className,
columns,
@@ -23,20 +22,24 @@ const Header = (props) => {
bootstrap4
} = props;
+ let SelectionHeaderCellComp = () => null;
+ let ExpansionHeaderCellComp = () => null;
+
+ if (expandRow.showExpandColumn) {
+ ExpansionHeaderCellComp = withHeaderExpansion(ExpandHeaderCell);
+ }
+
+ if (selectRow) {
+ SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell);
+ }
+
return (
+
{
- (expandRow && expandRow.showExpandColumn)
- ? : null
- }
- {
- (selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
- ? : null
+ !selectRow.hideSelectColumn ?
+ : null
}
{
columns.map((column, i) => {
diff --git a/packages/react-bootstrap-table2/src/props-resolver/expand-row-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/expand-row-resolver.js
deleted file mode 100644
index 015e321..0000000
--- a/packages/react-bootstrap-table2/src/props-resolver/expand-row-resolver.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export default ExtendBase =>
- class ExpandRowResolver extends ExtendBase {
- resolveExpandRowProps() {
- const { expandRow, expanded, onRowExpand, onAllRowExpand, isAnyExpands } = this.props;
- if (expandRow) {
- return {
- ...expandRow,
- expanded,
- onRowExpand,
- onAllRowExpand,
- isAnyExpands,
- nonExpandable: expandRow.nonExpandable || []
- };
- }
- return null;
- }
- };
diff --git a/packages/react-bootstrap-table2/src/props-resolver/index.js b/packages/react-bootstrap-table2/src/props-resolver/index.js
index 2154ce3..0c3c92d 100644
--- a/packages/react-bootstrap-table2/src/props-resolver/index.js
+++ b/packages/react-bootstrap-table2/src/props-resolver/index.js
@@ -1,11 +1,7 @@
import ColumnResolver from './column-resolver';
-import ExpandRowResolver from './expand-row-resolver';
-import Const from '../const';
-import _ from '../utils';
export default ExtendBase =>
- class TableResolver extends
- ExpandRowResolver(ColumnResolver(ExtendBase)) {
+ class TableResolver extends ColumnResolver(ExtendBase) {
validateProps() {
const { keyField } = this.props;
if (!keyField) {
@@ -19,63 +15,4 @@ export default ExtendBase =>
isEmpty() {
return this.props.data.length === 0;
}
-
- /**
- * props resolver for cell selection
- * @param {Object} options - addtional options like callback which are about to merge into props
- *
- * @returns {Object} result - props for cell selections
- * @returns {String} result.mode - input type of row selection or disabled.
- */
- resolveSelectRowProps(options) {
- const { selectRow } = this.props;
- const { ROW_SELECT_DISABLED } = Const;
-
- if (_.isDefined(selectRow)) {
- return {
- ...selectRow,
- ...options
- };
- }
-
- return {
- mode: ROW_SELECT_DISABLED
- };
- }
-
- /**
- * props resolver for header cell selection
- * @param {Object} options - addtional options like callback which are about to merge into props
- *
- * @returns {Object} result - props for cell selections
- * @returns {String} result.mode - input type of row selection or disabled.
- * @returns {String} result.checkedStatus - checkbox status depending on selected rows counts
- */
- resolveSelectRowPropsForHeader(options = {}) {
- const { selectRow } = this.props;
- const { allRowsSelected, allRowsNotSelected, ...rest } = options;
- const {
- ROW_SELECT_DISABLED, CHECKBOX_STATUS_CHECKED,
- CHECKBOX_STATUS_INDETERMINATE, CHECKBOX_STATUS_UNCHECKED
- } = Const;
-
- if (_.isDefined(selectRow)) {
- let checkedStatus;
-
- // checkbox status depending on selected rows counts
- if (allRowsSelected) checkedStatus = CHECKBOX_STATUS_CHECKED;
- else if (allRowsNotSelected) checkedStatus = CHECKBOX_STATUS_UNCHECKED;
- else checkedStatus = CHECKBOX_STATUS_INDETERMINATE;
-
- return {
- ...selectRow,
- ...rest,
- checkedStatus
- };
- }
-
- return {
- mode: ROW_SELECT_DISABLED
- };
- }
};
diff --git a/packages/react-bootstrap-table2/src/row-event-delegater.js b/packages/react-bootstrap-table2/src/row-event-delegater.js
index ee62606..4727134 100644
--- a/packages/react-bootstrap-table2/src/row-event-delegater.js
+++ b/packages/react-bootstrap-table2/src/row-event-delegater.js
@@ -1,6 +1,3 @@
-import _ from './utils';
-import Const from './const';
-
const events = [
'onClick',
'onDoubleClick',
@@ -15,7 +12,6 @@ export default ExtendBase =>
super(props);
this.clickNum = 0;
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
- this.createClickEventHandler = this.createClickEventHandler.bind(this);
}
createDefaultEventHandler(cb) {
@@ -25,65 +21,11 @@ export default ExtendBase =>
};
}
- createClickEventHandler(cb) {
- return (e) => {
- const {
- row,
- selected,
- keyField,
- selectable,
- expandable,
- rowIndex,
- expanded,
- expandRow,
- selectRow,
- cellEdit: {
- mode,
- DBCLICK_TO_CELL_EDIT,
- DELAY_FOR_DBCLICK
- }
- } = this.props;
-
- const clickFn = () => {
- if (cb) {
- cb(e, row, rowIndex);
- }
- const key = _.get(row, keyField);
- if (expandRow && expandable) {
- expandRow.onRowExpand(key, !expanded, rowIndex, e);
- }
- if (selectRow.mode !== Const.ROW_SELECT_DISABLED && selectable) {
- selectRow.onRowSelect(key, !selected, rowIndex, e);
- }
- };
-
- if (mode === DBCLICK_TO_CELL_EDIT && selectRow.clickToEdit) {
- this.clickNum += 1;
- _.debounce(() => {
- if (this.clickNum === 1) {
- clickFn();
- }
- this.clickNum = 0;
- }, DELAY_FOR_DBCLICK)();
- } else {
- clickFn();
- }
- };
- }
-
delegate(attrs = {}) {
- const newAttrs = {};
- const { expandRow, selectRow } = this.props;
- if (expandRow || (selectRow && selectRow.clickToSelect)) {
- newAttrs.onClick = this.createClickEventHandler(attrs.onClick);
- }
+ const newAttrs = { ...attrs };
Object.keys(attrs).forEach((attr) => {
- if (!newAttrs[attr]) {
- if (events.includes(attr)) {
- newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
- } else {
- newAttrs[attr] = attrs[attr];
- }
+ if (events.includes(attr)) {
+ newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
}
});
return newAttrs;
diff --git a/packages/react-bootstrap-table2/src/row-expand/expand-cell.js b/packages/react-bootstrap-table2/src/row-expand/expand-cell.js
index 3df5b1c..4e626ee 100644
--- a/packages/react-bootstrap-table2/src/row-expand/expand-cell.js
+++ b/packages/react-bootstrap-table2/src/row-expand/expand-cell.js
@@ -12,7 +12,8 @@ export default class ExpandCell extends Component {
expanded: PropTypes.bool.isRequired,
onRowExpand: PropTypes.func.isRequired,
expandColumnRenderer: PropTypes.func,
- rowIndex: PropTypes.number
+ rowIndex: PropTypes.number,
+ tabIndex: PropTypes.number
}
constructor() {
@@ -20,17 +21,29 @@ export default class ExpandCell extends Component {
this.handleClick = this.handleClick.bind(this);
}
+ shouldComponentUpdate(nextProps) {
+ const shouldUpdate =
+ this.props.rowIndex !== nextProps.rowIndex ||
+ this.props.expanded !== nextProps.expanded ||
+ this.props.rowKey !== nextProps.rowKey ||
+ this.props.tabIndex !== nextProps.tabIndex;
+
+ return shouldUpdate;
+ }
+
handleClick(e) {
const { rowKey, expanded, onRowExpand, rowIndex } = this.props;
- onRowExpand(rowKey, expanded, rowIndex, e);
+ onRowExpand(rowKey, !expanded, rowIndex, e);
}
render() {
- const { expanded, expandColumnRenderer } = this.props;
+ const { expanded, expandColumnRenderer, tabIndex } = this.props;
+ const attrs = {};
+ if (tabIndex !== -1) attrs.tabIndex = tabIndex;
return (
-
+
{
expandColumnRenderer ? expandColumnRenderer({
expanded
diff --git a/packages/react-bootstrap-table2/src/row-expand/expand-header-cell-consumer.js b/packages/react-bootstrap-table2/src/row-expand/expand-header-cell-consumer.js
new file mode 100644
index 0000000..4460b44
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row-expand/expand-header-cell-consumer.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import ExpansionContext from '../contexts/row-expand-context';
+
+export default Component => () => (
+
+ { expandRow => }
+
+);
diff --git a/packages/react-bootstrap-table2/src/row-expand/expand-header-cell.js b/packages/react-bootstrap-table2/src/row-expand/expand-header-cell.js
index 65c099b..e30fd9a 100644
--- a/packages/react-bootstrap-table2/src/row-expand/expand-header-cell.js
+++ b/packages/react-bootstrap-table2/src/row-expand/expand-header-cell.js
@@ -3,11 +3,11 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-export default class SelectionHeaderCell extends Component {
+export default class ExpansionHeaderCell extends Component {
static propTypes = {
- anyExpands: PropTypes.bool.isRequired,
+ isAnyExpands: PropTypes.bool.isRequired,
onAllRowExpand: PropTypes.func.isRequired,
- renderer: PropTypes.func
+ expandHeaderColumnRenderer: PropTypes.func
}
constructor() {
@@ -16,13 +16,13 @@ export default class SelectionHeaderCell extends Component {
}
handleCheckBoxClick(e) {
- const { anyExpands, onAllRowExpand } = this.props;
+ const { isAnyExpands, onAllRowExpand } = this.props;
- onAllRowExpand(e, !anyExpands);
+ onAllRowExpand(e, !isAnyExpands);
}
render() {
- const { anyExpands, renderer } = this.props;
+ const { isAnyExpands, expandHeaderColumnRenderer } = this.props;
const attrs = {
onClick: this.handleCheckBoxClick
};
@@ -30,9 +30,9 @@ export default class SelectionHeaderCell extends Component {
return (
{
- renderer ?
- renderer({ isAnyExpands: anyExpands }) :
- (anyExpands ? '(-)' : '(+)')
+ expandHeaderColumnRenderer ?
+ expandHeaderColumnRenderer({ isAnyExpands }) :
+ (isAnyExpands ? '(-)' : '(+)')
}
);
diff --git a/packages/react-bootstrap-table2/src/row-expand/row-consumer.js b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js
new file mode 100644
index 0000000..77f5e36
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js
@@ -0,0 +1,34 @@
+/* eslint react/prop-types: 0 */
+import React from 'react';
+import ExpandRow from './expand-row';
+import ExpansionContext from '../contexts/row-expand-context';
+
+export default (Component, visibleColumnSize) => {
+ const renderWithExpansion = (props, expandRow) => {
+ const key = props.value;
+
+ const expanded = expandRow.expanded.includes(key);
+ const expandable = !expandRow.nonExpandable || !expandRow.nonExpandable.includes(key);
+
+ return [
+ ,
+ expanded ?
+ { expandRow.renderer(props.row) }
+ : null
+ ];
+ };
+ return props => (
+
+ { expandRow => renderWithExpansion(props, expandRow) }
+
+ );
+};
diff --git a/packages/react-bootstrap-table2/src/row-selection/row-consumer.js b/packages/react-bootstrap-table2/src/row-selection/row-consumer.js
new file mode 100644
index 0000000..5a51534
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row-selection/row-consumer.js
@@ -0,0 +1,63 @@
+/* eslint react/prop-types: 0 */
+import React from 'react';
+import cs from 'classnames';
+import _ from '../utils';
+import SelectionContext from '../contexts/selection-context';
+
+export default (Component) => {
+ const renderWithSelection = (props, selectRow) => {
+ const key = props.value;
+ const selected = selectRow.selected.includes(key);
+ const selectable = !selectRow.nonSelectable || !selectRow.nonSelectable.includes(key);
+
+ let {
+ style,
+ className
+ } = props;
+
+ if (selected) {
+ const selectedStyle = _.isFunction(selectRow.style)
+ ? selectRow.style(props.row, props.rowIndex)
+ : selectRow.style;
+
+ const selectedClasses = _.isFunction(selectRow.classes)
+ ? selectRow.classes(props.row, props.rowIndex)
+ : selectRow.classes;
+
+ style = {
+ ...style,
+ ...selectedStyle
+ };
+ className = cs(className, selectedClasses) || undefined;
+
+ if (selectRow.bgColor) {
+ style = style || {};
+ style.backgroundColor = _.isFunction(selectRow.bgColor)
+ ? selectRow.bgColor(props.row, props.rowIndex)
+ : selectRow.bgColor;
+ }
+ }
+
+ return (
+
+ );
+ };
+
+ function withConsumer(props) {
+ return (
+
+ { selectRow => renderWithSelection(props, selectRow) }
+
+ );
+ }
+
+ withConsumer.displayName = 'WithSelectionRowConsumer';
+ return withConsumer;
+};
diff --git a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js
index cdd6bbd..3d6f023 100644
--- a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js
+++ b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js
@@ -15,6 +15,7 @@ export default class SelectionCell extends Component {
onRowSelect: PropTypes.func,
disabled: PropTypes.bool,
rowIndex: PropTypes.number,
+ tabIndex: PropTypes.number,
clickToSelect: PropTypes.bool,
selectionRenderer: PropTypes.func
}
@@ -25,9 +26,14 @@ export default class SelectionCell extends Component {
}
shouldComponentUpdate(nextProps) {
- const { selected } = this.props;
+ const shouldUpdate =
+ this.props.rowIndex !== nextProps.rowIndex ||
+ this.props.selected !== nextProps.selected ||
+ this.props.disabled !== nextProps.disabled ||
+ this.props.rowKey !== nextProps.rowKey ||
+ this.props.tabIndex !== nextProps.tabIndex;
- return nextProps.selected !== selected;
+ return shouldUpdate;
}
handleClick(e) {
@@ -56,14 +62,18 @@ export default class SelectionCell extends Component {
mode: inputType,
selected,
disabled,
+ tabIndex,
selectionRenderer
} = this.props;
+ const attrs = {};
+ if (tabIndex !== -1) attrs.tabIndex = tabIndex;
+
return (
{
({ bootstrap4 }) => (
-
+
{
selectionRenderer ? selectionRenderer({
mode: inputType,
diff --git a/packages/react-bootstrap-table2/src/row-selection/selection-header-cell-consumer.js b/packages/react-bootstrap-table2/src/row-selection/selection-header-cell-consumer.js
new file mode 100644
index 0000000..de7d3c3
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row-selection/selection-header-cell-consumer.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import SelectionContext from '../contexts/selection-context';
+
+export default Component => () => (
+
+ { selectRow => }
+
+);
diff --git a/packages/react-bootstrap-table2/src/row.js b/packages/react-bootstrap-table2/src/row.js
deleted file mode 100644
index e3254b5..0000000
--- a/packages/react-bootstrap-table2/src/row.js
+++ /dev/null
@@ -1,183 +0,0 @@
-/* eslint react/prop-types: 0 */
-/* eslint react/no-array-index-key: 0 */
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-
-import _ from './utils';
-import Cell from './cell';
-import SelectionCell from './row-selection/selection-cell';
-import ExpandCell from './row-expand/expand-cell';
-import eventDelegater from './row-event-delegater';
-import Const from './const';
-
-class Row extends eventDelegater(Component) {
- render() {
- const {
- row,
- columns,
- keyField,
- rowIndex,
- className,
- style,
- attrs,
- cellEdit,
- selected,
- selectRow,
- expanded,
- expandRow,
- selectable,
- editable: editableRow
- } = this.props;
-
- const {
- mode,
- onStart,
- EditingCell,
- ridx: editingRowIdx,
- cidx: editingColIdx,
- CLICK_TO_CELL_EDIT,
- DBCLICK_TO_CELL_EDIT,
- ...rest
- } = cellEdit;
-
- const key = _.get(row, keyField);
- const { hideSelectColumn } = selectRow;
- const { showExpandColumn } = expandRow || {};
- const trAttrs = this.delegate(attrs);
-
- return (
-
- {
- showExpandColumn ? (
-
- ) : null
- }
- {
- (selectRow.mode !== Const.ROW_SELECT_DISABLED && !hideSelectColumn)
- ? (
-
- )
- : null
- }
- {
- columns.map((column, index) => {
- if (!column.hidden) {
- const { dataField } = column;
- const content = _.get(row, dataField);
- let editable = _.isDefined(column.editable) ? column.editable : true;
- if (dataField === keyField || !editableRow) editable = false;
- if (_.isFunction(column.editable)) {
- editable = column.editable(content, row, rowIndex, index);
- }
- if (rowIndex === editingRowIdx && index === editingColIdx) {
- let editCellstyle = column.editCellStyle || {};
- let editCellclasses = column.editCellClasses;
- if (_.isFunction(column.editCellStyle)) {
- editCellstyle = column.editCellStyle(content, row, rowIndex, index);
- }
- if (_.isFunction(column.editCellClasses)) {
- editCellclasses = column.editCellClasses(content, row, rowIndex, index);
- }
- return (
-
- );
- }
- // render cell
- let cellTitle;
- let cellStyle = {};
- const cellAttrs = {
- ..._.isFunction(column.attrs)
- ? column.attrs(content, row, rowIndex, index)
- : column.attrs,
- ...column.events
- };
-
- const cellClasses = _.isFunction(column.classes)
- ? column.classes(content, row, rowIndex, index)
- : column.classes;
-
- if (column.style) {
- cellStyle = _.isFunction(column.style)
- ? column.style(content, row, rowIndex, index)
- : column.style;
- cellStyle = Object.assign({}, cellStyle) || {};
- }
-
-
- if (column.title) {
- cellTitle = _.isFunction(column.title)
- ? column.title(content, row, rowIndex, index)
- : content;
- cellAttrs.title = cellTitle;
- }
-
- if (column.align) {
- cellStyle.textAlign =
- _.isFunction(column.align)
- ? column.align(content, row, rowIndex, index)
- : column.align;
- }
-
- if (cellClasses) cellAttrs.className = cellClasses;
- if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
-
- return (
- |
- );
- }
- return false;
- })
- }
-
- );
- }
-}
-
-Row.propTypes = {
- row: PropTypes.object.isRequired,
- rowIndex: PropTypes.number.isRequired,
- columns: PropTypes.array.isRequired,
- style: PropTypes.object,
- className: PropTypes.string,
- attrs: PropTypes.object
-};
-
-Row.defaultProps = {
- editable: true,
- style: {},
- className: null,
- attrs: {}
-};
-
-export default Row;
diff --git a/packages/react-bootstrap-table2/src/row/aggregate-row.js b/packages/react-bootstrap-table2/src/row/aggregate-row.js
new file mode 100644
index 0000000..32c997a
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row/aggregate-row.js
@@ -0,0 +1,117 @@
+/* eslint react/prop-types: 0 */
+/* eslint no-plusplus: 0 */
+import React from 'react';
+import PropTypes from 'prop-types';
+import _ from '../utils';
+import ExpandCell from '../row-expand/expand-cell';
+import SelectionCell from '../row-selection/selection-cell';
+import shouldUpdater from './should-updater';
+import eventDelegater from './event-delegater';
+import RowPureContent from './row-pure-content';
+
+export default class RowAggregator extends shouldUpdater(eventDelegater(React.Component)) {
+ static propTypes = {
+ attrs: PropTypes.object,
+ style: PropTypes.object
+ }
+
+ static defaultProps = {
+ attrs: {},
+ style: {}
+ }
+
+ constructor(props) {
+ super(props);
+ this.clickNum = 0;
+ this.shouldUpdateRowContent = false;
+ this.createClickEventHandler = this.createClickEventHandler.bind(this);
+ }
+
+ shouldComponentUpdate(nextProps) {
+ if (
+ this.props.selected !== nextProps.selected ||
+ this.props.expanded !== nextProps.expanded ||
+ this.props.selectable !== nextProps.selectable ||
+ this.shouldUpdatedBySelfProps(nextProps)
+ ) {
+ this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
+ return true;
+ }
+ this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
+
+ return this.shouldUpdateRowContent;
+ }
+
+ render() {
+ const {
+ row,
+ columns,
+ keyField,
+ rowIndex,
+ style,
+ className,
+ attrs,
+ selectRow,
+ expandRow,
+ expanded,
+ selected,
+ selectable,
+ visibleColumnSize,
+ tabIndexCell,
+ ...rest
+ } = this.props;
+ const key = _.get(row, keyField);
+ const { hideSelectColumn, clickToSelect } = selectRow;
+ const { showExpandColumn } = expandRow;
+
+ const newAttrs = this.delegate({ ...attrs });
+ if (clickToSelect || !!expandRow.renderer) {
+ newAttrs.onClick = this.createClickEventHandler(newAttrs.onClick);
+ }
+
+ let tabIndexStart = (rowIndex * visibleColumnSize) + 1;
+
+ return (
+
+ {
+ showExpandColumn ? (
+
+ ) : null
+ }
+ {
+ !hideSelectColumn
+ ? (
+
+ )
+ : null
+ }
+
+
+ );
+ }
+}
diff --git a/packages/react-bootstrap-table2/src/row/event-delegater.js b/packages/react-bootstrap-table2/src/row/event-delegater.js
new file mode 100644
index 0000000..0a17aa2
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row/event-delegater.js
@@ -0,0 +1,83 @@
+import _ from '../utils';
+import Const from '../const';
+
+const events = [
+ 'onClick',
+ 'onDoubleClick',
+ 'onMouseEnter',
+ 'onMouseLeave',
+ 'onContextMenu'
+];
+
+export default ExtendBase =>
+ class RowEventDelegater extends ExtendBase {
+ constructor(props) {
+ super(props);
+ this.clickNum = 0;
+ this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
+ this.createClickEventHandler = this.createClickEventHandler.bind(this);
+ }
+
+ createClickEventHandler(cb) {
+ return (e) => {
+ const {
+ row,
+ selected,
+ keyField,
+ selectable,
+ expandable,
+ rowIndex,
+ expanded,
+ expandRow,
+ selectRow,
+ DELAY_FOR_DBCLICK
+ } = this.props;
+ const clickFn = () => {
+ if (cb) {
+ cb(e, row, rowIndex);
+ }
+ const key = _.get(row, keyField);
+ if (expandRow && expandable && !expandRow.expandByColumnOnly) {
+ if (
+ (selectRow.mode !== Const.ROW_SELECT_DISABLED && selectRow.clickToExpand) ||
+ selectRow.mode === Const.ROW_SELECT_DISABLED
+ ) {
+ expandRow.onRowExpand(key, !expanded, rowIndex, e);
+ }
+ }
+ if (selectRow.clickToSelect && selectable) {
+ selectRow.onRowSelect(key, !selected, rowIndex, e);
+ }
+ };
+
+ if (DELAY_FOR_DBCLICK) {
+ this.clickNum += 1;
+ _.debounce(() => {
+ if (this.clickNum === 1) {
+ clickFn();
+ }
+ this.clickNum = 0;
+ }, DELAY_FOR_DBCLICK)();
+ } else {
+ clickFn();
+ }
+ };
+ }
+
+ createDefaultEventHandler(cb) {
+ return (e) => {
+ const { row, rowIndex } = this.props;
+ cb(e, row, rowIndex);
+ };
+ }
+
+ delegate(attrs = {}) {
+ const newAttrs = { ...attrs };
+ Object.keys(attrs).forEach((attr) => {
+ if (events.includes(attr)) {
+ newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
+ }
+ });
+ return newAttrs;
+ }
+ };
diff --git a/packages/react-bootstrap-table2/src/row/row-pure-content.js b/packages/react-bootstrap-table2/src/row/row-pure-content.js
new file mode 100644
index 0000000..5b498ff
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row/row-pure-content.js
@@ -0,0 +1,116 @@
+/* eslint react/prop-types: 0 */
+/* eslint react/no-array-index-key: 0 */
+/* eslint no-plusplus: 0 */
+import React from 'react';
+
+import _ from '../utils';
+import Cell from '../cell';
+
+export default class RowPureContent extends React.Component {
+ shouldComponentUpdate(nextProps) {
+ if (typeof nextProps.shouldUpdate !== 'undefined') {
+ return nextProps.shouldUpdate;
+ }
+ return true;
+ }
+
+ render() {
+ const {
+ row,
+ keyField,
+ columns,
+ rowIndex,
+ editable,
+ editingRowIdx,
+ editingColIdx,
+ onStart,
+ clickToEdit,
+ dbclickToEdit,
+ EditingCellComponent,
+ tabIndexStart
+ } = this.props;
+
+ let tabIndex = tabIndexStart;
+
+ return columns.map((column, index) => {
+ if (!column.hidden) {
+ const { dataField } = column;
+ const content = _.get(row, dataField);
+ if (rowIndex === editingRowIdx && index === editingColIdx) {
+ return (
+
+ );
+ }
+ // render cell
+ let cellTitle;
+ let cellStyle = {};
+ const cellAttrs = {
+ ..._.isFunction(column.attrs)
+ ? column.attrs(content, row, rowIndex, index)
+ : column.attrs,
+ ...column.events
+ };
+
+ const cellClasses = _.isFunction(column.classes)
+ ? column.classes(content, row, rowIndex, index)
+ : column.classes;
+
+ if (column.style) {
+ cellStyle = _.isFunction(column.style)
+ ? column.style(content, row, rowIndex, index)
+ : column.style;
+ cellStyle = Object.assign({}, cellStyle) || {};
+ }
+
+ if (column.title) {
+ cellTitle = _.isFunction(column.title)
+ ? column.title(content, row, rowIndex, index)
+ : content;
+ cellAttrs.title = cellTitle;
+ }
+
+ if (column.align) {
+ cellStyle.textAlign =
+ _.isFunction(column.align)
+ ? column.align(content, row, rowIndex, index)
+ : column.align;
+ }
+
+ if (cellClasses) cellAttrs.className = cellClasses;
+ if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
+
+ let editableCell = _.isDefined(column.editable) ? column.editable : true;
+ if (column.dataField === keyField || !editable) editableCell = false;
+ if (_.isFunction(column.editable)) {
+ editableCell = column.editable(content, row, rowIndex, index);
+ }
+
+ if (tabIndexStart !== -1) {
+ cellAttrs.tabIndex = tabIndex++;
+ }
+
+ return (
+ |
+ );
+ }
+ return false;
+ });
+ }
+}
diff --git a/packages/react-bootstrap-table2/src/row-section.js b/packages/react-bootstrap-table2/src/row/row-section.js
similarity index 100%
rename from packages/react-bootstrap-table2/src/row-section.js
rename to packages/react-bootstrap-table2/src/row/row-section.js
diff --git a/packages/react-bootstrap-table2/src/row/should-updater.js b/packages/react-bootstrap-table2/src/row/should-updater.js
new file mode 100644
index 0000000..7756b73
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row/should-updater.js
@@ -0,0 +1,37 @@
+/* eslint react/prop-types: 0 */
+import _ from '../utils';
+
+export default ExtendBase =>
+ class RowShouldUpdater extends ExtendBase {
+ shouldUpdateByCellEditing(nextProps) {
+ if (!(this.props.clickToEdit || this.props.dbclickToEdit)) return false;
+ return (
+ nextProps.editingRowIdx === nextProps.rowIndex ||
+ (this.props.editingRowIdx === nextProps.rowIndex &&
+ nextProps.editingRowIdx === null)
+ );
+ }
+
+ shouldUpdatedBySelfProps(nextProps) {
+ return (
+ this.props.className !== nextProps.className ||
+ !_.isEqual(this.props.style, nextProps.style) ||
+ !_.isEqual(this.props.attrs, nextProps.attrs)
+ );
+ }
+
+ shouldUpdatedByNormalProps(nextProps) {
+ const shouldUpdate =
+ this.props.rowIndex !== nextProps.rowIndex ||
+ this.props.editable !== nextProps.editable ||
+ !_.isEqual(this.props.row, nextProps.row) ||
+ this.props.columns.length !== nextProps.columns.length;
+
+ return shouldUpdate;
+ }
+
+ shouldUpdateChild(nextProps) {
+ return this.shouldUpdateByCellEditing(nextProps) ||
+ this.shouldUpdatedByNormalProps(nextProps);
+ }
+ };
diff --git a/packages/react-bootstrap-table2/src/row/simple-row.js b/packages/react-bootstrap-table2/src/row/simple-row.js
new file mode 100644
index 0000000..83aff66
--- /dev/null
+++ b/packages/react-bootstrap-table2/src/row/simple-row.js
@@ -0,0 +1,64 @@
+/* eslint react/prop-types: 0 */
+/* eslint react/no-array-index-key: 0 */
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+
+import RowPureContent from './row-pure-content';
+import eventDelegater from './event-delegater';
+import shouldUpdater from './should-updater';
+
+class SimpleRow extends shouldUpdater(eventDelegater(Component)) {
+ constructor(props) {
+ super(props);
+ this.shouldUpdateRowContent = false;
+ }
+
+ shouldComponentUpdate(nextProps) {
+ this.shouldUpdateRowContent = false;
+ this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
+ if (this.shouldUpdateRowContent) return true;
+
+ return this.shouldUpdatedBySelfProps(nextProps);
+ }
+
+ render() {
+ const {
+ className,
+ style,
+ attrs,
+ visibleColumnSize,
+ tabIndexCell,
+ ...rest
+ } = this.props;
+ const trAttrs = this.delegate(attrs);
+ const tabIndexStart = (this.props.rowIndex * visibleColumnSize) + 1;
+
+ return (
+
+
+
+ );
+ }
+}
+
+SimpleRow.propTypes = {
+ row: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number.isRequired,
+ columns: PropTypes.array.isRequired,
+ style: PropTypes.object,
+ className: PropTypes.string,
+ attrs: PropTypes.object
+};
+
+SimpleRow.defaultProps = {
+ editable: true,
+ style: {},
+ className: null,
+ attrs: {}
+};
+
+export default SimpleRow;
diff --git a/packages/react-bootstrap-table2/src/store/selection.js b/packages/react-bootstrap-table2/src/store/selection.js
index 135651d..af6a8c1 100644
--- a/packages/react-bootstrap-table2/src/store/selection.js
+++ b/packages/react-bootstrap-table2/src/store/selection.js
@@ -6,7 +6,7 @@ export const getSelectionSummary = (
keyField,
selected = []
) => {
- let allRowsSelected = true;
+ let allRowsSelected = data.length > 0;
let allRowsNotSelected = true;
const rowKeys = data.map(d => d[keyField]);
diff --git a/packages/react-bootstrap-table2/test/body.test.js b/packages/react-bootstrap-table2/test/body.test.js
index ea624e5..764af39 100644
--- a/packages/react-bootstrap-table2/test/body.test.js
+++ b/packages/react-bootstrap-table2/test/body.test.js
@@ -1,11 +1,15 @@
+import 'jsdom-global/register';
import React from 'react';
import sinon from 'sinon';
-import { shallow } from 'enzyme';
+import { shallow, mount } from 'enzyme';
import Body from '../src/body';
-import Row from '../src/row';
+import Row from '../src/row/simple-row';
+import RowAggregator from '../src/row/aggregate-row';
import Const from '../src/const';
-import RowSection from '../src/row-section';
+import RowSection from '../src/row/row-section';
+import SelectionContext from '../src/contexts/selection-context';
+import ExpansionContext from '../src/contexts/row-expand-context';
import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props';
describe('Body', () => {
@@ -169,92 +173,6 @@ describe('Body', () => {
});
});
});
-
- describe('when selectRow.style is defined', () => {
- const selectedRowKey = data[0][keyField];
- const selectedRowKeys = [selectedRowKey];
- const selectedStyle = { backgroundColor: 'green', fontWeight: 'bold' };
- const selectRow = { mode: 'radio', style: selectedStyle };
-
- beforeEach(() => {
- wrapper = shallow(
- );
- });
-
- it('should rendering selected Row component with mixing selectRow.style correctly', () => {
- const selectedRow = wrapper.find(Row).get(0);
- expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
- ...rowStyle,
- ...selectedStyle
- }));
- });
-
- describe('and selectRow.bgColor is also defined', () => {
- beforeEach(() => {
- selectRow.bgColor = 'gray';
- wrapper = shallow(
- );
- });
-
- it('should rendering selected Row component with mixing selectRow.style correctly', () => {
- const selectedRow = wrapper.find(Row).get(0);
- expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
- ...rowStyle,
- ...selectedStyle,
- backgroundColor: selectRow.bgColor
- }));
- });
-
- it('should render selected Row component with correct style.backgroundColor', () => {
- const selectedRow = wrapper.find(Row).get(0);
- expect(selectedRow.props.style.backgroundColor).toEqual(selectRow.bgColor);
- });
- });
- });
-
- describe('when selectRow.bgColor is defined', () => {
- const selectedRowKey = data[0][keyField];
- const selectedRowKeys = [selectedRowKey];
- const selectRow = { mode: 'radio', bgColor: 'gray' };
-
- beforeEach(() => {
- selectRow.bgColor = 'gray';
- wrapper = shallow(
- );
- });
-
- it('should rendering selected Row component with correct style', () => {
- const selectedRow = wrapper.find(Row).get(0);
- expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
- ...rowStyle,
- backgroundColor: selectRow.bgColor
- }));
- });
- });
});
describe('when rowClasses prop is defined', () => {
@@ -310,31 +228,6 @@ describe('Body', () => {
});
});
});
-
- describe('when selectRow.classes is defined', () => {
- const selectedRowKey = data[0][keyField];
- const selectedRowKeys = [selectedRowKey];
- const selectedClasses = 'selected-classes';
- const selectRow = { mode: 'radio', classes: selectedClasses };
-
- beforeEach(() => {
- wrapper = shallow(
- );
- });
-
- it('should rendering selected Row component with mixing selectRow.classes correctly', () => {
- const selectedRow = wrapper.find(Row).get(0);
- expect(selectedRow.props.className).toBe(`${rowClasses} ${selectedClasses}`);
- });
- });
});
describe('when rowEvents prop is defined', () => {
@@ -361,11 +254,14 @@ describe('Body', () => {
});
});
- describe('when cellEdit.nonEditableRows props is defined', () => {
- const nonEditableRows = [data[1].id];
+ describe('when cellEdit.createContext props is defined', () => {
+ const EditingCellComponent = () => null;
+ const RowComponent = props =>
;
const cellEdit = {
- mode: Const.CLICK_TO_CELL_EDIT,
- nonEditableRows
+ options: { onStartEdit: jest.fn() },
+ createContext: jest.fn(),
+ createEditingCell: jest.fn().mockReturnValue(EditingCellComponent),
+ withRowLevelCellEdit: jest.fn().mockReturnValue(RowComponent)
};
beforeEach(() => {
wrapper = shallow(
@@ -379,259 +275,82 @@ describe('Body', () => {
);
});
- it('should render Row component with correct editable prop', () => {
+ it('should render Row Component correctly', () => {
expect(wrapper.length).toBe(1);
- const rows = wrapper.find(Row);
- for (let i = 0; i < rows.length; i += 1) {
- if (nonEditableRows.indexOf(rows.get(i).props.row[keyField]) > -1) {
- expect(rows.get(i).props.editable).toBeFalsy();
- } else {
- expect(rows.get(i).props.editable).toBeTruthy();
- }
- }
+ expect(cellEdit.createEditingCell).toHaveBeenCalledTimes(1);
+ expect(cellEdit.withRowLevelCellEdit).toHaveBeenCalledTimes(1);
+ expect(wrapper.find(RowComponent)).toHaveLength(2);
+ const aRowElement = wrapper.find(RowComponent).get(0);
+ expect(aRowElement.props.EditingCellComponent).toBeDefined();
});
});
- describe('when selectRow.mode is checkbox or radio (row was selectable)', () => {
+ describe('when selectRow.mode is ROW_SELECT_DISABLED or expandRow.renderer is undefined', () => {
+ beforeEach(() => {
+ wrapper = shallow(
+
+ );
+ });
+
+ it('shouldn\'t render RowAggregator component', () => {
+ expect(wrapper.find(RowAggregator)).toHaveLength(0);
+ });
+ });
+
+ describe('when selectRow.mode is defined correctly', () => {
const selectRow = { mode: 'checkbox' };
- const selectedRowKey = data[0][keyField];
- const selectedRowKeys = [selectedRowKey];
beforeEach(() => {
- wrapper = shallow(
-
+ wrapper = mount(
+
+
+
);
});
- it('should render Row component with correct selected prop', () => {
- const rows = wrapper.find(Row);
- for (let i = 0; i < rows.length; i += 1) {
- const row = rows.get(i);
- expect(row.props.selected).toBe(selectedRowKeys.indexOf(row.props.row[keyField]) > -1);
- }
- });
+ it('should render RowAggregator component correctly', () => {
+ const rowAggregator = wrapper.find(RowAggregator);
- describe('if selectRow.style is defined as an object', () => {
- const style = { backgroundColor: 'red' };
-
- beforeEach(() => {
- selectRow.style = style;
- wrapper = shallow(
-
- );
- });
-
- it('should render Row component with correct style prop', () => {
- expect(JSON.stringify(wrapper.find(Row).get(0).props.style)).toBe(JSON.stringify(style));
- });
- });
-
- describe('if selectRow.style is defined as a function', () => {
- const style = { backgroundColor: 'red' };
- const styleCallBack = sinon.stub().returns(style);
-
- beforeEach(() => {
- selectRow.style = styleCallBack;
- wrapper = shallow(
-