Skip to content

Commit 2a432a6

Browse files
Merged SideBarLayoutContext and PaneMenulItem (#37)
- Removed `SideBarLayoutContext` as can be replaced by `PanelSideBarLayoutContext` - Merged `PaneMenulItem` into `PanelItem`
1 parent eb09843 commit 2a432a6

11 files changed

Lines changed: 85 additions & 155 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- PanelSidebar cypress test and toggle it on any screen size
1313

14+
### Merged
15+
16+
- Merged `PaneMenulItem` into `PanelItem`
17+
1418
### Removed
1519

1620
- `DateHandler` utils as it belongs to another package and will be replaced in the future
21+
- Removed `SideBarLayoutContext` as can be replaced by `PanelSideBarLayoutContext`
1722

1823
### Fixed
1924

src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ export * from "./lib/Layout/AuthenticationLayout";
33

44
// Panel sidebar layout
55
export * from "./lib/Layout/PanelSideBarLayout/PanelSideBarLayout";
6-
export * from "./lib/Layout/PanelSideBarLayout/Context/PanelSideBarLayoutContext";
76
export * from "./lib/Layout/PanelSideBarLayout/PanelSideBar/Context/PanelSideBarContext";
87
export * from "./lib/Layout/PanelSideBarLayout/PanelSideBar/Definitions/PanelSideBarMenuItem";
98

109
// Sidebar layout
1110
export * from "./lib/Layout/SideBarLayout/SideBarLayout";
12-
export * from "./lib/Layout/SideBarLayout/SideBarLayoutContext";
1311
export * from "./lib/SideBar/SideBarMenuContext";
1412
export * from "./lib/Paging/Paging";
1513
export * from "./lib/SideBar/ISideBarMenuItem";

src/lib/Layout/PanelSideBarLayout/Context/PanelSideBarLayoutContext.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/lib/Layout/PanelSideBarLayout/PanelSideBar/Context/PanelSideBarContext.tsx

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,74 @@
11
import React, { ComponentType, createContext, ReactNode, useContext, useState } from "react";
2-
import { PanelItem, PanelMenuItem } from "../Definitions/PanelSideBarMenuItem";
2+
import { PanelItem } from "../Definitions/PanelSideBarMenuItem";
33

44
export interface PanelLinkRendererProps<T> {
5-
item: PanelMenuItem<T>;
5+
item: PanelItem<T>;
66
children: ReactNode;
77
}
88

9-
export type MenuItemToggleFn<TMenuItem> = (menuItem: PanelMenuItem<TMenuItem>) => void;
9+
export type MenuItemToggleFn<TMenuItem> = (menuItem: PanelItem<TMenuItem>) => void;
1010

1111
export interface PanelSideBarContextProps<TPanelItem, TMenuItem> {
1212
activePanelId: string;
1313
/**
1414
* The global panel items.
1515
*/
1616
globalItems: PanelItem<TPanelItem, TMenuItem>[];
17+
/**
18+
* The local panel items.
19+
*/
20+
localItems: PanelItem[];
1721
/**
1822
* The component used to render the menu item links.
1923
*/
2024
setActivePanel: (panelId: string) => void;
2125
LinkRenderer: ComponentType<PanelLinkRendererProps<TMenuItem>>;
2226
toggledMenuItemIds: string[];
2327
toggleMenuItem: MenuItemToggleFn<TMenuItem>;
28+
/**
29+
* The footer content.
30+
*/
31+
footer?: ReactNode;
32+
/**
33+
* The brand content shown on the top navigation bar.
34+
*/
35+
brand?: ReactNode;
36+
/**
37+
* The user dropdown toggle content.
38+
*/
39+
userDropDownMenuToggle?: ReactNode;
40+
/**
41+
* The user dropdown menu content.
42+
*/
43+
userDropDownMenu?: ReactNode;
44+
/**
45+
* The other menu content.
46+
*/
47+
topBarCustomItems?: ReactNode[];
2448
}
2549

2650
export const PanelSideBarContext = createContext<PanelSideBarContextProps<any, any> | null>(null);
2751

2852
export interface PanelSideBarMenuProviderProps<TPanelItem, TMenuItem>
29-
extends Pick<PanelSideBarContextProps<TPanelItem, TMenuItem>, "globalItems" | "LinkRenderer"> {
53+
extends Pick<PanelSideBarContextProps<TPanelItem, TMenuItem>, "globalItems" | "LinkRenderer" | "brand" | "footer" | "userDropDownMenu" | "userDropDownMenuToggle" | "topBarCustomItems" | "localItems"> {
3054
/**
3155
* The children elements.
3256
*/
3357
children: React.ReactNode;
3458
}
3559

3660
export const PanelSideBarProvider = <TPanelItem, TMenuItem>(props: PanelSideBarMenuProviderProps<TPanelItem, TMenuItem>) => {
37-
const { children, globalItems, LinkRenderer } = props;
61+
const { children, globalItems, localItems, LinkRenderer, brand = null, footer = null, userDropDownMenu, userDropDownMenuToggle, topBarCustomItems } = props;
3862

39-
const firstActivePanel = globalItems.find((x) => x.items?.find((y) => y.active)) ?? globalItems.find((x) => x.id);
63+
const firstActivePanel = globalItems.find(x => x.children
64+
?.find(y => y.children ? y.children.find(s => s.active) : y.active));
4065

4166
const getActivePanelId = () => firstActivePanel?.id ?? "";
4267

4368
const [activePanelId, setActivePanelId] = useState(getActivePanelId());
4469

45-
const [toggledMenuItemIds, setToggledMenuItemIds] = useState<string[]>([firstActivePanel?.items?.find((x) => x.active)?.id ?? ""]);
70+
const [toggledMenuItemIds, setToggledMenuItemIds] = useState<string[]>([(firstActivePanel?.children ?
71+
firstActivePanel.children?.find((x) => x.children?.find(s => s.active)) : firstActivePanel?.children?.find((x) => x.active))?.id ?? ""]);
4672

4773
const setActivePanel = (panelId: string) => setActivePanelId(panelId);
4874

@@ -63,10 +89,16 @@ export const PanelSideBarProvider = <TPanelItem, TMenuItem>(props: PanelSideBarM
6389
value={{
6490
activePanelId,
6591
globalItems,
92+
localItems,
6693
LinkRenderer,
6794
setActivePanel,
6895
toggledMenuItemIds,
6996
toggleMenuItem,
97+
footer,
98+
userDropDownMenu,
99+
userDropDownMenuToggle,
100+
topBarCustomItems,
101+
brand
70102
}}
71103
>
72104
{children}
Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { IconProp } from "@fortawesome/fontawesome-svg-core";
2-
import { ReactNode } from "react";
2+
import {ReactNode} from "react";
33

44
export type PanelItem<TPanelItem = Record<string, unknown>, TMenuItem = Record<string, unknown>> = TPanelItem & {
55
/**
66
* The panel icon.
77
*/
8-
icon: IconProp;
8+
icon?: IconProp;
99
/**
1010
* The panel item identifier.
1111
*/
@@ -19,34 +19,15 @@ export type PanelItem<TPanelItem = Record<string, unknown>, TMenuItem = Record<s
1919
/**
2020
* The panel menu items.
2121
*/
22-
items: PanelMenuItem<TMenuItem>[];
22+
children?: PanelItem<TMenuItem>[];
2323
/**
2424
* Whether the panel is disabled.
2525
*/
2626
disabled?: boolean;
2727
/**
2828
* The panel title.
2929
*/
30-
title?: string;
31-
};
32-
33-
export type PanelMenuItem<T = Record<string, unknown>> = T & {
34-
/**
35-
* The menu item identifier.
36-
*/
37-
id: string;
38-
/**
39-
* The menu item title.
40-
*/
4130
title: ReactNode;
42-
/**
43-
* Whether the item is active.
44-
*/
45-
active?: boolean;
46-
/**
47-
* The menu item children items.
48-
*/
49-
children?: Omit<PanelMenuItem<T>, "children">[];
5031
/**
5132
* Whether the item should be displayed.
5233
*/
@@ -56,7 +37,7 @@ export type PanelMenuItem<T = Record<string, unknown>> = T & {
5637
*/
5738
expanded?: boolean;
5839
/**
59-
* The menu item icon.
40+
* Whether the item is active.
6041
*/
61-
icon?: IconProp;
42+
active?: boolean;
6243
};

src/lib/Layout/PanelSideBarLayout/PanelSideBar/PanelSideBarItem.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
import { IconProp } from "@fortawesome/fontawesome-svg-core";
21
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
32
import classNames from "classnames";
4-
import { ComponentType } from "react";
3+
import {ComponentType, useState} from "react";
54
import { Collapse, NavItem } from "reactstrap";
65
import { LinkRendererProps } from "src/lib/SideBar/SideBarMenuContext";
7-
import { PanelMenuItem } from "./Definitions/PanelSideBarMenuItem";
6+
import {PanelItem} from "src/lib/Layout/PanelSideBarLayout/PanelSideBar/Definitions/PanelSideBarMenuItem";
87

98
export interface PanelSideBarItemProps {
10-
item: PanelMenuItem<unknown>;
9+
children: PanelItem<unknown>;
1110
LinkRenderer: ComponentType<LinkRendererProps>;
12-
onClick?: (menuItem: PanelMenuItem<unknown>) => void;
11+
onClick?: (menuItem: PanelItem<unknown>) => void;
1312
depth?: number;
1413
active?: boolean;
1514
toggledItemIds: string[];
1615
}
1716

1817
const PanelSideBarItem = (props: PanelSideBarItemProps) => {
19-
const { depth = 0, item, LinkRenderer, onClick, toggledItemIds = [] } = props;
20-
21-
const hasChildren = !!item.children?.length;
22-
const isOpen = toggledItemIds?.includes(item.id) || item.expanded;
18+
const { depth = 0, children: item, LinkRenderer, onClick, toggledItemIds = [] } = props;
2319

20+
const hasitem = !!item.children?.length;
21+
const [isOpen, setIsOpen] = useState(toggledItemIds?.includes(item.id) || item.expanded);
2422
if (item.display === false) {
2523
return null;
2624
}
@@ -29,34 +27,34 @@ const PanelSideBarItem = (props: PanelSideBarItemProps) => {
2927
<>
3028
<NavItem
3129
onClick={() => onClick && onClick(item)}
32-
className={classNames({ "menu-open": isOpen, active: item.active })}
30+
className={classNames({ "menu-open": isOpen, active: item?.children ? item.children?.find(s => s.active) : item.active })}
3331
style={{ paddingLeft: depth ? `${depth + 1}rem` : undefined }}
3432
>
35-
{hasChildren ? (
36-
<div className={classNames({ dropend: !isOpen })}>
37-
<a role="button" className={classNames("nav-link", { "dropdown-toggle": hasChildren })}>
38-
{item.icon && <FontAwesomeIcon className="me-2" icon={item.icon as IconProp} />}
33+
{hasitem ? (
34+
<div>
35+
<a role="button" className={classNames("nav-link", { "dropdown-toggle": hasitem })} onClick={() => setIsOpen(!isOpen)}>
36+
{item.icon && <FontAwesomeIcon className="me-2" icon={item.icon} />}
3937
<div className="text-justify">{item.title}</div>
4038
</a>
4139
</div>
4240
) : (
4341
<>
4442
<LinkRenderer item={item}>
4543
<span className="nav-link">
46-
{item.icon && <FontAwesomeIcon icon={item.icon as IconProp} className="me-2" />}
44+
{item.icon && <FontAwesomeIcon icon={item.icon} className="me-2" />}
4745
{item.title}
4846
</span>
4947
</LinkRenderer>
5048
</>
5149
)}
5250
</NavItem>
5351

54-
{hasChildren && (
55-
<Collapse isOpen={isOpen} navbar className={classNames("items-menu", { "mb-1": isOpen })}>
52+
{hasitem && (
53+
<Collapse isOpen={isOpen} navbar className={classNames("item-menu", { "mb-1": isOpen })}>
5654
{item.children?.map((childItem) => (
5755
<PanelSideBarItem
5856
key={childItem.id}
59-
item={childItem}
57+
children={childItem}
6058
LinkRenderer={LinkRenderer}
6159
onClick={() => onClick && onClick(childItem)}
6260
depth={depth + 1}

src/lib/Layout/PanelSideBarLayout/PanelSideBar/PanelSidebar.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { usePanelSideBarContext } from "./Context/PanelSideBarContext";
55
import { PanelItem } from "./Definitions/PanelSideBarMenuItem";
66
import { PanelSideBarItem } from "./PanelSideBarItem";
77
import { useState } from "react";
8-
interface PanelSideBarProps {
9-
localItems?: PanelItem[];
10-
}
118

12-
export const PanelSideBar = (props: PanelSideBarProps) => {
13-
const { localItems = [] } = props;
9+
export const PanelSideBar = () => {
10+
const { activePanelId, globalItems, localItems, LinkRenderer, setActivePanel, toggledMenuItemIds, toggleMenuItem } = usePanelSideBarContext();
11+
const panelItems = localItems.concat(globalItems);
12+
13+
if (globalItems.find(x => !x.icon) || localItems.find(x => !x.icon)) {
14+
throw new Error("Outer panel icon is required");
15+
}
1416

1517
let localMenuId: string | null = null;
1618

@@ -21,11 +23,8 @@ export const PanelSideBar = (props: PanelSideBarProps) => {
2123

2224
const [localItemId, setLocalItemId] = useState<string | null>(localMenuId);
2325

24-
const { activePanelId, globalItems, LinkRenderer, setActivePanel, toggledMenuItemIds, toggleMenuItem } = usePanelSideBarContext();
25-
2626
const localActivePanelId = localItemId ?? activePanelId;
27-
const activePanel: PanelItem = globalItems.find((x) => x.id === localActivePanelId);
28-
const localActivePanel: PanelItem | undefined = localItems?.find((x) => x.id === localActivePanelId);
27+
const activePanel: PanelItem | undefined = panelItems.find((x) => x.id === localActivePanelId);
2928

3029
const panelItemsRenderer = (items: PanelItem[]) =>
3130
items?.map(({ disabled, icon, onClick, id, title }) => (
@@ -42,10 +41,10 @@ export const PanelSideBar = (props: PanelSideBarProps) => {
4241
setActivePanel(id);
4342
}
4443
}}
45-
title={title}
44+
title={typeof title == "string" ? String(title) : ""}
4645
disabled={disabled}
4746
>
48-
<FontAwesomeIcon icon={icon} size="lg" fixedWidth />
47+
{icon && <FontAwesomeIcon icon={icon} size="lg" fixedWidth />}
4948
</Button>
5049
));
5150

@@ -57,19 +56,15 @@ export const PanelSideBar = (props: PanelSideBarProps) => {
5756
</div>
5857

5958
<div className="side-nav__items">
60-
{activePanel?.items?.map((item) => (
59+
{activePanel?.children?.map((item) => (
6160
<PanelSideBarItem
6261
key={item.id}
63-
item={item}
62+
children={item}
6463
LinkRenderer={LinkRenderer}
6564
onClick={(menuItem) => toggleMenuItem(menuItem)}
6665
toggledItemIds={toggledMenuItemIds}
6766
/>
6867
))}
69-
70-
{localActivePanel?.items?.map((item) => (
71-
<PanelSideBarItem key={item.id} item={item} LinkRenderer={LinkRenderer} toggledItemIds={toggledMenuItemIds} />
72-
))}
7368
</div>
7469
</nav>
7570
);

0 commit comments

Comments
 (0)