mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
feat: optimize sidebar and datatable expandable rows.
This commit is contained in:
@@ -68,45 +68,6 @@ function AccountsDataTable({
|
|||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
const columns = useMemo(() => [
|
const columns = useMemo(() => [
|
||||||
{
|
|
||||||
// Build our expander column
|
|
||||||
id: 'expander', // Make sure it has an ID
|
|
||||||
className: 'expander',
|
|
||||||
Header: ({
|
|
||||||
getToggleAllRowsExpandedProps,
|
|
||||||
isAllRowsExpanded
|
|
||||||
}) => (
|
|
||||||
<span {...getToggleAllRowsExpandedProps()} className="toggle">
|
|
||||||
{isAllRowsExpanded ?
|
|
||||||
(<span class="arrow-down" />) :
|
|
||||||
(<span class="arrow-right" />)
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
Cell: ({ row }) =>
|
|
||||||
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
|
|
||||||
// to build the toggle for expanding a row
|
|
||||||
row.canExpand ? (
|
|
||||||
<span
|
|
||||||
{...row.getToggleRowExpandedProps({
|
|
||||||
style: {
|
|
||||||
// We can even use the row.depth property
|
|
||||||
// and paddingLeft to indicate the depth
|
|
||||||
// of the row
|
|
||||||
paddingLeft: `${row.depth * 2}rem`,
|
|
||||||
},
|
|
||||||
className: 'toggle',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{row.isExpanded ?
|
|
||||||
(<span class="arrow-down" />) :
|
|
||||||
(<span class="arrow-right" />)
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
) : null,
|
|
||||||
width: 20,
|
|
||||||
disableResizing: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'name',
|
id: 'name',
|
||||||
Header: 'Account Name',
|
Header: 'Account Name',
|
||||||
@@ -197,7 +158,9 @@ function AccountsDataTable({
|
|||||||
data={accounts}
|
data={accounts}
|
||||||
onFetchData={handleDatatableFetchData}
|
onFetchData={handleDatatableFetchData}
|
||||||
manualSortBy={true}
|
manualSortBy={true}
|
||||||
selectionColumn={selectionColumn} />
|
selectionColumn={selectionColumn}
|
||||||
|
expandable={true}
|
||||||
|
treeGraph={true} />
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import {Checkbox} from '@blueprintjs/core';
|
import {Checkbox} from '@blueprintjs/core';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { FixedSizeList } from 'react-window'
|
import { FixedSizeList } from 'react-window'
|
||||||
import Icon from 'components/Icon';
|
import { ConditionalWrapper } from 'utils';
|
||||||
|
|
||||||
const IndeterminateCheckbox = React.forwardRef(
|
const IndeterminateCheckbox = React.forwardRef(
|
||||||
({ indeterminate, ...rest }, ref) => {
|
({ indeterminate, ...rest }, ref) => {
|
||||||
@@ -38,6 +38,8 @@ export default function DataTable({
|
|||||||
fixedSizeHeight = 100,
|
fixedSizeHeight = 100,
|
||||||
fixedItemSize = 30,
|
fixedItemSize = 30,
|
||||||
payload,
|
payload,
|
||||||
|
expandable = false,
|
||||||
|
expandToggleColumn = 2,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
getTableProps,
|
getTableProps,
|
||||||
@@ -56,6 +58,8 @@ export default function DataTable({
|
|||||||
setPageSize,
|
setPageSize,
|
||||||
selectedFlatRows,
|
selectedFlatRows,
|
||||||
totalColumnsWidth,
|
totalColumnsWidth,
|
||||||
|
getToggleAllRowsExpandedProps,
|
||||||
|
isAllRowsExpanded,
|
||||||
|
|
||||||
// Get the state from the instance
|
// Get the state from the instance
|
||||||
state: { pageIndex, pageSize, sortBy, selectedRowIds },
|
state: { pageIndex, pageSize, sortBy, selectedRowIds },
|
||||||
@@ -116,6 +120,32 @@ export default function DataTable({
|
|||||||
onFetchData && onFetchData({ pageIndex, pageSize, sortBy })
|
onFetchData && onFetchData({ pageIndex, pageSize, sortBy })
|
||||||
}, [pageIndex, pageSize, sortBy]);
|
}, [pageIndex, pageSize, sortBy]);
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
|
||||||
// Renders table row.
|
// Renders table row.
|
||||||
const RenderRow = useCallback(({ style = {}, row }) => {
|
const RenderRow = useCallback(({ style = {}, row }) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
@@ -123,16 +153,18 @@ export default function DataTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...row.getRowProps({
|
<div {...row.getRowProps({
|
||||||
className: classnames('tr', rowClasses),
|
className: classnames('tr', rowClasses), style
|
||||||
style
|
|
||||||
})}>
|
})}>
|
||||||
{row.cells.map((cell) => {
|
{row.cells.map((cell, i) => {
|
||||||
|
const index = i + 1;
|
||||||
return <div {...cell.getCellProps({
|
return <div {...cell.getCellProps({
|
||||||
className: classnames(cell.column.className || '', 'td'),
|
className: classnames(cell.column.className || '', 'td'),
|
||||||
})}>{ cell.render('Cell') }</div>
|
})}>
|
||||||
|
{ RenderCell({ cell, row, index }) }
|
||||||
|
</div>
|
||||||
})}
|
})}
|
||||||
</div>);
|
</div>);
|
||||||
}, [prepareRow, rowClassNames]);
|
}, [prepareRow, rowClassNames, expandable, RenderCell, expandToggleColumn]);
|
||||||
|
|
||||||
// Renders virtualize circle table rows.
|
// Renders virtualize circle table rows.
|
||||||
const RenderVirtualizedRows = useCallback(({ index, style }) => {
|
const RenderVirtualizedRows = useCallback(({ index, style }) => {
|
||||||
@@ -144,6 +176,7 @@ export default function DataTable({
|
|||||||
return page.map((row, index) => RenderRow({ row }));
|
return page.map((row, index) => RenderRow({ row }));
|
||||||
}, [RenderRow, page]);
|
}, [RenderRow, page]);
|
||||||
|
|
||||||
|
// Renders fixed size tbody.
|
||||||
const RenderTBody = useCallback(() => {
|
const RenderTBody = useCallback(() => {
|
||||||
return (virtualizedRows) ? (
|
return (virtualizedRows) ? (
|
||||||
<FixedSizeList
|
<FixedSizeList
|
||||||
@@ -154,19 +187,31 @@ export default function DataTable({
|
|||||||
{RenderVirtualizedRows}
|
{RenderVirtualizedRows}
|
||||||
</FixedSizeList>
|
</FixedSizeList>
|
||||||
) : RenderPage();
|
) : RenderPage();
|
||||||
}, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows,
|
}, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows, RenderVirtualizedRows, RenderPage]);
|
||||||
RenderVirtualizedRows, RenderPage])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('bigcapital-datatable', className, {'has-sticky-header': stickyHeader})}>
|
<div className={classnames(
|
||||||
|
'bigcapital-datatable',
|
||||||
|
className,
|
||||||
|
{'has-sticky-header': stickyHeader, 'is-expandable': expandable})}>
|
||||||
<div {...getTableProps()} className="table">
|
<div {...getTableProps()} className="table">
|
||||||
<div className="thead">
|
<div className="thead">
|
||||||
{headerGroups.map(headerGroup => (
|
{headerGroups.map(headerGroup => (
|
||||||
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
||||||
{headerGroup.headers.map(column => (
|
{headerGroup.headers.map((column, index) => (
|
||||||
<div {...column.getHeaderProps({
|
<div {...column.getHeaderProps({
|
||||||
className: classnames(column.className || '', 'th'),
|
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()}>
|
<div {...column.getSortByToggleProps()}>
|
||||||
{column.render('Header')}
|
{column.render('Header')}
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default class MenuItem extends AbstractPureComponent2 {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isCollapseActive: false,
|
isCollapseActive: this.props.callapseActive || false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +193,13 @@ export default class MenuItem extends AbstractPureComponent2 {
|
|||||||
}</li>;
|
}</li>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps){
|
||||||
|
if(nextProps.callapseActive!==this.props.callapseActive){
|
||||||
|
//Perform some operation
|
||||||
|
this.setState({ isCollapseActive: nextProps.callapseActive });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
maybeRenderLabel(labelElement) {
|
maybeRenderLabel(labelElement) {
|
||||||
const { label, labelClassName } = this.props;
|
const { label, labelClassName } = this.props;
|
||||||
if (label == null && labelElement == null) {
|
if (label == null && labelElement == null) {
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {Menu, MenuDivider, Collapse} from "@blueprintjs/core";
|
import {Menu, MenuDivider, Collapse} from "@blueprintjs/core";
|
||||||
import {useHistory} from 'react-router-dom';
|
import {useHistory, useLocation} from 'react-router-dom';
|
||||||
import sidebarMenuList from 'config/sidebarMenu';
|
import sidebarMenuList from 'config/sidebarMenu';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import MenuItem from 'components/MenuItem';
|
import MenuItem from 'components/MenuItem';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export default function SidebarMenu() {
|
export default function SidebarMenu() {
|
||||||
let history = useHistory();
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
const menuItemsMapper = (list) => {
|
const menuItemsMapper = (list) => {
|
||||||
return list.map((item, index) => {
|
return list.map((item, index) => {
|
||||||
const children = Array.isArray(item.children) ? menuItemsMapper(item.children) : null;
|
const children = Array.isArray(item.children) ? menuItemsMapper(item.children) : null;
|
||||||
|
const isActive = (item.href && item.href === location.pathname) ||
|
||||||
|
(item.children && item.children.some((c) => c.href === location.pathname));
|
||||||
|
|
||||||
|
const handleItemClick = () => {
|
||||||
|
if (item.href) { history.push(item.href); }
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
(item.divider) ?
|
(item.divider) ?
|
||||||
<MenuDivider
|
<MenuDivider title={item.title} /> :
|
||||||
title={item.title} /> :
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
active={isActive}
|
||||||
icon={<Icon icon={item.icon} iconSize={item.iconSize} />}
|
icon={<Icon icon={item.icon} iconSize={item.iconSize} />}
|
||||||
text={item.text}
|
text={item.text}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
@@ -24,11 +31,9 @@ export default function SidebarMenu() {
|
|||||||
children={children}
|
children={children}
|
||||||
dropdownType={item.dropdownType || 'collapse'}
|
dropdownType={item.dropdownType || 'collapse'}
|
||||||
caretIconSize={15}
|
caretIconSize={15}
|
||||||
onClick={() => {
|
onClick={handleItemClick}
|
||||||
if (item.href) {
|
callapseActive={!!isActive}
|
||||||
history.push(item.href);
|
className={classNames({ 'is-active': isActive })} />
|
||||||
}
|
|
||||||
}} />
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,6 +129,6 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Auditing System',
|
text: 'Auditing System',
|
||||||
href: '/dashboard/accounts'
|
href: '/dashboard/auditing/list'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ const ItemCategoriesList = ({
|
|||||||
changePageTitle,
|
changePageTitle,
|
||||||
views,
|
views,
|
||||||
requestFetchItemCategories,
|
requestFetchItemCategories,
|
||||||
requestEditItemCategory
|
requestEditItemCategory,
|
||||||
|
requestDeleteItemCategory,
|
||||||
}) => {
|
}) => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [deleteCategory, setDeleteCategory] = useState(false);
|
const [deleteCategory, setDeleteCategory] = useState(false);
|
||||||
@@ -40,7 +41,7 @@ const ItemCategoriesList = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handelConfirmCategoryDelete = useCallback(() => {
|
const handelConfirmCategoryDelete = useCallback(() => {
|
||||||
requestEditItemCategory(deleteCategory.id).then(() => {
|
requestDeleteItemCategory(deleteCategory.id).then(() => {
|
||||||
setDeleteCategory(false);
|
setDeleteCategory(false);
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'the_category_has_been_delete'
|
message: 'the_category_has_been_delete'
|
||||||
|
|||||||
@@ -144,17 +144,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tr .th.expander,
|
.tr .th,
|
||||||
.tr .td.expander{
|
.tr .td{
|
||||||
padding: 0;
|
.expand-toggle{
|
||||||
|
|
||||||
.toggle{
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: inline-block;
|
||||||
padding: 14px 8px;
|
padding: 0 8px 0 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
margin: auto 0;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-right{
|
.arrow-right{
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -173,11 +171,9 @@
|
|||||||
border-top: 8px solid #acacac;
|
border-top: 8px solid #acacac;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ .td,
|
|
||||||
+ .th{
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +198,7 @@
|
|||||||
|
|
||||||
&--financial-report{
|
&--financial-report{
|
||||||
|
|
||||||
|
.table {
|
||||||
.thead{
|
.thead{
|
||||||
.tr .th{
|
.tr .th{
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -213,5 +210,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,10 @@ $sidebar-popover-submenu-bg: rgb(1, 20, 62);
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
&:hover{
|
&:hover,
|
||||||
|
&.bp3-active{
|
||||||
background: #012470;
|
background: #012470;
|
||||||
color: #b2bbd0;
|
color: #c1c9dd;
|
||||||
}
|
}
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active{
|
&:active{
|
||||||
@@ -89,7 +90,8 @@ $sidebar-popover-submenu-bg: rgb(1, 20, 62);
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #8a95b6;
|
color: #8a95b6;
|
||||||
|
|
||||||
&:hover{
|
&:hover,
|
||||||
|
&.bp3-active{
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #C5CBE3;
|
color: #C5CBE3;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,10 +136,12 @@ export const defaultExpanderReducer = (tableRows, level) => {
|
|||||||
return expended;
|
return expended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function formattedAmount(cents, currency) {
|
export function formattedAmount(cents, currency) {
|
||||||
const { symbol, decimal_digits: precision } = Currency[currency];
|
const { symbol, decimal_digits: precision } = Currency[currency];
|
||||||
const amount = cents / Math.pow(10, precision);
|
const amount = cents / Math.pow(10, precision);
|
||||||
|
|
||||||
return accounting.formatMoney(amount, { symbol, precision });
|
return accounting.formatMoney(amount, { symbol, precision });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ConditionalWrapper = ({ condition, wrapper, children }) =>
|
||||||
|
condition ? wrapper(children) : children;
|
||||||
Reference in New Issue
Block a user