@etsoo/toolpad 1.0.29 → 1.0.30

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.
@@ -28,6 +28,7 @@ export interface NavigationPageItem {
28
28
  action?: React.ReactNode;
29
29
  children?: Navigation;
30
30
  hidden?: boolean;
31
+ pageHeader?: React.ReactNode;
31
32
  }
32
33
  export interface NavigationSubheaderItem {
33
34
  kind: "header";
@@ -25,8 +25,7 @@ export type PageData = {
25
25
  title?: string;
26
26
  page?: string;
27
27
  breadcrumbs?: Breadcrumb[];
28
- noBreadcrumbs?: boolean;
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
  */
@@ -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] !== state[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, noBreadcrumbs, noPageHeader, ...rest } = props;
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
- noBreadcrumbs,
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 { defaultTitle, slots, slotProps } = props;
91
+ const { slots, slotProps } = props;
93
92
  const { state } = React.useContext(exports.PageDataContext);
94
93
  const activePage = (0, useActivePage_1.useActivePage)();
95
- React.useLayoutEffect(() => {
96
- // Reset the state without rerendering
97
- state.breadcrumbs = undefined;
98
- state.noBreadcrumbs = undefined;
99
- state.noPageHeader = undefined;
100
- state.page = undefined;
101
- state.title = undefined;
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,19 @@ function PageContainerBar(props) {
115
107
  externalSlotProps: slotProps?.toolbar,
116
108
  additionalProps: {}
117
109
  });
118
- return state.noPageHeader !== true ? ((0, jsx_runtime_1.jsxs)(Stack_1.default, { children: [state.noBreadcrumbs !== true && ((0, jsx_runtime_1.jsx)(Breadcrumbs_1.default, { "aria-label": "breadcrumb", children: resolvedBreadcrumbs
119
- ? resolvedBreadcrumbs.map((item, index) => {
120
- return index < resolvedBreadcrumbs.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));
121
- })
122
- : null })), (0, jsx_runtime_1.jsxs)(PageContentHeader, { children: [title ? (0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "h4", children: title }) : null, (0, jsx_runtime_1.jsx)(ToolbarComponent, { ...toolbarSlotProps })] })] })) : undefined;
110
+ const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
111
+ const title = state.title ?? activePage?.title ?? "";
112
+ const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
113
+ if (pageHeader === false)
114
+ return undefined;
115
+ if (pageHeader != null)
116
+ return pageHeader;
117
+ if (state.page) {
118
+ breadcrumbs.push({ title: state.page, path: "#" });
119
+ }
120
+ 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) => {
121
+ 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));
122
+ }) })), (0, jsx_runtime_1.jsxs)(PageContentHeader, { children: [title ? (0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "h4", children: title }) : null, (0, jsx_runtime_1.jsx)(ToolbarComponent, { ...toolbarSlotProps })] })] }));
123
123
  }
124
124
  /**
125
125
  * A container component to provide a title and breadcrumbs for your pages.
@@ -133,6 +133,6 @@ function PageContainerBar(props) {
133
133
  * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
134
134
  */
135
135
  function PageContainer(props) {
136
- const { children, defaultTitle, slots, slotProps, ...rest } = props;
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, { defaultTitle: defaultTitle, slots: slots, slotProps: slotProps }), children] }));
136
+ const { children, slots, slotProps, ...rest } = props;
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, { slots: slots, slotProps: slotProps }), children] }));
138
138
  }
