From 225ac70634759607550d20d9866f6db13948f2e1 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Fri, 31 Jan 2025 09:22:24 +0100 Subject: [PATCH 01/31] fix: package.json pretty print Closes #4643 --- packages/create-toolpad-app/src/generateProject.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/src/generateProject.ts b/packages/create-toolpad-app/src/generateProject.ts index 0ea3b8e7641..82f9b3d9cb1 100644 --- a/packages/create-toolpad-app/src/generateProject.ts +++ b/packages/create-toolpad-app/src/generateProject.ts @@ -62,7 +62,7 @@ export default function generateProject( [ 'package.json', { - content: JSON.stringify(packageJson(options)), + content: JSON.stringify(packageJson(options), null, 2), }, ], ]); From 982a79bf1a9052dfb02805826a1f8e03815ec6e2 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Fri, 28 Mar 2025 12:06:58 +0100 Subject: [PATCH 02/31] chore(dashboard-layout): add custom toolbar --- .../src/DashboardLayout/DashboardLayout.tsx | 18 ++++++++++++++---- playground/nextjs-pages/.env.example | 7 +++++++ playground/nextjs-pages/package.json | 3 ++- 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 playground/nextjs-pages/.env.example diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 4a3c29f6fb1..776f35136b6 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -40,6 +40,7 @@ export interface DashboardLayoutSlotProps { toolbarActions?: {}; toolbarAccount?: AccountProps; sidebarFooter?: SidebarFooterProps; + toolbar?: {}; } export interface DashboardLayoutSlots { @@ -63,6 +64,10 @@ export interface DashboardLayoutSlots { * @default null */ sidebarFooter?: React.JSXElementConstructor; + /** + * Optional toolbar component used in the layout header instead of the default. + */ + toolbar?: React.JSXElementConstructor<{}>; } export interface DashboardLayoutProps { @@ -385,10 +390,15 @@ function DashboardLayout(props: DashboardLayoutProps) { )} - - - - + {/* show slots.toolbar if provided, else the stack */} + {slots?.toolbar ? ( + + ) : ( + + + + + )} diff --git a/playground/nextjs-pages/.env.example b/playground/nextjs-pages/.env.example new file mode 100644 index 00000000000..9cefba375ff --- /dev/null +++ b/playground/nextjs-pages/.env.example @@ -0,0 +1,7 @@ +# Force development mode +NODE_ENV=development +# Set up OAuth client: https://github.com/settings/developers +GITHUB_CLIENT_ID="" +# Read the docs: https://authjs.dev/guides/configuring-github +GITHUB_CLIENT_SECRET="" +# echo "AUTH_SECRET=$(openssl rand -base64 32)" >> .env.local diff --git a/playground/nextjs-pages/package.json b/playground/nextjs-pages/package.json index 5ea114db676..aa045a7d40c 100644 --- a/playground/nextjs-pages/package.json +++ b/playground/nextjs-pages/package.json @@ -3,6 +3,7 @@ "version": "0.13.0", "private": true, "scripts": { + "predev": "[ ! -f .env.local ] && cp .env.example .env.local && echo \"AUTH_SECRET=\"$(openssl rand -base64 32)\"\" >> .env.local || echo '.env.local already exists'", "dev": "next dev", "lint": "next lint" }, @@ -22,4 +23,4 @@ "react-dom": "^19.0.0", "zod": "3.24.2" } -} +} \ No newline at end of file From e26102d3a7285c2582dd67997950939a224098bf Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Mon, 31 Mar 2025 09:49:20 +0200 Subject: [PATCH 03/31] feat(dashboard-layout): add cart and custom toolbar --- .../src/DashboardLayout/DashboardLayout.tsx | 8 ++ playground/nextjs-pages/src/pages/_app.tsx | 71 ++++++++++++++++- playground/nextjs/package.json | 3 +- .../nextjs/src/app/(dashboard)/layout.tsx | 79 ++++++++++++++++++- 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 776f35136b6..a63084ceff2 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -13,6 +13,7 @@ import useMediaQuery from '@mui/material/useMediaQuery'; import type {} from '@mui/material/themeCssVarsAugmentation'; import MenuIcon from '@mui/icons-material/Menu'; import MenuOpenIcon from '@mui/icons-material/MenuOpen'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { BrandingContext, NavigationContext, WindowContext } from '../shared/context'; import { Account, type AccountProps } from '../Account'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; @@ -59,6 +60,11 @@ export interface DashboardLayoutSlots { * @default Account */ toolbarAccount?: React.JSXElementConstructor; + /** + * Optional footer component used in the layout sidebar. + * @default null + */ + toolbarCart?: React.JSXElementConstructor<{}>; /** * Optional footer component used in the layout sidebar. * @default null @@ -265,6 +271,7 @@ function DashboardLayout(props: DashboardLayoutProps) { const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; const ToolbarAccountSlot = slots?.toolbarAccount ?? Account; + const ToolbarCartSlot = slots?.toolbarCart ?? React.Fragment; const SidebarFooterSlot = slots?.sidebarFooter ?? null; const getDrawerContent = React.useCallback( @@ -397,6 +404,7 @@ function DashboardLayout(props: DashboardLayoutProps) { + )} diff --git a/playground/nextjs-pages/src/pages/_app.tsx b/playground/nextjs-pages/src/pages/_app.tsx index 6ee56f6040b..fb060c6d813 100644 --- a/playground/nextjs-pages/src/pages/_app.tsx +++ b/playground/nextjs-pages/src/pages/_app.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useRouter } from 'next/router'; import { NextAppProvider } from '@toolpad/core/nextjs'; import { PageContainer } from '@toolpad/core/PageContainer'; -import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, ToolbarActions } from '@toolpad/core/DashboardLayout'; import Head from 'next/head'; import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter'; import DashboardIcon from '@mui/icons-material/Dashboard'; @@ -12,6 +12,9 @@ import type { AppProps } from 'next/app'; import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; +import { Box, Button, IconButton, InputBase, Paper, Stack } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import { Account } from '@toolpad/core/Account'; export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; @@ -48,6 +51,70 @@ const AUTHENTICATION = { signOut, }; +function SearchBar() { + return ( + theme.palette.action.hover, + display: { xs: 'none', sm: 'flex' }, + }} + > + + + + + + ); +} + +function CustomToolbar() { + return ( + + + + + + + + + + + + + + ); +} + +function ToolbarCart() { + return ( + + ); +} + function DefaultLayout({ page }: { page: React.ReactElement }) { const router = useRouter(); const { segments = [] } = router.query; @@ -67,7 +134,7 @@ function DefaultLayout({ page }: { page: React.ReactElement }) { }, [orderId, router.asPath]); return ( - + {page} ); diff --git a/playground/nextjs/package.json b/playground/nextjs/package.json index 7a45d4a338a..ff27648a406 100644 --- a/playground/nextjs/package.json +++ b/playground/nextjs/package.json @@ -3,6 +3,7 @@ "version": "0.13.0", "private": true, "scripts": { + "predev": "[ ! -f .env.local ] && cp .env.example .env.local && echo \"AUTH_SECRET=\"$(openssl rand -base64 32)\"\" >> .env.local || echo '.env.local already exists'", "dev": "next dev", "lint": "next lint" }, @@ -23,4 +24,4 @@ "react-dom": "^19.0.0", "zod": "3.24.2" } -} +} \ No newline at end of file diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index 52b3ac44e6b..0b05a687750 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -1,5 +1,6 @@ 'use client'; -import * as React from 'react'; + +import React from 'react'; import { usePathname, useParams } from 'next/navigation'; import Typography from '@mui/material/Typography'; import Stack from '@mui/material/Stack'; @@ -16,8 +17,11 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, SidebarFooterProps, ToolbarActions } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; +import { Box, Button, IconButton, InputBase, Paper } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; const accounts = [ { @@ -157,6 +161,70 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { ); } +function SearchBar() { + return ( + theme.palette.action.hover, + display: { xs: 'none', sm: 'flex' }, + }} + > + + + + + + ); +} + +function CustomToolbar() { + return ( + + + + + + + + + + + + + + ); +} + +function ToolbarCart() { + return ( + + ); +} + export default function DashboardPagesLayout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -176,7 +244,12 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - null }}> + {props.children} ); From 32ae5b657b2dd746d057e399cc4afa105cd934ce Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Mon, 31 Mar 2025 09:50:37 +0200 Subject: [PATCH 04/31] chore: add example --- playground/nextjs/.env.example | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 playground/nextjs/.env.example diff --git a/playground/nextjs/.env.example b/playground/nextjs/.env.example new file mode 100644 index 00000000000..9cefba375ff --- /dev/null +++ b/playground/nextjs/.env.example @@ -0,0 +1,7 @@ +# Force development mode +NODE_ENV=development +# Set up OAuth client: https://github.com/settings/developers +GITHUB_CLIENT_ID="" +# Read the docs: https://authjs.dev/guides/configuring-github +GITHUB_CLIENT_SECRET="" +# echo "AUTH_SECRET=$(openssl rand -base64 32)" >> .env.local From 434c893da371874b6ec1dc9fca80adb6363ddbf6 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Mon, 31 Mar 2025 09:55:57 +0200 Subject: [PATCH 05/31] fix: searchbar layout: --- playground/nextjs-pages/src/pages/_app.tsx | 3 +-- playground/nextjs/src/app/(dashboard)/layout.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/playground/nextjs-pages/src/pages/_app.tsx b/playground/nextjs-pages/src/pages/_app.tsx index fb060c6d813..a4bf2ef9ecd 100644 --- a/playground/nextjs-pages/src/pages/_app.tsx +++ b/playground/nextjs-pages/src/pages/_app.tsx @@ -58,12 +58,11 @@ function SearchBar() { elevation={0} sx={{ alignItems: 'center', - width: 600, + width: { xl: 600, lg: 400, md: 'auto' }, height: 40, px: 1.5, borderRadius: 2, backgroundColor: (theme) => theme.palette.action.hover, - display: { xs: 'none', sm: 'flex' }, }} > diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index 0b05a687750..f40f93e9870 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -168,12 +168,11 @@ function SearchBar() { elevation={0} sx={{ alignItems: 'center', - width: 600, height: 40, px: 1.5, borderRadius: 2, backgroundColor: (theme) => theme.palette.action.hover, - display: { xs: 'none', sm: 'flex' }, + width: { xl: 600, lg: 400, md: 'auto' }, }} > From 5f45f016a14bc506a2aa775df61c73f8bde96a1d Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Wed, 2 Apr 2025 11:23:41 +0200 Subject: [PATCH 06/31] chore(dashboard-layout): applied comments --- .../src/DashboardLayout/DashboardLayout.tsx | 206 ++++++++++++------ 1 file changed, 145 insertions(+), 61 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index a63084ceff2..3e6ab0cb886 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -1,7 +1,14 @@ 'use client'; + import * as React from 'react'; import PropTypes from 'prop-types'; -import { styled, useTheme, SxProps } from '@mui/material'; + +// MUI: Styles & Theme +import { styled, useTheme, SxProps } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import type {} from '@mui/material/themeCssVarsAugmentation'; + +// MUI: Components import MuiAppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; @@ -9,16 +16,21 @@ import IconButton from '@mui/material/IconButton'; import Stack from '@mui/material/Stack'; import Toolbar from '@mui/material/Toolbar'; import Tooltip from '@mui/material/Tooltip'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import type {} from '@mui/material/themeCssVarsAugmentation'; + +// MUI: Icons import MenuIcon from '@mui/icons-material/Menu'; import MenuOpenIcon from '@mui/icons-material/MenuOpen'; -import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; + +// Toolpad: Context import { BrandingContext, NavigationContext, WindowContext } from '../shared/context'; + +// Toolpad: Components import { Account, type AccountProps } from '../Account'; +import { AppTitle, type AppTitleProps } from './AppTitle'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; import { ToolbarActions } from './ToolbarActions'; -import { AppTitle, AppTitleProps } from './AppTitle'; + +// Toolpad: Utils & Constants import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from './utils'; import { MINI_DRAWER_WIDTH } from './shared'; import type { Branding, Navigation } from '../AppProvider'; @@ -60,20 +72,81 @@ export interface DashboardLayoutSlots { * @default Account */ toolbarAccount?: React.JSXElementConstructor; - /** - * Optional footer component used in the layout sidebar. - * @default null - */ - toolbarCart?: React.JSXElementConstructor<{}>; /** * Optional footer component used in the layout sidebar. * @default null */ sidebarFooter?: React.JSXElementConstructor; + /** - * Optional toolbar component used in the layout header instead of the default. + * Use this slot to replace the full default toolbar. + * + * This component will completely override the internal layout of the top bar. + * You can still use the built-in ``, `` and `` components inside. + * + * @default null + * @see https://mui.com/toolpad/core/api/dashboard-layout + * @example + * ```tsx + * import { Stack, IconButton, Box, InputBase, Button } from '@mui/material'; + * import { MenuOpen, Search, ShoppingCart } from '@mui/icons-material'; + * import { Account } from '@toolpad/core/Account'; + * import { DashboardLayout, ToolbarActions } from '@toolpad/core/DashboardLayout'; + * + * function CustomToolbar() { + * return ( + * + * + * + * + * + * + * + * + * theme.palette.action.hover, + * }} + * placeholder="Search" + * inputProps={{ 'aria-label': 'search' }} + * startAdornment={} + * /> + * + * + * + * + * + * + * + * + * + * ); + * } + * + * export default function CustomDashboardLayout() { + * return ; + * } + * ``` */ toolbar?: React.JSXElementConstructor<{}>; + + /** Use this slot to replace the left of the default toolbar. + * @default null + */ + toolbarLeft?: React.JSXElementConstructor<{}>; + + /** Use this slot to replace the center of the default toolbar. + * @default null + */ + toolbarCenter?: React.JSXElementConstructor<{}>; + + /** Use this slot to replace the right of the default toolbar. + * @default null + */ + toolbarRight?: React.JSXElementConstructor<{}>; } export interface DashboardLayoutProps { @@ -271,7 +344,6 @@ function DashboardLayout(props: DashboardLayoutProps) { const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; const ToolbarAccountSlot = slots?.toolbarAccount ?? Account; - const ToolbarCartSlot = slots?.toolbarCart ?? React.Fragment; const SidebarFooterSlot = slots?.sidebarFooter ?? null; const getDrawerContent = React.useCallback( @@ -356,58 +428,70 @@ function DashboardLayout(props: DashboardLayoutProps) { > - - - {!hideNavigation ? ( - - - {getMenuIcon(isMobileNavigationExpanded)} - - - {getMenuIcon(isDesktopNavigationExpanded)} - - - ) : null} - {slots?.appTitle ? ( - + {slots?.toolbar ? ( + + ) : ( + + {/* Toolbar Left section */} + {slots?.toolbarLeft ? ( + + ) : ( + + {!hideNavigation ? ( + + + {getMenuIcon(isMobileNavigationExpanded)} + + + {getMenuIcon(isDesktopNavigationExpanded)} + + + ) : null} + {slots?.appTitle ? ( + + ) : ( + /* Hierarchy of application of `branding` + * 1. Branding prop passed in the `slotProps.appTitle` + * 2. Branding prop passed to the `DashboardLayout` + * 3. Branding prop passed to the `AppProvider` + */ + + )} + + )} + + {/* Toolbar Center section */} + {slots?.toolbarCenter ? : null} + + {/* Toolbar Right section */} + {slots?.toolbarRight ? ( + ) : ( - /* Hierarchy of application of `branding` - * 1. Branding prop passed in the `slotProps.appTitle` - * 2. Branding prop passed to the `DashboardLayout` - * 3. Branding prop passed to the `AppProvider` - */ - + + + + )} - {/* show slots.toolbar if provided, else the stack */} - {slots?.toolbar ? ( - - ) : ( - - - - - - )} - + )} From 772344776ba5ba1e9a4a1ebfe865737f94137393 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Wed, 2 Apr 2025 11:27:10 +0200 Subject: [PATCH 07/31] chore(playground): reverts changes --- playground/nextjs-pages/.env.example | 7 -- playground/nextjs-pages/package.json | 3 +- playground/nextjs-pages/src/pages/_app.tsx | 70 +---------------- playground/nextjs/.env.example | 7 -- playground/nextjs/package.json | 3 +- .../nextjs/src/app/(dashboard)/layout.tsx | 78 +------------------ 6 files changed, 7 insertions(+), 161 deletions(-) delete mode 100644 playground/nextjs-pages/.env.example delete mode 100644 playground/nextjs/.env.example diff --git a/playground/nextjs-pages/.env.example b/playground/nextjs-pages/.env.example deleted file mode 100644 index 9cefba375ff..00000000000 --- a/playground/nextjs-pages/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -# Force development mode -NODE_ENV=development -# Set up OAuth client: https://github.com/settings/developers -GITHUB_CLIENT_ID="" -# Read the docs: https://authjs.dev/guides/configuring-github -GITHUB_CLIENT_SECRET="" -# echo "AUTH_SECRET=$(openssl rand -base64 32)" >> .env.local diff --git a/playground/nextjs-pages/package.json b/playground/nextjs-pages/package.json index aa045a7d40c..5ea114db676 100644 --- a/playground/nextjs-pages/package.json +++ b/playground/nextjs-pages/package.json @@ -3,7 +3,6 @@ "version": "0.13.0", "private": true, "scripts": { - "predev": "[ ! -f .env.local ] && cp .env.example .env.local && echo \"AUTH_SECRET=\"$(openssl rand -base64 32)\"\" >> .env.local || echo '.env.local already exists'", "dev": "next dev", "lint": "next lint" }, @@ -23,4 +22,4 @@ "react-dom": "^19.0.0", "zod": "3.24.2" } -} \ No newline at end of file +} diff --git a/playground/nextjs-pages/src/pages/_app.tsx b/playground/nextjs-pages/src/pages/_app.tsx index a4bf2ef9ecd..6ee56f6040b 100644 --- a/playground/nextjs-pages/src/pages/_app.tsx +++ b/playground/nextjs-pages/src/pages/_app.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useRouter } from 'next/router'; import { NextAppProvider } from '@toolpad/core/nextjs'; import { PageContainer } from '@toolpad/core/PageContainer'; -import { DashboardLayout, ToolbarActions } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout } from '@toolpad/core/DashboardLayout'; import Head from 'next/head'; import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter'; import DashboardIcon from '@mui/icons-material/Dashboard'; @@ -12,9 +12,6 @@ import type { AppProps } from 'next/app'; import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; -import { Box, Button, IconButton, InputBase, Paper, Stack } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; -import { Account } from '@toolpad/core/Account'; export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; @@ -51,69 +48,6 @@ const AUTHENTICATION = { signOut, }; -function SearchBar() { - return ( - theme.palette.action.hover, - }} - > - - - - - - ); -} - -function CustomToolbar() { - return ( - - - - - - - - - - - - - - ); -} - -function ToolbarCart() { - return ( - - ); -} - function DefaultLayout({ page }: { page: React.ReactElement }) { const router = useRouter(); const { segments = [] } = router.query; @@ -133,7 +67,7 @@ function DefaultLayout({ page }: { page: React.ReactElement }) { }, [orderId, router.asPath]); return ( - + {page} ); diff --git a/playground/nextjs/.env.example b/playground/nextjs/.env.example deleted file mode 100644 index 9cefba375ff..00000000000 --- a/playground/nextjs/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -# Force development mode -NODE_ENV=development -# Set up OAuth client: https://github.com/settings/developers -GITHUB_CLIENT_ID="" -# Read the docs: https://authjs.dev/guides/configuring-github -GITHUB_CLIENT_SECRET="" -# echo "AUTH_SECRET=$(openssl rand -base64 32)" >> .env.local diff --git a/playground/nextjs/package.json b/playground/nextjs/package.json index ff27648a406..7a45d4a338a 100644 --- a/playground/nextjs/package.json +++ b/playground/nextjs/package.json @@ -3,7 +3,6 @@ "version": "0.13.0", "private": true, "scripts": { - "predev": "[ ! -f .env.local ] && cp .env.example .env.local && echo \"AUTH_SECRET=\"$(openssl rand -base64 32)\"\" >> .env.local || echo '.env.local already exists'", "dev": "next dev", "lint": "next lint" }, @@ -24,4 +23,4 @@ "react-dom": "^19.0.0", "zod": "3.24.2" } -} \ No newline at end of file +} diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index f40f93e9870..52b3ac44e6b 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -1,6 +1,5 @@ 'use client'; - -import React from 'react'; +import * as React from 'react'; import { usePathname, useParams } from 'next/navigation'; import Typography from '@mui/material/Typography'; import Stack from '@mui/material/Stack'; @@ -17,11 +16,8 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps, ToolbarActions } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; -import { Box, Button, IconButton, InputBase, Paper } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; -import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; const accounts = [ { @@ -161,69 +157,6 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { ); } -function SearchBar() { - return ( - theme.palette.action.hover, - width: { xl: 600, lg: 400, md: 'auto' }, - }} - > - - - - - - ); -} - -function CustomToolbar() { - return ( - - - - - - - - - - - - - - ); -} - -function ToolbarCart() { - return ( - - ); -} - export default function DashboardPagesLayout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -243,12 +176,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - + null }}> {props.children} ); From 6ad6279b44249be606195e2026c2470e3081f4f0 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Wed, 2 Apr 2025 13:33:24 +0200 Subject: [PATCH 08/31] refactor(dashboard-layout): add properties --- .../src/DashboardLayout/DashboardLayout.tsx | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 3e6ab0cb886..53b5f56663f 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -48,12 +48,19 @@ export interface SidebarFooterProps { mini: boolean; } +export interface ToolbarProps { + menuIcon: React.ReactElement; +} + export interface DashboardLayoutSlotProps { appTitle?: AppTitleProps; toolbarActions?: {}; toolbarAccount?: AccountProps; sidebarFooter?: SidebarFooterProps; toolbar?: {}; + toolbarLeft?: {}; + toolbarMiddle?: {}; + toolbarRight?: {}; } export interface DashboardLayoutSlots { @@ -131,22 +138,22 @@ export interface DashboardLayoutSlots { * } * ``` */ - toolbar?: React.JSXElementConstructor<{}>; + toolbar?: React.JSXElementConstructor; /** Use this slot to replace the left of the default toolbar. * @default null */ - toolbarLeft?: React.JSXElementConstructor<{}>; + toolbarLeft?: React.JSXElementConstructor; /** Use this slot to replace the center of the default toolbar. * @default null */ - toolbarCenter?: React.JSXElementConstructor<{}>; + toolbarMiddle?: React.JSXElementConstructor; /** Use this slot to replace the right of the default toolbar. * @default null */ - toolbarRight?: React.JSXElementConstructor<{}>; + toolbarRight?: React.JSXElementConstructor; } export interface DashboardLayoutProps { @@ -414,6 +421,11 @@ function DashboardLayout(props: DashboardLayoutProps) { const layoutRef = React.useRef(null); + const toolbarSlotProps: ToolbarProps = { + ...slotProps?.toolbar, + menuIcon: getMenuIcon(isMobileNavigationExpanded), + }; + return ( {slots?.toolbar ? ( - + ) : ( {/* Toolbar Left section */} {slots?.toolbarLeft ? ( - + ) : ( {!hideNavigation ? ( @@ -479,11 +491,11 @@ function DashboardLayout(props: DashboardLayoutProps) { )} {/* Toolbar Center section */} - {slots?.toolbarCenter ? : null} + {slots?.toolbarMiddle ? : null} {/* Toolbar Right section */} {slots?.toolbarRight ? ( - + ) : ( From 3062c81d98458487d1632e3a6da4b9b117406c4f Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Wed, 2 Apr 2025 13:41:57 +0200 Subject: [PATCH 09/31] refactor(dashboard-layout): removes extensive docs --- .../src/DashboardLayout/DashboardLayout.tsx | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 53b5f56663f..4d9edd2ca74 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -92,51 +92,6 @@ export interface DashboardLayoutSlots { * You can still use the built-in ``, `` and `` components inside. * * @default null - * @see https://mui.com/toolpad/core/api/dashboard-layout - * @example - * ```tsx - * import { Stack, IconButton, Box, InputBase, Button } from '@mui/material'; - * import { MenuOpen, Search, ShoppingCart } from '@mui/icons-material'; - * import { Account } from '@toolpad/core/Account'; - * import { DashboardLayout, ToolbarActions } from '@toolpad/core/DashboardLayout'; - * - * function CustomToolbar() { - * return ( - * - * - * - * - * - * - * - * - * theme.palette.action.hover, - * }} - * placeholder="Search" - * inputProps={{ 'aria-label': 'search' }} - * startAdornment={} - * /> - * - * - * - * - * - * - * - * - * - * ); - * } - * - * export default function CustomDashboardLayout() { - * return ; - * } - * ``` */ toolbar?: React.JSXElementConstructor; From be5fc8e0b789395de237814a3eb74613ac11ed28 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Thu, 3 Apr 2025 07:26:41 +0200 Subject: [PATCH 10/31] docs(dashboard-layout): Add custom toolbar --- .../DashboardLayoutCustomToolbar.js | 258 +++++++++++++++++ .../DashboardLayoutCustomToolbar.tsx | 267 ++++++++++++++++++ .../DashboardLayoutCustomToolbar.tsx.preview | 26 ++ .../dashboard-layout/dashboard-layout.md | 20 +- .../src/DashboardLayout/DashboardLayout.tsx | 106 +++---- 5 files changed, 607 insertions(+), 70 deletions(-) create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx create mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js new file mode 100644 index 00000000000..34b8108f243 --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js @@ -0,0 +1,258 @@ +// React +import * as React from 'react'; + +// MUI Core +import Box from '@mui/material/Box'; +import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormLabel from '@mui/material/FormLabel'; +import IconButton from '@mui/material/IconButton'; +import Popover from '@mui/material/Popover'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import Tooltip from '@mui/material/Tooltip'; +import Typography from '@mui/material/Typography'; +import { Button, InputBase, Paper, Stack } from '@mui/material'; +import { createTheme, useColorScheme } from '@mui/material/styles'; + +// MUI Icons +import DashboardIcon from '@mui/icons-material/Dashboard'; +import SearchIcon from '@mui/icons-material/Search'; +import SettingsIcon from '@mui/icons-material/Settings'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; + +// Toolpad Core +import { Account } from '@toolpad/core/Account'; +import { AppProvider } from '@toolpad/core/AppProvider'; +import { + DashboardLayout, + // ToolbarActions, +} from '@toolpad/core/DashboardLayout'; +// import { AppTitle } from '@toolpad/core/DashboardLayout/AppTitle'; +import { useDemoRouter } from '@toolpad/core/internal'; + +const NAVIGATION = [ + { + kind: 'header', + title: 'Main items', + }, + { + segment: 'dashboard', + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function DemoPageContent({ pathname }) { + return ( + + Dashboard content for {pathname} + + ); +} + +function CustomThemeSwitcher() { + const { setMode } = useColorScheme(); + + const handleThemeChange = React.useCallback( + (event) => { + setMode(event.target.value); + }, + [setMode], + ); + + const [isMenuOpen, setIsMenuOpen] = React.useState(false); + const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); + + const toggleMenu = React.useCallback( + (event) => { + setMenuAnchorEl(isMenuOpen ? null : event.currentTarget); + setIsMenuOpen((previousIsMenuOpen) => !previousIsMenuOpen); + }, + [isMenuOpen], + ); + + return ( + + +

