Skip to content

Commit 4924335

Browse files
author
Gianmarco Manni
committed
partial
1 parent 7e16567 commit 4924335

14 files changed

Lines changed: 272 additions & 315 deletions

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Possibility to set fully control `menuItems`
13+
- Possibility to provide custom render component, e.g. skeleton, for mananing special case like asynchronus render (TODO)
14+
- Possibility to dinamically open or close `sidebar`
15+
16+
### Changed
17+
18+
- `topBarLeftCustomItems` and `topBarRigthCustomItems` renamed to `navbarLeftItems` and `navbarRightItems`
19+
- PanelItem Id type changed from `string` to being strongly typed
20+
21+
### Fixed
22+
23+
- When `footer` is null, the whole section will not be rendered
24+
25+
### Removed
26+
27+
- `Delete Action`component
28+
- Built-in support for `userDropdown`. It should provide it in the navbar items.
29+
1030
## [3.4.0] - 2024-03-12
1131

1232
### Added

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export * from "./lib/DeleteAction/DeleteAction";
21
export * from "./lib/Layout/AuthenticationLayout";
32

43
// Panel sidebar layout

src/lib/DeleteAction/DeleteAction.tsx

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

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

Lines changed: 46 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,82 @@
1-
import React, { ComponentType, createContext, ReactNode, useContext, useState } from "react";
2-
import { PanelItem } from "../Definitions/PanelItem";
1+
import React, { Context, createContext, useCallback, useContext, useEffect, useState } from "react";
2+
import { getActivePanel } from "../Utils/getActivePanel";
3+
import { PanelSideBarContextProps } from "./PanelSideBarContextProps";
34

4-
export interface PanelLinkRendererProps<T> {
5-
/**
6-
* The generic panel item.
7-
*/
8-
item: PanelItem<T>;
9-
/**
10-
* The panel children item.
11-
*/
12-
children: ReactNode;
13-
}
14-
15-
export type MenuItemToggleFn<TPanelItem> = (menuItem: PanelItem<TPanelItem>) => void;
16-
17-
export interface PanelSideBarContextProps<TPanelItem> {
18-
activePanelId: string;
19-
/**
20-
* The global panel items.
21-
*/
22-
globalItems: PanelItem<TPanelItem>[];
23-
/**
24-
* The local panel items.
25-
*/
26-
localItems?: PanelItem[];
27-
/**
28-
* The function used to set a panel as active
29-
* @param panelId The panel item identifier
30-
*/
31-
setActivePanel: (panelId: string) => void;
32-
/**
33-
* The component used to render the menu item links.
34-
*/
35-
LinkRenderer: ComponentType<PanelLinkRendererProps<TPanelItem>>;
36-
/**
37-
* The list of toggled menu item identifier
38-
*/
39-
toggledMenuItemIds: string[];
40-
/**
41-
* The function used to toggle a menu item
42-
*/
43-
toggleMenuItem: MenuItemToggleFn<TPanelItem>;
44-
/**
45-
* The footer content.
46-
*/
47-
footer?: ReactNode;
48-
/**
49-
* The brand content shown on the top navigation bar.
50-
*/
51-
brand?: ReactNode;
52-
/**
53-
* The user dropdown toggle content.
54-
*/
55-
userDropDownMenuToggle?: ReactNode;
56-
/**
57-
* The user dropdown menu content.
58-
*/
59-
userDropDownMenu?: ReactNode;
60-
/**
61-
* The menu content on the right.
62-
*/
63-
topBarRightCustomItems?: ReactNode[];
64-
/**
65-
* The menu content on the left.
66-
*/
67-
topBarLeftCustomItems?: ReactNode[];
68-
/**
69-
* The context theme
70-
*/
71-
theme?: "light";
72-
/**
73-
* Boolean indicating if you want to render first items level as icons or directly as menu entries
74-
*/
75-
renderFirstItemsLevelAsTiles?: boolean;
76-
77-
/**
78-
* Boolean indicating if you want to render first level items as links or as button
79-
*/
80-
renderTilesAsLinks?: boolean;
81-
82-
/**
83-
* The default active panel id that will be taken if no active panel is dinamically found
84-
*/
85-
defaultActivePanelId?: string;
86-
}
5+
export type MenuItemToggleFn<TPanelItemId extends string> = (menuItemId: TPanelItemId) => void;
876