@@ -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 pageRef = React.useRef(null);
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
- if (!activeItem) {
49
- pageRef.current = null;
50
- }
51
- else {
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
- const title = (0, navigation_1.getItemTitle)(activeItem);
78
- const path = (0, navigation_1.getItemPath)(navigationContext, activeItem);
79
- if (pageRef.current == null ||
80
- pageRef.current.title !== title ||
81
- pageRef.current.path !== path ||
82
- pageRef.current.sourcePath !== pathname) {
83
- pageRef.current = {
84
- title,
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
  }
@@ -28,6 +28,7 @@ export interface NavigationPageItem {
28
28
  action?: React.ReactNode;
29
29
  children?: Navigation;
30
30
  hidden?: boolean;
31
+ pageHeader?: React.ReactNode;
31
32
  }
32
33
  export interface NavigationSubheaderItem {
33
34
  kind: "header";
@@ -25,8 +25,7 @@ export type PageData = {
25
25
  title?: string;
26
26
  page?: string;
27
27
  breadcrumbs?: Breadcrumb[];
28
- noBreadcrumbs?: boolean;
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
  */
@@ -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] !== state[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, noBreadcrumbs, noPageHeader, ...rest } = props;
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
- noBreadcrumbs,
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 { defaultTitle, slots, slotProps } = props;
50
+ const { slots, slotProps } = props;
52
51
  const { state } = React.useContext(PageDataContext);
53
52
  const activePage = useActivePage();
54
- React.useLayoutEffect(() => {
55
- // Reset the state without rerendering
56
- state.breadcrumbs = undefined;
57
- state.noBreadcrumbs = undefined;
58
- state.noPageHeader = undefined;
59
- state.page = undefined;
60
- state.title = undefined;
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,19 @@ function PageContainerBar(props) {
74
66
  externalSlotProps: slotProps?.toolbar,
75
67
  additionalProps: {}
76
68
  });
77
- return state.noPageHeader !== true ? (_jsxs(Stack, { children: [state.noBreadcrumbs !== true && (_jsx(Breadcrumbs, { "aria-label": "breadcrumb", children: resolvedBreadcrumbs
78
- ? resolvedBreadcrumbs.map((item, index) => {
79
- return index < resolvedBreadcrumbs.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));
80
- })
81
- : null })), _jsxs(PageContentHeader, { children: [title ? _jsx(Typography, { variant: "h4", children: title }) : null, _jsx(ToolbarComponent, { ...toolbarSlotProps })] })] })) : undefined;
69
+ const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
70
+ const title = state.title ?? activePage?.title ?? "";
71
+ const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
72
+ if (pageHeader === false)
73
+ return undefined;
74
+ if (pageHeader != null)
75
+ return pageHeader;
76
+ if (state.page) {
77
+ breadcrumbs.push({ title: state.page, path: "#" });
78
+ }
79
+ return (_jsxs(Stack, { children: [breadcrumbs && (_jsx(Breadcrumbs, { "aria-label": "breadcrumb", children: breadcrumbs.map((item, index) => {
80
+ 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));
81
+ }) })), _jsxs(PageContentHeader, { children: [title ? _jsx(Typography, { variant: "h4", children: title }) : null, _jsx(ToolbarComponent, { ...toolbarSlotProps })] })] }));
82
82
  }
83
83
  /**
84
84
  * A container component to provide a title and breadcrumbs for your pages.
@@ -92,7 +92,7 @@ function PageContainerBar(props) {
92
92
  * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
93
93
  */
94
94
  function PageContainer(props) {
95
- const { children, defaultTitle, slots, slotProps, ...rest } = props;
96
- return (_jsxs(Stack, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [_jsx(PageContainerBar, { defaultTitle: defaultTitle, slots: slots, slotProps: slotProps }), children] }));
95
+ const { children, slots, slotProps, ...rest } = props;
96
+ return (_jsxs(Stack, { sx: { mx: 3, my: 2 }, spacing: 2, ...rest, children: [_jsx(PageContainerBar, { slots: slots, slotProps: slotProps }), children] }));
97
97
  }
98
98
  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 pageRef = React.useRef(null);
9
- let pathname = routerContext?.pathname ?? "/";
8
+ const pathname = routerContext?.pathname ?? "/";
10
9
  const activeItem = matchPath(navigationContext, pathname);
11
10
  const rootItem = matchPath(navigationContext, "/");
