feat: Sidebar overlay.

This commit is contained in:
a.bouhuolia
2021-08-02 09:36:45 +02:00
parent 9dbd128236
commit 1de166bac4
6 changed files with 274 additions and 48 deletions

View File

@@ -145,9 +145,10 @@ export default class MenuItem extends AbstractPureComponent2 {
tagName = "a", tagName = "a",
dropdownType, dropdownType,
caretIconSize = 16, caretIconSize = 16,
hasSubmenu,
...htmlProps ...htmlProps
} = this.props; } = this.props;
const hasSubmenu = children != null;
const intentClass = Classes.intentClass(intent); const intentClass = Classes.intentClass(intent);
const anchorClasses = classNames( const anchorClasses = classNames(

View File

@@ -8,6 +8,11 @@ import { MenuItemLabel } from 'components';
import classNames from 'classnames'; import classNames from 'classnames';
import SidebarOverlay from 'components/SidebarOverlay'; import SidebarOverlay from 'components/SidebarOverlay';
const DEFAULT_ITEM = {
text: '',
href: '',
}
export default function SidebarMenu() { export default function SidebarMenu() {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
@@ -17,18 +22,16 @@ export default function SidebarMenu() {
const menuItemsMapper = (list) => { const menuItemsMapper = (list) => {
return list.map((item, index) => { return list.map((item, index) => {
const children = Array.isArray(item.children) const hasChildren = Array.isArray(item.children);
? menuItemsMapper(item.children)
: null;
const matchPath = (pathname, path) => { const matchPath = (pathname, path) => {
return item.matchExact return item.matchExact
? pathname === path ? pathname === path
: pathname.indexOf(path) !== -1; : pathname.indexOf(path) !== -1;
}; };
const isActive = item.children const isActive = (item.children
? item.children.some((c) => matchPath(location.pathname, c.href)) ? item.children.some((c) => matchPath(location.pathname, c.href))
: item.href && matchPath(location.pathname, item.href); : item.href && matchPath(location.pathname, item.href)) || currentItem ===item;
const handleItemClick = () => { const handleItemClick = () => {
if (item.href) { if (item.href) {
@@ -69,28 +72,34 @@ export default function SidebarMenu() {
text={item.text} text={item.text}
label={maybeRenderLabel(item)} label={maybeRenderLabel(item)}
disabled={item.disabled} disabled={item.disabled}
children={children}
dropdownType={item.dropdownType || 'collapse'} dropdownType={item.dropdownType || 'collapse'}
caretIconSize={16} caretIconSize={16}
onClick={handleItemClick} onClick={handleItemClick}
callapseActive={!!isActive} callapseActive={!!isActive}
itemClassName={classNames({ itemClassName={classNames({
'is-active': isActive, 'is-active': isActive,
'has-icon': !children && item.icon, 'has-icon': !hasChildren && item.icon,
})} })}
hasSubmenu={hasChildren}
/> />
); );
}); });
}; };
const items = menuItemsMapper(sidebarMenuList); const items = menuItemsMapper(sidebarMenuList);
const handleSidebarOverlayClose = () => {
setIsOpen(false);
}
return ( return (
<div> <div>
<Menu className="sidebar-menu">{items}</Menu>{' '} <Menu className="sidebar-menu">{items}</Menu>{' '}
<SidebarOverlay <SidebarOverlay
isOpen={isOpen} isOpen={isOpen}
label={currentItem?.text || ''}
items={currentItem?.children || []} items={currentItem?.children || []}
onClose={handleSidebarOverlayClose}
/> />
</div> </div>
); );

View File

@@ -2,24 +2,24 @@ import React from 'react';
import { Overlay } from '@blueprintjs/core'; import { Overlay } from '@blueprintjs/core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import SidebarOverlayContainer from './SidebarOverlayContainer'; import SidebarOverlayContainer from './SidebarOverlayContainer';
interface ISidebarOverlayItem { interface ISidebarOverlayItem {
text: string; text: string;
href: string; href: string;
divider?: boolean; divider?: boolean;
label?: boolean label?: boolean;
} }
interface ISidebarOverlayProps { interface ISidebarOverlayProps {
isOpen: boolean; isOpen: boolean;
items: ISidebarOverlayItem[]; items: ISidebarOverlayItem[];
overlayProps: any; label: string;
overlayContainerRef: any; onClose: Function;
} }
interface ISidebarOverlayItemProps { interface ISidebarOverlayItemProps {
text: string; text: string;
href: string; href: string;
onLinkClick: Function;
} }
interface ISidebarOverlayItemDivider { interface ISidebarOverlayItemDivider {
@@ -28,16 +28,25 @@ interface ISidebarOverlayItemDivider {
/** /**
* Sidebar overlay item. * Sidebar overlay item.
*/ */
function SidebarOverlayItem({ text, href }: ISidebarOverlayItemProps) { function SidebarOverlayItem({
text,
href,
onLinkClick,
}: ISidebarOverlayItemProps) {
const handleLinkClick = () => {
onLinkClick && onLinkClick();
};
return ( return (
<div className="sidebar-overlay__item"> <div className="sidebar-overlay__item">
<Link to={href}>{text}</Link> <Link onClick={handleLinkClick} to={href}>
{text}
</Link>
</div> </div>
); );
} }
interface ISidebarOverlayItemLabel { interface ISidebarOverlayItemLabel {
text: string; text: string;
} }
function SidebarOverlayLabel({ text }: ISidebarOverlayItemLabel) { function SidebarOverlayLabel({ text }: ISidebarOverlayItemLabel) {
@@ -52,34 +61,75 @@ function SidebarOverlayDivider() {
* Sidebar overlay component. * Sidebar overlay component.
*/ */
export default function SidebarOverlay({ export default function SidebarOverlay({
isOpen = false, label,
isOpen: controllerdIsOpen,
onClose,
items, items,
}: ISidebarOverlayProps) { }: ISidebarOverlayProps) {
// const [isEverOpened, setEverOpened] = React.useState(false);
if (!isOpen) { const [isOpen, setIsOpen] = React.useState(controllerdIsOpen);
React.useEffect(() => {
if (controllerdIsOpen && isOpen !== controllerdIsOpen) {
setIsOpen(controllerdIsOpen);
}
}, [controllerdIsOpen, setIsOpen, isOpen]);
React.useEffect(() => {
if (isOpen && !isEverOpened) {
setEverOpened(true);
}
}, [isEverOpened, isOpen]);
if (!isEverOpened) {
return ''; return '';
} }
// Handle overlay close event.
const handleOverlayClose = () => { const handleOverlayClose = () => {
setIsOpen(false);
onClose && onClose();
};
// Handle overlay open event.
const handleOverlayOpen = () => {
setIsOpen(true);
};
// Handle sidebar item link click.
const handleItemClick = () => {
setIsOpen(false);
onClose && onClose();
}; };
return ( return (
<Overlay <Overlay
isOpen={isOpen} isOpen={isOpen}
portalContainer={document.getElementById('dashboard') || document.body} portalContainer={document.getElementById('dashboard') || document.body}
onClose={handleOverlayClose} onClose={handleOverlayClose}
onOpening={handleOverlayOpen}
transitionDuration={200}
backdropClassName={'sidebar-overlay-backdrop'}
> >
<div className="sidebar-overlay"> <div className="sidebar-overlay sidebar-overlay-transition">
<SidebarOverlayContainer> <SidebarOverlayContainer>
<div className="sidebar-overlay__menu"> <div className="sidebar-overlay__menu">
{label && (
<>
<SidebarOverlayLabel text={label} />
<SidebarOverlayDivider />
</>
)}
{items.map((item) => {items.map((item) =>
item.divider ? ( item.divider ? (
<SidebarOverlayDivider /> <SidebarOverlayDivider />
) : item.label ? ( ) : item.label ? (
<SidebarOverlayLabel text={item.text} /> <SidebarOverlayLabel text={item.text} />
) : ( ) : (
<SidebarOverlayItem text={item.text} href={item.href} /> <SidebarOverlayItem
onLinkClick={handleItemClick}
text={item.text}
href={item.href}
/>
), ),
)} )}
</div> </div>

View File

@@ -27,6 +27,28 @@ export default [
text: <T id={'category_list'} />, text: <T id={'category_list'} />,
href: '/items/categories', href: '/items/categories',
}, },
{
text: 'New tasks',
label: true,
},
{
divider: true,
},
{
text: <T id={'New inventory item'} />,
href: '/items/new',
},
{
text: <T id={'New service'} />,
href: '/items/new',
},
{
text: <T id={'New item category'} />,
href: '/items/categories/new',
},
{
text: 'New inventory adjustment',
},
], ],
}, },
{ {
@@ -48,14 +70,34 @@ export default [
href: '/payment-receives', href: '/payment-receives',
newTabHref: '/payment-receives/new', newTabHref: '/payment-receives/new',
}, },
{
divider: true,
},
{ {
text: <T id={'receipts'} />, text: <T id={'receipts'} />,
href: '/receipts', href: '/receipts',
newTabHref: '/receipts/new', newTabHref: '/receipts/new',
}, },
{
text: 'New tasks',
label: true,
},
{
divider: true,
},
{
text: <T id={'new_estimate'} />,
href: '/estimates/new',
},
{
text: <T id={'new_invoice'} />,
href: '/invoices/new',
},
{
text: <T id={'new_receipt'} />,
href: '/receipts/new',
},
{
text: <T id={'new_payment_receive'} />,
href: '/payment-receives/new',
},
], ],
}, },
{ {
@@ -71,6 +113,21 @@ export default [
href: '/payment-mades', href: '/payment-mades',
newTabHref: '/payment-mades/new', newTabHref: '/payment-mades/new',
}, },
{
text: 'New tasks',
label: true,
},
{
divider: true,
},
{
text: <T id={'New purchase invoice'} />,
href: '/bills/new',
},
{
text: <T id={'new_payment_made'} />,
href: '/payment-mades/new',
},
], ],
}, },
{ {
@@ -86,6 +143,21 @@ export default [
href: '/vendors', href: '/vendors',
newTabHref: '/vendors/new', newTabHref: '/vendors/new',
}, },
{
text: 'New tasks',
label: true,
},
{
divider: true,
},
{
text: <T id={'new_customer'} />,
href: '/customers/new',
},
{
text: <T id={'new_vendor'} />,
href: '/vendors/new',
},
], ],
}, },
{ {
@@ -103,14 +175,21 @@ export default [
text: <T id={'manual_journals'} />, text: <T id={'manual_journals'} />,
href: '/manual-journals', href: '/manual-journals',
}, },
{
text: <T id={'make_journal_entry'} />,
href: '/make-journal-entry',
},
{ {
text: <T id={'exchange_rate'} />, text: <T id={'exchange_rate'} />,
href: '/exchange-rates', href: '/exchange-rates',
}, },
{
text: 'New tasks',
label: true,
},
{
divider: true,
},
{
text: <T id={'make_journal_entry'} />,
href: '/make-journal-entry',
},
], ],
}, },
{ {
@@ -133,15 +212,6 @@ export default [
{ {
text: <T id={'financial_reports'} />, text: <T id={'financial_reports'} />,
children: [ children: [
{
text: <T id={'all_reports'} />,
href: '/financial-reports',
matchExact: true,
label: true
},
{
divider: true,
},
{ {
text: <T id={'balance_sheet'} />, text: <T id={'balance_sheet'} />,
href: '/financial-reports/balance-sheet', href: '/financial-reports/balance-sheet',
@@ -162,6 +232,10 @@ export default [
text: <T id={'profit_loss_sheet'} />, text: <T id={'profit_loss_sheet'} />,
href: '/financial-reports/profit-loss-sheet', href: '/financial-reports/profit-loss-sheet',
}, },
{
text: <T id={'cash_flow_statement'} />,
href: '/financial-reports/cash-flow',
},
{ {
text: <T id={'AR_Aging_Summary'} />, text: <T id={'AR_Aging_Summary'} />,
href: '/financial-reports/receivable-aging-summary', href: '/financial-reports/receivable-aging-summary',
@@ -170,6 +244,52 @@ export default [
text: <T id={'AP_Aging_Summary'} />, text: <T id={'AP_Aging_Summary'} />,
href: '/financial-reports/payable-aging-summary', href: '/financial-reports/payable-aging-summary',
}, },
{
text: <T id={'sales_purchases_reports'} />,
label: true,
},
{
divider: true,
},
{
text: <T id={'purchases_by_items'} />,
href: '/financial-reports/purchases-by-items',
},
{
text: <T id={'sales_by_items'} />,
href: '/financial-reports/sales-by-items',
},
{
text: <T id={'customers_transactions'} />,
href: '/financial-reports/transactions-by-customers',
},
{
text: <T id={'vendors_transactions'} />,
href: '/financial-reports/transactions-by-vendors',
},
{
text: <T id={'customers_balance_summary'} />,
href: '/financial-reports/customers-balance-summary',
},
{
text: <T id={'vendors_balance_summary'} />,
href: '/financial-reports/vendors-balance-summary',
},
{
text: <T id={'inventory'} />,
label: true,
},
{
divider: true,
},
{
text: <T id={'inventory_item_details'} />,
href: '/financial-reports/inventory-item-details',
},
{
text: <T id={'inventory_valuation'} />,
href: '/financial-reports/inventory-valuation',
},
], ],
}, },
{ {

View File

@@ -1172,5 +1172,9 @@
"invoice_details":"Invoice details", "invoice_details":"Invoice details",
"receipt_details":"Receipt details", "receipt_details":"Receipt details",
"payment_receive_details":"Payment receive details", "payment_receive_details":"Payment receive details",
"payment_made_details":"Payment made details" "payment_made_details":"Payment made details",
"New item category": "New item category",
"New service": "New service",
"New inventory item": "New inventory item",
"New purchase invoice": "New purchase invoice"
} }

View File

@@ -204,7 +204,8 @@ html[lang^="ar"] {
.sidebar-overlay { .sidebar-overlay {
background: #fff; background: #fff;
height: 100%; height: 100%;
width: 300px; width: 240px;
outline: 0;
&__scroll-wrapper { &__scroll-wrapper {
height: 100% height: 100%
@@ -237,23 +238,23 @@ html[lang^="ar"] {
&.ScrollbarsCustom-ThumbX, &.ScrollbarsCustom-ThumbX,
&.ScrollbarsCustom-ThumbY { &.ScrollbarsCustom-ThumbY {
background: rgba(255, 255, 255, 0.25); background: rgba(0, 0, 0, 0.5);
} }
} }
} }
&__menu { &__menu {
margin: 20px 0; margin: 16px 0;
} }
&__item { &__item {
font-size: 15px; font-size: 15px;
color: #032259; color: #001944;
a { a {
color: inherit; color: inherit;
display: block; display: block;
padding: 9px 22px; padding: 10px 22px;
text-decoration: none; text-decoration: none;
&:hover, &:hover,
@@ -265,15 +266,56 @@ html[lang^="ar"] {
&__divider { &__divider {
height: 1px; height: 1px;
margin: 4px 0; margin: 6px 0;
background: #ebebeb; background: #e2e5ec;
} }
&__label{ &__label{
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; font-size: 12px;
padding: 4px 20px; padding: 14px 20px 10px;
letter-spacing: 0.8px; letter-spacing: 1px;
color: #707a85; color: #707a85;
} }
&__label + .sidebar-overlay__divider{
margin-top: 0;
}
}
.sidebar-overlay-transition {
transform: translateX(-100%);
&.bp3-overlay{
&-appear,
&-enter {
transform: translateX(-100%)
}
&-appear-active,
&-enter-active {
transform: translateX(0) !important;
transition: all 200ms ease-in-out;
}
&-appear-done,
&-enter-done {
transform: translateX(0) !important;
}
&-exit {
transform: translateX(0) !important;
}
&-exit-active {
transform: translateX(-100%) !important;
transition: all 200ms ease-in-out;
}
&-exit-done{
transform: translateX(-100%) !important;
}
}
}
.sidebar-overlay-backdrop{
background-color: rgba(0, 10, 30, 0.6);
} }