diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index b4ca400..7b29989 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -7,6 +7,7 @@ import cs from 'classnames'; import _ from './utils'; import Row from './row'; +import ExpandRow from './row-expand/expand-row'; import RowSection from './row-section'; import Const from './const'; @@ -23,7 +24,8 @@ const Body = (props) => { selectedRowKeys, rowStyle, rowClasses, - rowEvents + rowEvents, + expandRow } = props; const { @@ -74,8 +76,10 @@ const Body = (props) => { } const selectable = !nonSelectable || !nonSelectable.includes(key); + const expandable = expandRow && !expandRow.nonExpandable.includes(key); + const expanded = expandRow && expandRow.expanded.includes(key); - return ( + const result = [ { cellEdit={ cellEdit } editable={ editable } selectable={ selectable } + expandable={ expandable } selected={ selected } + expanded={ expanded } selectRow={ selectRow } + expandRow={ expandRow } style={ style } className={ classes } attrs={ attrs } /> - ); + ]; + + if (expanded) { + result.push(( + + { expandRow.renderer(row) } + + )); + } + + return result; }); } diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 5f8e5c4..7f3a16c 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -97,6 +97,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { cellEdit={ this.props.cellEdit || {} } selectRow={ cellSelectionInfo } selectedRowKeys={ selected } + expandRow={ this.resolveExpandRowProps() } rowStyle={ rowStyle } rowClasses={ rowClasses } rowEvents={ rowEvents } @@ -145,6 +146,14 @@ BootstrapTable.propTypes = { }), onRowSelect: PropTypes.func, onAllRowsSelect: PropTypes.func, + expandRow: PropTypes.shape({ + renderer: PropTypes.func.isRequired, + expanded: PropTypes.array, + nonExpandable: PropTypes.array, + showExpandColumn: PropTypes.bool, + expandColumnRenderer: PropTypes.func + }), + onRowExpand: PropTypes.func, rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), rowEvents: PropTypes.object, rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js index 031d699..d434478 100644 --- a/packages/react-bootstrap-table2/src/contexts/index.js +++ b/packages/react-bootstrap-table2/src/contexts/index.js @@ -5,6 +5,7 @@ import _ from '../utils'; import createDataContext from './data-context'; import createSortContext from './sort-context'; import createSelectionContext from './selection-context'; +import createRowExpandContext from './row-expand-context'; import remoteResolver from '../props-resolver/remote-resolver'; import dataOperator from '../store/operators'; @@ -23,6 +24,10 @@ const withContext = Base => this.SelectionContext = createSelectionContext(dataOperator); } + if (props.expandRow) { + this.RowExpandContext = createRowExpandContext(dataOperator); + } + if (props.cellEdit && props.cellEdit.createContext) { this.CellEditContext = props.cellEdit.createContext( _, dataOperator, this.isRemoteCellEdit, this.handleRemoteCellChange); @@ -52,6 +57,7 @@ const withContext = Base => searchProps, sortProps, paginationProps, + expandProps, selectionProps ) => ( { ...filterProps } { ...searchProps } { ...paginationProps } + { ...expandProps } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } /> ); @@ -74,7 +81,8 @@ const withContext = Base => filterProps, searchProps, sortProps, - paginationProps + paginationProps, + expandProps ) => ( searchProps, sortProps, paginationProps, + expandProps, selectionProps ) } @@ -98,6 +107,37 @@ const withContext = Base => ); } + renderWithRowExpandCtx(base, baseProps) { + return ( + rootProps, + cellEditProps, + filterProps, + searchProps, + sortProps, + paginationProps + ) => ( + + + { + expandProps => base( + rootProps, + cellEditProps, + filterProps, + searchProps, + sortProps, + paginationProps, + expandProps + ) + } + + + ); + } + renderWithPaginationCtx(base) { return ( rootProps, @@ -232,6 +272,10 @@ const withContext = Base => base = this.renderWithSelectionCtx(base, baseProps); } + if (this.RowExpandContext) { + base = this.renderWithRowExpandCtx(base, baseProps); + } + if (this.PaginationContext) { base = this.renderWithPaginationCtx(base, baseProps); } diff --git a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js new file mode 100644 index 0000000..d43d55a --- /dev/null +++ b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js @@ -0,0 +1,62 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; +import PropTypes from 'prop-types'; + +export default ( + dataOperator +) => { + const RowExpandContext = React.createContext(); + + 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 || [] }; + + componentWillReceiveProps(nextProps) { + if (nextProps.expandRow) { + this.setState(() => ({ + expanded: nextProps.expandRow.expanded || this.state.expanded + })); + } + } + + handleRowExpand = (rowKey, expanded, rowIndex, e) => { + const { data, keyField, expandRow: { onExpand } } = this.props; + + let currExpanded = [...this.state.expanded]; + + if (expanded) { + 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 })); + } + + render() { + return ( + + { this.props.children } + + ); + } + } + return { + Provider: RowExpandProvider, + Consumer: RowExpandContext.Consumer + }; +}; diff --git a/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js index c330c7d..11fb652 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js +++ b/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js @@ -1,10 +1,13 @@ export default ExtendBase => class ColumnResolver extends ExtendBase { visibleColumnSize(includeSelectColumn = true) { - const columnLen = this.props.columns.filter(c => !c.hidden).length; + let columnLen = this.props.columns.filter(c => !c.hidden).length; if (!includeSelectColumn) return columnLen; if (this.props.selectRow && !this.props.selectRow.hideSelectColumn) { - return columnLen + 1; + columnLen += 1; + } + if (this.props.expandRow && this.props.expandRow.showExpandColumn) { + columnLen += 1; } return columnLen; } 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 new file mode 100644 index 0000000..c959776 --- /dev/null +++ b/packages/react-bootstrap-table2/src/props-resolver/expand-row-resolver.js @@ -0,0 +1,15 @@ +export default ExtendBase => + class ExpandRowResolver extends ExtendBase { + resolveExpandRowProps() { + const { expandRow, expanded, onRowExpand } = this.props; + if (expandRow) { + return { + ...expandRow, + expanded, + onRowExpand, + 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 be81f50..2154ce3 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/index.js +++ b/packages/react-bootstrap-table2/src/props-resolver/index.js @@ -1,9 +1,11 @@ import ColumnResolver from './column-resolver'; +import ExpandRowResolver from './expand-row-resolver'; import Const from '../const'; import _ from '../utils'; export default ExtendBase => - class TableResolver extends ColumnResolver(ExtendBase) { + class TableResolver extends + ExpandRowResolver(ColumnResolver(ExtendBase)) { validateProps() { const { keyField } = this.props; if (!keyField) { diff --git a/packages/react-bootstrap-table2/src/row-event-delegater.js b/packages/react-bootstrap-table2/src/row-event-delegater.js index 60d8a62..283ffd6 100644 --- a/packages/react-bootstrap-table2/src/row-event-delegater.js +++ b/packages/react-bootstrap-table2/src/row-event-delegater.js @@ -1,4 +1,5 @@ import _ from './utils'; +import Const from './const'; const events = [ 'onClick', @@ -30,11 +31,11 @@ export default ExtendBase => selected, keyField, selectable, + expandable, rowIndex, - selectRow: { - onRowSelect, - clickToEdit - }, + expanded, + expandRow, + selectRow, cellEdit: { mode, DBCLICK_TO_CELL_EDIT, @@ -46,13 +47,16 @@ export default ExtendBase => if (cb) { cb(e, row, rowIndex); } - if (selectable) { - const key = _.get(row, keyField); - onRowSelect(key, !selected, rowIndex, e); + 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 && clickToEdit) { + if (mode === DBCLICK_TO_CELL_EDIT && selectRow.clickToEdit) { this.clickNum += 1; _.debounce(() => { if (this.clickNum === 1) { @@ -68,7 +72,8 @@ export default ExtendBase => delegate(attrs = {}) { const newAttrs = {}; - if (this.props.selectRow && this.props.selectRow.clickToSelect) { + const { expandRow, selectRow } = this.props; + if (expandRow || (selectRow && selectRow.clickToSelect)) { newAttrs.onClick = this.createClickEventHandler(attrs.onClick); } Object.keys(attrs).forEach((attr) => { diff --git a/packages/react-bootstrap-table2/src/row-expand/expand-row.js b/packages/react-bootstrap-table2/src/row-expand/expand-row.js new file mode 100644 index 0000000..0a89628 --- /dev/null +++ b/packages/react-bootstrap-table2/src/row-expand/expand-row.js @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ExpandRow = ({ children, ...rest }) => ( + + { children } + +); + +ExpandRow.propTypes = { + children: PropTypes.node +}; + +ExpandRow.defaultProps = { + children: null +}; + +export default ExpandRow; diff --git a/packages/react-bootstrap-table2/style/react-bootstrap-table2.scss b/packages/react-bootstrap-table2/style/react-bootstrap-table2.scss index 2476781..55a1a09 100644 --- a/packages/react-bootstrap-table2/style/react-bootstrap-table2.scss +++ b/packages/react-bootstrap-table2/style/react-bootstrap-table2.scss @@ -30,6 +30,10 @@ text-align: center; } + tr.expanding-row { + padding: 5px; + } + td.react-bootstrap-table-editing-cell { .animated { animation-fill-mode: both;