@etsoo/toolpad 1.0.9 → 1.0.11

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.
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import * as React from "react";
3
- import PropTypes from "prop-types";
4
3
  import Breadcrumbs from "@mui/material/Breadcrumbs";
5
4
  import Container, { ContainerProps } from "@mui/material/Container";
6
5
  import Link from "@mui/material/Link";
@@ -46,32 +45,62 @@ export interface Breadcrumb {
46
45
  path: string;
47
46
  }
48
47
 
49
- // TODO: Remove in the next major version
50
- /**
51
- * @deprecated Use `Breadcrumb` instead.
52
- */
53
- export type BreadCrumb = Breadcrumb;
54
-
55
- export interface PageContainerProps extends ContainerProps {
56
- children?: React.ReactNode;
57
- /**
58
- * The title of the page. Leave blank to use the active page title.
59
- */
48
+ export type PageData = {
60
49
  title?: string;
61
- /**
62
- * The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
63
- */
50
+ page?: string;
64
51
  breadcrumbs?: Breadcrumb[];
65
- /**
66
- * The components used for each slot inside.
67
- */
68
- slots?: PageContainerSlots;
69
- /**
70
- * The props used for each slot inside.
71
- */
72
- slotProps?: PageContainerSlotProps;
52
+ };
53
+
54
+ type PageDataAction = PageData | true;
55
+
56
+ export const PageDataContext = React.createContext<{
57
+ state: PageData;
58
+ dispatch: React.Dispatch<PageDataAction>;
59
+ }>({ state: {}, dispatch: (value) => value });
60
+
61
+ function reducer(state: PageData, action: PageDataAction) {
62
+ if (action === true) {
63
+ // Reset the state
64
+ if (
65
+ state.breadcrumbs == null &&
66
+ state.title == null &&
67
+ state.page == null
68
+ ) {
69
+ return state;
70
+ } else {
71
+ return {};
72
+ }
73
+ }
74
+
75
+ return { ...state, ...action };
76
+ }
77
+
78
+ export function PageDataContextProvider(
79
+ props: React.PropsWithChildren<PageData>
80
+ ) {
81
+ // Destruct
82
+ const { title, breadcrumbs, ...rest } = props;
83
+
84
+ // useReducer hook to manage state with our reducer function and initial state
85
+ const [state, dispatch] = React.useReducer(reducer, { title, breadcrumbs });
86
+
87
+ // Provide the state and dispatch function to the context value
88
+ return <PageDataContext.Provider value={{ state, dispatch }} {...rest} />;
73
89
  }
74
90
 
91
+ export type PageContainerProps = React.PropsWithChildren<
92
+ ContainerProps & {
93
+ /**
94
+ * The components used for each slot inside.
95
+ */
96
+ slots?: PageContainerSlots;
97
+ /**
98
+ * The props used for each slot inside.
99
+ */
100
+ slotProps?: PageContainerSlotProps;
101
+ }
102
+ >;
103
+
75
104
  /**
76
105
  * A container component to provide a title and breadcrumbs for your pages.
77
106
  *
@@ -84,13 +113,30 @@ export interface PageContainerProps extends ContainerProps {
84
113
  * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
85
114
  */
86
115
  function PageContainer(props: PageContainerProps) {
87
- const { children, slots, slotProps, breadcrumbs, ...rest } = props;
116
+ const { children, slots, slotProps, ...rest } = props;
117
+
118
+ const loaded = React.useRef(false);
119
+ const { state, dispatch } = React.useContext(PageDataContext);
88
120
 
89
121
  const activePage = useActivePage();
90
122
 
91
- // TODO: Remove `props.breadCrumbs` in the next major version
92
- const resolvedBreadcrumbs = breadcrumbs ?? activePage?.breadcrumbs ?? [];
93
- const title = props.title ?? activePage?.title ?? "";
123
+ React.useLayoutEffect(() => {
124
+ if (loaded.current) {
125
+ dispatch(true);
126
+ } else {
127
+ loaded.current = true;
128
+ }
129
+ }, [activePage?.sourcePath]);
130
+
131
+ let resolvedBreadcrumbs = state.breadcrumbs ?? activePage?.breadcrumbs ?? [];
132
+ const title = state.title ?? activePage?.title ?? "";
133
+
134
+ if (state.page) {
135
+ resolvedBreadcrumbs = [
136
+ ...resolvedBreadcrumbs,
137
+ { title: state.page, path: "#" }
138
+ ];
139
+ }
94
140
 
95
141
  const ToolbarComponent = props?.slots?.toolbar ?? PageContainerToolbar;
96
142
  const toolbarSlotProps = useSlotProps({
@@ -137,42 +183,4 @@ function PageContainer(props: PageContainerProps) {
137
183
  );
138
184
  }
139
185
 
140
- PageContainer.propTypes /* remove-proptypes */ = {
141
- // ┌────────────────────────────── Warning ──────────────────────────────┐
142
- // │ These PropTypes are generated from the TypeScript type definitions. │
143
- // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
144
- // └─────────────────────────────────────────────────────────────────────┘
145
- /**
146
- * The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
147
- */
148
- breadcrumbs: PropTypes.arrayOf(
149
- PropTypes.shape({
150
- path: PropTypes.string.isRequired,
151
- title: PropTypes.string.isRequired
152
- })
153
- ),
154
- /**
155
- * @ignore
156
- */
157
- children: PropTypes.node,
158
- /**
159
- * The props used for each slot inside.
160
- */
161
- slotProps: PropTypes.shape({
162
- toolbar: PropTypes.shape({
163
- children: PropTypes.node
164
- }).isRequired
165
- }),
166
- /**
167
- * The components used for each slot inside.
168
- */
169
- slots: PropTypes.shape({
170
- toolbar: PropTypes.elementType
171
- }),
172
- /**
173
- * The title of the page. Leave blank to use the active page title.
174
- */
175
- title: PropTypes.string
176
- } as any;
177
-
178
186
  export { PageContainer };
@@ -32,9 +32,15 @@ export function isPageItemSelected(
32
32
  basePath: string,
33
33
  pathname: string
34
34
  ) {
35
- return navigationItem.pattern
36
- ? pathToRegexp(`${basePath}/${navigationItem.pattern}`).test(pathname)
37
- : getPageItemFullPath(basePath, navigationItem) === pathname;
35
+ if (navigationItem.pattern) {
36
+ return pathToRegexp(`${basePath}/${navigationItem.pattern}`).test(pathname);
37
+ }
38
+
39
+ if (navigationItem.subs) {
40
+ return navigationItem.subs.some((sub) => new RegExp(sub).test(pathname));
41
+ }
42
+
43
+ return getPageItemFullPath(basePath, navigationItem) === pathname;
38
44
  }
39
45
 
40
46
  export function hasSelectedNavigationChildren(
@@ -172,11 +178,12 @@ export function matchPath(
172
178
  const lookup = getItemLookup(navigation);
173
179
 
174
180
  for (const [key, item] of lookup.entries()) {
175
- if (typeof key === "string" && key === path) {
176
- return item;
177
- }
178
- if (key instanceof RegExp && key.test(path)) {
179
- return item;
181
+ if (typeof key === "string") {
182
+ if (key === path) return item;
183
+ else if (item.subs?.some((sub) => new RegExp(sub).test(path)))
184
+ return item;
185
+ } else if (key instanceof RegExp) {
186
+ if (key.test(path)) return item;
180
187
  }
181
188
  }
182
189
 
@@ -7,10 +7,7 @@ import type { Breadcrumb } from "../PageContainer";
7
7
  export interface ActivePage {
8
8
  title: string;
9
9
  path: string;
10
- /**
11
- * @deprecated Use `breadcrumbs` instead.
12
- */
13
- breadCrumbs: Breadcrumb[];
10
+ sourcePath: string;
14
11
  breadcrumbs: Breadcrumb[];
15
12
  }
16
13
 
@@ -58,9 +55,8 @@ export function useActivePage(): ActivePage | null {
58
55
  return {
59
56
  title: getItemTitle(activeItem),
60
57
  path: getItemPath(navigationContext, activeItem),
61
- breadcrumbs,
62
- // TODO: Remove in the next major version
63
- breadCrumbs: breadcrumbs
58
+ sourcePath: pathname,
59
+ breadcrumbs
64
60
  };
65
61
  }, [activeItem, rootItem, pathname, navigationContext]);
66
62
  }