feat: optimize accounts performance.

feat: optimize alerts architecture.
feat: optimize datatable architecture.
feat: optimize datatable style.
This commit is contained in:
a.bouhuolia
2021-01-26 08:44:11 +02:00
parent 0655963607
commit b26f6c937c
70 changed files with 1607 additions and 1012 deletions

View File

@@ -14,7 +14,7 @@ export default function AccountsSelectList({
onAccountSelected,
disabled = false,
popoverFill = false,
filterByRootTypes = [],
filterByParentTypes = [],
filterByTypes = [],
filterByNormal,
buttonProps = {}
@@ -23,23 +23,23 @@ export default function AccountsSelectList({
const filteredAccounts = useMemo(() => {
let filteredAccounts = [...accounts];
if (!isEmpty(filterByRootTypes)) {
if (!isEmpty(filterByParentTypes)) {
filteredAccounts = filteredAccounts.filter(
(account) => filterByRootTypes.indexOf(account.type.root_type) !== -1,
(account) => filterByParentTypes.indexOf(account.account_parent_type) !== -1,
);
}
if (!isEmpty(filterByTypes)) {
filteredAccounts = filteredAccounts.filter(
(account) => filterByTypes.indexOf(account.type.key) !== -1,
(account) => filterByTypes.indexOf(account.account_type) !== -1,
);
}
if (!isEmpty(filterByNormal)) {
filteredAccounts = filteredAccounts.filter(
(account) => filterByTypes.indexOf(account.type.normal) === filterByNormal,
(account) => filterByTypes.indexOf(account.account_normal) === filterByNormal,
);
}
return filteredAccounts;
}, [accounts, filterByRootTypes, filterByTypes, filterByNormal]);
}, [accounts, filterByParentTypes, filterByTypes, filterByNormal]);
// Find initial account object to set it as default account in initial render.
const initialAccount = useMemo(

View File

@@ -119,7 +119,7 @@ export default function AccountsSuggestField({
inputProps={{ placeholder: defaultSelectText }}
resetOnClose={true}
fill={true}
popoverProps={{ minimal: true }}
popoverProps={{ minimal: true, boundary: 'window' }}
inputValueRenderer={handleInputValueRenderer}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,

View File

@@ -1,7 +1,7 @@
import { Position, Toaster, Intent } from "@blueprintjs/core";
const AppToaster = Toaster.create({
position: Position.TOP,
position: Position.RIGHT_BOTTOM,
intent: Intent.WARNING,
});

View File

@@ -94,8 +94,7 @@ export default function ContactsSuggestField({
selectedItem={selecetedContact}
inputProps={{ placeholder: defaultTextSelect }}
resetOnClose={true}
// fill={true}
popoverProps={{ minimal: true }}
popoverProps={{ minimal: true, boundary: 'window' }}
inputValueRenderer={handleInputValueRenderer}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,

View File

@@ -9,7 +9,7 @@ function DashboardSplitPane({
sidebarExpended,
children
}) {
const initialSize = 190;
const initialSize = 200;
const [defaultSize, setDefaultSize] = useState(
parseInt(localStorage.getItem('dashboard-size'), 10) || initialSize,

View File

@@ -6,6 +6,7 @@ import {
MenuDivider,
Button,
Popover,
Position,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
@@ -50,7 +51,7 @@ function DashboardTopbarUser({ requestLogout, user }) {
);
return (
<Popover content={userAvatarDropMenu}>
<Popover content={userAvatarDropMenu} position={Position.BOTTOM}>
<Button>
<div className="user-text">
{firstLettersArgs(user.first_name, user.last_name)}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useCallback, useMemo } from 'react';
import React, { useEffect, useRef } from 'react';
import {
useTable,
useExpanded,
@@ -9,104 +9,101 @@ import {
useFlexLayout,
useAsyncDebounce,
} from 'react-table';
import { Checkbox, Spinner, ContextMenu } from '@blueprintjs/core';
import classnames from 'classnames';
import { FixedSizeList } from 'react-window';
import { useSticky } from 'react-table-sticky';
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
import { useUpdateEffect } from 'hooks';
import { If, Pagination, Choose } from 'components';
import { ConditionalWrapper, saveInvoke } from 'utils';
import { saveInvoke } from 'utils';
import 'style/components/DataTable/DataTable.scss';
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
return <Checkbox indeterminate={indeterminate} {...rest} />;
},
);
import TableNoResultsRow from './Datatable/TableNoResultsRow';
import TableLoadingRow from './Datatable/TableLoading';
import TableHeader from './Datatable/TableHeader';
import TablePage from './Datatable/TablePage';
import TableRow from './Datatable/TableRow';
import TableRows from './Datatable/TableRows';
import TableCell from './Datatable/TableCell';
import TableTBody from './Datatable/TableTBody';
import TableContext from './Datatable/TableContext';
import TablePagination from './Datatable/TablePagination';
import TableWrapper from './Datatable/TableWrapper';
export default function DataTable({
columns,
data,
import TableIndeterminateCheckboxRow from './Datatable/TableIndeterminateCheckboxRow';
import TableIndeterminateCheckboxHeader from './Datatable/TableIndeterminateCheckboxHeader';
loading,
onFetchData,
onSelectedRowsChange,
manualSortBy = false,
manualPagination = true,
selectionColumn = false,
expandSubRows = true,
className,
noResults = 'This report does not contain any data.',
expanded = {},
rowClassNames,
sticky = false,
virtualizedRows = false,
fixedSizeHeight = 100,
fixedItemSize = 30,
payload,
expandable = false,
expandToggleColumn = 2,
noInitialFetch = false,
spinnerProps = { size: 30 },
pagination = false,
pagesCount: controlledPageCount,
// Pagination props.
initialPageIndex = 0,
initialPageSize = 10,
rowContextMenu,
expandColumnSpace = 1.5,
updateDebounceTime = 200,
selectionColumnWidth = 42,
// Read this document to know why! https://bit.ly/2Uw9SEc
autoResetPage = true,
autoResetExpanded = true,
autoResetGroupBy = true,
autoResetSelectedRows = true,
autoResetSortBy = true,
autoResetFilters = true,
autoResetRowState = true,
}) {
/**
* Datatable component.
*/
export default function DataTable(props) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
rows,
selectedFlatRows,
getToggleAllRowsExpandedProps,
isAllRowsExpanded,
totalColumnsWidth,
columns,
data,
// page,
pageCount,
canPreviousPage,
canNextPage,
gotoPage,
previousPage,
nextPage,
setPageSize,
onFetchData,
// Get the state from the instance
state: { pageIndex, pageSize, sortBy, selectedRowIds },
} = useTable(
onSelectedRowsChange,
manualSortBy = false,
manualPagination = true,
selectionColumn = false,
expandSubRows = true,
expanded = {},
rowClassNames,
payload,
expandable = false,
expandToggleColumn = 2,
noInitialFetch = false,
pagesCount: controlledPageCount,
// Pagination props.
initialPageIndex = 0,
initialPageSize = 10,
rowContextMenu,
expandColumnSpace = 1.5,
updateDebounceTime = 200,
selectionColumnWidth = 42,
autoResetPage,
autoResetExpanded,
autoResetGroupBy,
autoResetSelectedRows,
autoResetSortBy,
autoResetFilters,
autoResetRowState,
// Components
TableHeaderRenderer,
TablePageRenderer,
TableWrapperRenderer,
TableTBodyRenderer,
TablePaginationRenderer,
} = props;
const selectionColumnObj = {
id: 'selection',
disableResizing: true,
minWidth: selectionColumnWidth,
width: selectionColumnWidth,
maxWidth: selectionColumnWidth,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: TableIndeterminateCheckboxHeader,
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: TableIndeterminateCheckboxRow,
className: 'selection',
...(typeof selectionColumn === 'object' ? selectionColumn : {}),
};
const table = useTable(
{
columns,
data,
initialState: {
pageIndex: initialPageIndex,
pageSize: initialPageSize,
expanded
expanded,
},
manualPagination,
pageCount: controlledPageCount,
@@ -133,49 +130,23 @@ export default function DataTable({
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
...(selectionColumn
? [
{
id: 'selection',
disableResizing: true,
minWidth: selectionColumnWidth,
width: selectionColumnWidth,
maxWidth: selectionColumnWidth,
// 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 ? [selectionColumnObj] : []),
...columns,
]);
},
);
const {
selectedFlatRows,
state: { pageIndex, pageSize, sortBy, selectedRowIds },
} = table;
const isInitialMount = useRef(noInitialFetch);
const onFetchDataDebounced = useAsyncDebounce(
(...args) => {
saveInvoke(onFetchData, ...args);
},
updateDebounceTime,
);
const onFetchDataDebounced = useAsyncDebounce((...args) => {
saveInvoke(onFetchData, ...args);
}, updateDebounceTime);
// When these table states change, fetch new data!
useEffect(() => {
if (isInitialMount.current) {
@@ -189,285 +160,42 @@ export default function DataTable({
saveInvoke(onSelectedRowsChange, selectedFlatRows);
}, [selectedRowIds, onSelectedRowsChange]);
// Renders table cell.
const RenderCell = useCallback(
({ row, cell, column, index }) => (
<ConditionalWrapper
condition={expandToggleColumn === index && expandable}
wrapper={(children) => (
<div
style={{
'padding-left': `${row.depth * expandColumnSpace}rem`,
}}
className={'expend-padding'}
>
{children}
</div>
)}
>
{
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
}
<If
condition={
row.canExpand && expandable && index === expandToggleColumn
}
>
<span
{...row.getToggleRowExpandedProps({ className: 'expand-toggle' })}
>
<span
className={classnames({
'arrow-down': row.isExpanded,
'arrow-right': !row.isExpanded,
})}
/>
</span>
</If>
<ConditionalWrapper
condition={cell.column.textOverview}
wrapper={(children) => (
<span class="text-overview">{ children }</span>
)}>
{cell.render('Cell')}
</ConditionalWrapper>
</ConditionalWrapper>
),
[expandable, expandToggleColumn, expandColumnSpace],
);
// Handle rendering row context menu.
const handleRowContextMenu = useMemo(
() => (cell, row) => (e) => {
if (typeof rowContextMenu === 'function') {
e.preventDefault();
const tr = e.currentTarget.closest('.tr');
tr.classList.add('is-context-menu-active');
const DropdownEl = rowContextMenu(cell, row);
ContextMenu.show(
DropdownEl,
{ left: e.clientX, top: e.clientY },
() => {
tr.classList.remove('is-context-menu-active');
},
);
}
},
[rowContextMenu],
);
// Renders table row.
const RenderRow = useCallback(
({ style = {}, row }) => {
prepareRow(row);
const rowClasses = rowClassNames && rowClassNames(row);
return (
<div
{...row.getRowProps({
className: classnames(
'tr',
{
'is-expanded': row.isExpanded && row.canExpand,
},
rowClasses,
),
style,
})}
>
{row.cells.map((cell, i) => {
const index = i + 1;
return (
<div
{...cell.getCellProps({
className: classnames(
cell.column.className,
'td',
{
'is-text-overview': cell.column.textOverview,
}
),
})}
onContextMenu={handleRowContextMenu(cell, row)}
>
{RenderCell({ cell, row, index })}
</div>
);
})}
</div>
);
},
[prepareRow, rowClassNames, RenderCell, handleRowContextMenu],
);
// Renders virtualize circle table rows.
const RenderVirtualizedRows = useCallback(
({ index, style }) => {
const row = rows[index];
return RenderRow({ row, style });
},
[RenderRow, rows],
);
// Renders page with multi-rows.
const RenderPage = useCallback(
({ style, index } = {}) => {
return page.map((row, index) => RenderRow({ row }));
},
[RenderRow, page],
);
// Renders fixed size tbody.
const RenderTBody = useCallback(() => {
return virtualizedRows ? (
<FixedSizeList
height={fixedSizeHeight}
itemCount={rows.length}
itemSize={fixedItemSize}
>
{RenderVirtualizedRows}
</FixedSizeList>
) : (
RenderPage()
);
}, [
fixedSizeHeight,
rows,
fixedItemSize,
virtualizedRows,
RenderVirtualizedRows,
RenderPage,
]);
const handlePageChange = useCallback(
(currentPage) => {
gotoPage(currentPage - 1);
},
[gotoPage],
);
const handlePageSizeChange = useCallback(
(pageSize, currentPage) => {
gotoPage(0);
setPageSize(pageSize);
},
[gotoPage, setPageSize],
);
return (
<div
className={classnames('bigcapital-datatable', className, {
'has-sticky': sticky,
'has-pagination': pagination,
'is-expandable': expandable,
'is-loading': loading,
'has-virtualized-rows': virtualizedRows,
})}
>
<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.getHeaderProps({
className: classnames(column.className || '', 'th'),
})}
>
<If
condition={
expandable && index + 1 === expandToggleColumn
}
>
<span
{...getToggleAllRowsExpandedProps()}
className="expand-toggle"
>
<span
className={classnames({
'arrow-down': isAllRowsExpanded,
'arrow-right': !isAllRowsExpanded,
})}
/>
</span>
</If>
<TableContext.Provider value={{ table, props }}>
<TableWrapperRenderer>
<TableHeaderRenderer />
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
<TableTBodyRenderer>
<TablePageRenderer />
</TableTBodyRenderer>
</TableWrapperRenderer>
<If condition={column.isSorted}>
<span
className={classnames(
{
'sort-icon--desc': column.isSortedDesc,
'sort-icon--asc': !column.isSortedDesc,
},
'sort-icon',
)}
></span>
</If>
</div>
{column.canResize && (
<div
{...column.getResizerProps()}
className={`resizer ${
column.isResizing ? 'isResizing' : ''
}`}
>
<div class="inner-resizer" />
</div>
)}
</div>
))}
</div>
))}
</div>
</ScrollSyncPane>
<ScrollSyncPane>
<div {...getTableBodyProps()} className="tbody">
<div class="tbody-inner" style={{ minWidth: totalColumnsWidth }}>
<Choose>
<Choose.When condition={loading}>
<div class="loading">
<Spinner {...spinnerProps} />
</div>
</Choose.When>
<Choose.Otherwise>
{RenderTBody()}
<If condition={page.length === 0}>
<div className={'tr no-results'}>
<div class="td">{noResults}</div>
</div>
</If>
</Choose.Otherwise>
</Choose>
</div>
</div>
</ScrollSyncPane>
</div>
</ScrollSync>
<If condition={pagination && !loading}>
<Pagination
initialPage={pageIndex + 1}
total={pageSize * pageCount}
size={pageSize}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
/>
</If>
</div>
<TablePaginationRenderer />
</TableContext.Provider>
);
}
DataTable.defaultProps = {
pagination: false,
spinnerProps: { size: 30 },
autoResetPage: true,
autoResetExpanded: true,
autoResetGroupBy: true,
autoResetSelectedRows: true,
autoResetSortBy: true,
autoResetFilters: true,
autoResetRowState: true,
TableHeaderRenderer: TableHeader,
TableLoadingRenderer: TableLoadingRow,
TablePageRenderer: TablePage,
TableRowsRenderer: TableRows,
TableRowRenderer: TableRow,
TableCellRenderer: TableCell,
TableWrapperRenderer: TableWrapper,
TableTBodyRenderer: TableTBody,
TablePaginationRenderer: TablePagination,
TableNoResultsRowRenderer: TableNoResultsRow,
};

View File

@@ -0,0 +1,11 @@
export default function TableBody({}) {
return (
<ScrollSyncPane>
<div {...getTableBodyProps()} className="tbody">
<div class="tbody-inner" style={{ minWidth: totalColumnsWidth }}></div>
</div>
</ScrollSyncPane>
);
}

View File

@@ -0,0 +1,52 @@
import React, { useContext } from 'react';
import classNames from 'classnames';
import { If } from 'components';
import { ConditionalWrapper } from 'utils';
import TableContext from './TableContext';
/**
* Tabl cell.
*/
export default function TableCell({ cell, row, index }) {
const {
props: { expandToggleColumn, expandable }
} = useContext(TableContext);
return (
<div
{...cell.getCellProps({
className: classNames(cell.column.className, 'td', {
'is-text-overview': cell.column.textOverview,
}),
})}
>
{
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
}
<If
condition={
cell.row.canExpand && expandable && index === expandToggleColumn
}
>
<span
{...row.getToggleRowExpandedProps({ className: 'expand-toggle' })}
>
<span
className={classNames({
'arrow-down': row.isExpanded,
'arrow-right': !row.isExpanded,
})}
/>
</span>
</If>
<ConditionalWrapper
condition={cell.column.textOverview}
wrapper={(children) => <span class="text-overview">{children}</span>}
>
{cell.render('Cell')}
</ConditionalWrapper>
</div>
);
}

View File

@@ -0,0 +1,3 @@
import { createContext } from 'react';
export default createContext();

View File

@@ -0,0 +1,18 @@
import React, { memo } from 'react';
import TableCell from './TableCell';
function TableFastCell({ cell, row, index }) {
return <TableCell cell={cell} row={row} index={index} />;
}
export default memo(TableFastCell, (prevProps, nextProps) => {
if (
prevProps.cell.value === nextProps.cell.value &&
prevProps.cell.maxWidth === nextProps.cell.maxWidth &&
prevProps.cell.width === nextProps.cell.width
) {
return true;
} else {
return false;
}
});

View File

@@ -0,0 +1,82 @@
import React, { useContext } from 'react';
import classNames from 'classnames';
import { ScrollSyncPane } from 'react-scroll-sync';
import { If } from 'components';
import TableContext from './TableContext';
function TableHeaderGroup({ headerGroup }) {
const {
table: { getToggleAllRowsExpandedProps, isAllRowsExpanded },
props: { expandable, expandToggleColumn },
} = useContext(TableContext);
return (
<div {...headerGroup.getHeaderGroupProps()} className="tr">
{headerGroup.headers.map((column, index) => (
<div
{...column.getHeaderProps({
className: classNames(column.className || '', 'th'),
})}
>
<If condition={expandable && index + 1 === expandToggleColumn}>
<span
{...getToggleAllRowsExpandedProps()}
className="expand-toggle"
>
<span
className={classNames({
'arrow-down': isAllRowsExpanded,
'arrow-right': !isAllRowsExpanded,
})}
/>
</span>
</If>
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
<If condition={column.isSorted}>
<span
className={classNames(
{
'sort-icon--desc': column.isSortedDesc,
'sort-icon--asc': !column.isSortedDesc,
},
'sort-icon',
)}
></span>
</If>
</div>
{column.canResize && (
<div
{...column.getResizerProps()}
className={`resizer ${column.isResizing ? 'isResizing' : ''}`}
>
<div class="inner-resizer" />
</div>
)}
</div>
))}
</div>
);
}
/**
* Table header.
*/
export default function TableHeader() {
const {
table: { headerGroups },
} = useContext(TableContext);
return (
<ScrollSyncPane>
<div className="thead">
{headerGroups.map((headerGroup) => (
<TableHeaderGroup headerGroup={headerGroup} />
))}
</div>
</ScrollSyncPane>
);
}

View File

@@ -0,0 +1,12 @@
import React from 'react';
import { Checkbox } from '@blueprintjs/core';
export default function TableIndeterminateCheckboxHeader({
getToggleAllRowsSelectedProps,
}) {
return (
<div>
<Checkbox {...getToggleAllRowsSelectedProps()} />
</div>
);
}

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { Checkbox } from '@blueprintjs/core';
export default function TableIndeterminateCheckboxRow({ row }) {
return (
<div>
<Checkbox {...row.getToggleRowSelectedProps()} />
</div>
);
}

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { Spinner } from '@blueprintjs/core';
/**
* Table loading component.
*/
export default function TableLoading({
spinnerProps
}) {
return (
<div class="loading">
<Spinner {...spinnerProps} />
</div>
);
}

View File

@@ -0,0 +1,17 @@
import React, { useContext } from 'react';
import TableContext from './TableContext';
/**
* Table no-results row text.
*/
export default function TableNoResultsRow() {
const {
props: { noResults }
} = useContext(TableContext);
return (
<div className={'tr no-results'}>
<div class="td">{ noResults }</div>
</div>
);
}

View File

@@ -0,0 +1,26 @@
import React, { useContext } from 'react';
import TableContext from './TableContext';
/**
* Table page.
*/
export default function TablePage() {
const {
table: { page },
props: {
spinnerProps,
loading,
TableRowsRenderer,
TableLoadingRenderer,
TableNoResultsRow,
},
} = useContext(TableContext);
if (loading) {
return <TableLoadingRenderer spinnerProps={spinnerProps} />;
}
if (page.length === 0) {
return <TableNoResultsRow />;
}
return (<TableRowsRenderer />);
}

View File

@@ -0,0 +1,45 @@
import React, { useCallback, useContext } from 'react';
import { If, Pagination } from 'components';
import TableContext from './TableContext';
/**
* Table pagination.
*/
export default function TablePagination({}) {
const {
table: {
gotoPage,
setPageSize,
pageCount,
state: { pageIndex, pageSize },
},
props: { pagination, loading },
} = useContext(TableContext);
const handlePageChange = useCallback(
(currentPage) => {
gotoPage(currentPage - 1);
},
[gotoPage],
);
const handlePageSizeChange = useCallback(
(pageSize, currentPage) => {
gotoPage(0);
setPageSize(pageSize);
},
[gotoPage, setPageSize],
);
return (
<If condition={pagination && !loading}>
<Pagination
initialPage={pageIndex + 1}
total={pageSize * pageCount}
size={pageSize}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
/>
</If>
);
}

View File

@@ -0,0 +1,51 @@
import React, { useContext } from 'react';
import classNames from 'classnames';
import { ContextMenu } from '@blueprintjs/core';
import TableContext from './TableContext';
import { saveInvoke } from 'utils';
/**
* Table row.
*/
export default function TableRow({ row, className, style }) {
const {
props: { TableCellRenderer, rowContextMenu, rowClassNames },
} = useContext(TableContext);
// Handle rendering row context menu.
const handleRowContextMenu = (row) => (e) => {
if (typeof rowContextMenu === 'function') {
e.preventDefault();
const tr = e.currentTarget.closest('.tr');
tr.classList.add('is-context-menu-active');
const DropdownEl = rowContextMenu({ row });
ContextMenu.show(DropdownEl, { left: e.clientX, top: e.clientY }, () => {
tr.classList.remove('is-context-menu-active');
});
}
};
return (
<div
{...row.getRowProps({
className: classNames(
'tr',
{
'is-expanded': row.isExpanded && row.canExpand,
},
saveInvoke(rowClassNames, row),
className,
),
style,
onContextMenu: handleRowContextMenu(row)
})}
>
{row.cells.map((cell, index) => (
<TableCellRenderer cell={cell} row={row} index={index + 1} />
))}
</div>
);
}

View File

@@ -0,0 +1,17 @@
import React, { useContext } from "react";
import TableContext from "./TableContext";
/**
* Table rows.
*/
export default function TableRows() {
const {
table: { prepareRow, page },
props: { TableRowRenderer, TableCellRenderer },
} = useContext(TableContext);
return page.map((row) => {
prepareRow(row);
return <TableRowRenderer row={row} TableCellRenderer={TableCellRenderer} />;
});
}

View File

@@ -0,0 +1,21 @@
import React, { useContext } from 'react';
import { ScrollSyncPane } from 'react-scroll-sync';
import TableContext from './TableContext';
export default function TableTBody({
children
}) {
const {
table: { getTableBodyProps }
} = useContext(TableContext);
return (
<ScrollSyncPane>
<div {...getTableBodyProps()} className="tbody">
<div class="tbody-inner">
{ children }
</div>
</div>
</ScrollSyncPane>
);
}

View File

@@ -0,0 +1,66 @@
import React, { useContext } from 'react';
import { WindowScroller, AutoSizer, List } from 'react-virtualized';
import { CLASSES } from 'common/classes';
import TableContext from './TableContext';
function TableVirtualizedListRow({
index,
isScrolling,
isVisible,
key,
style,
}) {
const {
table: { page, prepareRow },
props: { TableRowRenderer },
} = useContext(TableContext);
const row = page[index];
prepareRow(row);
return <TableRowRenderer row={row} style={style} />;
}
/**
* Table virtualized list rows.
*/
export default function TableVirtualizedListRows() {
const {
table: { page },
props: { vListrowHeight, vListOverscanRowCount }
} = useContext(TableContext);
// Dashboard content pane.
const dashboardContentPane = document.querySelector(
`.${CLASSES.DASHBOARD_CONTENT_PANE}`,
);
return (
<WindowScroller scrollElement={dashboardContentPane}>
{({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => (
<div className={'WindowScrollerWrapper'}>
<AutoSizer disableHeight>
{({ width }) => (
<div ref={registerChild}>
<List
autoHeight={true}
className={'List'}
height={height}
isScrolling={isScrolling}
onScroll={onChildScroll}
overscanRowCount={vListOverscanRowCount}
rowCount={page.length}
rowHeight={vListrowHeight}
rowRenderer={({ ...args }) => {
return <TableVirtualizedListRow {...args} />;
}}
scrollTop={scrollTop}
width={width}
/>
</div>
)}
</AutoSizer>
</div>
)}
</WindowScroller>
);
}

View File

@@ -0,0 +1,35 @@
import React, { useContext } from 'react';
import classNames from 'classnames';
import { ScrollSync } from 'react-scroll-sync';
import TableContext from './TableContext';
/**
* Table wrapper.
*/
export default function TableWrapper({ children }) {
const {
table: { getTableProps },
props: { sticky, pagination, loading, expandable, virtualizedRows, className },
} = useContext(TableContext);
return (
<div
className={classNames('bigcapital-datatable', className, {
'has-sticky': sticky,
'has-pagination': pagination,
'is-expandable': expandable,
'is-loading': loading,
'has-virtualized-rows': virtualizedRows,
})}
>
<ScrollSync>
<div
{...getTableProps({ style: { minWidth: 'none' } })}
className="table"
>
{children}
</div>
</ScrollSync>
</div>
);
}

View File

@@ -100,7 +100,7 @@ export default function ItemsSuggestField({
inputProps={{ placeholder: defautlSelectText }}
resetOnClose={true}
fill={true}
popoverProps={{ minimal: true }}
popoverProps={{ minimal: true, boundary: 'window' }}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}

View File

@@ -45,6 +45,9 @@ import PageFormBigNumber from './PageFormBigNumber';
import AccountsMultiSelect from './AccountsMultiSelect';
import CustomersMultiSelect from './CustomersMultiSelect';
import TableFastCell from './Datatable/TableFastCell';
const Hint = FieldHint;
export {
@@ -93,5 +96,7 @@ export {
PageFormBigNumber,
AccountsMultiSelect,
DataTableEditable,
CustomersMultiSelect
CustomersMultiSelect,
TableFastCell,
};