@etsoo/toolpad 1.0.29 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/AppProvider/AppProvider.d.ts +1 -0
- package/build/cjs/PageContainer/PageContainer.d.ts +5 -6
- package/build/cjs/PageContainer/PageContainer.js +29 -27
- package/build/cjs/useActivePage/useActivePage.d.ts +2 -0
- package/build/cjs/useActivePage/useActivePage.js +13 -21
- package/build/mjs/AppProvider/AppProvider.d.ts +1 -0
- package/build/mjs/PageContainer/PageContainer.d.ts +5 -6
- package/build/mjs/PageContainer/PageContainer.js +29 -27
- package/build/mjs/useActivePage/useActivePage.d.ts +2 -0
- package/build/mjs/useActivePage/useActivePage.js +13 -21
- package/package.json +1 -1
- package/src/AppProvider/AppProvider.tsx +1 -0
- package/src/PageContainer/PageContainer.tsx +60 -54
- package/src/useActivePage/useActivePage.ts +15 -25
|
@@ -25,8 +25,7 @@ export type PageData = {
|
|
|
25
25
|
title?: string;
|
|
26
26
|
page?: string;
|
|
27
27
|
breadcrumbs?: Breadcrumb[];
|
|
28
|
-
|
|
29
|
-
noPageHeader?: boolean;
|
|
28
|
+
pageHeader?: React.ReactNode;
|
|
30
29
|
};
|
|
31
30
|
type PageDataAction = PageData;
|
|
32
31
|
export declare const PageDataContext: React.Context<{
|
|
@@ -35,10 +34,6 @@ export declare const PageDataContext: React.Context<{
|
|
|
35
34
|
}>;
|
|
36
35
|
export declare function PageDataContextProvider(props: React.PropsWithChildren<PageData>): import("react/jsx-runtime").JSX.Element;
|
|
37
36
|
type PageContainerBarProps = {
|
|
38
|
-
/**
|
|
39
|
-
* The default title of the page.
|
|
40
|
-
*/
|
|
41
|
-
defaultTitle?: string;
|
|
42
37
|
/**
|
|
43
38
|
* The components used for each slot inside.
|
|
44
39
|
*/
|
|
@@ -47,6 +42,10 @@ type PageContainerBarProps = {
|
|
|
47
42
|
* The props used for each slot inside.
|
|
48
43
|
*/
|
|
49
44
|
slotProps?: PageContainerSlotProps;
|
|
45
|
+
/**
|
|
46
|
+
* The component that renders the actions toolbar.
|
|
47
|
+
*/
|
|
48
|
+
titleBar?: false | ((title: string) => React.ReactNode);
|
|
50
49
|
};
|
|
51
50
|
export type PageContainerProps = React.PropsWithChildren<StackProps & PageContainerBarProps>;
|
|
52
51
|
/**
|
|
@@ -64,7 +64,7 @@ function reducer(state, action) {
|
|
|
64
64
|
let key;
|
|
65
65
|
let isSame = true;
|
|
66
66
|
for (key in action) {
|
|
67
|
-
if (action[key]
|
|
67
|
+
if (action[key] != state[key]) {
|
|
68
68
|
isSame = false;
|
|
69
69
|
break;
|
|
70
70
|
}
|
|
@@ -76,38 +76,30 @@ function reducer(state, action) {
|
|
|
76
76
|
}
|
|
77
77
|
function PageDataContextProvider(props) {
|
|
78
78
|
// Destruct
|
|
79
|
-
const { title, page, breadcrumbs,
|
|
79
|
+
const { title, page, breadcrumbs, pageHeader, ...rest } = props;
|
|
80
80
|
// useReducer hook to manage state with our reducer function and initial state
|
|
81
81
|
const [state, dispatch] = React.useReducer(reducer, {
|
|
82
82
|
title,
|
|
83
83
|
page,
|
|
84
84
|
breadcrumbs,
|
|
85
|
-
|
|
86
|
-
noPageHeader
|
|
85
|
+
pageHeader
|
|
87
86
|
});
|
|
88
87
|
// Provide the state and dispatch function to the context value
|
|
89
88
|
return (0, jsx_runtime_1.jsx)(exports.PageDataContext.Provider, { value: { state, dispatch }, ...rest });
|
|
90
89
|
}
|
|
91
90
|
function PageContainerBar(props) {
|
|
92
|
-
const {
|
|
91
|
+
const { slots, slotProps, titleBar } = props;
|
|
93
92
|
const { state } = React.useContext(exports.PageDataContext);
|
|
94
93
|
const activePage = (0, useActivePage_1.useActivePage)();
|
|
95
|
-
React.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
return () => {
|
|
96
|
+
// Reset the state when the component unmounts
|
|
97
|
+
state.breadcrumbs = undefined;
|
|
98
|
+
state.page = undefined;
|
|
99
|
+
state.pageHeader = undefined;
|
|
100
|
+
state.title = undefined;
|
|
101
|
+
};
|
|
102
102
|
}, [activePage?.sourcePath]);
|
|
103
|
-
let resolvedBreadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
104
|
-
const title = state.title ?? defaultTitle ?? activePage?.title ?? "";
|
|
105
|
-
if (state.page) {
|
|
106
|
-
resolvedBreadcrumbs = [
|
|
107
|
-
...resolvedBreadcrumbs,
|
|
108
|
-
{ title: state.page, path: "#" }
|
|
109
|
-
];
|
|
110
|
-
}
|
|
111
103
|
const ToolbarComponent = slots?.toolbar ?? PageContainerToolbar_1.PageContainerToolbar;
|
|
112
104
|
const toolbarSlotProps = (0, useSlotProps_1.default)({
|
|
113
105
|
elementType: ToolbarComponent,
|
|
@@ -115,11 +107,21 @@ function PageContainerBar(props) {
|
|
|
115
107
|
externalSlotProps: slotProps?.toolbar,
|
|
116
108
|
additionalProps: {}
|
|
117
109
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
111
|
+
const title = state.title ?? activePage?.title ?? "";
|
|
112
|
+
const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
|
|
113
|
+
// No page header
|
|
114
|
+
if (pageHeader === false)
|
|
115
|
+
return undefined;
|
|
116
|
+
// Custom page header
|
|
117
|
+
if (pageHeader != null)
|
|
118
|
+
return pageHeader;
|
|
119
|
+
if (state.page) {
|
|
120
|
+
breadcrumbs.push({ title: state.page, path: "#" });
|
|
121
|
+
}
|
|
122
|
+
return ((0, jsx_runtime_1.jsxs)(Stack_1.default, { children: [breadcrumbs && ((0, jsx_runtime_1.jsx)(Breadcrumbs_1.default, { "aria-label": "breadcrumb", children: breadcrumbs.map((item, index) => {
|
|
123
|
+
return index < breadcrumbs.length - 1 ? ((0, jsx_runtime_1.jsx)(Link_1.default, { component: Link_2.Link, underline: "hover", color: "inherit", href: item.path, children: (0, navigation_1.getItemTitle)(item) }, item.path)) : ((0, jsx_runtime_1.jsx)(Typography_1.default, { color: "text.primary", children: (0, navigation_1.getItemTitle)(item) }, item.path));
|
|
124
|
+
}) })), (0, jsx_runtime_1.jsxs)(PageContentHeader, { children: [typeof titleBar === "function" ? (titleBar(title)) : titleBar === false ? undefined : ((0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "h4", children: title })), (0, jsx_runtime_1.jsx)(ToolbarComponent, { ...toolbarSlotProps })] })] }));
|
|
123
125
|
}
|
|
124
126
|
/**
|
|
125
127
|
* A container component to provide a title and breadcrumbs for your pages.
|
|
@@ -133,6 +135,6 @@ function PageContainerBar(props) {
|
|
|
133
135
|
* - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
|
|
134
136
|
*/
|
|
135
137
|
function PageContainer(props) {
|
|
136
|
-
const { children,
|
|
137
|
-
return ((0, jsx_runtime_1.jsxs)(Stack_1.default, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [(0, jsx_runtime_1.jsx)(PageContainerBar, {
|
|
138
|
+
const { children, slots, slotProps, titleBar, ...rest } = props;
|
|
139
|
+
return ((0, jsx_runtime_1.jsxs)(Stack_1.default, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [(0, jsx_runtime_1.jsx)(PageContainerBar, { slots: slots, slotProps: slotProps, titleBar: titleBar }), children] }));
|
|
138
140
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import type { Breadcrumb } from "../PageContainer";
|
|
2
3
|
export interface ActivePage {
|
|
3
4
|
title: string;
|
|
4
5
|
path: string;
|
|
5
6
|
sourcePath: string;
|
|
6
7
|
breadcrumbs: Breadcrumb[];
|
|
8
|
+
pageHeader?: React.ReactNode;
|
|
7
9
|
}
|
|
8
10
|
export declare function useActivePage(): ActivePage | null;
|
|
@@ -41,14 +41,13 @@ const navigation_1 = require("../shared/navigation");
|
|
|
41
41
|
function useActivePage() {
|
|
42
42
|
const navigationContext = React.useContext(context_1.NavigationContext);
|
|
43
43
|
const routerContext = React.useContext(context_1.RouterContext);
|
|
44
|
-
const
|
|
45
|
-
let pathname = routerContext?.pathname ?? "/";
|
|
44
|
+
const pathname = routerContext?.pathname ?? "/";
|
|
46
45
|
const activeItem = (0, navigation_1.matchPath)(navigationContext, pathname);
|
|
47
46
|
const rootItem = (0, navigation_1.matchPath)(navigationContext, "/");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
return React.useMemo(() => {
|
|
48
|
+
if (!activeItem) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
52
51
|
const breadcrumbs = [];
|
|
53
52
|
if (rootItem) {
|
|
54
53
|
breadcrumbs.push({
|
|
@@ -74,19 +73,12 @@ function useActivePage() {
|
|
|
74
73
|
});
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
path,
|
|
86
|
-
sourcePath: pathname,
|
|
87
|
-
breadcrumbs
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return pageRef.current;
|
|
76
|
+
return {
|
|
77
|
+
title: (0, navigation_1.getItemTitle)(activeItem),
|
|
78
|
+
path: (0, navigation_1.getItemPath)(navigationContext, activeItem),
|
|
79
|
+
sourcePath: pathname,
|
|
80
|
+
breadcrumbs,
|
|
81
|
+
pageHeader: activeItem.pageHeader
|
|
82
|
+
};
|
|
83
|
+
}, [activeItem, rootItem, pathname, navigationContext]);
|
|
92
84
|
}
|
|
@@ -25,8 +25,7 @@ export type PageData = {
|
|
|
25
25
|
title?: string;
|
|
26
26
|
page?: string;
|
|
27
27
|
breadcrumbs?: Breadcrumb[];
|
|
28
|
-
|
|
29
|
-
noPageHeader?: boolean;
|
|
28
|
+
pageHeader?: React.ReactNode;
|
|
30
29
|
};
|
|
31
30
|
type PageDataAction = PageData;
|
|
32
31
|
export declare const PageDataContext: React.Context<{
|
|
@@ -35,10 +34,6 @@ export declare const PageDataContext: React.Context<{
|
|
|
35
34
|
}>;
|
|
36
35
|
export declare function PageDataContextProvider(props: React.PropsWithChildren<PageData>): import("react/jsx-runtime").JSX.Element;
|
|
37
36
|
type PageContainerBarProps = {
|
|
38
|
-
/**
|
|
39
|
-
* The default title of the page.
|
|
40
|
-
*/
|
|
41
|
-
defaultTitle?: string;
|
|
42
37
|
/**
|
|
43
38
|
* The components used for each slot inside.
|
|
44
39
|
*/
|
|
@@ -47,6 +42,10 @@ type PageContainerBarProps = {
|
|
|
47
42
|
* The props used for each slot inside.
|
|
48
43
|
*/
|
|
49
44
|
slotProps?: PageContainerSlotProps;
|
|
45
|
+
/**
|
|
46
|
+
* The component that renders the actions toolbar.
|
|
47
|
+
*/
|
|
48
|
+
titleBar?: false | ((title: string) => React.ReactNode);
|
|
50
49
|
};
|
|
51
50
|
export type PageContainerProps = React.PropsWithChildren<StackProps & PageContainerBarProps>;
|
|
52
51
|
/**
|
|
@@ -23,7 +23,7 @@ function reducer(state, action) {
|
|
|
23
23
|
let key;
|
|
24
24
|
let isSame = true;
|
|
25
25
|
for (key in action) {
|
|
26
|
-
if (action[key]
|
|
26
|
+
if (action[key] != state[key]) {
|
|
27
27
|
isSame = false;
|
|
28
28
|
break;
|
|
29
29
|
}
|
|
@@ -35,38 +35,30 @@ function reducer(state, action) {
|
|
|
35
35
|
}
|
|
36
36
|
export function PageDataContextProvider(props) {
|
|
37
37
|
// Destruct
|
|
38
|
-
const { title, page, breadcrumbs,
|
|
38
|
+
const { title, page, breadcrumbs, pageHeader, ...rest } = props;
|
|
39
39
|
// useReducer hook to manage state with our reducer function and initial state
|
|
40
40
|
const [state, dispatch] = React.useReducer(reducer, {
|
|
41
41
|
title,
|
|
42
42
|
page,
|
|
43
43
|
breadcrumbs,
|
|
44
|
-
|
|
45
|
-
noPageHeader
|
|
44
|
+
pageHeader
|
|
46
45
|
});
|
|
47
46
|
// Provide the state and dispatch function to the context value
|
|
48
47
|
return _jsx(PageDataContext.Provider, { value: { state, dispatch }, ...rest });
|
|
49
48
|
}
|
|
50
49
|
function PageContainerBar(props) {
|
|
51
|
-
const {
|
|
50
|
+
const { slots, slotProps, titleBar } = props;
|
|
52
51
|
const { state } = React.useContext(PageDataContext);
|
|
53
52
|
const activePage = useActivePage();
|
|
54
|
-
React.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
return () => {
|
|
55
|
+
// Reset the state when the component unmounts
|
|
56
|
+
state.breadcrumbs = undefined;
|
|
57
|
+
state.page = undefined;
|
|
58
|
+
state.pageHeader = undefined;
|
|
59
|
+
state.title = undefined;
|
|
60
|
+
};
|
|
61
61
|
}, [activePage?.sourcePath]);
|
|
62
|
-
let resolvedBreadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
63
|
-
const title = state.title ?? defaultTitle ?? activePage?.title ?? "";
|
|
64
|
-
if (state.page) {
|
|
65
|
-
resolvedBreadcrumbs = [
|
|
66
|
-
...resolvedBreadcrumbs,
|
|
67
|
-
{ title: state.page, path: "#" }
|
|
68
|
-
];
|
|
69
|
-
}
|
|
70
62
|
const ToolbarComponent = slots?.toolbar ?? PageContainerToolbar;
|
|
71
63
|
const toolbarSlotProps = useSlotProps({
|
|
72
64
|
elementType: ToolbarComponent,
|
|
@@ -74,11 +66,21 @@ function PageContainerBar(props) {
|
|
|
74
66
|
externalSlotProps: slotProps?.toolbar,
|
|
75
67
|
additionalProps: {}
|
|
76
68
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
70
|
+
const title = state.title ?? activePage?.title ?? "";
|
|
71
|
+
const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
|
|
72
|
+
// No page header
|
|
73
|
+
if (pageHeader === false)
|
|
74
|
+
return undefined;
|
|
75
|
+
// Custom page header
|
|
76
|
+
if (pageHeader != null)
|
|
77
|
+
return pageHeader;
|
|
78
|
+
if (state.page) {
|
|
79
|
+
breadcrumbs.push({ title: state.page, path: "#" });
|
|
80
|
+
}
|
|
81
|
+
return (_jsxs(Stack, { children: [breadcrumbs && (_jsx(Breadcrumbs, { "aria-label": "breadcrumb", children: breadcrumbs.map((item, index) => {
|
|
82
|
+
return index < breadcrumbs.length - 1 ? (_jsx(Link, { component: ToolpadLink, underline: "hover", color: "inherit", href: item.path, children: getItemTitle(item) }, item.path)) : (_jsx(Typography, { color: "text.primary", children: getItemTitle(item) }, item.path));
|
|
83
|
+
}) })), _jsxs(PageContentHeader, { children: [typeof titleBar === "function" ? (titleBar(title)) : titleBar === false ? undefined : (_jsx(Typography, { variant: "h4", children: title })), _jsx(ToolbarComponent, { ...toolbarSlotProps })] })] }));
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
86
|
* A container component to provide a title and breadcrumbs for your pages.
|
|
@@ -92,7 +94,7 @@ function PageContainerBar(props) {
|
|
|
92
94
|
* - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
|
|
93
95
|
*/
|
|
94
96
|
function PageContainer(props) {
|
|
95
|
-
const { children,
|
|
96
|
-
return (_jsxs(Stack, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [_jsx(PageContainerBar, {
|
|
97
|
+
const { children, slots, slotProps, titleBar, ...rest } = props;
|
|
98
|
+
return (_jsxs(Stack, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [_jsx(PageContainerBar, { slots: slots, slotProps: slotProps, titleBar: titleBar }), children] }));
|
|
97
99
|
}
|
|
98
100
|
export { PageContainer };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import type { Breadcrumb } from "../PageContainer";
|
|
2
3
|
export interface ActivePage {
|
|
3
4
|
title: string;
|
|
4
5
|
path: string;
|
|
5
6
|
sourcePath: string;
|
|
6
7
|
breadcrumbs: Breadcrumb[];
|
|
8
|
+
pageHeader?: React.ReactNode;
|
|
7
9
|
}
|
|
8
10
|
export declare function useActivePage(): ActivePage | null;
|
|
@@ -5,14 +5,13 @@ import { getItemPath, getItemTitle, matchPath } from "../shared/navigation";
|
|
|
5
5
|
export function useActivePage() {
|
|
6
6
|
const navigationContext = React.useContext(NavigationContext);
|
|
7
7
|
const routerContext = React.useContext(RouterContext);
|
|
8
|
-
const
|
|
9
|
-
let pathname = routerContext?.pathname ?? "/";
|
|
8
|
+
const pathname = routerContext?.pathname ?? "/";
|
|
10
9
|
const activeItem = matchPath(navigationContext, pathname);
|
|
11
10
|
const rootItem = matchPath(navigationContext, "/");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
return React.useMemo(() => {
|
|
12
|
+
if (!activeItem) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
16
15
|
const breadcrumbs = [];
|
|
17
16
|
if (rootItem) {
|
|
18
17
|
breadcrumbs.push({
|
|
@@ -38,19 +37,12 @@ export function useActivePage() {
|
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
path,
|
|
50
|
-
sourcePath: pathname,
|
|
51
|
-
breadcrumbs
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return pageRef.current;
|
|
40
|
+
return {
|
|
41
|
+
title: getItemTitle(activeItem),
|
|
42
|
+
path: getItemPath(navigationContext, activeItem),
|
|
43
|
+
sourcePath: pathname,
|
|
44
|
+
breadcrumbs,
|
|
45
|
+
pageHeader: activeItem.pageHeader
|
|
46
|
+
};
|
|
47
|
+
}, [activeItem, rootItem, pathname, navigationContext]);
|
|
56
48
|
}
|
package/package.json
CHANGED
|
@@ -48,8 +48,7 @@ export type PageData = {
|
|
|
48
48
|
title?: string;
|
|
49
49
|
page?: string;
|
|
50
50
|
breadcrumbs?: Breadcrumb[];
|
|
51
|
-
|
|
52
|
-
noPageHeader?: boolean;
|
|
51
|
+
pageHeader?: React.ReactNode;
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
type PageDataAction = PageData;
|
|
@@ -64,7 +63,7 @@ function reducer(state: PageData, action: PageDataAction) {
|
|
|
64
63
|
let key: keyof PageDataAction;
|
|
65
64
|
let isSame = true;
|
|
66
65
|
for (key in action) {
|
|
67
|
-
if (action[key]
|
|
66
|
+
if (action[key] != state[key]) {
|
|
68
67
|
isSame = false;
|
|
69
68
|
break;
|
|
70
69
|
}
|
|
@@ -81,16 +80,14 @@ export function PageDataContextProvider(
|
|
|
81
80
|
props: React.PropsWithChildren<PageData>
|
|
82
81
|
) {
|
|
83
82
|
// Destruct
|
|
84
|
-
const { title, page, breadcrumbs,
|
|
85
|
-
props;
|
|
83
|
+
const { title, page, breadcrumbs, pageHeader, ...rest } = props;
|
|
86
84
|
|
|
87
85
|
// useReducer hook to manage state with our reducer function and initial state
|
|
88
86
|
const [state, dispatch] = React.useReducer(reducer, {
|
|
89
87
|
title,
|
|
90
88
|
page,
|
|
91
89
|
breadcrumbs,
|
|
92
|
-
|
|
93
|
-
noPageHeader
|
|
90
|
+
pageHeader
|
|
94
91
|
});
|
|
95
92
|
|
|
96
93
|
// Provide the state and dispatch function to the context value
|
|
@@ -98,46 +95,39 @@ export function PageDataContextProvider(
|
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
type PageContainerBarProps = {
|
|
101
|
-
/**
|
|
102
|
-
* The default title of the page.
|
|
103
|
-
*/
|
|
104
|
-
defaultTitle?: string;
|
|
105
98
|
/**
|
|
106
99
|
* The components used for each slot inside.
|
|
107
100
|
*/
|
|
108
101
|
slots?: PageContainerSlots;
|
|
102
|
+
|
|
109
103
|
/**
|
|
110
104
|
* The props used for each slot inside.
|
|
111
105
|
*/
|
|
112
106
|
slotProps?: PageContainerSlotProps;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The component that renders the actions toolbar.
|
|
110
|
+
*/
|
|
111
|
+
titleBar?: false | ((title: string) => React.ReactNode);
|
|
113
112
|
};
|
|
114
113
|
|
|
115
114
|
function PageContainerBar(props: PageContainerBarProps) {
|
|
116
|
-
const {
|
|
115
|
+
const { slots, slotProps, titleBar } = props;
|
|
117
116
|
|
|
118
117
|
const { state } = React.useContext(PageDataContext);
|
|
119
118
|
|
|
120
119
|
const activePage = useActivePage();
|
|
121
120
|
|
|
122
|
-
React.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
return () => {
|
|
123
|
+
// Reset the state when the component unmounts
|
|
124
|
+
state.breadcrumbs = undefined;
|
|
125
|
+
state.page = undefined;
|
|
126
|
+
state.pageHeader = undefined;
|
|
127
|
+
state.title = undefined;
|
|
128
|
+
};
|
|
129
129
|
}, [activePage?.sourcePath]);
|
|
130
130
|
|
|
131
|
-
let resolvedBreadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
132
|
-
const title = state.title ?? defaultTitle ?? activePage?.title ?? "";
|
|
133
|
-
|
|
134
|
-
if (state.page) {
|
|
135
|
-
resolvedBreadcrumbs = [
|
|
136
|
-
...resolvedBreadcrumbs,
|
|
137
|
-
{ title: state.page, path: "#" }
|
|
138
|
-
];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
131
|
const ToolbarComponent = slots?.toolbar ?? PageContainerToolbar;
|
|
142
132
|
const toolbarSlotProps = useSlotProps({
|
|
143
133
|
elementType: ToolbarComponent,
|
|
@@ -146,37 +136,53 @@ function PageContainerBar(props: PageContainerBarProps) {
|
|
|
146
136
|
additionalProps: {}
|
|
147
137
|
});
|
|
148
138
|
|
|
149
|
-
|
|
139
|
+
const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
140
|
+
const title = state.title ?? activePage?.title ?? "";
|
|
141
|
+
const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
|
|
142
|
+
|
|
143
|
+
// No page header
|
|
144
|
+
if (pageHeader === false) return undefined;
|
|
145
|
+
|
|
146
|
+
// Custom page header
|
|
147
|
+
if (pageHeader != null) return pageHeader;
|
|
148
|
+
|
|
149
|
+
if (state.page) {
|
|
150
|
+
breadcrumbs.push({ title: state.page, path: "#" });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return (
|
|
150
154
|
<Stack>
|
|
151
|
-
{
|
|
155
|
+
{breadcrumbs && (
|
|
152
156
|
<Breadcrumbs aria-label="breadcrumb">
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
})
|
|
171
|
-
: null}
|
|
157
|
+
{breadcrumbs.map((item, index) => {
|
|
158
|
+
return index < breadcrumbs.length - 1 ? (
|
|
159
|
+
<Link
|
|
160
|
+
key={item.path}
|
|
161
|
+
component={ToolpadLink}
|
|
162
|
+
underline="hover"
|
|
163
|
+
color="inherit"
|
|
164
|
+
href={item.path}
|
|
165
|
+
>
|
|
166
|
+
{getItemTitle(item)}
|
|
167
|
+
</Link>
|
|
168
|
+
) : (
|
|
169
|
+
<Typography key={item.path} color="text.primary">
|
|
170
|
+
{getItemTitle(item)}
|
|
171
|
+
</Typography>
|
|
172
|
+
);
|
|
173
|
+
})}
|
|
172
174
|
</Breadcrumbs>
|
|
173
175
|
)}
|
|
174
176
|
<PageContentHeader>
|
|
175
|
-
{
|
|
177
|
+
{typeof titleBar === "function" ? (
|
|
178
|
+
titleBar(title)
|
|
179
|
+
) : titleBar === false ? undefined : (
|
|
180
|
+
<Typography variant="h4">{title}</Typography>
|
|
181
|
+
)}
|
|
176
182
|
<ToolbarComponent {...toolbarSlotProps} />
|
|
177
183
|
</PageContentHeader>
|
|
178
184
|
</Stack>
|
|
179
|
-
)
|
|
185
|
+
);
|
|
180
186
|
}
|
|
181
187
|
|
|
182
188
|
export type PageContainerProps = React.PropsWithChildren<
|
|
@@ -195,14 +201,14 @@ export type PageContainerProps = React.PropsWithChildren<
|
|
|
195
201
|
* - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
|
|
196
202
|
*/
|
|
197
203
|
function PageContainer(props: PageContainerProps) {
|
|
198
|
-
const { children,
|
|
204
|
+
const { children, slots, slotProps, titleBar, ...rest } = props;
|
|
199
205
|
|
|
200
206
|
return (
|
|
201
207
|
<Stack sx={{ mx: 3, my: 2 }} spacing={2} {...rest}>
|
|
202
208
|
<PageContainerBar
|
|
203
|
-
defaultTitle={defaultTitle}
|
|
204
209
|
slots={slots}
|
|
205
210
|
slotProps={slotProps}
|
|
211
|
+
titleBar={titleBar}
|
|
206
212
|
/>
|
|
207
213
|
{children}
|
|
208
214
|
</Stack>
|
|
@@ -9,22 +9,23 @@ export interface ActivePage {
|
|
|
9
9
|
path: string;
|
|
10
10
|
sourcePath: string;
|
|
11
11
|
breadcrumbs: Breadcrumb[];
|
|
12
|
+
pageHeader?: React.ReactNode;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export function useActivePage(): ActivePage | null {
|
|
15
16
|
const navigationContext = React.useContext(NavigationContext);
|
|
16
17
|
const routerContext = React.useContext(RouterContext);
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
let pathname = routerContext?.pathname ?? "/";
|
|
19
|
+
const pathname = routerContext?.pathname ?? "/";
|
|
21
20
|
const activeItem = matchPath(navigationContext, pathname);
|
|
22
21
|
|
|
23
22
|
const rootItem = matchPath(navigationContext, "/");
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
return React.useMemo(() => {
|
|
25
|
+
if (!activeItem) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
28
29
|
const breadcrumbs: Breadcrumb[] = [];
|
|
29
30
|
|
|
30
31
|
if (rootItem) {
|
|
@@ -53,23 +54,12 @@ export function useActivePage(): ActivePage | null {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
) {
|
|
65
|
-
pageRef.current = {
|
|
66
|
-
title,
|
|
67
|
-
path,
|
|
68
|
-
sourcePath: pathname,
|
|
69
|
-
breadcrumbs
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return pageRef.current;
|
|
57
|
+
return {
|
|
58
|
+
title: getItemTitle(activeItem),
|
|
59
|
+
path: getItemPath(navigationContext, activeItem),
|
|
60
|
+
sourcePath: pathname,
|
|
61
|
+
breadcrumbs,
|
|
62
|
+
pageHeader: activeItem.pageHeader
|
|
63
|
+
};
|
|
64
|
+
}, [activeItem, rootItem, pathname, navigationContext]);
|
|
75
65
|
}
|