88-
export const PanelSideBarContext = createContext<PanelSideBarContextProps<any> | null>(null);
7+
const PanelSideBarContext = createContext<PanelSideBarContextProps<any, any> | null>(null);
898

90-
export interface PanelSideBarMenuProviderProps<TPanelItem>
91-
extends Pick<
92-
PanelSideBarContextProps<TPanelItem>,
93-
| "globalItems"
94-
| "LinkRenderer"
95-
| "brand"
96-
| "footer"
97-
| "userDropDownMenu"
98-
| "userDropDownMenuToggle"
99-
| "topBarRightCustomItems"
100-
| "topBarLeftCustomItems"
101-
| "localItems"
102-
| "theme"
103-
| "renderFirstItemsLevelAsTiles"
104-
| "renderTilesAsLinks"
105-
| "defaultActivePanelId"
106-
> {
9+
export interface PanelSideBarMenuProviderProps<TPanelItemId extends string, TPanelItem>
10+
extends Pick<PanelSideBarContextProps<TPanelItemId, TPanelItem>, "menuItems" | "defaultActivePanelId"> {
10711
/**
10812
* The children elements.
10913
*/
11014
children: React.ReactNode;
111-
}
112-
113-
export const PanelSideBarProvider = <TPanelItem,>(props: PanelSideBarMenuProviderProps<TPanelItem>) => {
114-
const {
115-
children,
116-
globalItems,
117-
localItems = [],
118-
LinkRenderer,
119-
brand = null,
120-
footer = null,
121-
userDropDownMenu,
122-
userDropDownMenuToggle,
123-
topBarRightCustomItems,
124-
topBarLeftCustomItems,
125-
renderFirstItemsLevelAsTiles = true,
126-
renderTilesAsLinks = false,
127-
theme = "light",
128-
defaultActivePanelId,
129-
} = props;
130-
131-
const activePanel = globalItems.find((x) =>
132-
x.children ? x.children.find((y) => (y.children ? y.children.find((s) => s.active) : y.active)) : x.active,
133-
);
134-
const firstActivePanel = activePanel ?? globalItems.find((x) => (defaultActivePanelId ? x.id === defaultActivePanelId : x.id));
13515

136-
const getActivePanelId = () => localItems?.at(0)?.id ?? firstActivePanel?.id ?? "";
137-
138-
const [activePanelId, setActivePanelId] = useState(getActivePanelId());
16+
/**
17+
* if the sidebar should be open by default.
18+
*/
19+
sidebarOpenByDefault?: boolean;
20+
}
13921

140-
const [toggledMenuItemIds, setToggledMenuItemIds] = useState<string[]>([
141-
(firstActivePanel?.children
142-
? firstActivePanel.children?.find((x) => x.children?.find((s) => s.active))
143-
: firstActivePanel?.children?.find((x) => x.active)
144-
)?.id ?? "",
145-
]);
22+
export const PanelSideBarProvider = <TPanelItemId extends string, TPanelItem>(
23+
props: PanelSideBarMenuProviderProps<TPanelItemId, TPanelItem>,
24+
) => {
25+
const { children, defaultActivePanelId, sidebarOpenByDefault = true } = props;
26+
const [menuItems, setMenuItems] = useState(props.menuItems);
27+
const [isSidebarOpen, setIsSidebarOpen] = useState(sidebarOpenByDefault);
28+
const toggleSidebar = () => setIsSidebarOpen((prev) => !prev);
14629

147-
const setActivePanel = (panelId: string) => setActivePanelId(panelId);
30+
const [activePanelId, setActivePanelId] = useState(getActivePanel(menuItems, defaultActivePanelId)?.id);
31+
const [toggledMenuItemIds, setToggledMenuItemIds] = useState<TPanelItemId[]>(activePanelId ? [activePanelId] : []);
32+
const setActivePanel = (panelId: TPanelItemId) => setActivePanelId(panelId);
14833

149-
const toggleMenuItem: MenuItemToggleFn<TPanelItem> = (menuItem) => {
34+
const toggleMenuItem: MenuItemToggleFn<TPanelItemId> = (menuItemId) => {
15035
setToggledMenuItemIds((prev) => {
151-
const idExists = !!prev.find((id) => id == menuItem.id);
36+
const idExists = !!prev.find((id) => id == menuItemId);
15237

15338
if (idExists) {
154-
return prev.filter((id) => id !== menuItem.id);
39+
return prev.filter((id) => id !== menuItemId);
15540
}
15641

157-
return [...prev, menuItem.id];
42+
return [...prev, menuItemId];
15843
});
15944
};
16045

46+
const untoggleMenuItems = () => setToggledMenuItemIds([]);
47+
48+
const recomputeActivePanel = useCallback(() => {
49+
const activePanelId = getActivePanel(menuItems, defaultActivePanelId)?.id;
50+
setActivePanelId(activePanelId);
51+
if (activePanelId) {
52+
setToggledMenuItemIds(prev => prev.includes(activePanelId) ? prev : [...prev, activePanelId]);
53+
}
54+
}, []);
55+
56+
useEffect(() => recomputeActivePanel(), [menuItems]);
57+
16158
return (
16259
<PanelSideBarContext.Provider
16360
value={{
61+
menuItems,
62+
setMenuItems,
16463
activePanelId,
165-
globalItems,
166-
localItems,
167-
LinkRenderer,
16864
setActivePanel,
65+
recomputeActivePanel,
16966
toggledMenuItemIds,
17067
toggleMenuItem,
171-
footer,
172-
userDropDownMenu,
173-
userDropDownMenuToggle,
174-
topBarRightCustomItems,
175-
topBarLeftCustomItems,
176-
brand,
177-
theme,
178-
renderFirstItemsLevelAsTiles,
179-
renderTilesAsLinks,
68+
untoggleMenuItems,
69+
isSidebarOpen,
70+
toggleSidebar,
18071
}}
18172
>
18273
{children}
18374
</PanelSideBarContext.Provider>
18475
);
18576
};
18677

187-
export const usePanelSideBarContext = () => {
188-
const context = useContext(PanelSideBarContext);
78+
export const usePanelSideBarContext = <TPanelItemId extends string, TPanelItem>() => {
79+
const context = useContext(PanelSideBarContext as Context<PanelSideBarContextProps<TPanelItemId, TPanelItem>>);
18980
if (context === null) {
19081
throw new Error("usePanelSideBarContext must be used within a PanelSideBarProvider");
19182
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Dispatch, SetStateAction } from "react";
2+
import { PanelItem } from "../Definitions/PanelItem";
3+
import { MenuItemToggleFn } from "./PanelSideBarContext";
4+
5+
export interface PanelSideBarContextProps<TPanelItemId extends string, TPanelItem> {
6+
/**
7+
* The active panel id.
8+
*/
9+
activePanelId?: TPanelItemId;
10+
/**
11+
* The menu items.
12+
*/
13+
menuItems: PanelItem<TPanelItemId, TPanelItem>[];
14+
15+
/**
16+
* The setter for menu items.
17+
*/
18+
setMenuItems: Dispatch<SetStateAction<PanelItem<TPanelItemId, TPanelItem>[]>>;
19+
/**
20+
* The function used to set a panel as active
21+
* @param panelId The panel item identifier
22+
*/
23+
setActivePanel: (panelId: TPanelItemId) => void;
24+
/**
25+
* The list of toggled menu item identifier
26+
*/
27+
toggledMenuItemIds: string[];
28+
/**
29+
* The function used to toggle a menu item
30+
*/
31+
toggleMenuItem: MenuItemToggleFn<TPanelItemId>;
32+
33+
/**
34+
* The default active panel id that will be taken if no active panel is dinamically found
35+
*/
36+
defaultActivePanelId?: TPanelItemId;
37+
38+
/**
39+
* Function for untoggling all menu items
40+
*/
41+
untoggleMenuItems: () => void;
42+
43+
/**
44+
* Function for recomputing active item
45+
*/
46+
recomputeActivePanel: () => void;
47+
48+
/**
49+
* If the sidebar is currently open or not
50+
*/
51+
isSidebarOpen: boolean;
52+
/**
53+
* Function for toggling sidebar
54+
*/
55+
toggleSidebar: () => void;
56+
}

0 commit comments

Comments
 (0)