12
- if (!activeItem) {
13
- pageRef.current = null;
14
- }
15
- else {
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
- const title = getItemTitle(activeItem);
42
- const path = getItemPath(navigationContext, activeItem);
43
- if (pageRef.current == null ||
44
- pageRef.current.title !== title ||
45
- pageRef.current.path !== path ||
46
- pageRef.current.sourcePath !== pathname) {
47
- pageRef.current = {
48
- title,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/toolpad",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "author": "ETSOO",
5
5
  "description": "Dashboard framework extention based on Toolpad Core",
6
6
  "main": "build/cjs/index.js",
@@ -36,6 +36,7 @@ export interface NavigationPageItem {
36
36
  action?: React.ReactNode;
37
37
  children?: Navigation;
38
38
  hidden?: boolean;
39
+ pageHeader?: React.ReactNode;
39
40
  }
40
41
 
41
42
  export interface NavigationSubheaderItem {
@@ -48,8 +48,7 @@ export type PageData = {
48
48
  title?: string;
49
49
  page?: string;
50
50
  breadcrumbs?: Breadcrumb[];
51
- noBreadcrumbs?: boolean;
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] !== state[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, noBreadcrumbs, noPageHeader, ...rest } =
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
- noBreadcrumbs,
93
- noPageHeader
90
+ pageHeader
94
91
  });
95
92
 
96
93
  // Provide the state and dispatch function to the context value
@@ -98,10 +95,6 @@ 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
  */
@@ -113,31 +106,22 @@ type PageContainerBarProps = {
113
106
  };
114
107
 
115
108
  function PageContainerBar(props: PageContainerBarProps) {
116
- const { defaultTitle, slots, slotProps } = props;
109
+ const { slots, slotProps } = props;
117
110
 
118
111
  const { state } = React.useContext(PageDataContext);
119
112
 
120
113
  const activePage = useActivePage();
121
114
 
122
- React.useLayoutEffect(() => {
123
- // Reset the state without rerendering
124
- state.breadcrumbs = undefined;
125
- state.noBreadcrumbs = undefined;
126
- state.noPageHeader = undefined;
127
- state.page = undefined;
128
- state.title = undefined;
115
+ React.useEffect(() => {
116
+ return () => {
117
+ // Reset the state when the component unmounts
118
+ state.breadcrumbs = undefined;
119
+ state.page = undefined;
120
+ state.pageHeader = undefined;
121
+ state.title = undefined;
122
+ };
129
123
  }, [activePage?.sourcePath]);
130
124
 
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
125
  const ToolbarComponent = slots?.toolbar ?? PageContainerToolbar;
142
126
  const toolbarSlotProps = useSlotProps({
143
127
  elementType: ToolbarComponent,
@@ -146,29 +130,38 @@ function PageContainerBar(props: PageContainerBarProps) {
146
130
  additionalProps: {}
147
131
  });
148
132
 
149
- return state.noPageHeader !== true ? (
133
+ const breadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
134
+ const title = state.title ?? activePage?.title ?? "";
135
+ const pageHeader = state.pageHeader ?? activePage?.pageHeader ?? null;
136
+
137
+ if (pageHeader === false) return undefined;
138
+ if (pageHeader != null) return pageHeader;
139
+
140
+ if (state.page) {
141
+ breadcrumbs.push({ title: state.page, path: "#" });
142
+ }
143
+
144
+ return (
150
145
  <Stack>
151
- {state.noBreadcrumbs !== true && (
146
+ {breadcrumbs && (
152
147
  <Breadcrumbs aria-label="breadcrumb">
153
- {resolvedBreadcrumbs
154
- ? resolvedBreadcrumbs.map((item, index) => {
155
- return index < resolvedBreadcrumbs.length - 1 ? (
156
- <Link
157
- key={item.path}
158
- component={ToolpadLink}
159
- underline="hover"
160
- color="inherit"
161
- href={item.path}
162
- >
163
- {getItemTitle(item)}
164
- </Link>
165
- ) : (
166
- <Typography key={item.path} color="text.primary">
167
- {getItemTitle(item)}
168
- </Typography>
169
- );
170
- })
171
- : null}
148
+ {breadcrumbs.map((item, index) => {
149
+ return index < breadcrumbs.length - 1 ? (
150
+ <Link
151
+ key={item.path}
152
+ component={ToolpadLink}
153
+ underline="hover"
154
+ color="inherit"
155
+ href={item.path}
156
+ >
157
+ {getItemTitle(item)}
158
+ </Link>
159
+ ) : (
160
+ <Typography key={item.path} color="text.primary">
161
+ {getItemTitle(item)}
162
+ </Typography>
163
+ );
164
+ })}
172
165
  </Breadcrumbs>
173
166
  )}
174
167
  <PageContentHeader>
@@ -176,7 +169,7 @@ function PageContainerBar(props: PageContainerBarProps) {
176
169
  <ToolbarComponent {...toolbarSlotProps} />
177
170
  </PageContentHeader>
178
171
  </Stack>
179
- ) : undefined;
172
+ );
180
173
  }
181
174
 
182
175
  export type PageContainerProps = React.PropsWithChildren<
@@ -195,15 +188,11 @@ export type PageContainerProps = React.PropsWithChildren<
195
188
  * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
196
189
  */
197
190
  function PageContainer(props: PageContainerProps) {
198
- const { children, defaultTitle, slots, slotProps, ...rest } = props;
191
+ const { children, slots, slotProps, ...rest } = props;
199
192
 
200
193
  return (
201
194
  <Stack sx={{ mx: 3, my: 2 }} spacing={2} {...rest}>
202
- <PageContainerBar
203
- defaultTitle={defaultTitle}
204
- slots={slots}
205
- slotProps={slotProps}
206
- />
195
+ <PageContainerBar slots={slots} slotProps={slotProps} />
207
196
  {children}
208
197
  </Stack>
209
198
  );
@@ -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 pageRef = React.useRef<ActivePage>(null);
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
- if (!activeItem) {
26
- pageRef.current = null;
27
- } else {
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
- const title = getItemTitle(activeItem);
57
- const path = getItemPath(navigationContext, activeItem);
58
-
59
- if (
60
- pageRef.current == null ||
61
- pageRef.current.title !== title ||
62
- pageRef.current.path !== path ||
63
- pageRef.current.sourcePath !== pathname
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
  }