@etsoo/toolpad 1.0.0

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.
Files changed (202) hide show
  1. package/.github/workflows/main.yml +49 -0
  2. package/.vscode/settings.json +3 -0
  3. package/LICENSE +21 -0
  4. package/README.md +11 -0
  5. package/build/src/Account/Account.d.ts +71 -0
  6. package/build/src/Account/Account.js +117 -0
  7. package/build/src/Account/Account.test.d.ts +1 -0
  8. package/build/src/Account/Account.test.js +35 -0
  9. package/build/src/Account/AccountPopoverFooter.d.ts +6 -0
  10. package/build/src/Account/AccountPopoverFooter.js +21 -0
  11. package/build/src/Account/AccountPopoverHeader.d.ts +6 -0
  12. package/build/src/Account/AccountPopoverHeader.js +15 -0
  13. package/build/src/Account/AccountPreview.d.ts +67 -0
  14. package/build/src/Account/AccountPreview.js +76 -0
  15. package/build/src/Account/AccountPreview.test.d.ts +1 -0
  16. package/build/src/Account/AccountPreview.test.js +47 -0
  17. package/build/src/Account/SignInButton.d.ts +2 -0
  18. package/build/src/Account/SignInButton.js +26 -0
  19. package/build/src/Account/SignOutButton.d.ts +3 -0
  20. package/build/src/Account/SignOutButton.js +28 -0
  21. package/build/src/Account/index.d.ts +6 -0
  22. package/build/src/Account/index.js +6 -0
  23. package/build/src/AppProvider/AppProvider.d.ts +98 -0
  24. package/build/src/AppProvider/AppProvider.js +4 -0
  25. package/build/src/AppProvider/AppProvider.test.d.ts +4 -0
  26. package/build/src/AppProvider/AppProvider.test.js +16 -0
  27. package/build/src/AppProvider/AppProviderComponent.d.ts +17 -0
  28. package/build/src/AppProvider/AppProviderComponent.js +120 -0
  29. package/build/src/AppProvider/AppThemeProvider.d.ts +12 -0
  30. package/build/src/AppProvider/AppThemeProvider.js +73 -0
  31. package/build/src/AppProvider/index.d.ts +1 -0
  32. package/build/src/AppProvider/index.js +1 -0
  33. package/build/src/DashboardLayout/DashboardLayout.d.ts +83 -0
  34. package/build/src/DashboardLayout/DashboardLayout.js +286 -0
  35. package/build/src/DashboardLayout/DashboardLayout.test.d.ts +1 -0
  36. package/build/src/DashboardLayout/DashboardLayout.test.js +291 -0
  37. package/build/src/DashboardLayout/DashboardSidebarSubNavigation.d.ts +16 -0
  38. package/build/src/DashboardLayout/DashboardSidebarSubNavigation.js +149 -0
  39. package/build/src/DashboardLayout/ThemeSwitcher.d.ts +5 -0
  40. package/build/src/DashboardLayout/ThemeSwitcher.js +40 -0
  41. package/build/src/DashboardLayout/ToolbarActions.d.ts +5 -0
  42. package/build/src/DashboardLayout/ToolbarActions.js +9 -0
  43. package/build/src/DashboardLayout/index.d.ts +2 -0
  44. package/build/src/DashboardLayout/index.js +3 -0
  45. package/build/src/DashboardLayout/utils.d.ts +8 -0
  46. package/build/src/DashboardLayout/utils.js +16 -0
  47. package/build/src/PageContainer/PageContainer.d.ts +66 -0
  48. package/build/src/PageContainer/PageContainer.js +97 -0
  49. package/build/src/PageContainer/PageContainer.test.d.ts +1 -0
  50. package/build/src/PageContainer/PageContainer.test.js +127 -0
  51. package/build/src/PageContainer/PageContainerToolbar.d.ts +16 -0
  52. package/build/src/PageContainer/PageContainerToolbar.js +24 -0
  53. package/build/src/PageContainer/PageContainerToolbar.test.d.ts +1 -0
  54. package/build/src/PageContainer/PageContainerToolbar.test.js +10 -0
  55. package/build/src/PageContainer/index.d.ts +2 -0
  56. package/build/src/PageContainer/index.js +2 -0
  57. package/build/src/index.d.ts +8 -0
  58. package/build/src/index.js +8 -0
  59. package/build/src/nextjs/AppProvider.d.ts +6 -0
  60. package/build/src/nextjs/AppProvider.js +16 -0
  61. package/build/src/nextjs/AppProvider.test.d.ts +1 -0
  62. package/build/src/nextjs/AppProvider.test.js +36 -0
  63. package/build/src/nextjs/AppProviderNextApp.d.ts +5 -0
  64. package/build/src/nextjs/AppProviderNextApp.js +27 -0
  65. package/build/src/nextjs/AppProviderNextPages.d.ts +5 -0
  66. package/build/src/nextjs/AppProviderNextPages.js +37 -0
  67. package/build/src/nextjs/index.d.ts +1 -0
  68. package/build/src/nextjs/index.js +1 -0
  69. package/build/src/persistence/codec.d.ts +47 -0
  70. package/build/src/persistence/codec.js +57 -0
  71. package/build/src/persistence/index.d.ts +1 -0
  72. package/build/src/persistence/index.js +1 -0
  73. package/build/src/persistence/useStorageState.d.ts +50 -0
  74. package/build/src/persistence/useStorageState.js +116 -0
  75. package/build/src/persistence/useStorageState.test.d.ts +1 -0
  76. package/build/src/persistence/useStorageState.test.js +61 -0
  77. package/build/src/react-router-dom/AppProvider.d.ts +6 -0
  78. package/build/src/react-router-dom/AppProvider.js +29 -0
  79. package/build/src/react-router-dom/AppProvider.test.d.ts +1 -0
  80. package/build/src/react-router-dom/AppProvider.test.js +12 -0
  81. package/build/src/react-router-dom/index.d.ts +1 -0
  82. package/build/src/react-router-dom/index.js +1 -0
  83. package/build/src/shared/Link.d.ts +8 -0
  84. package/build/src/shared/Link.js +19 -0
  85. package/build/src/shared/branding.d.ts +1 -0
  86. package/build/src/shared/branding.js +6 -0
  87. package/build/src/shared/components.d.ts +5 -0
  88. package/build/src/shared/components.js +23 -0
  89. package/build/src/shared/context.d.ts +12 -0
  90. package/build/src/shared/context.js +10 -0
  91. package/build/src/shared/locales/LocaleContext.d.ts +19 -0
  92. package/build/src/shared/locales/LocaleContext.js +18 -0
  93. package/build/src/shared/locales/en.d.ts +6 -0
  94. package/build/src/shared/locales/en.js +8 -0
  95. package/build/src/shared/navigation.d.ts +16 -0
  96. package/build/src/shared/navigation.js +129 -0
  97. package/build/src/useActivePage/index.d.ts +1 -0
  98. package/build/src/useActivePage/index.js +1 -0
  99. package/build/src/useActivePage/useActivePage.d.ts +11 -0
  100. package/build/src/useActivePage/useActivePage.js +48 -0
  101. package/build/src/useLocalStorageState/index.d.ts +1 -0
  102. package/build/src/useLocalStorageState/index.js +1 -0
  103. package/build/src/useLocalStorageState/useLocalStorageState.d.ts +2 -0
  104. package/build/src/useLocalStorageState/useLocalStorageState.js +14 -0
  105. package/build/src/useSessionStorageState/index.d.ts +1 -0
  106. package/build/src/useSessionStorageState/index.js +1 -0
  107. package/build/src/useSessionStorageState/useSessionStorageState.d.ts +2 -0
  108. package/build/src/useSessionStorageState/useSessionStorageState.js +14 -0
  109. package/build/src/utils/collections.d.ts +44 -0
  110. package/build/src/utils/collections.js +43 -0
  111. package/build/src/utils/describeConformance.d.ts +7 -0
  112. package/build/src/utils/describeConformance.js +3 -0
  113. package/build/src/utils/events.d.ts +27 -0
  114. package/build/src/utils/events.js +54 -0
  115. package/build/src/utils/hooks/index.d.ts +2 -0
  116. package/build/src/utils/hooks/index.js +2 -0
  117. package/build/src/utils/hooks/useBoolean.d.ts +11 -0
  118. package/build/src/utils/hooks/useBoolean.js +11 -0
  119. package/build/src/utils/hooks/useDebounced.d.ts +8 -0
  120. package/build/src/utils/hooks/useDebounced.js +28 -0
  121. package/build/src/utils/hooks/useDebouncedHandler.d.ts +12 -0
  122. package/build/src/utils/hooks/useDebouncedHandler.js +41 -0
  123. package/build/src/utils/hooks/useLatest.d.ts +6 -0
  124. package/build/src/utils/hooks/useLatest.js +9 -0
  125. package/build/src/utils/hooks/usePageTitle.d.ts +4 -0
  126. package/build/src/utils/hooks/usePageTitle.js +13 -0
  127. package/build/src/utils/hooks/useSsr.d.ts +4 -0
  128. package/build/src/utils/hooks/useSsr.js +16 -0
  129. package/build/src/utils/hooks/useStorageState.d.ts +10 -0
  130. package/build/src/utils/hooks/useStorageState.js +51 -0
  131. package/build/src/utils/warnOnce.d.ts +5 -0
  132. package/build/src/utils/warnOnce.js +11 -0
  133. package/build/vitest.setup.d.ts +1 -0
  134. package/build/vitest.setup.js +19 -0
  135. package/package.json +60 -0
  136. package/src/Account/Account.test.tsx +56 -0
  137. package/src/Account/Account.tsx +226 -0
  138. package/src/Account/AccountPopoverFooter.tsx +33 -0
  139. package/src/Account/AccountPopoverHeader.tsx +20 -0
  140. package/src/Account/AccountPreview.test.tsx +79 -0
  141. package/src/Account/AccountPreview.tsx +189 -0
  142. package/src/Account/SignInButton.tsx +38 -0
  143. package/src/Account/SignOutButton.tsx +44 -0
  144. package/src/Account/index.ts +6 -0
  145. package/src/AppProvider/AppProvider.test.tsx +25 -0
  146. package/src/AppProvider/AppProvider.tsx +116 -0
  147. package/src/AppProvider/AppProviderComponent.tsx +162 -0
  148. package/src/AppProvider/AppThemeProvider.tsx +177 -0
  149. package/src/AppProvider/index.ts +1 -0
  150. package/src/DashboardLayout/DashboardLayout.test.tsx +429 -0
  151. package/src/DashboardLayout/DashboardLayout.tsx +558 -0
  152. package/src/DashboardLayout/DashboardSidebarSubNavigation.tsx +301 -0
  153. package/src/DashboardLayout/ThemeSwitcher.tsx +81 -0
  154. package/src/DashboardLayout/ToolbarActions.tsx +11 -0
  155. package/src/DashboardLayout/index.ts +4 -0
  156. package/src/DashboardLayout/utils.ts +23 -0
  157. package/src/PageContainer/PageContainer.test.tsx +194 -0
  158. package/src/PageContainer/PageContainer.tsx +201 -0
  159. package/src/PageContainer/PageContainerToolbar.test.tsx +11 -0
  160. package/src/PageContainer/PageContainerToolbar.tsx +31 -0
  161. package/src/PageContainer/index.ts +2 -0
  162. package/src/index.ts +15 -0
  163. package/src/nextjs/AppProvider.test.tsx +48 -0
  164. package/src/nextjs/AppProvider.tsx +18 -0
  165. package/src/nextjs/AppProviderNextApp.tsx +37 -0
  166. package/src/nextjs/AppProviderNextPages.tsx +52 -0
  167. package/src/nextjs/index.tsx +1 -0
  168. package/src/persistence/codec.tsx +81 -0
  169. package/src/persistence/index.ts +1 -0
  170. package/src/persistence/useStorageState.test.tsx +101 -0
  171. package/src/persistence/useStorageState.tsx +244 -0
  172. package/src/react-router-dom/AppProvider.test.tsx +17 -0
  173. package/src/react-router-dom/AppProvider.tsx +40 -0
  174. package/src/react-router-dom/index.tsx +1 -0
  175. package/src/shared/Link.tsx +37 -0
  176. package/src/shared/branding.ts +7 -0
  177. package/src/shared/components.tsx +46 -0
  178. package/src/shared/context.ts +21 -0
  179. package/src/shared/locales/LocaleContext.tsx +44 -0
  180. package/src/shared/locales/en.tsx +9 -0
  181. package/src/shared/navigation.tsx +197 -0
  182. package/src/useActivePage/index.ts +1 -0
  183. package/src/useActivePage/useActivePage.ts +66 -0
  184. package/src/useLocalStorageState/index.ts +1 -0
  185. package/src/useLocalStorageState/useLocalStorageState.tsx +23 -0
  186. package/src/useSessionStorageState/index.tsx +1 -0
  187. package/src/useSessionStorageState/useSessionStorageState.tsx +23 -0
  188. package/src/utils/collections.ts +125 -0
  189. package/src/utils/describeConformance.ts +14 -0
  190. package/src/utils/events.ts +81 -0
  191. package/src/utils/hooks/index.ts +2 -0
  192. package/src/utils/hooks/useBoolean.ts +12 -0
  193. package/src/utils/hooks/useDebounced.ts +39 -0
  194. package/src/utils/hooks/useDebouncedHandler.ts +71 -0
  195. package/src/utils/hooks/useLatest.ts +16 -0
  196. package/src/utils/hooks/usePageTitle.ts +14 -0
  197. package/src/utils/hooks/useSsr.ts +24 -0
  198. package/src/utils/hooks/useStorageState.ts +130 -0
  199. package/src/utils/warnOnce.ts +12 -0
  200. package/tsconfig.json +19 -0
  201. package/vite.config.ts +27 -0
  202. package/vitest.setup.ts +22 -0