+ + + +
+ + + + + Theme + + } label="Light" /> + } label="System" /> + } label="Dark" /> + + + + + + ); +} + +function SearchBar() { + return ( + theme.palette.action.hover, + // display: { xs: 'none', sm: 'flex' }, + }} + > + + + + + + ); +} + +function Left({ menuIcon }) { + return ( + + {menuIcon} + {/* put the AppTitle here if you want to use the title component */} + {/* */} + + ); +} + +function Middle() { + return ( + + + + ); +} + +function Right() { + return ( + + + {/* put the ToolbarActions here if you want to use the toolbar actions component */} + {/* */} + + + + + ); +} + +function CustomToolbar(props) { + return ( + + + + + + ); +} + +export default function DashboardLayoutCustomToolbar(props) { + const { window } = props; + + const router = useDemoRouter('/dashboard'); + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + return ( + + + + + + ); +} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx new file mode 100644 index 00000000000..1cee91c9751 --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx @@ -0,0 +1,267 @@ +// React +import * as React from 'react'; + +// MUI Core +import Box from '@mui/material/Box'; +import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormLabel from '@mui/material/FormLabel'; +import IconButton from '@mui/material/IconButton'; +import Popover from '@mui/material/Popover'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import Tooltip from '@mui/material/Tooltip'; +import Typography from '@mui/material/Typography'; +import { Button, InputBase, Paper, Stack } from '@mui/material'; +import { createTheme, useColorScheme } from '@mui/material/styles'; + +// MUI Icons +import DashboardIcon from '@mui/icons-material/Dashboard'; +import SearchIcon from '@mui/icons-material/Search'; +import SettingsIcon from '@mui/icons-material/Settings'; +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; + +// Toolpad Core +import { Account } from '@toolpad/core/Account'; +import { AppProvider, type Navigation } from '@toolpad/core/AppProvider'; +import { + DashboardLayout, + // ToolbarActions, + ToolbarProps, +} from '@toolpad/core/DashboardLayout'; +// import { AppTitle } from '@toolpad/core/DashboardLayout/AppTitle'; +import { useDemoRouter } from '@toolpad/core/internal'; + +const NAVIGATION: Navigation = [ + { + kind: 'header', + title: 'Main items', + }, + { + segment: 'dashboard', + title: 'Dashboard', + icon: , + }, + { + segment: 'orders', + title: 'Orders', + icon: , + }, +]; + +const demoTheme = createTheme({ + cssVariables: { + colorSchemeSelector: 'data-toolpad-color-scheme', + }, + colorSchemes: { light: true, dark: true }, + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 600, + lg: 1200, + xl: 1536, + }, + }, +}); + +function DemoPageContent({ pathname }: { pathname: string }) { + return ( + + Dashboard content for {pathname} + + ); +} + +function CustomThemeSwitcher() { + const { setMode } = useColorScheme(); + + const handleThemeChange = React.useCallback( + (event: React.ChangeEvent) => { + setMode(event.target.value as 'light' | 'dark' | 'system'); + }, + [setMode], + ); + + const [isMenuOpen, setIsMenuOpen] = React.useState(false); + const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); + + const toggleMenu = React.useCallback( + (event: React.MouseEvent) => { + setMenuAnchorEl(isMenuOpen ? null : event.currentTarget); + setIsMenuOpen((previousIsMenuOpen) => !previousIsMenuOpen); + }, + [isMenuOpen], + ); + + return ( + + +
+ + + +
+
+ + + + Theme + + } label="Light" /> + } label="System" /> + } label="Dark" /> + + + + +
+ ); +} + +interface DemoProps { + /** + * Injected by the documentation to work in an iframe. + * Remove this when copying and pasting into your project. + */ + window?: () => Window; +} + +function SearchBar() { + return ( + theme.palette.action.hover, + // display: { xs: 'none', sm: 'flex' }, + }} + > + + + + + + ); +} + +function Left({ menuIcon }: ToolbarProps) { + return ( + + {menuIcon} + {/* put the AppTitle here if you want to use the title component */} + {/* */} + + ); +} + +function Middle() { + return ( + + + + ); +} + +function Right() { + return ( + + + {/* put the ToolbarActions here if you want to use the toolbar actions component */} + {/* */} + + + + + ); +} + +function CustomToolbar(props: ToolbarProps) { + return ( + + + + + + ); +} + +export default function DashboardLayoutCustomToolbar(props: DemoProps) { + const { window } = props; + + const router = useDemoRouter('/dashboard'); + + // Remove this const when copying and pasting into your project. + const demoWindow = window !== undefined ? window() : undefined; + + return ( + + + + + + ); +} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview new file mode 100644 index 00000000000..62a0d18da8a --- /dev/null +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview @@ -0,0 +1,26 @@ +function CustomToolbar(props: ToolbarProps) { + return ( + + + + + + ); +} + + + + \ No newline at end of file diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 016da5bb08f..cbfc98241ef 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -159,20 +159,30 @@ Some possibly useful slots: - `appTitle`: allows you to customize the app title section in the layout header. -- `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` component can be imported and used if you wish to do so, as shown in the example below. +- `toolbarAccount`: (deprecated, use toolbar) allows you to replace the default `Account` slot in the header. + +- `toolbarActions`: (deprecated, use toolbar) allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` component can be imported and used if you wish to do so, as shown in the example below. - `sidebarFooter`: allows you to add footer content in the sidebar. -{{"demo": "DashboardLayoutSlots.js", "height": 400, "iframe": true}} +- `toolbar`: This component will completely override the internal layout of the top bar and allows full control over the toolbar. The default `ToolbarActions` component, menuIcon and `Account` can be imported and used if you wish to do so, as shown in the example below. -### Examples +### sidebarFooter + +{{"demo": "DashboardLayoutSlots.js", "height": 400, "iframe": true}} -#### User account in layout sidebar +### User account in layout sidebar {{"demo": "DashboardLayoutAccountSidebar.js", "height": 400, "iframe": true}} -#### Settings menu with custom theme switcher +### Settings menu with custom theme switcher The `useColorScheme` hook can be used to create a custom theme switcher. {{"demo": "DashboardLayoutCustomThemeSwitcher.js", "height": 400, "iframe": true}} + +### Replaced the full toolbar with a custom one + +The `toolbar` slot can be replaced with a custom component, that in turn reuses the out of the box `Account`, `menuIcon`, `AppTitle`and `ToolbarActions` components, giving full control on their order, appearance and behavior. + +{{"demo": "DashboardLayoutCustomToolbar.js", "height": 400, "iframe": true}} diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 4d9edd2ca74..8c4647a0f36 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -58,9 +58,6 @@ export interface DashboardLayoutSlotProps { toolbarAccount?: AccountProps; sidebarFooter?: SidebarFooterProps; toolbar?: {}; - toolbarLeft?: {}; - toolbarMiddle?: {}; - toolbarRight?: {}; } export interface DashboardLayoutSlots { @@ -72,6 +69,8 @@ export interface DashboardLayoutSlots { /** * The toolbar actions component used in the layout header. * @default ToolbarActions + * @deprecated Use `slots.toolbar` instead. + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ toolbarActions?: React.JSXElementConstructor<{}>; /** @@ -82,6 +81,8 @@ export interface DashboardLayoutSlots { /** * Optional footer component used in the layout sidebar. * @default null + * @deprecated Use `slots.toolbar` instead. + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ sidebarFooter?: React.JSXElementConstructor; @@ -92,23 +93,9 @@ export interface DashboardLayoutSlots { * You can still use the built-in ``, `` and `` components inside. * * @default null + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ toolbar?: React.JSXElementConstructor; - - /** Use this slot to replace the left of the default toolbar. - * @default null - */ - toolbarLeft?: React.JSXElementConstructor; - - /** Use this slot to replace the center of the default toolbar. - * @default null - */ - toolbarMiddle?: React.JSXElementConstructor; - - /** Use this slot to replace the right of the default toolbar. - * @default null - */ - toolbarRight?: React.JSXElementConstructor; } export interface DashboardLayoutProps { @@ -408,55 +395,44 @@ function DashboardLayout(props: DashboardLayoutProps) { }} > {/* Toolbar Left section */} - {slots?.toolbarLeft ? ( - - ) : ( - - {!hideNavigation ? ( - - - {getMenuIcon(isMobileNavigationExpanded)} - - - {getMenuIcon(isDesktopNavigationExpanded)} - - - ) : null} - {slots?.appTitle ? ( - - ) : ( - /* Hierarchy of application of `branding` - * 1. Branding prop passed in the `slotProps.appTitle` - * 2. Branding prop passed to the `DashboardLayout` - * 3. Branding prop passed to the `AppProvider` - */ - - )} - - )} - - {/* Toolbar Center section */} - {slots?.toolbarMiddle ? : null} + + {!hideNavigation ? ( + + + {getMenuIcon(isMobileNavigationExpanded)} + + + {getMenuIcon(isDesktopNavigationExpanded)} + + + ) : null} + {slots?.appTitle ? ( + + ) : ( + /* Hierarchy of application of `branding` + * 1. Branding prop passed in the `slotProps.appTitle` + * 2. Branding prop passed to the `DashboardLayout` + * 3. Branding prop passed to the `AppProvider` + */ + + )} + {/* Toolbar Right section */} - {slots?.toolbarRight ? ( - - ) : ( - - - - - )} + + + + )} From f0e8805208f0ea9c06ddc14e685d9f88a56e43ce Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Thu, 3 Apr 2025 07:31:56 +0200 Subject: [PATCH 11/31] refactor(dashboard-layout): improve layout --- .../src/DashboardLayout/DashboardLayout.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 8c4647a0f36..2792ff7e556 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -66,23 +66,30 @@ export interface DashboardLayoutSlots { * @default Link */ appTitle?: React.ElementType; + /** * The toolbar actions component used in the layout header. * @default ToolbarActions * @deprecated Use `slots.toolbar` instead. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + * + * TODO: Remove this deprecated prop in the next major version. */ toolbarActions?: React.JSXElementConstructor<{}>; + /** * The toolbar account component used in the layout header. * @default Account + * @deprecated Use `slots.toolbar` instead. + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + * + * TODO: Remove this deprecated prop in the next major version. */ toolbarAccount?: React.JSXElementConstructor; + /** * Optional footer component used in the layout sidebar. * @default null - * @deprecated Use `slots.toolbar` instead. - * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ sidebarFooter?: React.JSXElementConstructor; From d37db359ee42beb92362b112122956b92e102afa Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Mon, 7 Apr 2025 06:42:24 +0200 Subject: [PATCH 12/31] chore: revert playground --- .../nextjs/src/app/(dashboard)/layout.tsx | 95 +------------------ 1 file changed, 3 insertions(+), 92 deletions(-) diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index b6ad0d6cc4b..52b3ac44e6b 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -16,14 +16,8 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps, ToolbarProps } from '@toolpad/core/DashboardLayout'; -import { ToolbarActions } from '@toolpad/core/DashboardLayout/ToolbarActions'; -import { AppTitle } from '@toolpad/core/DashboardLayout/AppTitle'; +import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; -import { Button, IconButton, InputBase, Paper, Theme } from '@mui/material'; -import ShoppingCart from '@mui/icons-material/ShoppingCart'; -import Link from 'next/link'; -import SearchIcon from '@mui/icons-material/Search'; const accounts = [ { @@ -47,33 +41,6 @@ const accounts = [ }, ]; -function SearchBar() { - return ( - theme.palette.action.hover, - display: { xs: 'none', sm: 'flex' }, - }} - > - - - - - - ); -} - function AccountSidebarPreview(props: AccountPreviewProps & { mini: boolean }) { const { handleClick, open, mini } = props; return ( @@ -166,7 +133,7 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { elevation: 0, sx: { overflow: 'visible', - filter: (theme: Theme) => + filter: (theme) => `drop-shadow(0px 2px 8px ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.32)'})`, mt: 1, '&::before': { @@ -190,57 +157,6 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { ); } -function Left({ menuIcon }: ToolbarProps) { - return ( - - {menuIcon} - - - ); -} - -function Middle() { - return ( - - - - ); -} - -function Right() { - return ( - - - - - - - - - ); -} - -function CustomToolbar(props: ToolbarProps) { - return ( - - - - - - ); -} - export default function DashboardPagesLayout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -260,12 +176,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - + null }}> {props.children} ); From fd67d8b67106d716142a38ef9d1ab90e45cbf154 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Sat, 19 Apr 2025 07:47:30 +0200 Subject: [PATCH 13/31] refactor(dashboard-layout): reduced documentation, cleanup comments --- .../DashboardLayoutCustomToolbar.js | 258 ----------------- .../DashboardLayoutCustomToolbar.tsx | 267 ------------------ .../DashboardLayoutCustomToolbar.tsx.preview | 26 -- .../dashboard-layout/dashboard-layout.md | 14 +- .../src/DashboardLayout/DashboardLayout.tsx | 24 +- 5 files changed, 8 insertions(+), 581 deletions(-) delete mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js delete mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx delete mode 100644 docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js deleted file mode 100644 index 34b8108f243..00000000000 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.js +++ /dev/null @@ -1,258 +0,0 @@ -// React -import * as React from 'react'; - -// MUI Core -import Box from '@mui/material/Box'; -import FormControl from '@mui/material/FormControl'; -import FormControlLabel from '@mui/material/FormControlLabel'; -import FormLabel from '@mui/material/FormLabel'; -import IconButton from '@mui/material/IconButton'; -import Popover from '@mui/material/Popover'; -import Radio from '@mui/material/Radio'; -import RadioGroup from '@mui/material/RadioGroup'; -import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; -import { Button, InputBase, Paper, Stack } from '@mui/material'; -import { createTheme, useColorScheme } from '@mui/material/styles'; - -// MUI Icons -import DashboardIcon from '@mui/icons-material/Dashboard'; -import SearchIcon from '@mui/icons-material/Search'; -import SettingsIcon from '@mui/icons-material/Settings'; -import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; - -// Toolpad Core -import { Account } from '@toolpad/core/Account'; -import { AppProvider } from '@toolpad/core/AppProvider'; -import { - DashboardLayout, - // ToolbarActions, -} from '@toolpad/core/DashboardLayout'; -// import { AppTitle } from '@toolpad/core/DashboardLayout/AppTitle'; -import { useDemoRouter } from '@toolpad/core/internal'; - -const NAVIGATION = [ - { - kind: 'header', - title: 'Main items', - }, - { - segment: 'dashboard', - title: 'Dashboard', - icon: , - }, - { - segment: 'orders', - title: 'Orders', - icon: , - }, -]; - -const demoTheme = createTheme({ - cssVariables: { - colorSchemeSelector: 'data-toolpad-color-scheme', - }, - colorSchemes: { light: true, dark: true }, - breakpoints: { - values: { - xs: 0, - sm: 600, - md: 600, - lg: 1200, - xl: 1536, - }, - }, -}); - -function DemoPageContent({ pathname }) { - return ( - - Dashboard content for {pathname} - - ); -} - -function CustomThemeSwitcher() { - const { setMode } = useColorScheme(); - - const handleThemeChange = React.useCallback( - (event) => { - setMode(event.target.value); - }, - [setMode], - ); - - const [isMenuOpen, setIsMenuOpen] = React.useState(false); - const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); - - const toggleMenu = React.useCallback( - (event) => { - setMenuAnchorEl(isMenuOpen ? null : event.currentTarget); - setIsMenuOpen((previousIsMenuOpen) => !previousIsMenuOpen); - }, - [isMenuOpen], - ); - - return ( - - -
- - - -
-
- - - - Theme - - } label="Light" /> - } label="System" /> - } label="Dark" /> - - - - -
- ); -} - -function SearchBar() { - return ( - theme.palette.action.hover, - // display: { xs: 'none', sm: 'flex' }, - }} - > - - - - - - ); -} - -function Left({ menuIcon }) { - return ( - - {menuIcon} - {/* put the AppTitle here if you want to use the title component */} - {/* */} - - ); -} - -function Middle() { - return ( - - - - ); -} - -function Right() { - return ( - - - {/* put the ToolbarActions here if you want to use the toolbar actions component */} - {/* */} - - - - - ); -} - -function CustomToolbar(props) { - return ( - - - - - - ); -} - -export default function DashboardLayoutCustomToolbar(props) { - const { window } = props; - - const router = useDemoRouter('/dashboard'); - - // Remove this const when copying and pasting into your project. - const demoWindow = window !== undefined ? window() : undefined; - - return ( - - - - - - ); -} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx deleted file mode 100644 index 1cee91c9751..00000000000 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx +++ /dev/null @@ -1,267 +0,0 @@ -// React -import * as React from 'react'; - -// MUI Core -import Box from '@mui/material/Box'; -import FormControl from '@mui/material/FormControl'; -import FormControlLabel from '@mui/material/FormControlLabel'; -import FormLabel from '@mui/material/FormLabel'; -import IconButton from '@mui/material/IconButton'; -import Popover from '@mui/material/Popover'; -import Radio from '@mui/material/Radio'; -import RadioGroup from '@mui/material/RadioGroup'; -import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; -import { Button, InputBase, Paper, Stack } from '@mui/material'; -import { createTheme, useColorScheme } from '@mui/material/styles'; - -// MUI Icons -import DashboardIcon from '@mui/icons-material/Dashboard'; -import SearchIcon from '@mui/icons-material/Search'; -import SettingsIcon from '@mui/icons-material/Settings'; -import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; - -// Toolpad Core -import { Account } from '@toolpad/core/Account'; -import { AppProvider, type Navigation } from '@toolpad/core/AppProvider'; -import { - DashboardLayout, - // ToolbarActions, - ToolbarProps, -} from '@toolpad/core/DashboardLayout'; -// import { AppTitle } from '@toolpad/core/DashboardLayout/AppTitle'; -import { useDemoRouter } from '@toolpad/core/internal'; - -const NAVIGATION: Navigation = [ - { - kind: 'header', - title: 'Main items', - }, - { - segment: 'dashboard', - title: 'Dashboard', - icon: , - }, - { - segment: 'orders', - title: 'Orders', - icon: , - }, -]; - -const demoTheme = createTheme({ - cssVariables: { - colorSchemeSelector: 'data-toolpad-color-scheme', - }, - colorSchemes: { light: true, dark: true }, - breakpoints: { - values: { - xs: 0, - sm: 600, - md: 600, - lg: 1200, - xl: 1536, - }, - }, -}); - -function DemoPageContent({ pathname }: { pathname: string }) { - return ( - - Dashboard content for {pathname} - - ); -} - -function CustomThemeSwitcher() { - const { setMode } = useColorScheme(); - - const handleThemeChange = React.useCallback( - (event: React.ChangeEvent) => { - setMode(event.target.value as 'light' | 'dark' | 'system'); - }, - [setMode], - ); - - const [isMenuOpen, setIsMenuOpen] = React.useState(false); - const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); - - const toggleMenu = React.useCallback( - (event: React.MouseEvent) => { - setMenuAnchorEl(isMenuOpen ? null : event.currentTarget); - setIsMenuOpen((previousIsMenuOpen) => !previousIsMenuOpen); - }, - [isMenuOpen], - ); - - return ( - - -
- - - -
-
- - - - Theme - - } label="Light" /> - } label="System" /> - } label="Dark" /> - - - - -
- ); -} - -interface DemoProps { - /** - * Injected by the documentation to work in an iframe. - * Remove this when copying and pasting into your project. - */ - window?: () => Window; -} - -function SearchBar() { - return ( - theme.palette.action.hover, - // display: { xs: 'none', sm: 'flex' }, - }} - > - - - - - - ); -} - -function Left({ menuIcon }: ToolbarProps) { - return ( - - {menuIcon} - {/* put the AppTitle here if you want to use the title component */} - {/* */} - - ); -} - -function Middle() { - return ( - - - - ); -} - -function Right() { - return ( - - - {/* put the ToolbarActions here if you want to use the toolbar actions component */} - {/* */} - - - - - ); -} - -function CustomToolbar(props: ToolbarProps) { - return ( - - - - - - ); -} - -export default function DashboardLayoutCustomToolbar(props: DemoProps) { - const { window } = props; - - const router = useDemoRouter('/dashboard'); - - // Remove this const when copying and pasting into your project. - const demoWindow = window !== undefined ? window() : undefined; - - return ( - - - - - - ); -} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview deleted file mode 100644 index 62a0d18da8a..00000000000 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomToolbar.tsx.preview +++ /dev/null @@ -1,26 +0,0 @@ -function CustomToolbar(props: ToolbarProps) { - return ( - - - - - - ); -} - - - - \ No newline at end of file diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index cbfc98241ef..681112c71bd 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -159,13 +159,13 @@ Some possibly useful slots: - `appTitle`: allows you to customize the app title section in the layout header. -- `toolbarAccount`: (deprecated, use toolbar) allows you to replace the default `Account` slot in the header. +- `toolbarActions`: (deprecated) allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` component can be imported and used if you wish to do so, as shown in the example below. -- `toolbarActions`: (deprecated, use toolbar) allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` component can be imported and used if you wish to do so, as shown in the example below. +- `toolbarAccount`: (deprecated) allows you to replace the default `Account` slot in the header. -- `sidebarFooter`: allows you to add footer content in the sidebar. +- `toolbar`: allows you to completely customize the toolbar layout. This slot gives you full control over the toolbar's structure while still allowing you to use the built-in components like `AppTitle`, `ToolbarActions`, and `Account`. -- `toolbar`: This component will completely override the internal layout of the top bar and allows full control over the toolbar. The default `ToolbarActions` component, menuIcon and `Account` can be imported and used if you wish to do so, as shown in the example below. +- `sidebarFooter`: allows you to add a footer to the sidebar. ### sidebarFooter @@ -180,9 +180,3 @@ Some possibly useful slots: The `useColorScheme` hook can be used to create a custom theme switcher. {{"demo": "DashboardLayoutCustomThemeSwitcher.js", "height": 400, "iframe": true}} - -### Replaced the full toolbar with a custom one - -The `toolbar` slot can be replaced with a custom component, that in turn reuses the out of the box `Account`, `menuIcon`, `AppTitle`and `ToolbarActions` components, giving full control on their order, appearance and behavior. - -{{"demo": "DashboardLayoutCustomToolbar.js", "height": 400, "iframe": true}} diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index c742b1a5ae4..fc7ad0c8033 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -2,13 +2,9 @@ import * as React from 'react'; import PropTypes from 'prop-types'; - -// MUI: Styles & Theme import { styled, useTheme, SxProps } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import type {} from '@mui/material/themeCssVarsAugmentation'; - -// MUI: Components import MuiAppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; @@ -16,21 +12,13 @@ import IconButton from '@mui/material/IconButton'; import Stack from '@mui/material/Stack'; import Toolbar from '@mui/material/Toolbar'; import Tooltip from '@mui/material/Tooltip'; - -// MUI: Icons import MenuIcon from '@mui/icons-material/Menu'; import MenuOpenIcon from '@mui/icons-material/MenuOpen'; - -// Toolpad: Context import { BrandingContext, NavigationContext, WindowContext } from '../shared/context'; - -// Toolpad: Components import { Account, type AccountProps } from '../Account'; import { AppTitle, type AppTitleProps } from './AppTitle'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; import { ToolbarActions } from './ToolbarActions'; - -// Toolpad: Utils & Constants import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from './utils'; import { MINI_DRAWER_WIDTH } from './shared'; import type { Branding, Navigation } from '../AppProvider'; @@ -70,20 +58,16 @@ export interface DashboardLayoutSlots { /** * The toolbar actions component used in the layout header. * @default ToolbarActions - * @deprecated Use `slots.toolbar` instead. + * @deprecated Use `slots.toolbar` instead for full customization of the toolbar. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) - * - * TODO: Remove this deprecated prop in the next major version. */ toolbarActions?: React.JSXElementConstructor<{}>; /** * The toolbar account component used in the layout header. * @default Account - * @deprecated Use `slots.toolbar` instead. + * @deprecated Use `slots.toolbar` instead for full customization of the toolbar. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) - * - * TODO: Remove this deprecated prop in the next major version. */ toolbarAccount?: React.JSXElementConstructor; @@ -95,10 +79,8 @@ export interface DashboardLayoutSlots { /** * Use this slot to replace the full default toolbar. - * * This component will completely override the internal layout of the top bar. * You can still use the built-in ``, `` and `` components inside. - * * @default null * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ @@ -619,6 +601,7 @@ DashboardLayout.propTypes /* remove-proptypes */ = { }), }), toolbarActions: PropTypes.object, + toolbar: PropTypes.object, }), /** * The components used for each slot inside. @@ -629,6 +612,7 @@ DashboardLayout.propTypes /* remove-proptypes */ = { sidebarFooter: PropTypes.elementType, toolbarAccount: PropTypes.elementType, toolbarActions: PropTypes.elementType, + toolbar: PropTypes.elementType, }), /** * The system prop that allows defining system overrides as well as additional CSS styles. From 1cd788e9a379cc3e76341cefeba6cdc6b78453f9 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Sat, 19 Apr 2025 07:52:42 +0200 Subject: [PATCH 14/31] docs: cleanup --- .../core/components/dashboard-layout/dashboard-layout.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 681112c71bd..20647686bab 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -171,11 +171,11 @@ Some possibly useful slots: {{"demo": "DashboardLayoutSlots.js", "height": 400, "iframe": true}} -### User account in layout sidebar +#### User account in layout sidebar {{"demo": "DashboardLayoutAccountSidebar.js", "height": 400, "iframe": true}} -### Settings menu with custom theme switcher +#### Settings menu with custom theme switcher The `useColorScheme` hook can be used to create a custom theme switcher. From 48213a421c58fb588f83965f3f589233247e909c Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Thu, 24 Apr 2025 14:27:07 +0200 Subject: [PATCH 15/31] fix(dashboard-layout): apply suggested comments --- .../dashboard-layout/dashboard-layout.md | 6 +- .../src/DashboardLayout/DashboardLayout.tsx | 29 ++++--- .../src/DashboardLayout/ToolbarActions.tsx | 2 + .../nextjs/src/app/(dashboard)/layout.tsx | 76 ++++++++++++++++++- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 6d9440bc386..dcd1b6bbef6 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -169,11 +169,9 @@ Some possibly useful slots: - `appTitle`: allows you to customize the app title section in the layout header. -- `toolbarActions`: (deprecated) allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` component can be imported and used if you wish to do so, as shown in the example below. +- `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` and `Account` component can be imported and used if you wish to do so, as shown in the example below. -- `toolbarAccount`: (deprecated) allows you to replace the default `Account` slot in the header. - -- `toolbar`: allows you to completely customize the toolbar layout. This slot gives you full control over the toolbar's structure while still allowing you to use the built-in components like `AppTitle`, `ToolbarActions`, and `Account`. +- `appBar`: allows you to fully replace the layout header. This slot gives you full control over the toolbar's structure while still allowing you to use the built-in components like `AppTitle`, `ToolbarActions`, and `Account`. - `sidebarFooter`: allows you to add a footer to the sidebar. diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 2f4a017f079..3bea1d2edd5 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -45,7 +45,7 @@ export interface DashboardLayoutSlotProps { toolbarActions?: {}; toolbarAccount?: AccountProps; sidebarFooter?: SidebarFooterProps; - toolbar?: {}; + appBar?: {}; } export interface DashboardLayoutSlots { @@ -56,17 +56,16 @@ export interface DashboardLayoutSlots { appTitle?: React.ElementType; /** - * The toolbar actions component used in the layout header. + * The ToolbarActions component used in the layout header. * @default ToolbarActions - * @deprecated Use `slots.toolbar` instead for full customization of the toolbar. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbarActions?: React.JSXElementConstructor<{}>; + appBarActions?: React.JSXElementConstructor<{}>; /** - * The toolbar account component used in the layout header. + * The toolbarAccount component used in the layout header. * @default Account - * @deprecated Use `slots.toolbar` instead for full customization of the toolbar. + * @deprecated it is moved to the toolbarActions component by default * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ toolbarAccount?: React.JSXElementConstructor; @@ -84,7 +83,7 @@ export interface DashboardLayoutSlots { * @default null * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbar?: React.JSXElementConstructor; + appBar?: React.JSXElementConstructor; } export interface DashboardLayoutProps { @@ -289,8 +288,8 @@ function DashboardLayout(props: DashboardLayoutProps) { const hasDrawerTransitions = isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport); - const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; - const ToolbarAccountSlot = slots?.toolbarAccount ?? Account; + const ToolbarActionsSlot = slots?.appBarActions ?? ToolbarActions; + const ToolbarAccountSlot = slots?.toolbarAccount ?? (() => null); const SidebarFooterSlot = slots?.sidebarFooter ?? null; const getDrawerContent = React.useCallback( @@ -363,8 +362,8 @@ function DashboardLayout(props: DashboardLayoutProps) { [isNavigationExpanded, sidebarExpandedWidth], ); - const toolbarSlotProps: ToolbarProps = { - ...slotProps?.toolbar, + const appBarSlotProps: ToolbarProps = { + ...slotProps?.appBar, menuIcon: getMenuIcon(isMobileNavigationExpanded), }; @@ -381,8 +380,8 @@ function DashboardLayout(props: DashboardLayoutProps) { > - {slots?.toolbar ? ( - + {slots?.appBar ? ( + ) : ( + ); } diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index 52b3ac44e6b..a5cb1642c9f 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -16,8 +16,15 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, SidebarFooterProps, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; +import MuiAppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import InputBase from '@mui/material/InputBase'; +import MenuIcon from '@mui/icons-material/Menu'; +import SearchIcon from '@mui/icons-material/Search'; +import { styled, alpha } from '@mui/material/styles'; const accounts = [ { @@ -41,6 +48,45 @@ const accounts = [ }, ]; +const Search = styled('div')(({ theme }) => ({ + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: alpha(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: alpha(theme.palette.common.white, 0.25), + }, + marginRight: theme.spacing(2), + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(3), + width: 'auto', + }, +})); + +const SearchIconWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +})); + +const StyledInputBase = styled(InputBase)(({ theme }) => ({ + color: 'inherit', + '& .MuiInputBase-input': { + padding: theme.spacing(1, 1, 1, 0), + paddingLeft: `calc(1em + ${theme.spacing(4)})`, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: '20ch', + }, + }, +})); + function AccountSidebarPreview(props: AccountPreviewProps & { mini: boolean }) { const { handleClick, open, mini } = props; return ( @@ -157,6 +203,32 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { ); } +function CustomAppBar(props: { menuIcon: React.ReactElement }) { + return ( + + {props.menuIcon} + + + + + + + + + + + + ); +} + export default function DashboardPagesLayout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -176,7 +248,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - null }}> + {props.children} ); From 12b67709db6aa85d7163e5ba84bd688821902d9d Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Thu, 24 Apr 2025 14:28:54 +0200 Subject: [PATCH 16/31] chore: revert playground --- .../nextjs/src/app/(dashboard)/layout.tsx | 76 +------------------ 1 file changed, 2 insertions(+), 74 deletions(-) diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index a5cb1642c9f..52b3ac44e6b 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -16,15 +16,8 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; -import MuiAppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import IconButton from '@mui/material/IconButton'; -import InputBase from '@mui/material/InputBase'; -import MenuIcon from '@mui/icons-material/Menu'; -import SearchIcon from '@mui/icons-material/Search'; -import { styled, alpha } from '@mui/material/styles'; const accounts = [ { @@ -48,45 +41,6 @@ const accounts = [ }, ]; -const Search = styled('div')(({ theme }) => ({ - position: 'relative', - borderRadius: theme.shape.borderRadius, - backgroundColor: alpha(theme.palette.common.white, 0.15), - '&:hover': { - backgroundColor: alpha(theme.palette.common.white, 0.25), - }, - marginRight: theme.spacing(2), - marginLeft: 0, - width: '100%', - [theme.breakpoints.up('sm')]: { - marginLeft: theme.spacing(3), - width: 'auto', - }, -})); - -const SearchIconWrapper = styled('div')(({ theme }) => ({ - padding: theme.spacing(0, 2), - height: '100%', - position: 'absolute', - pointerEvents: 'none', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', -})); - -const StyledInputBase = styled(InputBase)(({ theme }) => ({ - color: 'inherit', - '& .MuiInputBase-input': { - padding: theme.spacing(1, 1, 1, 0), - paddingLeft: `calc(1em + ${theme.spacing(4)})`, - transition: theme.transitions.create('width'), - width: '100%', - [theme.breakpoints.up('md')]: { - width: '20ch', - }, - }, -})); - function AccountSidebarPreview(props: AccountPreviewProps & { mini: boolean }) { const { handleClick, open, mini } = props; return ( @@ -203,32 +157,6 @@ function SidebarFooterAccount({ mini }: SidebarFooterProps) { ); } -function CustomAppBar(props: { menuIcon: React.ReactElement }) { - return ( - - {props.menuIcon} - - - - - - - - - - - - ); -} - export default function DashboardPagesLayout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -248,7 +176,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - + null }}> {props.children} ); From b76506070393fed1889818d29331c104b3196d4f Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Thu, 24 Apr 2025 14:42:39 +0200 Subject: [PATCH 17/31] fix(dashboard-layout): wrap the full appbar --- .../src/DashboardLayout/DashboardLayout.tsx | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 3bea1d2edd5..03b7f2551cf 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -56,16 +56,16 @@ export interface DashboardLayoutSlots { appTitle?: React.ElementType; /** - * The ToolbarActions component used in the layout header. + * The toolbar actions component used in the layout header. * @default ToolbarActions * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ appBarActions?: React.JSXElementConstructor<{}>; /** - * The toolbarAccount component used in the layout header. + * The toolbar actions component used in the layout header. * @default Account - * @deprecated it is moved to the toolbarActions component by default + * @deprecated it is moved to the toolbarActions component * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ toolbarAccount?: React.JSXElementConstructor; @@ -77,11 +77,43 @@ export interface DashboardLayoutSlots { sidebarFooter?: React.JSXElementConstructor; /** - * Use this slot to replace the full default toolbar. * This component will completely override the internal layout of the top bar. - * You can still use the built-in ``, `` and `` components inside. + * You can still use the built-in ``, `` and `` components inside. * @default null * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + * @example + * ```tsx + * function CustomAppBar(props: { menuIcon: React.ReactElement }) { + * return ( + * + * + * + * {props.menuIcon} + * + * + * + * + * + * + * + * + * + * + * + * + * + * ); + * } + * + * + * {children} + * + * ``` */ appBar?: React.JSXElementConstructor; } @@ -378,11 +410,11 @@ function DashboardLayout(props: DashboardLayoutProps) { ...sx, }} > - - - {slots?.appBar ? ( - - ) : ( + {slots?.appBar ? ( + + ) : ( + + - )} - - + + + )} {!hideNavigation ? ( From 695b23fee305653dece1c4c8b98dae3ee1b07ad2 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Sat, 26 Apr 2025 08:13:33 +0200 Subject: [PATCH 18/31] chore: revert accidantally toolbarActions property rename --- .../toolpad-core/src/DashboardLayout/DashboardLayout.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 03b7f2551cf..3628a1bd48a 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -60,7 +60,7 @@ export interface DashboardLayoutSlots { * @default ToolbarActions * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - appBarActions?: React.JSXElementConstructor<{}>; + toolbarActions?: React.JSXElementConstructor<{}>; /** * The toolbar actions component used in the layout header. @@ -320,7 +320,7 @@ function DashboardLayout(props: DashboardLayoutProps) { const hasDrawerTransitions = isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport); - const ToolbarActionsSlot = slots?.appBarActions ?? ToolbarActions; + const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; const ToolbarAccountSlot = slots?.toolbarAccount ?? (() => null); const SidebarFooterSlot = slots?.sidebarFooter ?? null; @@ -625,6 +625,7 @@ DashboardLayout.propTypes /* remove-proptypes */ = { * @default {} */ slotProps: PropTypes.shape({ + appBar: PropTypes.object, appTitle: PropTypes.shape({ branding: PropTypes.shape({ homeUrl: PropTypes.string, @@ -653,18 +654,17 @@ DashboardLayout.propTypes /* remove-proptypes */ = { }), }), toolbarActions: PropTypes.object, - appBar: PropTypes.object, }), /** * The components used for each slot inside. * @default {} */ slots: PropTypes.shape({ + appBar: PropTypes.elementType, appTitle: PropTypes.elementType, sidebarFooter: PropTypes.elementType, toolbarAccount: PropTypes.elementType, toolbarActions: PropTypes.elementType, - appBar: PropTypes.elementType, }), /** * The system prop that allows defining system overrides as well as additional CSS styles. From 201a17b4e35cefa61ff667df946fa50f81bf1233 Mon Sep 17 00:00:00 2001 From: Remi Kristelijn Date: Fri, 2 May 2025 07:55:15 +0200 Subject: [PATCH 19/31] Apply suggestions from code review Co-authored-by: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Signed-off-by: Remi Kristelijn --- .../core/components/dashboard-layout/dashboard-layout.md | 4 ++-- packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index dcd1b6bbef6..65cff201fa7 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -169,9 +169,9 @@ Some possibly useful slots: - `appTitle`: allows you to customize the app title section in the layout header. -- `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` and `Account` component can be imported and used if you wish to do so, as shown in the example below. +- `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` and `Account` components can be imported and used if you wish to do so, as shown in the example below. -- `appBar`: allows you to fully replace the layout header. This slot gives you full control over the toolbar's structure while still allowing you to use the built-in components like `AppTitle`, `ToolbarActions`, and `Account`. +- `appBar`: allows you to fully replace the layout header. - `sidebarFooter`: allows you to add a footer to the sidebar. diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 25560a6e095..9e0663cfa72 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -63,9 +63,9 @@ export interface DashboardLayoutSlots { toolbarActions?: React.JSXElementConstructor<{}>; /** - * The toolbar actions component used in the layout header. + * The toolbar account component used in the layout header. * @default Account - * @deprecated it is moved to the toolbarActions component + * @deprecated Place your custom component on the right in the `toolbarActions` slot instead. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ toolbarAccount?: React.JSXElementConstructor; From 275340541bd3a99c87867ab72c402bcd62aa170c Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Tue, 6 May 2025 19:22:42 +0200 Subject: [PATCH 20/31] feat(dashboard-layout): add default example --- .../src/DashboardLayout/DashboardLayout.tsx | 37 ++-------- .../src/DashboardLayout/DefaultAppBar.tsx | 39 +++++++++++ .../src/DashboardLayout/SearchBar.tsx | 67 +++++++++++++++++++ .../toolpad-core/src/DashboardLayout/index.ts | 6 +- .../nextjs/src/app/(dashboard)/layout.tsx | 10 +-- 5 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 packages/toolpad-core/src/DashboardLayout/DefaultAppBar.tsx create mode 100644 packages/toolpad-core/src/DashboardLayout/SearchBar.tsx diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 9e0663cfa72..0ec679c4ebe 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -15,7 +15,7 @@ import Tooltip from '@mui/material/Tooltip'; import MenuIcon from '@mui/icons-material/Menu'; import MenuOpenIcon from '@mui/icons-material/MenuOpen'; import { BrandingContext, NavigationContext, WindowContext } from '../shared/context'; -import { Account, type AccountProps } from '../Account'; +import { type AccountProps } from '../Account'; import { AppTitle, type AppTitleProps } from './AppTitle'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; import { ToolbarActions } from './ToolbarActions'; @@ -77,40 +77,15 @@ export interface DashboardLayoutSlots { sidebarFooter?: React.JSXElementConstructor; /** - * This component will completely override the internal layout of the top bar. + * This component is used for the layout header. * You can still use the built-in ``, `` and `` components inside. * @default null * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) * @example * ```tsx - * function CustomAppBar(props: { menuIcon: React.ReactElement }) { - * return ( - * - * - * - * {props.menuIcon} - * - * - * - * - * - * - * - * - * - * - * - * - * - * ); - * } + * import { DefaultAppBar } from './DefaultAppBar'; * - * + * * {children} * * ``` @@ -398,7 +373,9 @@ function DashboardLayout(props: DashboardLayoutProps) { const appBarSlotProps: ToolbarProps = { ...slotProps?.appBar, - menuIcon: getMenuIcon(isMobileNavigationExpanded), + menuIcon: isOverMdViewport + ? getMenuIcon(isDesktopNavigationExpanded) + : getMenuIcon(isMobileNavigationExpanded), }; return ( diff --git a/packages/toolpad-core/src/DashboardLayout/DefaultAppBar.tsx b/packages/toolpad-core/src/DashboardLayout/DefaultAppBar.tsx new file mode 100644 index 00000000000..b0b619edd97 --- /dev/null +++ b/packages/toolpad-core/src/DashboardLayout/DefaultAppBar.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; + +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Stack from '@mui/material/Stack'; + +import { Account } from '../Account'; +import { ThemeSwitcher } from './ThemeSwitcher'; +import { SearchBar } from './SearchBar'; +import { AppTitle } from './AppTitle'; + +export function DefaultAppBar(props: { menuIcon: React.ReactElement }) { + return ( + theme.zIndex.drawer + 1 }} + > + + + + {props.menuIcon} + + + {}} /> + + + + + + + + ); +} diff --git a/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx b/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx new file mode 100644 index 00000000000..914374eaa2e --- /dev/null +++ b/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import { styled, alpha } from '@mui/material/styles'; +import InputBase from '@mui/material/InputBase'; +import SearchIcon from '@mui/icons-material/Search'; + +const Search = styled('div')(({ theme }) => ({ + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: alpha(theme.palette.common.black, 0.05), + '&:hover': { + backgroundColor: alpha(theme.palette.common.black, 0.08), + }, + marginRight: theme.spacing(2), + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(3), + width: 'auto', + }, +})); + +const SearchIconWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +})); + +const StyledInputBase = styled(InputBase)(({ theme }) => ({ + color: 'inherit', + '& .MuiInputBase-input': { + padding: theme.spacing(1, 1, 1, 0), + paddingLeft: `calc(1em + ${theme.spacing(4)})`, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: '20ch', + }, + }, +})); + +export interface SearchBarProps { + onSearch?: (value: string) => void; + placeholder?: string; +} + +export function SearchBar({ onSearch, placeholder = 'Search…' }: SearchBarProps) { + const handleChange = (event: React.ChangeEvent) => { + onSearch?.(event.target.value); + }; + + return ( + + + + + + + ); +} diff --git a/packages/toolpad-core/src/DashboardLayout/index.ts b/packages/toolpad-core/src/DashboardLayout/index.ts index d60a4e03f93..b478cabbbf5 100644 --- a/packages/toolpad-core/src/DashboardLayout/index.ts +++ b/packages/toolpad-core/src/DashboardLayout/index.ts @@ -1,4 +1,6 @@ export * from './DashboardLayout'; -export * from './ToolbarActions'; -export * from './ThemeSwitcher'; export * from './DashboardSidebarPageItem'; +export * from './DefaultAppBar'; +export * from './SearchBar'; +export * from './ThemeSwitcher'; +export * from './ToolbarActions'; diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index 52b3ac44e6b..be36f11cba9 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -15,9 +15,11 @@ import { AccountPopoverFooter, SignOutButton, AccountPreviewProps, -} from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; -import { PageContainer } from '@toolpad/core/PageContainer'; + DashboardLayout, + SidebarFooterProps, + DefaultAppBar, + PageContainer, +} from '@toolpad/core'; const accounts = [ { @@ -176,7 +178,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - null }}> + {props.children} ); From bb6a3ada556f7d5945aa3b9dd15b498b6be53db1 Mon Sep 17 00:00:00 2001 From: "remi.kristelijn" Date: Tue, 6 May 2025 19:28:02 +0200 Subject: [PATCH 21/31] feat(playground): ooops, that playground again --- playground/nextjs/src/app/(dashboard)/layout.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index be36f11cba9..52b3ac44e6b 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -15,11 +15,9 @@ import { AccountPopoverFooter, SignOutButton, AccountPreviewProps, - DashboardLayout, - SidebarFooterProps, - DefaultAppBar, - PageContainer, -} from '@toolpad/core'; +} from '@toolpad/core/Account'; +import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; +import { PageContainer } from '@toolpad/core/PageContainer'; const accounts = [ { @@ -178,7 +176,7 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - + null }}> {props.children} ); From 28bf06c4e476bb8fc283191b82b325f18e2bcc2d Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 15:35:43 +0100 Subject: [PATCH 22/31] Review, add fixes and improvements, refactor DashboardHeader as separate component --- .../DashboardLayoutAccountSidebar.js | 15 +- .../DashboardLayoutAccountSidebar.tsx | 19 +- .../DashboardLayoutAccountSidebar.tsx.preview | 5 +- .../dashboard-layout/dashboard-layout.md | 8 +- docs/data/toolpad/core/pagesApi.js | 1 + .../toolpad/core/api/dashboard-header.js | 23 + .../toolpad/core/api/dashboard-header.json | 57 +++ .../toolpad/core/api/dashboard-layout.json | 10 +- .../dashboard-header/dashboard-header.json | 19 + .../dashboard-layout/dashboard-layout.json | 1 + .../src/DashboardLayout/DashboardHeader.tsx | 259 +++++++++++ .../src/DashboardLayout/DashboardLayout.tsx | 417 +++++++++++------- .../src/DashboardLayout/DefaultAppBar.tsx | 39 -- .../src/DashboardLayout/SearchBar.tsx | 67 --- .../toolpad-core/src/DashboardLayout/index.ts | 3 +- .../nextjs/src/app/(dashboard)/layout.tsx | 17 +- 16 files changed, 687 insertions(+), 273 deletions(-) create mode 100644 docs/pages/toolpad/core/api/dashboard-header.js create mode 100644 docs/pages/toolpad/core/api/dashboard-header.json create mode 100644 docs/translations/api-docs/dashboard-header/dashboard-header.json create mode 100644 packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx delete mode 100644 packages/toolpad-core/src/DashboardLayout/DefaultAppBar.tsx delete mode 100644 packages/toolpad-core/src/DashboardLayout/SearchBar.tsx diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js index 66e996bc4cc..9b105464552 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js @@ -13,7 +13,7 @@ import { createTheme } from '@mui/material/styles'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { AppProvider } from '@toolpad/core/AppProvider'; -import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; import { Account, AccountPreview, @@ -56,6 +56,14 @@ const demoTheme = createTheme({ }, }); +function CustomToolbarActions() { + return ( + + + + ); +} + function DemoPageContent({ pathname }) { return ( {/* preview-start */} null, sidebarFooter: SidebarFooterAccount }} + slots={{ + toolbarAccount: CustomToolbarActions, + sidebarFooter: SidebarFooterAccount, + }} > diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx index 78ab570494d..928dac06fef 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx @@ -12,7 +12,11 @@ import { createTheme } from '@mui/material/styles'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { AppProvider } from '@toolpad/core/AppProvider'; -import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; +import { + DashboardLayout, + SidebarFooterProps, + ThemeSwitcher, +} from '@toolpad/core/DashboardLayout'; import { Account, AccountPreview, @@ -56,6 +60,14 @@ const demoTheme = createTheme({ }, }); +function CustomToolbarActions() { + return ( + + + + ); +} + function DemoPageContent({ pathname }: { pathname: string }) { return ( {/* preview-start */} null, sidebarFooter: SidebarFooterAccount }} + slots={{ + toolbarAccount: CustomToolbarActions, + sidebarFooter: SidebarFooterAccount, + }} > diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview index f567d5793e9..edf2eda498b 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview @@ -1,5 +1,8 @@ null, sidebarFooter: SidebarFooterAccount }} + slots={{ + toolbarAccount: CustomToolbarActions, + sidebarFooter: SidebarFooterAccount, + }} > \ No newline at end of file diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 65cff201fa7..7a545d0404f 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -1,7 +1,7 @@ --- productId: toolpad-core title: Dashboard Layout -components: AppProvider, DashboardLayout, ToolbarActions, ThemeSwitcher, Account, DashboardSidebarPageItem +components: AppProvider, DashboardLayout, DashboardHeader, ToolbarActions, ThemeSwitcher, Account, DashboardSidebarPageItem --- # Dashboard Layout @@ -171,14 +171,14 @@ Some possibly useful slots: - `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` and `Account` components can be imported and used if you wish to do so, as shown in the example below. -- `appBar`: allows you to fully replace the layout header. +- `appBar`: allows you to fully replace and customize the layout header. - `sidebarFooter`: allows you to add a footer to the sidebar. -### sidebarFooter - {{"demo": "DashboardLayoutSlots.js", "height": 400, "iframe": true}} +### Examples + #### User account in layout sidebar {{"demo": "DashboardLayoutAccountSidebar.js", "height": 400, "iframe": true}} diff --git a/docs/data/toolpad/core/pagesApi.js b/docs/data/toolpad/core/pagesApi.js index b90368dc772..955852032b0 100644 --- a/docs/data/toolpad/core/pagesApi.js +++ b/docs/data/toolpad/core/pagesApi.js @@ -8,6 +8,7 @@ export default [ { pathname: '/toolpad/core/api/crud' }, { pathname: '/toolpad/core/api/crud-form' }, { pathname: '/toolpad/core/api/crud-provider' }, + { pathname: '/toolpad/core/api/dashboard-header' }, { pathname: '/toolpad/core/api/dashboard-layout' }, { pathname: '/toolpad/core/api/dashboard-sidebar-page-item' }, { pathname: '/toolpad/core/api/dialogs-provider' }, diff --git a/docs/pages/toolpad/core/api/dashboard-header.js b/docs/pages/toolpad/core/api/dashboard-header.js new file mode 100644 index 00000000000..50c578cd38a --- /dev/null +++ b/docs/pages/toolpad/core/api/dashboard-header.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './dashboard-header.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/dashboard-header', + false, + /\.\/dashboard-header.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/dashboard-header.json b/docs/pages/toolpad/core/api/dashboard-header.json new file mode 100644 index 00000000000..b3f83293739 --- /dev/null +++ b/docs/pages/toolpad/core/api/dashboard-header.json @@ -0,0 +1,57 @@ +{ + "props": { + "menuOpen": { "type": { "name": "bool" }, "required": true }, + "onToggleMenu": { "type": { "name": "func" }, "required": true }, + "branding": { + "type": { + "name": "shape", + "description": "{ homeUrl?: string, logo?: node, title?: string }" + }, + "default": "null" + }, + "hideMenuButton": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, toolbarAccount?: { localeText?: object, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" + }, + "default": "{}" + }, + "slots": { + "type": { + "name": "shape", + "description": "{ appTitle?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType }" + }, + "default": "{}", + "additionalInfo": { "slotsApi": true } + } + }, + "name": "DashboardHeader", + "imports": ["import { DashboardHeader } from '@toolpad/core/DashboardLayout';"], + "slots": [ + { + "name": "appTitle", + "description": "The component used for the app title section.", + "default": "Link", + "class": null + }, + { + "name": "toolbarActions", + "description": "The toolbar actions component to be used.", + "default": "ToolbarActions", + "class": null + }, + { + "name": "toolbarAccount", + "description": "The toolbar account component to be used.", + "default": "Account", + "class": null + } + ], + "classes": [], + "muiName": "DashboardHeader", + "filename": "/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index 5e4e61cd7a5..4f7160b4f1b 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -32,14 +32,14 @@ "slotProps": { "type": { "name": "shape", - "description": "{ appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: object, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" + "description": "{ appBar?: { branding?: { homeUrl?: string, logo?: node, title?: string }, hideMenuButton?: bool, menuOpen: bool, onToggleMenu: func, slotProps?: { appTitle?: object, toolbarAccount?: object, toolbarActions?: object }, slots?: { appTitle?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType } }, appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: object, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" }, "default": "{}" }, "slots": { "type": { "name": "shape", - "description": "{ appTitle?: elementType, sidebarFooter?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType }" + "description": "{ appBar?: elementType, appTitle?: 'a'
| 'abbr'
| 'address'
| 'animate'
| 'animateMotion'
| 'animateTransform'
| 'area'
| 'article'
| 'aside'
| 'audio'
| 'b'
| 'base'
| 'bdi'
| 'bdo'
| 'big'
| 'blockquote'
| 'body'
| 'br'
| 'button'
| 'canvas'
| 'caption'
| 'center'
| 'circle'
| 'cite'
| 'clipPath'
| 'code'
| 'col'
| 'colgroup'
| 'data'
| 'datalist'
| 'dd'
| 'defs'
| 'del'
| 'desc'
| 'details'
| 'dfn'
| 'dialog'
| 'div'
| 'dl'
| 'dt'
| 'ellipse'
| 'em'
| 'embed'
| 'feBlend'
| 'feColorMatrix'
| 'feComponentTransfer'
| 'feComposite'
| 'feConvolveMatrix'
| 'feDiffuseLighting'
| 'feDisplacementMap'
| 'feDistantLight'
| 'feDropShadow'
| 'feFlood'
| 'feFuncA'
| 'feFuncB'
| 'feFuncG'
| 'feFuncR'
| 'feGaussianBlur'
| 'feImage'
| 'feMerge'
| 'feMergeNode'
| 'feMorphology'
| 'feOffset'
| 'fePointLight'
| 'feSpecularLighting'
| 'feSpotLight'
| 'feTile'
| 'feTurbulence'
| 'fieldset'
| 'figcaption'
| 'figure'
| 'filter'
| 'footer'
| 'foreignObject'
| 'form'
| 'g'
| 'h1'
| 'h2'
| 'h3'
| 'h4'
| 'h5'
| 'h6'
| 'head'
| 'header'
| 'hgroup'
| 'hr'
| 'html'
| 'i'
| 'iframe'
| 'image'
| 'img'
| 'input'
| 'ins'
| 'kbd'
| 'keygen'
| 'label'
| 'legend'
| 'li'
| 'line'
| 'linearGradient'
| 'link'
| 'main'
| 'map'
| 'mark'
| 'marker'
| 'mask'
| 'menu'
| 'menuitem'
| 'meta'
| 'metadata'
| 'meter'
| 'mpath'
| 'nav'
| 'noindex'
| 'noscript'
| 'object'
| 'ol'
| 'optgroup'
| 'option'
| 'output'
| 'p'
| 'param'
| 'path'
| 'pattern'
| 'picture'
| 'polygon'
| 'polyline'
| 'pre'
| 'progress'
| 'q'
| 'radialGradient'
| 'rect'
| 'rp'
| 'rt'
| 'ruby'
| 's'
| 'samp'
| 'script'
| 'search'
| 'section'
| 'select'
| 'set'
| 'slot'
| 'small'
| 'source'
| 'span'
| 'stop'
| 'strong'
| 'style'
| 'sub'
| 'summary'
| 'sup'
| 'svg'
| 'switch'
| 'symbol'
| 'table'
| 'tbody'
| 'td'
| 'template'
| 'text'
| 'textarea'
| 'textPath'
| 'tfoot'
| 'th'
| 'thead'
| 'time'
| 'title'
| 'tr'
| 'track'
| 'tspan'
| 'u'
| 'ul'
| 'use'
| 'var'
| 'video'
| 'view'
| 'wbr'
| 'webview'
| func, sidebarFooter?: elementType, toolbarAccount?: func, toolbarActions?: func }" }, "default": "{}", "additionalInfo": { "slotsApi": true } @@ -76,6 +76,12 @@ "default": "Account", "class": null }, + { + "name": "appBar", + "description": "The component used for the layout header.", + "default": "DashboardHeader", + "class": null + }, { "name": "sidebarFooter", "description": "Optional footer component used in the layout sidebar.", diff --git a/docs/translations/api-docs/dashboard-header/dashboard-header.json b/docs/translations/api-docs/dashboard-header/dashboard-header.json new file mode 100644 index 00000000000..cd8affb7bd3 --- /dev/null +++ b/docs/translations/api-docs/dashboard-header/dashboard-header.json @@ -0,0 +1,19 @@ +{ + "componentDescription": "", + "propDescriptions": { + "branding": { "description": "Branding options for the header." }, + "hideMenuButton": { "description": "Whether the menu icon should always be hidden." }, + "menuOpen": { + "description": "If true, show menu button as if menu is expanded, otherwise show it as if menu is collapsed." + }, + "onToggleMenu": { "description": "Callback fired when the menu button is clicked." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." } + }, + "classDescriptions": {}, + "slotDescriptions": { + "appTitle": "The component used for the app title section.", + "toolbarAccount": "The toolbar account component to be used.", + "toolbarActions": "The toolbar actions component to be used." + } +} diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json index ea0ad31cdc5..af69e6a5e61 100644 --- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json +++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json @@ -25,6 +25,7 @@ }, "classDescriptions": {}, "slotDescriptions": { + "appBar": "The component used for the layout header.", "appTitle": "The component used for the app title section in the layout header.", "sidebarFooter": "Optional footer component used in the layout sidebar.", "toolbarAccount": "The toolbar account component used in the layout header.", diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx new file mode 100644 index 00000000000..030c04561d4 --- /dev/null +++ b/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx @@ -0,0 +1,259 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import MuiAppBar from '@mui/material/AppBar'; +import IconButton from '@mui/material/IconButton'; +import Toolbar from '@mui/material/Toolbar'; +import Tooltip from '@mui/material/Tooltip'; +import MenuIcon from '@mui/icons-material/Menu'; +import MenuOpenIcon from '@mui/icons-material/MenuOpen'; +import Stack from '@mui/material/Stack'; +import { BrandingContext } from '../shared/context'; +import type { Branding } from '../AppProvider'; +import type { AccountProps } from '../Account'; +import { AppTitle, type AppTitleProps } from './AppTitle'; +import { ToolbarActions } from './ToolbarActions'; + +const AppBar = styled(MuiAppBar)(({ theme }) => ({ + borderWidth: 0, + borderBottomWidth: 1, + borderStyle: 'solid', + borderColor: (theme.vars ?? theme).palette.divider, + boxShadow: 'none', + zIndex: theme.zIndex.drawer + 1, +})); + +export interface DashboardHeaderSlotProps { + appTitle?: AppTitleProps; + toolbarActions?: {}; + toolbarAccount?: AccountProps; +} + +export interface DashboardHeaderSlots { + /** + * The component used for the app title section. + * @default Link + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + */ + appTitle?: React.ElementType; + /** + * The toolbar actions component to be used. + * @default ToolbarActions + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + */ + toolbarActions?: React.JSXElementConstructor<{}>; + /** + * The toolbar account component to be used. + * @default Account + * @deprecated Place your custom component on the right in the `toolbarActions` slot instead. + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) + */ + toolbarAccount?: React.JSXElementConstructor; +} + +export interface DashboardHeaderProps { + /** + * Branding options for the header. + * @default null + */ + branding?: Branding | null; + /** + * If `true`, show menu button as if menu is expanded, otherwise show it as if menu is collapsed. + */ + menuOpen: boolean; + /** + * Callback fired when the menu button is clicked. + */ + onToggleMenu: (open: boolean) => void; + /** + * Whether the menu icon should always be hidden. + * @default false + */ + hideMenuButton?: boolean; + /** + * The components used for each slot inside. + * @default {} + */ + slots?: DashboardHeaderSlots; + /** + * The props used for each slot inside. + * @default {} + */ + slotProps?: DashboardHeaderSlotProps; +} +/** + * + * Demos: + * + * - [Dashboard Layout](https://mui.com/toolpad/core/react-dashboard-layout/) + * + * API: + * + * - [DashboardHeader API](https://mui.com/toolpad/core/api/dashboard-header) + */ +function DashboardHeader(props: DashboardHeaderProps) { + const { + branding: brandingProp, + menuOpen, + onToggleMenu, + hideMenuButton, + slots, + slotProps, + } = props; + + const brandingContext = React.useContext(BrandingContext); + + const branding = { ...brandingContext, ...brandingProp }; + + const handleMenuOpen = React.useCallback(() => { + onToggleMenu(!menuOpen); + }, [menuOpen, onToggleMenu]); + + const getMenuIcon = React.useCallback( + (isExpanded: boolean) => { + const expandMenuActionText = 'Expand'; + const collapseMenuActionText = 'Collapse'; + + return ( + +
+ + {isExpanded ? : } + +
+
+ ); + }, + [handleMenuOpen], + ); + + const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; + const ToolbarAccountSlot = slots?.toolbarAccount ?? (() => null); + + return ( + + + + + + {getMenuIcon(menuOpen)} + + + {getMenuIcon(menuOpen)} + + {slots?.appTitle ? ( + + ) : ( + /* Hierarchy of application of `branding` + * 1. Branding prop passed in the `slotProps.appTitle` + * 2. Branding prop passed to the `DashboardLayout` + * 3. Branding prop passed to the `AppProvider` + */ + + )} + + + + + + + + + ); +} + +DashboardHeader.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * Branding options for the header. + * @default null + */ + branding: PropTypes.shape({ + homeUrl: PropTypes.string, + logo: PropTypes.node, + title: PropTypes.string, + }), + /** + * Whether the menu icon should always be hidden. + * @default false + */ + hideMenuButton: PropTypes.bool, + /** + * If `true`, show menu button as if menu is expanded, otherwise show it as if menu is collapsed. + */ + menuOpen: PropTypes.bool.isRequired, + /** + * Callback fired when the menu button is clicked. + */ + onToggleMenu: PropTypes.func.isRequired, + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + appTitle: PropTypes.shape({ + branding: PropTypes.shape({ + homeUrl: PropTypes.string, + logo: PropTypes.node, + title: PropTypes.string, + }), + }), + toolbarAccount: PropTypes.shape({ + localeText: PropTypes.object, + slotProps: PropTypes.shape({ + popover: PropTypes.object, + popoverContent: PropTypes.object, + preview: PropTypes.object, + signInButton: PropTypes.object, + signOutButton: PropTypes.object, + }), + slots: PropTypes.shape({ + popover: PropTypes.elementType, + popoverContent: PropTypes.elementType, + preview: PropTypes.elementType, + signInButton: PropTypes.elementType, + signOutButton: PropTypes.elementType, + }), + }), + toolbarActions: PropTypes.object, + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + appTitle: PropTypes.elementType, + toolbarAccount: PropTypes.elementType, + toolbarActions: PropTypes.elementType, + }), +} as any; + +export { DashboardHeader }; diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 0ec679c4ebe..4f01b7578f9 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -1,96 +1,66 @@ 'use client'; - import * as React from 'react'; import PropTypes from 'prop-types'; -import { styled, useTheme, SxProps } from '@mui/material/styles'; +import { useTheme, SxProps } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import type {} from '@mui/material/themeCssVarsAugmentation'; -import MuiAppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; -import IconButton from '@mui/material/IconButton'; -import Stack from '@mui/material/Stack'; import Toolbar from '@mui/material/Toolbar'; -import Tooltip from '@mui/material/Tooltip'; -import MenuIcon from '@mui/icons-material/Menu'; -import MenuOpenIcon from '@mui/icons-material/MenuOpen'; -import { BrandingContext, NavigationContext, WindowContext } from '../shared/context'; -import { type AccountProps } from '../Account'; -import { AppTitle, type AppTitleProps } from './AppTitle'; +import { NavigationContext, WindowContext } from '../shared/context'; +import type { Branding, Navigation, NavigationPageItem } from '../AppProvider'; +import { + DashboardHeader, + type DashboardHeaderProps, + type DashboardHeaderSlots, + type DashboardHeaderSlotProps, +} from './DashboardHeader'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; -import { ToolbarActions } from './ToolbarActions'; import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from './utils'; import { MINI_DRAWER_WIDTH } from './shared'; -import type { Branding, Navigation, NavigationPageItem } from '../AppProvider'; - -const AppBar = styled(MuiAppBar)(({ theme }) => ({ - borderWidth: 0, - borderBottomWidth: 1, - borderStyle: 'solid', - borderColor: (theme.vars ?? theme).palette.divider, - boxShadow: 'none', - zIndex: theme.zIndex.drawer + 1, -})); export interface SidebarFooterProps { mini: boolean; } -export interface ToolbarProps { - menuIcon: React.ReactElement; -} - export interface DashboardLayoutSlotProps { - appTitle?: AppTitleProps; - toolbarActions?: {}; - toolbarAccount?: AccountProps; + appTitle?: DashboardHeaderSlotProps['appTitle']; + toolbarActions?: DashboardHeaderSlotProps['toolbarActions']; + toolbarAccount?: DashboardHeaderSlotProps['toolbarAccount']; sidebarFooter?: SidebarFooterProps; - appBar?: {}; + appBar?: DashboardHeaderProps; } export interface DashboardLayoutSlots { /** * The component used for the app title section in the layout header. * @default Link + * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - appTitle?: React.ElementType; - + appTitle?: DashboardHeaderSlots['appTitle']; /** * The toolbar actions component used in the layout header. * @default ToolbarActions * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbarActions?: React.JSXElementConstructor<{}>; - + toolbarActions?: DashboardHeaderSlots['toolbarActions']; /** * The toolbar account component used in the layout header. * @default Account * @deprecated Place your custom component on the right in the `toolbarActions` slot instead. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbarAccount?: React.JSXElementConstructor; - + toolbarAccount?: DashboardHeaderSlots['toolbarAccount']; /** - * Optional footer component used in the layout sidebar. - * @default null + * The component used for the layout header. + * @default DashboardHeader */ - sidebarFooter?: React.JSXElementConstructor; - + appBar?: React.JSXElementConstructor; /** - * This component is used for the layout header. - * You can still use the built-in ``, `` and `` components inside. + * Optional footer component used in the layout sidebar. * @default null - * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) - * @example - * ```tsx - * import { DefaultAppBar } from './DefaultAppBar'; - * - * - * {children} - * - * ``` */ - appBar?: React.JSXElementConstructor; + sidebarFooter?: React.JSXElementConstructor; } export interface DashboardLayoutProps { @@ -165,7 +135,7 @@ export interface DashboardLayoutProps { function DashboardLayout(props: DashboardLayoutProps) { const { children, - branding: brandingProp, + branding, navigation: navigationProp, defaultSidebarCollapsed = false, disableCollapsibleSidebar = false, @@ -179,11 +149,9 @@ function DashboardLayout(props: DashboardLayoutProps) { const theme = useTheme(); - const brandingContext = React.useContext(BrandingContext); const navigationContext = React.useContext(NavigationContext); const appWindowContext = React.useContext(WindowContext); - const branding = { ...brandingContext, ...brandingProp }; const navigation = navigationProp ?? navigationContext; const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] = @@ -258,9 +226,12 @@ function DashboardLayout(props: DashboardLayoutProps) { [setIsNavigationExpanded], ); - const toggleNavigationExpanded = React.useCallback(() => { - setIsNavigationExpanded(!isNavigationExpanded); - }, [isNavigationExpanded, setIsNavigationExpanded]); + const handleToggleHeaderMenu = React.useCallback( + (isExpanded: boolean) => { + setIsNavigationExpanded(isExpanded); + }, + [setIsNavigationExpanded], + ); const handleNavigationLinkClick = React.useCallback(() => { setIsMobileNavigationExpanded(false); @@ -269,34 +240,8 @@ function DashboardLayout(props: DashboardLayoutProps) { const isDesktopMini = !disableCollapsibleSidebar && !isDesktopNavigationExpanded; const isMobileMini = !disableCollapsibleSidebar && !isMobileNavigationExpanded; - const getMenuIcon = React.useCallback( - (isExpanded: boolean) => { - const expandMenuActionText = 'Expand'; - const collapseMenuActionText = 'Collapse'; - - return ( - -
- - {isExpanded ? : } - -
-
- ); - }, - [toggleNavigationExpanded], - ); - const hasDrawerTransitions = isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport); - const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions; - const ToolbarAccountSlot = slots?.toolbarAccount ?? (() => null); const SidebarFooterSlot = slots?.sidebarFooter ?? null; const getDrawerContent = React.useCallback( @@ -371,12 +316,36 @@ function DashboardLayout(props: DashboardLayoutProps) { [isNavigationExpanded, sidebarExpandedWidth], ); - const appBarSlotProps: ToolbarProps = { - ...slotProps?.appBar, - menuIcon: isOverMdViewport - ? getMenuIcon(isDesktopNavigationExpanded) - : getMenuIcon(isMobileNavigationExpanded), - }; + const AppBarSlot = slots?.appBar ?? DashboardHeader; + + const appBarSlotProps: DashboardHeaderProps = React.useMemo( + () => ({ + branding, + menuOpen: isNavigationExpanded, + onToggleMenu: handleToggleHeaderMenu, + hideMenuButton: hideNavigation || (isOverMdViewport && disableCollapsibleSidebar), + slots: { + appTitle: slots?.appTitle, + toolbarActions: slots?.toolbarActions, + toolbarAccount: slots?.toolbarAccount, + }, + slotProps: { + appTitle: slotProps?.appTitle, + toolbarActions: slotProps?.toolbarActions, + toolbarAccount: slotProps?.toolbarAccount, + }, + }), + [ + branding, + isNavigationExpanded, + handleToggleHeaderMenu, + hideNavigation, + isOverMdViewport, + disableCollapsibleSidebar, + slotProps, + slots, + ], + ); return ( - {slots?.appBar ? ( - - ) : ( - - - - {/* Toolbar Left section */} - - {!hideNavigation ? ( - - - {getMenuIcon(isMobileNavigationExpanded)} - - - {getMenuIcon(isDesktopNavigationExpanded)} - - - ) : null} - {slots?.appTitle ? ( - - ) : ( - /* Hierarchy of application of `branding` - * 1. Branding prop passed in the `slotProps.appTitle` - * 2. Branding prop passed to the `DashboardLayout` - * 3. Branding prop passed to the `AppProvider` - */ - - )} - - - {/* Toolbar Right section */} - - - - - - - - )} - + {!hideNavigation ? ( theme.zIndex.drawer + 1 }} - > - - - - {props.menuIcon} - - - {}} /> - - - - - - -
- ); -} diff --git a/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx b/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx deleted file mode 100644 index 914374eaa2e..00000000000 --- a/packages/toolpad-core/src/DashboardLayout/SearchBar.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as React from 'react'; -import { styled, alpha } from '@mui/material/styles'; -import InputBase from '@mui/material/InputBase'; -import SearchIcon from '@mui/icons-material/Search'; - -const Search = styled('div')(({ theme }) => ({ - position: 'relative', - borderRadius: theme.shape.borderRadius, - backgroundColor: alpha(theme.palette.common.black, 0.05), - '&:hover': { - backgroundColor: alpha(theme.palette.common.black, 0.08), - }, - marginRight: theme.spacing(2), - marginLeft: 0, - width: '100%', - [theme.breakpoints.up('sm')]: { - marginLeft: theme.spacing(3), - width: 'auto', - }, -})); - -const SearchIconWrapper = styled('div')(({ theme }) => ({ - padding: theme.spacing(0, 2), - height: '100%', - position: 'absolute', - pointerEvents: 'none', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', -})); - -const StyledInputBase = styled(InputBase)(({ theme }) => ({ - color: 'inherit', - '& .MuiInputBase-input': { - padding: theme.spacing(1, 1, 1, 0), - paddingLeft: `calc(1em + ${theme.spacing(4)})`, - transition: theme.transitions.create('width'), - width: '100%', - [theme.breakpoints.up('md')]: { - width: '20ch', - }, - }, -})); - -export interface SearchBarProps { - onSearch?: (value: string) => void; - placeholder?: string; -} - -export function SearchBar({ onSearch, placeholder = 'Search…' }: SearchBarProps) { - const handleChange = (event: React.ChangeEvent) => { - onSearch?.(event.target.value); - }; - - return ( - - - - - - - ); -} diff --git a/packages/toolpad-core/src/DashboardLayout/index.ts b/packages/toolpad-core/src/DashboardLayout/index.ts index b478cabbbf5..c2cc9697ec7 100644 --- a/packages/toolpad-core/src/DashboardLayout/index.ts +++ b/packages/toolpad-core/src/DashboardLayout/index.ts @@ -1,6 +1,5 @@ export * from './DashboardLayout'; +export * from './DashboardHeader'; export * from './DashboardSidebarPageItem'; -export * from './DefaultAppBar'; -export * from './SearchBar'; export * from './ThemeSwitcher'; export * from './ToolbarActions'; diff --git a/playground/nextjs/src/app/(dashboard)/layout.tsx b/playground/nextjs/src/app/(dashboard)/layout.tsx index 52b3ac44e6b..493cff95f43 100644 --- a/playground/nextjs/src/app/(dashboard)/layout.tsx +++ b/playground/nextjs/src/app/(dashboard)/layout.tsx @@ -16,9 +16,17 @@ import { SignOutButton, AccountPreviewProps, } from '@toolpad/core/Account'; -import { DashboardLayout, SidebarFooterProps } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, SidebarFooterProps, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; +function ToolbarActions() { + return ( + + + + ); +} + const accounts = [ { id: 1, @@ -176,7 +184,12 @@ export default function DashboardPagesLayout(props: { children: React.ReactNode }, [orderId, pathname]); return ( - null }}> + {props.children} ); From 9af5372837a422c8d043b09ffb134ca142c3ad86 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 15:45:08 +0100 Subject: [PATCH 23/31] Add fixes --- .../DashboardLayoutAccountSidebar.js | 2 +- .../DashboardLayoutAccountSidebar.tsx | 2 +- .../DashboardLayoutAccountSidebar.tsx.preview | 2 +- .../toolpad/core/api/dashboard-layout.json | 2 +- .../src/DashboardLayout/DashboardLayout.tsx | 257 +++--------------- 5 files changed, 41 insertions(+), 224 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js index 9b105464552..a0b9bc5b826 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.js @@ -290,7 +290,7 @@ function DashboardLayoutAccountSidebar(props) { {/* preview-start */} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx index 928dac06fef..9f954224c1a 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx @@ -280,7 +280,7 @@ export default function DashboardLayoutAccountSidebar(props: DemoProps) { {/* preview-start */} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview index edf2eda498b..d86d5248eda 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutAccountSidebar.tsx.preview @@ -1,6 +1,6 @@ diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index 4f7160b4f1b..3b52452334e 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -39,7 +39,7 @@ "slots": { "type": { "name": "shape", - "description": "{ appBar?: elementType, appTitle?: 'a'
| 'abbr'
| 'address'
| 'animate'
| 'animateMotion'
| 'animateTransform'
| 'area'
| 'article'
| 'aside'
| 'audio'
| 'b'
| 'base'
| 'bdi'
| 'bdo'
| 'big'
| 'blockquote'
| 'body'
| 'br'
| 'button'
| 'canvas'
| 'caption'
| 'center'
| 'circle'
| 'cite'
| 'clipPath'
| 'code'
| 'col'
| 'colgroup'
| 'data'
| 'datalist'
| 'dd'
| 'defs'
| 'del'
| 'desc'
| 'details'
| 'dfn'
| 'dialog'
| 'div'
| 'dl'
| 'dt'
| 'ellipse'
| 'em'
| 'embed'
| 'feBlend'
| 'feColorMatrix'
| 'feComponentTransfer'
| 'feComposite'
| 'feConvolveMatrix'
| 'feDiffuseLighting'
| 'feDisplacementMap'
| 'feDistantLight'
| 'feDropShadow'
| 'feFlood'
| 'feFuncA'
| 'feFuncB'
| 'feFuncG'
| 'feFuncR'
| 'feGaussianBlur'
| 'feImage'
| 'feMerge'
| 'feMergeNode'
| 'feMorphology'
| 'feOffset'
| 'fePointLight'
| 'feSpecularLighting'
| 'feSpotLight'
| 'feTile'
| 'feTurbulence'
| 'fieldset'
| 'figcaption'
| 'figure'
| 'filter'
| 'footer'
| 'foreignObject'
| 'form'
| 'g'
| 'h1'
| 'h2'
| 'h3'
| 'h4'
| 'h5'
| 'h6'
| 'head'
| 'header'
| 'hgroup'
| 'hr'
| 'html'
| 'i'
| 'iframe'
| 'image'
| 'img'
| 'input'
| 'ins'
| 'kbd'
| 'keygen'
| 'label'
| 'legend'
| 'li'
| 'line'
| 'linearGradient'
| 'link'
| 'main'
| 'map'
| 'mark'
| 'marker'
| 'mask'
| 'menu'
| 'menuitem'
| 'meta'
| 'metadata'
| 'meter'
| 'mpath'
| 'nav'
| 'noindex'
| 'noscript'
| 'object'
| 'ol'
| 'optgroup'
| 'option'
| 'output'
| 'p'
| 'param'
| 'path'
| 'pattern'
| 'picture'
| 'polygon'
| 'polyline'
| 'pre'
| 'progress'
| 'q'
| 'radialGradient'
| 'rect'
| 'rp'
| 'rt'
| 'ruby'
| 's'
| 'samp'
| 'script'
| 'search'
| 'section'
| 'select'
| 'set'
| 'slot'
| 'small'
| 'source'
| 'span'
| 'stop'
| 'strong'
| 'style'
| 'sub'
| 'summary'
| 'sup'
| 'svg'
| 'switch'
| 'symbol'
| 'table'
| 'tbody'
| 'td'
| 'template'
| 'text'
| 'textarea'
| 'textPath'
| 'tfoot'
| 'th'
| 'thead'
| 'time'
| 'title'
| 'tr'
| 'track'
| 'tspan'
| 'u'
| 'ul'
| 'use'
| 'var'
| 'video'
| 'view'
| 'wbr'
| 'webview'
| func, sidebarFooter?: elementType, toolbarAccount?: func, toolbarActions?: func }" + "description": "{ appBar?: elementType, appTitle?: elementType, sidebarFooter?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType }" }, "default": "{}", "additionalInfo": { "slotsApi": true } diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 4f01b7578f9..8a6747fb059 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -9,10 +9,10 @@ import Drawer from '@mui/material/Drawer'; import Toolbar from '@mui/material/Toolbar'; import { NavigationContext, WindowContext } from '../shared/context'; import type { Branding, Navigation, NavigationPageItem } from '../AppProvider'; +import type { AccountProps } from '../Account'; import { DashboardHeader, type DashboardHeaderProps, - type DashboardHeaderSlots, type DashboardHeaderSlotProps, } from './DashboardHeader'; import { DashboardSidebarSubNavigation } from './DashboardSidebarSubNavigation'; @@ -37,20 +37,20 @@ export interface DashboardLayoutSlots { * @default Link * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - appTitle?: DashboardHeaderSlots['appTitle']; + appTitle?: React.ElementType; /** * The toolbar actions component used in the layout header. * @default ToolbarActions * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbarActions?: DashboardHeaderSlots['toolbarActions']; + toolbarActions?: React.JSXElementConstructor<{}>; /** * The toolbar account component used in the layout header. * @default Account * @deprecated Place your custom component on the right in the `toolbarActions` slot instead. * @see [DashboardLayout#slots](https://mui.com/toolpad/core/react-dashboard-layout/#slots) */ - toolbarAccount?: DashboardHeaderSlots['toolbarAccount']; + toolbarAccount?: React.JSXElementConstructor; /** * The component used for the layout header. * @default DashboardHeader @@ -243,6 +243,36 @@ function DashboardLayout(props: DashboardLayoutProps) { const hasDrawerTransitions = isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport); const SidebarFooterSlot = slots?.sidebarFooter ?? null; + const AppBarSlot = slots?.appBar ?? DashboardHeader; + + const appBarSlotProps: DashboardHeaderProps = React.useMemo( + () => ({ + branding, + menuOpen: isNavigationExpanded, + onToggleMenu: handleToggleHeaderMenu, + hideMenuButton: hideNavigation || (isOverMdViewport && disableCollapsibleSidebar), + slots: { + appTitle: slots?.appTitle, + toolbarActions: slots?.toolbarActions, + toolbarAccount: slots?.toolbarAccount, + }, + slotProps: { + appTitle: slotProps?.appTitle, + toolbarActions: slotProps?.toolbarActions, + toolbarAccount: slotProps?.toolbarAccount, + }, + }), + [ + branding, + isNavigationExpanded, + handleToggleHeaderMenu, + hideNavigation, + isOverMdViewport, + disableCollapsibleSidebar, + slotProps, + slots, + ], + ); const getDrawerContent = React.useCallback( (isMini: boolean, viewport: 'phone' | 'tablet' | 'desktop') => ( @@ -316,37 +346,6 @@ function DashboardLayout(props: DashboardLayoutProps) { [isNavigationExpanded, sidebarExpandedWidth], ); - const AppBarSlot = slots?.appBar ?? DashboardHeader; - - const appBarSlotProps: DashboardHeaderProps = React.useMemo( - () => ({ - branding, - menuOpen: isNavigationExpanded, - onToggleMenu: handleToggleHeaderMenu, - hideMenuButton: hideNavigation || (isOverMdViewport && disableCollapsibleSidebar), - slots: { - appTitle: slots?.appTitle, - toolbarActions: slots?.toolbarActions, - toolbarAccount: slots?.toolbarAccount, - }, - slotProps: { - appTitle: slotProps?.appTitle, - toolbarActions: slotProps?.toolbarActions, - toolbarAccount: slotProps?.toolbarAccount, - }, - }), - [ - branding, - isNavigationExpanded, - handleToggleHeaderMenu, - hideNavigation, - isOverMdViewport, - disableCollapsibleSidebar, - slotProps, - slots, - ], - ); - return ( Date: Wed, 7 May 2025 15:55:31 +0100 Subject: [PATCH 24/31] More updates --- .../dashboard-layout/dashboard-layout.md | 2 +- .../app/(dashboard)/layout.tsx | 14 ++++++++-- .../firebase-vite/src/layouts/dashboard.tsx | 20 ++++++++------ .../src/templates/vite/dashboardLayout.ts | 26 ++++++++++++------- .../src/DashboardLayout/DashboardLayout.tsx | 10 +++---- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md index 7a545d0404f..5138ead4e35 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md +++ b/docs/data/toolpad/core/components/dashboard-layout/dashboard-layout.md @@ -171,7 +171,7 @@ Some possibly useful slots: - `toolbarActions`: allows you to add new items to the toolbar in the header, such as a search bar or button. The default `ThemeSwitcher` and `Account` components can be imported and used if you wish to do so, as shown in the example below. -- `appBar`: allows you to fully replace and customize the layout header. +- `header`: allows you to fully replace and customize the layout header. - `sidebarFooter`: allows you to add a footer to the sidebar. diff --git a/examples/core/auth-nextjs-themed/app/(dashboard)/layout.tsx b/examples/core/auth-nextjs-themed/app/(dashboard)/layout.tsx index 2bce076a6e8..a89b93527dd 100644 --- a/examples/core/auth-nextjs-themed/app/(dashboard)/layout.tsx +++ b/examples/core/auth-nextjs-themed/app/(dashboard)/layout.tsx @@ -1,11 +1,21 @@ 'use client'; import * as React from 'react'; -import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import Stack from '@mui/material/Stack'; +import { DashboardLayout, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; import { usePathname, useParams } from 'next/navigation'; import { PageContainer } from '@toolpad/core/PageContainer'; import Copyright from '../components/Copyright'; import SidebarFooterAccount, { ToolbarAccountOverride } from './SidebarFooterAccount'; +function CustomActions() { + return ( + + + + + ); +} + export default function Layout(props: { children: React.ReactNode }) { const pathname = usePathname(); const params = useParams(); @@ -27,7 +37,7 @@ export default function Layout(props: { children: React.ReactNode }) { return ( diff --git a/examples/core/firebase-vite/src/layouts/dashboard.tsx b/examples/core/firebase-vite/src/layouts/dashboard.tsx index 07e4183641b..b9b473b85cf 100644 --- a/examples/core/firebase-vite/src/layouts/dashboard.tsx +++ b/examples/core/firebase-vite/src/layouts/dashboard.tsx @@ -1,19 +1,23 @@ import * as React from 'react'; import LinearProgress from '@mui/material/LinearProgress'; +import Stack from '@mui/material/Stack'; import { Outlet, Navigate, useLocation } from 'react-router'; -import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; import { Account } from '@toolpad/core/Account'; import { useSession } from '../SessionContext'; -function CustomAccount() { +function CustomActions() { return ( - + + + + ); } @@ -37,7 +41,7 @@ export default function Layout() { } return ( - + diff --git a/packages/create-toolpad-app/src/templates/vite/dashboardLayout.ts b/packages/create-toolpad-app/src/templates/vite/dashboardLayout.ts index ecd6cd15846..e726dddec55 100644 --- a/packages/create-toolpad-app/src/templates/vite/dashboardLayout.ts +++ b/packages/create-toolpad-app/src/templates/vite/dashboardLayout.ts @@ -3,9 +3,14 @@ import { Template } from '../../types'; const dashboardTemplate: Template = (options) => { const { auth } = options; - return `import * as React from 'react';${auth ? `\nimport LinearProgress from '@mui/material/LinearProgress';` : ``} + return `import * as React from 'react';${ + auth + ? `\nimport LinearProgress from '@mui/material/LinearProgress';\nimport Stack from '@mui/material/Stack'; +` + : `` + } import { Outlet, useLocation, useParams, matchPath } from 'react-router'; -import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { DashboardLayout${auth ? ', ThemeSwitcher' : ''} } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; ${ auth @@ -13,13 +18,16 @@ ${ import { useSession } from '../SessionContext'; -function CustomAccount() { +function CustomActions() { return ( - + + + + ); }` : '' @@ -63,7 +71,7 @@ export default function Layout() { } return ( - + diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 8a6747fb059..f8451b0a88e 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -28,7 +28,7 @@ export interface DashboardLayoutSlotProps { toolbarActions?: DashboardHeaderSlotProps['toolbarActions']; toolbarAccount?: DashboardHeaderSlotProps['toolbarAccount']; sidebarFooter?: SidebarFooterProps; - appBar?: DashboardHeaderProps; + header?: DashboardHeaderProps; } export interface DashboardLayoutSlots { @@ -55,7 +55,7 @@ export interface DashboardLayoutSlots { * The component used for the layout header. * @default DashboardHeader */ - appBar?: React.JSXElementConstructor; + header?: React.JSXElementConstructor; /** * Optional footer component used in the layout sidebar. * @default null @@ -243,9 +243,9 @@ function DashboardLayout(props: DashboardLayoutProps) { const hasDrawerTransitions = isOverSmViewport && (!disableCollapsibleSidebar || isOverMdViewport); const SidebarFooterSlot = slots?.sidebarFooter ?? null; - const AppBarSlot = slots?.appBar ?? DashboardHeader; + const HeaderSlot = slots?.header ?? DashboardHeader; - const appBarSlotProps: DashboardHeaderProps = React.useMemo( + const headerSlotProps: DashboardHeaderProps = React.useMemo( () => ({ branding, menuOpen: isNavigationExpanded, @@ -357,7 +357,7 @@ function DashboardLayout(props: DashboardLayoutProps) { ...sx, }} > - + {!hideNavigation ? ( Date: Wed, 7 May 2025 15:57:56 +0100 Subject: [PATCH 25/31] Update docs --- .../toolpad/core/api/dashboard-layout.json | 6 +++--- .../dashboard-layout/dashboard-layout.json | 2 +- .../src/DashboardLayout/DashboardLayout.tsx | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index 3b52452334e..532ec3a3067 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -32,14 +32,14 @@ "slotProps": { "type": { "name": "shape", - "description": "{ appBar?: { branding?: { homeUrl?: string, logo?: node, title?: string }, hideMenuButton?: bool, menuOpen: bool, onToggleMenu: func, slotProps?: { appTitle?: object, toolbarAccount?: object, toolbarActions?: object }, slots?: { appTitle?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType } }, appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: object, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" + "description": "{ appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, header?: { branding?: { homeUrl?: string, logo?: node, title?: string }, hideMenuButton?: bool, menuOpen: bool, onToggleMenu: func, slotProps?: { appTitle?: object, toolbarAccount?: object, toolbarActions?: object }, slots?: { appTitle?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: object, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" }, "default": "{}" }, "slots": { "type": { "name": "shape", - "description": "{ appBar?: elementType, appTitle?: elementType, sidebarFooter?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType }" + "description": "{ appTitle?: elementType, header?: elementType, sidebarFooter?: elementType, toolbarAccount?: elementType, toolbarActions?: elementType }" }, "default": "{}", "additionalInfo": { "slotsApi": true } @@ -77,7 +77,7 @@ "class": null }, { - "name": "appBar", + "name": "header", "description": "The component used for the layout header.", "default": "DashboardHeader", "class": null diff --git a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json index af69e6a5e61..cd12927ad92 100644 --- a/docs/translations/api-docs/dashboard-layout/dashboard-layout.json +++ b/docs/translations/api-docs/dashboard-layout/dashboard-layout.json @@ -25,8 +25,8 @@ }, "classDescriptions": {}, "slotDescriptions": { - "appBar": "The component used for the layout header.", "appTitle": "The component used for the app title section in the layout header.", + "header": "The component used for the layout header.", "sidebarFooter": "Optional footer component used in the layout sidebar.", "toolbarAccount": "The toolbar account component used in the layout header.", "toolbarActions": "The toolbar actions component used in the layout header." diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index f8451b0a88e..844249907cf 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -515,7 +515,14 @@ DashboardLayout.propTypes /* remove-proptypes */ = { * @default {} */ slotProps: PropTypes.shape({ - appBar: PropTypes.shape({ + appTitle: PropTypes.shape({ + branding: PropTypes.shape({ + homeUrl: PropTypes.string, + logo: PropTypes.node, + title: PropTypes.string, + }), + }), + header: PropTypes.shape({ branding: PropTypes.shape({ homeUrl: PropTypes.string, logo: PropTypes.node, @@ -535,13 +542,6 @@ DashboardLayout.propTypes /* remove-proptypes */ = { toolbarActions: PropTypes.elementType, }), }), - appTitle: PropTypes.shape({ - branding: PropTypes.shape({ - homeUrl: PropTypes.string, - logo: PropTypes.node, - title: PropTypes.string, - }), - }), sidebarFooter: PropTypes.shape({ mini: PropTypes.bool.isRequired, }), @@ -569,8 +569,8 @@ DashboardLayout.propTypes /* remove-proptypes */ = { * @default {} */ slots: PropTypes.shape({ - appBar: PropTypes.elementType, appTitle: PropTypes.elementType, + header: PropTypes.elementType, sidebarFooter: PropTypes.elementType, toolbarAccount: PropTypes.elementType, toolbarActions: PropTypes.elementType, From fd56d9447f3f564b3f17dee5e2d822037823349c Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:02:15 +0100 Subject: [PATCH 26/31] Fix tests --- .../toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx index 8b89572c504..040e8d85149 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx @@ -395,7 +395,8 @@ describe('DashboardLayout', () => { ); const desktopNavigation = screen.queryByRole('navigation', { name: 'Desktop' }); - const navigationToggle = screen.queryByLabelText('Collapse menu'); + const navigationToggle = + desktopNavigation && within(desktopNavigation).queryByLabelText('Collapse menu'); // Expect that navigation and menu button are not rendered expect(desktopNavigation).toBeNull(); From 523527529b21bb67b71e302dd3ce1c2d6ee89720 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:08:37 +0100 Subject: [PATCH 27/31] Test solution was wrong --- .../src/DashboardLayout/DashboardHeader.tsx | 36 ++++++++++--------- .../DashboardLayout/DashboardLayout.test.tsx | 3 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx index 030c04561d4..b56997d6196 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardHeader.tsx @@ -150,22 +150,26 @@ function DashboardHeader(props: DashboardHeaderProps) { }} > - - {getMenuIcon(menuOpen)} - - - {getMenuIcon(menuOpen)} - + {!hideMenuButton ? ( + + + {getMenuIcon(menuOpen)} + + + {getMenuIcon(menuOpen)} + + + ) : null} {slots?.appTitle ? ( ) : ( diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx index 040e8d85149..8b89572c504 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.test.tsx @@ -395,8 +395,7 @@ describe('DashboardLayout', () => { ); const desktopNavigation = screen.queryByRole('navigation', { name: 'Desktop' }); - const navigationToggle = - desktopNavigation && within(desktopNavigation).queryByLabelText('Collapse menu'); + const navigationToggle = screen.queryByLabelText('Collapse menu'); // Expect that navigation and menu button are not rendered expect(desktopNavigation).toBeNull(); From 742853e9babd3488eab12c9f7974fc5f360764a0 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:10:28 +0100 Subject: [PATCH 28/31] Update docs demo --- .../core/components/dashboard-layout/DashboardLayoutSlots.js | 2 ++ .../core/components/dashboard-layout/DashboardLayoutSlots.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.js index cc71dc1308b..0bd4e14746f 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.js @@ -15,6 +15,7 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import SearchIcon from '@mui/icons-material/Search'; import { AppProvider } from '@toolpad/core/AppProvider'; import { DashboardLayout, ThemeSwitcher } from '@toolpad/core/DashboardLayout'; +import { Account } from '@toolpad/core/Account'; import { DemoProvider, useDemoRouter } from '@toolpad/core/internal'; const NAVIGATION = [ @@ -103,6 +104,7 @@ function ToolbarActionsSearch() { sx={{ display: { xs: 'none', md: 'inline-block' }, mr: 1 }} /> + ); } diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.tsx index ad0789f864e..fa7f4581b5a 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.tsx +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutSlots.tsx @@ -18,6 +18,7 @@ import { ThemeSwitcher, type SidebarFooterProps, } from '@toolpad/core/DashboardLayout'; +import { Account } from '@toolpad/core/Account'; import { DemoProvider, useDemoRouter } from '@toolpad/core/internal'; const NAVIGATION: Navigation = [ @@ -102,6 +103,7 @@ function ToolbarActionsSearch() { sx={{ display: { xs: 'none', md: 'inline-block' }, mr: 1 }} /> + ); } From ceb06cd41a95ad7d0afba338783806f39f3a70e8 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:13:25 +0100 Subject: [PATCH 29/31] Fix header slot props override --- packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index f39e9594d23..174f6ad99fd 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -270,6 +270,7 @@ function DashboardLayout(props: DashboardLayoutProps) { toolbarActions: slotProps?.toolbarActions, toolbarAccount: slotProps?.toolbarAccount, }, + ...slotProps?.header, }), [ branding, From bb7310766bc4a93a8335d09868feeb26f51a7fd1 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:26:18 +0100 Subject: [PATCH 30/31] Adjust one more docs demo --- .../DashboardLayoutCustomThemeSwitcher.js | 13 ++++++++++++- .../DashboardLayoutCustomThemeSwitcher.tsx | 13 ++++++++++++- .../DashboardLayoutCustomThemeSwitcher.tsx.preview | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.js b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.js index 149bf92fd22..58308648af4 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.js +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.js @@ -8,6 +8,7 @@ import IconButton from '@mui/material/IconButton'; import Popover from '@mui/material/Popover'; import Radio from '@mui/material/Radio'; import RadioGroup from '@mui/material/RadioGroup'; +import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import { createTheme, useColorScheme } from '@mui/material/styles'; @@ -16,6 +17,7 @@ import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import SettingsIcon from '@mui/icons-material/Settings'; import { AppProvider } from '@toolpad/core/AppProvider'; import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { Account } from '@toolpad/core/Account'; import { DemoProvider, useDemoRouter } from '@toolpad/core/internal'; const NAVIGATION = [ @@ -131,6 +133,15 @@ function CustomThemeSwitcher() { ); } +function CustomToolbarActions() { + return ( + + + + + ); +} + function DashboardLayoutCustomThemeSwitcher(props) { const { window } = props; @@ -151,7 +162,7 @@ function DashboardLayoutCustomThemeSwitcher(props) { {/* preview-start */} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx index 65e0136126e..d244844b39d 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx @@ -7,6 +7,7 @@ import IconButton from '@mui/material/IconButton'; import Popover from '@mui/material/Popover'; import Radio from '@mui/material/Radio'; import RadioGroup from '@mui/material/RadioGroup'; +import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import { createTheme, useColorScheme } from '@mui/material/styles'; @@ -15,6 +16,7 @@ import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import SettingsIcon from '@mui/icons-material/Settings'; import { AppProvider, type Navigation } from '@toolpad/core/AppProvider'; import { DashboardLayout } from '@toolpad/core/DashboardLayout'; +import { Account } from '@toolpad/core/Account'; import { DemoProvider, useDemoRouter } from '@toolpad/core/internal'; const NAVIGATION: Navigation = [ @@ -126,6 +128,15 @@ function CustomThemeSwitcher() { ); } +function CustomToolbarActions() { + return ( + + + + + ); +} + interface DemoProps { /** * Injected by the documentation to work in an iframe. @@ -154,7 +165,7 @@ export default function DashboardLayoutCustomThemeSwitcher(props: DemoProps) { {/* preview-start */} diff --git a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx.preview b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx.preview index c6e4f10b119..6c09aae96ab 100644 --- a/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx.preview +++ b/docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx.preview @@ -1,6 +1,6 @@ From 89fcb59f7dd6173b535b3c4a27d4e5acf76754e8 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira <10789765+apedroferreira@users.noreply.github.com> Date: Wed, 7 May 2025 17:50:40 +0100 Subject: [PATCH 31/31] Add todo comment --- packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 174f6ad99fd..199d38cef0e 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -32,6 +32,8 @@ export interface DashboardLayoutSlotProps { header?: DashboardHeaderProps; } +// @TODO: Deprecate `appTitle` and `toolbarActions` slots so that they must be used in DashboardHeader component only. Update docs accordingly. + export interface DashboardLayoutSlots { /** * The component used for the app title section in the layout header.