-
-
Notifications
You must be signed in to change notification settings - Fork 337
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow theme switcher override with slots (#4340)
- Loading branch information
1 parent
f1bc2bd
commit caf962e
Showing
18 changed files
with
476 additions
and
28 deletions.
There are no files selected for viewing
168 changes: 168 additions & 0 deletions
168
docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Box from '@mui/material/Box'; | ||
import FormControlLabel from '@mui/material/FormControlLabel'; | ||
import FormControl from '@mui/material/FormControl'; | ||
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 { createTheme, useColorScheme } from '@mui/material/styles'; | ||
import DashboardIcon from '@mui/icons-material/Dashboard'; | ||
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 { useDemoRouter } from '@toolpad/core/internal'; | ||
|
||
const NAVIGATION = [ | ||
{ | ||
kind: 'header', | ||
title: 'Main items', | ||
}, | ||
{ | ||
segment: 'dashboard', | ||
title: 'Dashboard', | ||
icon: <DashboardIcon />, | ||
}, | ||
{ | ||
segment: 'orders', | ||
title: 'Orders', | ||
icon: <ShoppingCartIcon />, | ||
}, | ||
]; | ||
|
||
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 ( | ||
<Box | ||
sx={{ | ||
py: 4, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
alignItems: 'center', | ||
textAlign: 'center', | ||
}} | ||
> | ||
<Typography>Dashboard content for {pathname}</Typography> | ||
</Box> | ||
); | ||
} | ||
|
||
DemoPageContent.propTypes = { | ||
pathname: PropTypes.string.isRequired, | ||
}; | ||
|
||
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 ( | ||
<React.Fragment> | ||
<Tooltip title="Settings" enterDelay={1000}> | ||
<div> | ||
<IconButton type="button" aria-label="settings" onClick={toggleMenu}> | ||
<SettingsIcon /> | ||
</IconButton> | ||
</div> | ||
</Tooltip> | ||
<Popover | ||
open={isMenuOpen} | ||
anchorEl={menuAnchorEl} | ||
onClose={toggleMenu} | ||
anchorOrigin={{ | ||
vertical: 'bottom', | ||
horizontal: 'left', | ||
}} | ||
disableAutoFocus | ||
> | ||
<Box sx={{ p: 2 }}> | ||
<FormControl> | ||
<FormLabel id="custom-theme-switcher-label">Theme</FormLabel> | ||
<RadioGroup | ||
aria-labelledby="custom-theme-switcher-label" | ||
defaultValue="system" | ||
name="custom-theme-switcher" | ||
onChange={handleThemeChange} | ||
> | ||
<FormControlLabel value="light" control={<Radio />} label="Light" /> | ||
<FormControlLabel value="system" control={<Radio />} label="System" /> | ||
<FormControlLabel value="dark" control={<Radio />} label="Dark" /> | ||
</RadioGroup> | ||
</FormControl> | ||
</Box> | ||
</Popover> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
function DashboardLayoutCustomThemeSwitcher(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 ( | ||
<AppProvider | ||
navigation={NAVIGATION} | ||
router={router} | ||
theme={demoTheme} | ||
window={demoWindow} | ||
> | ||
<DashboardLayout | ||
slots={{ | ||
toolbarActions: CustomThemeSwitcher, | ||
}} | ||
> | ||
<DemoPageContent pathname={router.pathname} /> | ||
</DashboardLayout> | ||
</AppProvider> | ||
); | ||
} | ||
|
||
DashboardLayoutCustomThemeSwitcher.propTypes = { | ||
/** | ||
* Injected by the documentation to work in an iframe. | ||
* Remove this when copying and pasting into your project. | ||
*/ | ||
window: PropTypes.func, | ||
}; | ||
|
||
export default DashboardLayoutCustomThemeSwitcher; |
161 changes: 161 additions & 0 deletions
161
docs/data/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import * as React from 'react'; | ||
import Box from '@mui/material/Box'; | ||
import FormControlLabel from '@mui/material/FormControlLabel'; | ||
import FormControl from '@mui/material/FormControl'; | ||
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 { createTheme, useColorScheme } from '@mui/material/styles'; | ||
import DashboardIcon from '@mui/icons-material/Dashboard'; | ||
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 { useDemoRouter } from '@toolpad/core/internal'; | ||
|
||
const NAVIGATION: Navigation = [ | ||
{ | ||
kind: 'header', | ||
title: 'Main items', | ||
}, | ||
{ | ||
segment: 'dashboard', | ||
title: 'Dashboard', | ||
icon: <DashboardIcon />, | ||
}, | ||
{ | ||
segment: 'orders', | ||
title: 'Orders', | ||
icon: <ShoppingCartIcon />, | ||
}, | ||
]; | ||
|
||
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 ( | ||
<Box | ||
sx={{ | ||
py: 4, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
alignItems: 'center', | ||
textAlign: 'center', | ||
}} | ||
> | ||
<Typography>Dashboard content for {pathname}</Typography> | ||
</Box> | ||
); | ||
} | ||
|
||
function CustomThemeSwitcher() { | ||
const { setMode } = useColorScheme(); | ||
|
||
const handleThemeChange = React.useCallback( | ||
(event: React.ChangeEvent<HTMLInputElement>) => { | ||
setMode(event.target.value as 'light' | 'dark' | 'system'); | ||
}, | ||
[setMode], | ||
); | ||
|
||
const [isMenuOpen, setIsMenuOpen] = React.useState(false); | ||
const [menuAnchorEl, setMenuAnchorEl] = React.useState<HTMLElement | null>(null); | ||
|
||
const toggleMenu = React.useCallback( | ||
(event: React.MouseEvent<HTMLElement>) => { | ||
setMenuAnchorEl(isMenuOpen ? null : event.currentTarget); | ||
setIsMenuOpen((previousIsMenuOpen) => !previousIsMenuOpen); | ||
}, | ||
[isMenuOpen], | ||
); | ||
|
||
return ( | ||
<React.Fragment> | ||
<Tooltip title="Settings" enterDelay={1000}> | ||
<div> | ||
<IconButton type="button" aria-label="settings" onClick={toggleMenu}> | ||
<SettingsIcon /> | ||
</IconButton> | ||
</div> | ||
</Tooltip> | ||
<Popover | ||
open={isMenuOpen} | ||
anchorEl={menuAnchorEl} | ||
onClose={toggleMenu} | ||
anchorOrigin={{ | ||
vertical: 'bottom', | ||
horizontal: 'left', | ||
}} | ||
disableAutoFocus | ||
> | ||
<Box sx={{ p: 2 }}> | ||
<FormControl> | ||
<FormLabel id="custom-theme-switcher-label">Theme</FormLabel> | ||
<RadioGroup | ||
aria-labelledby="custom-theme-switcher-label" | ||
defaultValue="system" | ||
name="custom-theme-switcher" | ||
onChange={handleThemeChange} | ||
> | ||
<FormControlLabel value="light" control={<Radio />} label="Light" /> | ||
<FormControlLabel value="system" control={<Radio />} label="System" /> | ||
<FormControlLabel value="dark" control={<Radio />} label="Dark" /> | ||
</RadioGroup> | ||
</FormControl> | ||
</Box> | ||
</Popover> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
interface DemoProps { | ||
/** | ||
* Injected by the documentation to work in an iframe. | ||
* Remove this when copying and pasting into your project. | ||
*/ | ||
window?: () => Window; | ||
} | ||
|
||
export default function DashboardLayoutCustomThemeSwitcher(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 ( | ||
<AppProvider | ||
navigation={NAVIGATION} | ||
router={router} | ||
theme={demoTheme} | ||
window={demoWindow} | ||
> | ||
<DashboardLayout | ||
slots={{ | ||
toolbarActions: CustomThemeSwitcher, | ||
}} | ||
> | ||
<DemoPageContent pathname={router.pathname} /> | ||
</DashboardLayout> | ||
</AppProvider> | ||
); | ||
} |
7 changes: 7 additions & 0 deletions
7
...a/toolpad/core/components/dashboard-layout/DashboardLayoutCustomThemeSwitcher.tsx.preview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<DashboardLayout | ||
slots={{ | ||
toolbarActions: CustomThemeSwitcher, | ||
}} | ||
> | ||
<DemoPageContent pathname={router.pathname} /> | ||
</DashboardLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.