@@ -0,0 +1,149 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { styled } from "@mui/material";
5
+ import Avatar from "@mui/material/Avatar";
6
+ import Collapse from "@mui/material/Collapse";
7
+ import Divider from "@mui/material/Divider";
8
+ import List from "@mui/material/List";
9
+ import ListItem from "@mui/material/ListItem";
10
+ import ListItemButton from "@mui/material/ListItemButton";
11
+ import ListItemIcon from "@mui/material/ListItemIcon";
12
+ import ListItemText from "@mui/material/ListItemText";
13
+ import ListSubheader from "@mui/material/ListSubheader";
14
+ import Tooltip from "@mui/material/Tooltip";
15
+ import ExpandLessIcon from "@mui/icons-material/ExpandLess";
16
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
17
+ import { Link } from "../shared/Link";
18
+ import { RouterContext } from "../shared/context";
19
+ import { getItemTitle, getPageItemFullPath, hasSelectedNavigationChildren, isPageItemSelected } from "../shared/navigation";
20
+ import { getDrawerSxTransitionMixin } from "./utils";
21
+ const NavigationListItemButton = styled(ListItemButton)(({ theme }) => ({
22
+ borderRadius: 8,
23
+ "&.Mui-selected": {
24
+ "& .MuiListItemIcon-root": {
25
+ color: (theme.vars ?? theme).palette.primary.dark
26
+ },
27
+ "& .MuiTypography-root": {
28
+ color: (theme.vars ?? theme).palette.primary.dark
29
+ },
30
+ "& .MuiSvgIcon-root": {
31
+ color: (theme.vars ?? theme).palette.primary.dark
32
+ },
33
+ "& .MuiAvatar-root": {
34
+ backgroundColor: (theme.vars ?? theme).palette.primary.dark
35
+ },
36
+ "& .MuiTouchRipple-child": {
37
+ backgroundColor: (theme.vars ?? theme).palette.primary.dark
38
+ }
39
+ },
40
+ "& .MuiSvgIcon-root": {
41
+ color: (theme.vars ?? theme).palette.action.active
42
+ },
43
+ "& .MuiAvatar-root": {
44
+ backgroundColor: (theme.vars ?? theme).palette.action.active
45
+ }
46
+ }));
47
+ /**
48
+ * @ignore - internal component.
49
+ */
50
+ function DashboardSidebarSubNavigation({ subNavigation, basePath = "", depth = 0, onLinkClick, isMini = false, isFullyExpanded = true, hasDrawerTransitions = false, selectedItemId }) {
51
+ const routerContext = React.useContext(RouterContext);
52
+ const pathname = routerContext?.pathname ?? "/";
53
+ const initialExpandedSidebarItemIds = React.useMemo(() => subNavigation
54
+ .map((navigationItem, navigationItemIndex) => ({
55
+ navigationItem,
56
+ originalIndex: navigationItemIndex
57
+ }))
58
+ .filter(({ navigationItem }) => hasSelectedNavigationChildren(navigationItem, basePath, pathname))
59
+ .map(({ originalIndex }) => `${depth}-${originalIndex}`), [basePath, depth, pathname, subNavigation]);
60
+ const [expandedSidebarItemIds, setExpandedSidebarItemIds] = React.useState(initialExpandedSidebarItemIds);
61
+ const handleOpenFolderClick = React.useCallback((itemId) => () => {
62
+ setExpandedSidebarItemIds((previousValue) => previousValue.includes(itemId)
63
+ ? previousValue.filter((previousValueItemId) => previousValueItemId !== itemId)
64
+ : [...previousValue, itemId]);
65
+ }, []);
66
+ return (_jsx(List, { sx: { padding: 0, mb: depth === 0 ? 4 : 1, pl: 2 * depth }, children: subNavigation.map((navigationItem, navigationItemIndex) => {
67
+ if (navigationItem.kind === "header") {
68
+ return (_jsx(ListSubheader, { component: "div", sx: {
69
+ fontSize: 12,
70
+ fontWeight: "700",
71
+ height: isMini ? 0 : 40,
72
+ ...(hasDrawerTransitions
73
+ ? getDrawerSxTransitionMixin(isFullyExpanded, "height")
74
+ : {}),
75
+ px: 2,
76
+ overflow: "hidden",
77
+ textOverflow: "ellipsis",
78
+ whiteSpace: "nowrap",
79
+ zIndex: 2
80
+ }, children: getItemTitle(navigationItem) }, `subheader-${depth}-${navigationItemIndex}`));
81
+ }
82
+ if (navigationItem.kind === "divider") {
83
+ const nextItem = subNavigation[navigationItemIndex + 1];
84
+ return (_jsx(Divider, { sx: {
85
+ borderBottomWidth: 2,
86
+ mx: 1,
87
+ mt: 1,
88
+ mb: nextItem?.kind === "header" && !isMini ? 0 : 1,
89
+ ...(hasDrawerTransitions
90
+ ? getDrawerSxTransitionMixin(isFullyExpanded, "margin")
91
+ : {})
92
+ } }, `divider-${depth}-${navigationItemIndex}`));
93
+ }
94
+ const navigationItemFullPath = getPageItemFullPath(basePath, navigationItem);
95
+ const navigationItemId = `${depth}-${navigationItemIndex}`;
96
+ const navigationItemTitle = getItemTitle(navigationItem);
97
+ const isNestedNavigationExpanded = expandedSidebarItemIds.includes(navigationItemId);
98
+ const nestedNavigationCollapseIcon = isNestedNavigationExpanded ? (_jsx(ExpandLessIcon, {})) : (_jsx(ExpandMoreIcon, {}));
99
+ const listItemIconSize = 34;
100
+ const isSelected = isPageItemSelected(navigationItem, basePath, pathname);
101
+ if (process.env.NODE_ENV !== "production" &&
102
+ isSelected &&
103
+ selectedItemId) {
104
+ console.warn(`Duplicate selected path in navigation: ${navigationItemFullPath}`);
105
+ }
106
+ if (isSelected && !selectedItemId) {
107
+ selectedItemId = navigationItemId;
108
+ }
109
+ const listItem = (_jsx(ListItem, { sx: {
110
+ py: 0,
111
+ px: 1,
112
+ overflowX: "hidden"
113
+ }, children: _jsxs(NavigationListItemButton, { selected: isSelected && (!navigationItem.children || isMini), sx: {
114
+ px: 1.4,
115
+ height: 48
116
+ }, ...(navigationItem.children && !isMini
117
+ ? {
118
+ onClick: handleOpenFolderClick(navigationItemId)
119
+ }
120
+ : {
121
+ LinkComponent: Link,
122
+ href: navigationItemFullPath,
123
+ onClick: onLinkClick
124
+ }), children: [navigationItem.icon || isMini ? (_jsxs(ListItemIcon, { sx: {
125
+ minWidth: listItemIconSize,
126
+ mr: 1.2
127
+ }, children: [navigationItem.icon ?? null, !navigationItem.icon && isMini ? (_jsx(Avatar, { sx: {
128
+ width: listItemIconSize - 7,
129
+ height: listItemIconSize - 7,
130
+ fontSize: 12,
131
+ ml: "-2px"
132
+ }, children: navigationItemTitle
133
+ .split(" ")
134
+ .slice(0, 2)
135
+ .map((itemTitleWord) => itemTitleWord.charAt(0).toUpperCase()) })) : null] })) : null, _jsx(ListItemText, { primary: navigationItemTitle, sx: {
136
+ whiteSpace: "nowrap",
137
+ zIndex: 1,
138
+ "& .MuiTypography-root": {
139
+ fontWeight: "500"
140
+ }
141
+ } }), navigationItem.action && !isMini && isFullyExpanded
142
+ ? navigationItem.action
143
+ : null, navigationItem.children && !isMini && isFullyExpanded
144
+ ? nestedNavigationCollapseIcon
145
+ : null] }) }));
146
+ return (_jsxs(React.Fragment, { children: [isMini ? (_jsx(Tooltip, { title: navigationItemTitle, placement: "right", children: listItem })) : (listItem), navigationItem.children && !isMini ? (_jsx(Collapse, { in: isNestedNavigationExpanded, timeout: "auto", unmountOnExit: true, children: _jsx(DashboardSidebarSubNavigation, { subNavigation: navigationItem.children, basePath: navigationItemFullPath, depth: depth + 1, onLinkClick: onLinkClick, selectedItemId: selectedItemId }) })) : null] }, navigationItemId));
147
+ }) }));
148
+ }
149
+ export { DashboardSidebarSubNavigation };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @ignore - internal component.
3
+ */
4
+ declare function ThemeSwitcher(): import("react/jsx-runtime").JSX.Element | null;
5
+ export { ThemeSwitcher };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useTheme } from "@mui/material";
5
+ import IconButton from "@mui/material/IconButton";
6
+ import Tooltip from "@mui/material/Tooltip";
7
+ import DarkModeIcon from "@mui/icons-material/DarkMode";
8
+ import LightModeIcon from "@mui/icons-material/LightMode";
9
+ import { PaletteModeContext } from "../shared/context";
10
+ import useSsr from "../utils/hooks/useSsr";
11
+ // TODO: When we use this component as the default for a slot, make it non-internal
12
+ /**
13
+ * @ignore - internal component.
14
+ */
15
+ function ThemeSwitcher() {
16
+ const isSsr = useSsr();
17
+ const theme = useTheme();
18
+ const { paletteMode, setPaletteMode, isDualTheme } = React.useContext(PaletteModeContext);
19
+ const toggleMode = React.useCallback(() => {
20
+ setPaletteMode(paletteMode === "dark" ? "light" : "dark");
21
+ }, [paletteMode, setPaletteMode]);
22
+ return isDualTheme ? (_jsx(Tooltip, { title: isSsr
23
+ ? "Switch mode"
24
+ : `${paletteMode === "dark" ? "Light" : "Dark"} mode`, enterDelay: 1000, children: _jsx("div", { children: _jsx(IconButton, { "aria-label": isSsr
25
+ ? "Switch theme mode"
26
+ : `Switch to ${paletteMode === "dark" ? "light" : "dark"} mode`, onClick: toggleMode, sx: {
27
+ color: (theme.vars ?? theme).palette.primary.dark
28
+ }, children: theme.getColorSchemeSelector ? (_jsxs(React.Fragment, { children: [_jsx(DarkModeIcon, { sx: {
29
+ display: "inline",
30
+ [theme.getColorSchemeSelector("dark")]: {
31
+ display: "none"
32
+ }
33
+ } }), _jsx(LightModeIcon, { sx: {
34
+ display: "none",
35
+ [theme.getColorSchemeSelector("dark")]: {
36
+ display: "inline"
37
+ }
38
+ } })] })) : (_jsx(React.Fragment, { children: isSsr || paletteMode !== "dark" ? (_jsx(DarkModeIcon, {})) : (_jsx(LightModeIcon, {})) })) }) }) })) : null;
39
+ }
40
+ export { ThemeSwitcher };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @ignore - internal component.
3
+ */
4
+ declare function ToolbarActions(): null;
5
+ export { ToolbarActions };
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ // TODO: When we add content to this component, make it non-internal
3
+ /**
4
+ * @ignore - internal component.
5
+ */
6
+ function ToolbarActions() {
7
+ return null;
8
+ }
9
+ export { ToolbarActions };
@@ -0,0 +1,2 @@
1
+ export * from "./DashboardLayout";
2
+ export * from "./ToolbarActions";
@@ -0,0 +1,3 @@
1
+ export * from "./DashboardLayout";
2
+ // Default slots components
3
+ export * from "./ToolbarActions";
@@ -0,0 +1,8 @@
1
+ import type { Theme } from "@mui/material";
2
+ export declare function getDrawerSxTransitionMixin(isExpanded: boolean, property: string): {
3
+ transition: (theme: Theme) => string;
4
+ };
5
+ export declare function getDrawerWidthTransitionMixin(isExpanded: boolean): {
6
+ overflowX: string;
7
+ transition: (theme: Theme) => string;
8
+ };
@@ -0,0 +1,16 @@
1
+ export function getDrawerSxTransitionMixin(isExpanded, property) {
2
+ return {
3
+ transition: (theme) => theme.transitions.create(property, {
4
+ easing: theme.transitions.easing.sharp,
5
+ duration: isExpanded
6
+ ? theme.transitions.duration.enteringScreen
7
+ : theme.transitions.duration.leavingScreen
8
+ })
9
+ };
10
+ }
11
+ export function getDrawerWidthTransitionMixin(isExpanded) {
12
+ return {
13
+ ...getDrawerSxTransitionMixin(isExpanded, "width"),
14
+ overflowX: "hidden"
15
+ };
16
+ }
@@ -0,0 +1,66 @@
1
+ import * as React from "react";
2
+ import { ContainerProps } from "@mui/material/Container";
3
+ import { PageContainerToolbarProps } from "./PageContainerToolbar";
4
+ export interface PageContainerSlotProps {
5
+ toolbar: PageContainerToolbarProps;
6
+ }
7
+ export interface PageContainerSlots {
8
+ /**
9
+ * The component that renders the actions toolbar.
10
+ * @default Snackbar
11
+ */
12
+ toolbar: React.ElementType;
13
+ }
14
+ export interface Breadcrumb {
15
+ /**
16
+ * The title of the breadcrumb segment.
17
+ */
18
+ title: string;
19
+ /**
20
+ * The path the breadcrumb links to.
21
+ */
22
+ path: string;
23
+ }
24
+ /**
25
+ * @deprecated Use `Breadcrumb` instead.
26
+ */
27
+ export type BreadCrumb = Breadcrumb;
28
+ export interface PageContainerProps extends ContainerProps {
29
+ children?: React.ReactNode;
30
+ /**
31
+ * The title of the page. Leave blank to use the active page title.
32
+ */
33
+ title?: string;
34
+ /**
35
+ * The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
36
+ */
37
+ breadcrumbs?: Breadcrumb[];
38
+ /**
39
+ * @deprecated Use `breadcrumbs` instead.
40
+ */
41
+ breadCrumbs?: Breadcrumb[];
42
+ /**
43
+ * The components used for each slot inside.
44
+ */
45
+ slots?: PageContainerSlots;
46
+ /**
47
+ * The props used for each slot inside.
48
+ */
49
+ slotProps?: PageContainerSlotProps;
50
+ }
51
+ /**
52
+ * A container component to provide a title and breadcrumbs for your pages.
53
+ *
54
+ * Demos:
55
+ *
56
+ * - [Page Container](https://mui.com/toolpad/core/react-page-container/)
57
+ *
58
+ * API:
59
+ *
60
+ * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
61
+ */
62
+ declare function PageContainer(props: PageContainerProps): import("react/jsx-runtime").JSX.Element;
63
+ declare namespace PageContainer {
64
+ var propTypes: any;
65
+ }
66
+ export { PageContainer };
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import PropTypes from "prop-types";
4
+ import Breadcrumbs from "@mui/material/Breadcrumbs";
5
+ import Container from "@mui/material/Container";
6
+ import Link from "@mui/material/Link";
7
+ import Stack from "@mui/material/Stack";
8
+ import Typography from "@mui/material/Typography";
9
+ import useSlotProps from "@mui/utils/useSlotProps";
10
+ import { styled } from "@mui/material";
11
+ import { Link as ToolpadLink } from "../shared/Link";
12
+ import { PageContainerToolbar } from "./PageContainerToolbar";
13
+ import { getItemTitle } from "../shared/navigation";
14
+ import { useActivePage } from "../useActivePage";
15
+ import warnOnce from "../utils/warnOnce";
16
+ const PageContentHeader = styled("div")(({ theme }) => ({
17
+ display: "flex",
18
+ flexDirection: "row",
19
+ justifyContent: "space-between",
20
+ gap: theme.spacing(2)
21
+ }));
22
+ /**
23
+ * A container component to provide a title and breadcrumbs for your pages.
24
+ *
25
+ * Demos:
26
+ *
27
+ * - [Page Container](https://mui.com/toolpad/core/react-page-container/)
28
+ *
29
+ * API:
30
+ *
31
+ * - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
32
+ */
33
+ function PageContainer(props) {
34
+ const { children, slots, slotProps, breadcrumbs, breadCrumbs, ...rest } = props;
35
+ if (process.env.NODE_ENV !== "production" && breadCrumbs) {
36
+ warnOnce("The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead.");
37
+ }
38
+ const activePage = useActivePage();
39
+ // TODO: Remove `props.breadCrumbs` in the next major version
40
+ const resolvedBreadcrumbs = breadcrumbs ?? breadCrumbs ?? activePage?.breadcrumbs ?? [];
41
+ const title = props.title ?? activePage?.title ?? "";
42
+ const ToolbarComponent = props?.slots?.toolbar ?? PageContainerToolbar;
43
+ const toolbarSlotProps = useSlotProps({
44
+ elementType: ToolbarComponent,
45
+ ownerState: props,
46
+ externalSlotProps: props?.slotProps?.toolbar,
47
+ additionalProps: {}
48
+ });
49
+ return (_jsx(Container, { ...rest, children: _jsxs(Stack, { sx: { my: 2 }, spacing: 2, children: [_jsxs(Stack, { children: [_jsx(Breadcrumbs, { "aria-label": "breadcrumb", children: resolvedBreadcrumbs
50
+ ? resolvedBreadcrumbs.map((item, index) => {
51
+ 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));
52
+ })
53
+ : null }), _jsxs(PageContentHeader, { children: [title ? _jsx(Typography, { variant: "h4", children: title }) : null, _jsx(ToolbarComponent, { ...toolbarSlotProps })] })] }), _jsx("div", { children: children })] }) }));
54
+ }
55
+ PageContainer.propTypes /* remove-proptypes */ = {
56
+ // ┌────────────────────────────── Warning ──────────────────────────────┐
57
+ // │ These PropTypes are generated from the TypeScript type definitions. │
58
+ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
59
+ // └─────────────────────────────────────────────────────────────────────┘
60
+ /**
61
+ * The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
62
+ */
63
+ breadcrumbs: PropTypes.arrayOf(PropTypes.shape({
64
+ path: PropTypes.string.isRequired,
65
+ title: PropTypes.string.isRequired
66
+ })),
67
+ /**
68
+ * @deprecated Use `breadcrumbs` instead.
69
+ */
70
+ breadCrumbs: PropTypes.arrayOf(PropTypes.shape({
71
+ path: PropTypes.string.isRequired,
72
+ title: PropTypes.string.isRequired
73
+ })),
74
+ /**
75
+ * @ignore
76
+ */
77
+ children: PropTypes.node,
78
+ /**
79
+ * The props used for each slot inside.
80
+ */
81
+ slotProps: PropTypes.shape({
82
+ toolbar: PropTypes.shape({
83
+ children: PropTypes.node
84
+ }).isRequired
85
+ }),
86
+ /**
87
+ * The components used for each slot inside.
88
+ */
89
+ slots: PropTypes.shape({
90
+ toolbar: PropTypes.elementType
91
+ }),
92
+ /**
93
+ * The title of the page. Leave blank to use the active page title.
94
+ */
95
+ title: PropTypes.string
96
+ };
97
+ export { PageContainer };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,127 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { expect, describe, test, vi } from "vitest";
3
+ import { render, within, screen } from "@testing-library/react";
4
+ import { userEvent } from "@testing-library/user-event";
5
+ import { PageContainer } from "./PageContainer";
6
+ import describeConformance from "../utils/describeConformance";
7
+ import { AppProvider } from "../AppProvider/AppProviderComponent";
8
+ describe("PageContainer", () => {
9
+ describeConformance(_jsx(PageContainer, {}), () => ({
10
+ skip: ["themeDefaultProps"],
11
+ slots: {
12
+ toolbar: {}
13
+ }
14
+ }));
15
+ test("renders page container correctly", async () => {
16
+ const user = await userEvent.setup();
17
+ const router = {
18
+ pathname: "/orders",
19
+ searchParams: new URLSearchParams(),
20
+ navigate: vi.fn()
21
+ };
22
+ render(_jsx(AppProvider, { navigation: [
23
+ { segment: "", title: "Home" },
24
+ { segment: "orders", title: "Orders" }
25
+ ], router: router, children: _jsx(PageContainer, {}) }));
26
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
27
+ const homeLink = within(breadcrumbs).getByRole("link", { name: "Home" });
28
+ await user.click(homeLink);
29
+ expect(router.navigate).toHaveBeenCalledWith("/", expect.objectContaining({}));
30
+ router.navigate.mockClear();
31
+ expect(within(breadcrumbs).getByText("Orders")).toBeTruthy();
32
+ expect(screen.getByText("Orders", { ignore: "nav *" }));
33
+ });
34
+ test("renders nested", async () => {
35
+ const navigation = [
36
+ { segment: "", title: "ACME" },
37
+ {
38
+ segment: "home",
39
+ title: "Home",
40
+ children: [
41
+ {
42
+ segment: "orders",
43
+ title: "Orders"
44
+ }
45
+ ]
46
+ }
47
+ ];
48
+ const router = {
49
+ pathname: "/home/orders",
50
+ searchParams: new URLSearchParams(),
51
+ navigate: vi.fn()
52
+ };
53
+ const branding = { title: "ACME" };
54
+ render(_jsx(AppProvider, { branding: branding, navigation: navigation, router: router, children: _jsx(PageContainer, {}) }));
55
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
56
+ expect(within(breadcrumbs).getByText("ACME")).toBeTruthy();
57
+ expect(within(breadcrumbs).getByText("Home")).toBeTruthy();
58
+ expect(within(breadcrumbs).getByText("Orders")).toBeTruthy();
59
+ });
60
+ test("renders dynamic correctly", async () => {
61
+ const user = await userEvent.setup();
62
+ const router = {
63
+ pathname: "/orders/123",
64
+ searchParams: new URLSearchParams(),
65
+ navigate: vi.fn()
66
+ };
67
+ render(_jsx(AppProvider, { navigation: [
68
+ { segment: "", title: "Home" },
69
+ { segment: "orders", title: "Orders", pattern: "orders/:id" }
70
+ ], router: router, children: _jsx(PageContainer, {}) }));
71
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
72
+ const homeLink = within(breadcrumbs).getByRole("link", { name: "Home" });
73
+ await user.click(homeLink);
74
+ expect(router.navigate).toHaveBeenCalledWith("/", expect.objectContaining({}));
75
+ router.navigate.mockClear();
76
+ expect(within(breadcrumbs).getByText("Orders")).toBeTruthy();
77
+ expect(screen.getByText("Orders", { ignore: "nav *" }));
78
+ });
79
+ test("renders nested dynamic correctly", async () => {
80
+ const router = {
81
+ pathname: "/users/invoices/123",
82
+ searchParams: new URLSearchParams(),
83
+ navigate: vi.fn()
84
+ };
85
+ render(_jsx(AppProvider, { navigation: [
86
+ {
87
+ segment: "users",
88
+ title: "Users",
89
+ children: [
90
+ {
91
+ segment: "invoices",
92
+ title: "Invoices",
93
+ pattern: "invoices/:id"
94
+ }
95
+ ]
96
+ }
97
+ ], router: router, children: _jsx(PageContainer, {}) }));
98
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
99
+ const homeLink = within(breadcrumbs).getByRole("link", { name: "Users" });
100
+ expect(homeLink.getAttribute("href")).toBe("/users");
101
+ expect(within(breadcrumbs).getByText("Invoices")).toBeTruthy();
102
+ });
103
+ test("renders custom breadcrumbs", async () => {
104
+ render(_jsx(PageContainer, { breadcrumbs: [
105
+ { title: "Hello", path: "/hello" },
106
+ { title: "World", path: "/world" }
107
+ ] }));
108
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
109
+ const helloLink = within(breadcrumbs).getByRole("link", { name: "Hello" });
110
+ expect(helloLink.getAttribute("href")).toBe("/hello");
111
+ expect(within(breadcrumbs).getByText("World")).toBeTruthy();
112
+ });
113
+ // TODO: Remove in the next major version
114
+ test("renders legacy breadcrumbs prop", async () => {
115
+ vi.spyOn(console, "warn").mockImplementation(() => { });
116
+ render(_jsx(PageContainer, { breadCrumbs: [
117
+ { title: "Hello", path: "/hello" },
118
+ { title: "World", path: "/world" }
119
+ ] }));
120
+ const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
121
+ const helloLink = within(breadcrumbs).getByRole("link", { name: "Hello" });
122
+ expect(helloLink.getAttribute("href")).toBe("/hello");
123
+ expect(within(breadcrumbs).getByText("World")).toBeTruthy();
124
+ expect(console.warn).toHaveBeenCalledOnce();
125
+ expect(console.warn).toHaveBeenCalledWith("The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead.");
126
+ });
127
+ });
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ export interface PageContainerToolbarProps {
3
+ children?: React.ReactNode;
4
+ }
5
+ /**
6
+ *
7
+ * Demos:
8
+ *
9
+ * - [Page Container](https://mui.com/toolpad/core/react-page-container/)
10
+ *
11
+ * API:
12
+ *
13
+ * - [PageContainerToolbar API](https://mui.com/toolpad/core/api/page-container-toolbar)
14
+ */
15
+ declare function PageContainerToolbar(props: PageContainerToolbarProps): import("react/jsx-runtime").JSX.Element;
16
+ export { PageContainerToolbar };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { styled } from "@mui/material";
4
+ const PageContainerToolbarRoot = styled("div")(({ theme }) => ({
5
+ display: "flex",
6
+ flexDirection: "row",
7
+ gap: theme.spacing(1),
8
+ // Ensure the toolbar is always on the right side, even after wrapping
9
+ marginLeft: "auto"
10
+ }));
11
+ /**
12
+ *
13
+ * Demos:
14
+ *
15
+ * - [Page Container](https://mui.com/toolpad/core/react-page-container/)
16
+ *
17
+ * API:
18
+ *
19
+ * - [PageContainerToolbar API](https://mui.com/toolpad/core/api/page-container-toolbar)
20
+ */
21
+ function PageContainerToolbar(props) {
22
+ return _jsx(PageContainerToolbarRoot, { ...props });
23
+ }
24
+ export { PageContainerToolbar };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describe, test } from "vitest";
3
+ import { PageContainerToolbar } from "./PageContainerToolbar";
4
+ import describeConformance from "../utils/describeConformance";
5
+ describe("PageContainerToolbar", () => {
6
+ describeConformance(_jsx(PageContainerToolbar, {}), () => ({
7
+ skip: ["themeDefaultProps"]
8
+ }));
9
+ test("dummy test", () => { });
10
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./PageContainer";
2
+ export * from "./PageContainerToolbar";
@@ -0,0 +1,2 @@
1
+ export * from "./PageContainer";
2
+ export * from "./PageContainerToolbar";
@@ -0,0 +1,8 @@
1
+ export * from "./AppProvider";
2
+ export * from "./DashboardLayout";
3
+ export * from "./Account";
4
+ export * from "./PageContainer";
5
+ export * from "./useActivePage";
6
+ export * from "./useLocalStorageState";
7
+ export * from "./useSessionStorageState";
8
+ export * from "./persistence/codec";
@@ -0,0 +1,8 @@
1
+ export * from "./AppProvider";
2
+ export * from "./DashboardLayout";
3
+ export * from "./Account";
4
+ export * from "./PageContainer";
5
+ export * from "./useActivePage";
6
+ export * from "./useLocalStorageState";
7
+ export * from "./useSessionStorageState";
8
+ export * from "./persistence/codec";
@@ -0,0 +1,6 @@
1
+ import type { AppProviderProps } from "../AppProvider";
2
+ /**
3
+ * @ignore - internal component.
4
+ */
5
+ declare function AppProvider(props: AppProviderProps): import("react/jsx-runtime").JSX.Element;
6
+ export { AppProvider };
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRouter } from "next/compat/router";
4
+ import { AppProviderNextApp } from "./AppProviderNextApp";
5
+ import { AppProviderNextPages } from "./AppProviderNextPages";
6
+ /**
7
+ * @ignore - internal component.
8
+ */
9
+ function AppProvider(props) {
10
+ const router = useRouter();
11
+ const AppProviderComponent = router
12
+ ? AppProviderNextPages
13
+ : AppProviderNextApp;
14
+ return _jsx(AppProviderComponent, { ...props });
15
+ }
16
+ export { AppProvider };
@@ -0,0 +1 @@
1
+ export {};