- Data table sticky header.

- Mini sidebar toggle.
- Refactor withDashboard and withDashboardActions.
This commit is contained in:
Ahmed Bouhuolia
2020-05-31 15:57:02 +02:00
parent c1659d191f
commit 2a466ce2da
49 changed files with 1045 additions and 669 deletions

View File

@@ -1,4 +1,4 @@
import React, {useEffect, useRef, useCallback} from 'react';
import React, { useEffect, useRef, useCallback } from 'react';
import {
useTable,
useExpanded,
@@ -6,19 +6,22 @@ import {
usePagination,
useResizeColumns,
useSortBy,
useFlexLayout
useFlexLayout,
} from 'react-table';
import { Checkbox, Spinner } from '@blueprintjs/core';
import classnames from 'classnames';
import { FixedSizeList } from 'react-window'
import { FixedSizeList } from 'react-window';
import { useSticky } from 'react-table-sticky';
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
import { ConditionalWrapper } from 'utils';
import { useUpdateEffect } from 'hooks';
import { If } from 'components';
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
return (<Checkbox indeterminate={indeterminate} {...rest} />);
}
return <Checkbox indeterminate={indeterminate} {...rest} />;
},
);
export default function DataTable({
@@ -34,11 +37,11 @@ export default function DataTable({
noResults = 'This report does not contain any data.',
expanded = {},
rowClassNames,
stickyHeader = true,
sticky = false,
virtualizedRows = false,
fixedSizeHeight = 100,
fixedItemSize = 30,
payload,
payload,
expandable = false,
expandToggleColumn = 2,
noInitialFetch = false,
@@ -54,9 +57,10 @@ export default function DataTable({
selectedFlatRows,
getToggleAllRowsExpandedProps,
isAllRowsExpanded,
totalColumnsWidth,
// Get the state from the instance
state: { pageIndex, pageSize, sortBy, selectedRowIds},
state: { pageIndex, pageSize, sortBy, selectedRowIds },
} = useTable(
{
columns,
@@ -67,7 +71,7 @@ export default function DataTable({
// This means we'll also have to provide our own
// pageCount.
// pageCount: controlledPageCount,
getSubRows: row => row.children,
getSubRows: (row) => row.children,
manualSortBy,
expandSubRows,
payload,
@@ -79,111 +83,150 @@ export default function DataTable({
usePagination,
useResizeColumns,
useFlexLayout,
hooks => {
hooks.visibleColumns.push(columns => [
useSticky,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
...(selectionColumn) ? [{
id: 'selection',
disableResizing: true,
minWidth: 42,
width: 42,
maxWidth: 42,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
className: 'selection',
...(typeof selectionColumn === 'object') ? selectionColumn : {},
}] : [],
...(selectionColumn
? [
{
id: 'selection',
disableResizing: true,
minWidth: 42,
width: 42,
maxWidth: 42,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox
{...getToggleAllRowsSelectedProps()}
/>
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox
{...row.getToggleRowSelectedProps()}
/>
</div>
),
className: 'selection',
...(typeof selectionColumn === 'object' ? selectionColumn : {}),
},
]
: []),
...columns,
])
}
]);
},
);
const isInitialMount = useRef(noInitialFetch);
// When these table states change, fetch new data!
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
onFetchData && onFetchData({ pageIndex, pageSize, sortBy })
onFetchData && onFetchData({ pageIndex, pageSize, sortBy });
}
}, [pageIndex, pageSize, sortBy,onFetchData]);
}, [pageIndex, pageSize, sortBy, onFetchData]);
useUpdateEffect(() => {
onSelectedRowsChange && onSelectedRowsChange(selectedFlatRows);
}, [selectedRowIds, onSelectedRowsChange]);
// Renders table cell.
const RenderCell = useCallback(({ row, cell, index }) => (
<ConditionalWrapper
condition={expandToggleColumn === index && expandable}
wrapper={(children) => (<div style={{
'padding-left': `${row.depth * 1.5}rem`,
}}>{children}</div>)}
>
{
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
(row.canExpand && expandable && index === expandToggleColumn) && (
<span {...row.getToggleRowExpandedProps({
className: 'expand-toggle',
})}>
<span className={classnames({
'arrow-down': row.isExpanded,
'arrow-right': !row.isExpanded,
})} />
</span>
)
}
{ cell.render('Cell') }
</ConditionalWrapper>
), [expandable, expandToggleColumn]);
const RenderCell = useCallback(
({ row, cell, index }) => (
<ConditionalWrapper
condition={expandToggleColumn === index && expandable}
wrapper={(children) => (
<div
style={{
'padding-left': `${row.depth * 1.5}rem`,
}}
>
{children}
</div>
)}
>
{
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand && expandable && index === expandToggleColumn && (
<span
{...row.getToggleRowExpandedProps({
className: 'expand-toggle',
})}
>
<span
className={classnames({
'arrow-down': row.isExpanded,
'arrow-right': !row.isExpanded,
})}
/>
</span>
)
}
{cell.render('Cell')}
</ConditionalWrapper>
),
[expandable, expandToggleColumn],
);
// Renders table row.
const RenderRow = useCallback(({ style = {}, row }) => {
prepareRow(row);
const rowClasses = rowClassNames && rowClassNames(row);
const RenderRow = useCallback(
({ style = {}, row }) => {
prepareRow(row);
const rowClasses = rowClassNames && rowClassNames(row);
return (
<div {...row.getRowProps({
className: classnames('tr', rowClasses), style
})}>
{row.cells.map((cell, i) => {
const index = i + 1;
return <div {...cell.getCellProps({
className: classnames(cell.column.className || '', 'td'),
})}>
{ RenderCell({ cell, row, index }) }
</div>
})}
</div>);
}, [prepareRow, rowClassNames, expandable, RenderCell, expandToggleColumn]);
return (
<div
{...row.getRowProps({
className: classnames('tr', rowClasses),
style,
})}
>
{row.cells.map((cell, i) => {
const index = i + 1;
return (
<div
{...cell.getCellProps({
className: classnames(cell.column.className || '', 'td'),
})}
>
{RenderCell({ cell, row, index })}
</div>
);
})}
</div>
);
},
[prepareRow, rowClassNames, expandable, RenderCell, expandToggleColumn],
);
// Renders virtualize circle table rows.
const RenderVirtualizedRows = useCallback(({ index, style }) => {
const row = rows[index];
return RenderRow({ row, style });
}, [RenderRow, rows]);
const RenderVirtualizedRows = useCallback(
({ index, style }) => {
const row = rows[index];
return RenderRow({ row, style });
},
[RenderRow, rows],
);
const RenderPage = useCallback(({ style, index } = {}) => {
return page.map((row, index) => RenderRow({ row }));
}, [RenderRow, page]);
const RenderPage = useCallback(
({ style, index } = {}) => {
return page.map((row, index) => RenderRow({ row }));
},
[RenderRow, page],
);
// Renders fixed size tbody.
const RenderTBody = useCallback(() => {
return (virtualizedRows) ? (
return virtualizedRows ? (
<FixedSizeList
height={fixedSizeHeight}
itemCount={rows.length}
@@ -191,79 +234,109 @@ export default function DataTable({
>
{RenderVirtualizedRows}
</FixedSizeList>
) : RenderPage();
}, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows,
RenderVirtualizedRows, RenderPage]);
) : (
RenderPage()
);
}, [
fixedSizeHeight,
rows,
fixedItemSize,
virtualizedRows,
RenderVirtualizedRows,
RenderPage,
]);
return (
<div className={classnames(
'bigcapital-datatable',
className,
{
'has-sticky-header': stickyHeader,
<div
className={classnames('bigcapital-datatable', className, {
'has-sticky': sticky,
'is-expandable': expandable,
'has-virtualized-rows': virtualizedRows,
})}>
<div {...getTableProps()} className="table">
<div className="thead">
{headerGroups.map(headerGroup => (
<div {...headerGroup.getHeaderGroupProps()} className="tr">
{headerGroup.headers.map((column, index) => (
<div {...column.getHeaderProps({
className: classnames(column.className || '', 'th'),
})}>
{(expandable && (index + 1) === expandToggleColumn) && (
<span
{...getToggleAllRowsExpandedProps()}
className="expand-toggle">
<span className={classnames({
'arrow-down': isAllRowsExpanded,
'arrow-right': !isAllRowsExpanded,
})} />
</span>)}
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
{column.isSorted && (
<span className={classnames({
'sort-icon--desc': column.isSortedDesc,
'sort-icon--asc': !column.isSortedDesc,
}, 'sort-icon')}>
</span>
)}
</div>
{column.canResize && (
})}
>
<ScrollSync>
<div
{...getTableProps({ style: { minWidth: 'none' } })}
className="table"
>
<ScrollSyncPane>
<div className="thead">
{headerGroups.map((headerGroup) => (
<div {...headerGroup.getHeaderGroupProps()} className="tr">
{headerGroup.headers.map((column, index) => (
<div
{...column.getResizerProps()}
className={`resizer ${
column.isResizing ? 'isResizing' : ''
}`}>
<div class="inner-resizer" />
{...column.getHeaderProps({
className: classnames(column.className || '', 'th'),
})}
>
{expandable && index + 1 === expandToggleColumn && (
<span
{...getToggleAllRowsExpandedProps()}
className="expand-toggle"
>
<span
className={classnames({
'arrow-down': isAllRowsExpanded,
'arrow-right': !isAllRowsExpanded,
})}
/>
</span>
)}
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
{column.isSorted && (
<span
className={classnames(
{
'sort-icon--desc': column.isSortedDesc,
'sort-icon--asc': !column.isSortedDesc,
},
'sort-icon',
)}
></span>
)}
</div>
{column.canResize && (
<div
{...column.getResizerProps()}
className={`resizer ${
column.isResizing ? 'isResizing' : ''
}`}
>
<div class="inner-resizer" />
</div>
)}
</div>
)}
))}
</div>
))}
</div>
))}
</div>
<div {...getTableBodyProps()} className="tbody">
<If condition={!loading}>
{ RenderTBody() }
</If>
<If condition={!loading && (page.length === 0)}>
<div className={'tr no-results'}>
<div class="td">{ noResults }</div>
</div>
</If>
</ScrollSyncPane>
<If condition={loading}>
<div class="loading"><Spinner size={spinnerProps.size} /></div>
</If>
<ScrollSyncPane>
<div {...getTableBodyProps()} className="tbody">
<div class="tbody-inner" style={{ minWidth: totalColumnsWidth }}>
<If condition={!loading}>{RenderTBody()}</If>
<If condition={!loading && page.length === 0}>
<div className={'tr no-results'}>
<div class="td">{noResults}</div>
</div>
</If>
<If condition={loading}>
<div class="loading">
<Spinner size={spinnerProps.size} />
</div>
</If>
</div>
</div>
</ScrollSyncPane>
</div>
</div>
</ScrollSync>
</div>
)
}
);
}