From 38e8f45864ca13804c439a771f9fe041e948fd3a Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Mon, 17 Jun 2024 20:34:23 -0300 Subject: [PATCH] refactor the implementation to use the same idea from the docs menu --- .../src/components/action/ProductMenuItem.tsx | 100 ++++++++ docs/src/components/header/HeaderNavBar.tsx | 221 ++++++------------ .../modules/components/MuiProductSelector.tsx | 103 +------- 3 files changed, 185 insertions(+), 239 deletions(-) create mode 100644 docs/src/components/action/ProductMenuItem.tsx diff --git a/docs/src/components/action/ProductMenuItem.tsx b/docs/src/components/action/ProductMenuItem.tsx new file mode 100644 index 00000000000000..c6aa3de0615498 --- /dev/null +++ b/docs/src/components/action/ProductMenuItem.tsx @@ -0,0 +1,100 @@ +import * as React from 'react'; +import NextLink from 'next/link'; +import { alpha } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import MenuItem, { MenuItemProps } from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; + +interface ProductItemProps extends MenuItemProps { + active?: boolean; + chip?: React.ReactNode; + description?: string; + docs?: boolean; + href: string; + icon?: React.ReactNode; + id?: string; + name: string; +} + +export default function ProductItem({ + active, + chip, + description, + docs, + href, + icon, + id, + name, + sx = [], + ...other +}: ProductItemProps) { + return ( + ({ + p: 1, + pl: '6px', + display: 'flex', + alignItems: docs ? 'start' : 'center', + gap: '8px', + flexGrow: 1, + backgroundColor: active ? alpha(theme.palette.primary[50], 0.8) : undefined, + border: '1px solid', + borderColor: active ? 'primary.100' : 'transparent', + borderRadius: '8px', + transition: '100ms ease-in background-color, border', + textDecorationLine: 'none', + '&:hover': { + backgroundColor: active ? alpha(theme.palette.primary[50], 0.8) : 'grey.50', + borderColor: 'divider', + }, + '&.Mui-focusVisible': { + backgroundColor: active ? (theme.vars || theme).palette.primary[50] : 'transparent', + }, + ...theme.applyDarkStyles({ + backgroundColor: active ? alpha(theme.palette.primary[900], 0.2) : undefined, + borderColor: active ? alpha(theme.palette.primary[300], 0.2) : 'transparent', + '&:hover': { + backgroundColor: active + ? alpha(theme.palette.primary[900], 0.3) + : alpha(theme.palette.primaryDark[700], 0.5), + }, + '&.Mui-focusVisible': { + backgroundColor: active ? alpha(theme.palette.primary[900], 0.5) : 'transparent', + }, + }), + }), + // You cannot spread `sx` directly because `SxProps` (typeof sx) can be an array. + ...(Array.isArray(sx) ? sx : [sx]), + ]} + {...other} + > + + {icon} + +
+ + + {name} + + {chip} + + + {description} + +
+
+ ); +} diff --git a/docs/src/components/header/HeaderNavBar.tsx b/docs/src/components/header/HeaderNavBar.tsx index e91d0998dfc451..c54e07207a9e2b 100644 --- a/docs/src/components/header/HeaderNavBar.tsx +++ b/docs/src/components/header/HeaderNavBar.tsx @@ -1,20 +1,22 @@ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ import * as React from 'react'; -import { styled, alpha } from '@mui/material/styles'; -import Box from '@mui/material/Box'; +import { styled, alpha, Theme } from '@mui/material/styles'; +import { unstable_debounce as debounce } from '@mui/utils'; import Chip from '@mui/material/Chip'; import ButtonBase from '@mui/material/ButtonBase'; import Popper from '@mui/material/Popper'; import Paper from '@mui/material/Paper'; -import { unstable_debounce as debounce } from '@mui/utils'; import Fade from '@mui/material/Fade'; -import Typography from '@mui/material/Typography'; +import MenuList from '@mui/material/MenuList'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; import IconImage from 'docs/src/components/icon/IconImage'; import ROUTES from 'docs/src/route'; import { Link } from '@mui/docs/Link'; +import ProductMenuItem from 'docs/src/components/action/ProductMenuItem'; import SvgMuiLogomark from 'docs/src/icons/SvgMuiLogomark'; import SvgBaseUiLogo from 'docs/src/icons/SvgBaseUiLogo'; import SvgPigmentLogo from 'docs/src/icons/SvgPigmentLogo'; +import SvgToolpadLogo from 'docs/src/icons/SvgToolpadLogo'; const Navigation = styled('nav')(({ theme }) => [ { @@ -72,86 +74,29 @@ const PRODUCT_IDS = [ 'product-material', 'product-base', 'product-pigment', - 'product-joy', - 'product-advanced', 'product-toolpad', + 'product-advanced', + 'product-joy', 'product-templates', 'product-design', ]; -type ProductSubMenuProps = { - icon: React.ReactElement; - name: React.ReactNode; - description: React.ReactNode; - chip?: React.ReactNode; - href: string; -} & Omit; - -const ProductSubMenu = React.forwardRef( - function ProductSubMenu({ icon, name, description, chip, href, ...props }, ref) { - return ( - ({ - p: 1, - display: 'flex', - alignItems: 'center', - gap: '12px', - border: '1px solid transparent', - borderRadius: '8px', - transition: '100ms ease-in background-color, border', - '&:hover, &:focus': { - backgroundColor: (theme.vars || theme).palette.grey[50], - borderColor: (theme.vars || theme).palette.divider, - outline: 0, - '@media (hover: none)': { - backgroundColor: 'initial', - outline: 'initial', - }, - }, - ...theme.applyDarkStyles({ - '&:hover, &:focus': { - backgroundColor: alpha(theme.palette.primaryDark[700], 0.4), - }, - }), - })} - {...props} - > - - {icon} - -
- - - {name} - - {chip} - - - {description} - -
-
- ); +const logoColor = (theme: Theme) => ({ + '& path': { + ...theme.applyDarkStyles({ + fill: (theme.vars || theme).palette.primary[400], + }), }, -); +}); export default function HeaderNavBar() { - const [subMenuOpen, setSubMenuOpen] = React.useState(null); + // const [subMenuOpen, setSubMenuOpen] = React.useState(null); + const [subMenuOpen, setSubMenuOpen] = React.useState(false); const [subMenuIndex, setSubMenuIndex] = React.useState(null); const navRef = React.useRef(null); const productsMenuRef = React.useRef(null); - const docsMenuRef = React.useRef(null); + // const docsMenuRef = React.useRef(null); + React.useEffect(() => { if (typeof subMenuIndex === 'number') { document.getElementById(PRODUCT_IDS[subMenuIndex])?.focus(); @@ -200,6 +145,17 @@ export default function HeaderNavBar() { } } + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event) => { + if (productsMenuRef.current && productsMenuRef.current.contains(event.target)) { + return; + } + setSubMenuOpen(false); + }; + const setSubMenuOpenDebounced = React.useMemo( () => debounce(setSubMenuOpen, 40), [setSubMenuOpen], @@ -210,11 +166,6 @@ export default function HeaderNavBar() { setSubMenuOpen(value); }; - const handleClickMenu = (value: typeof subMenuOpen) => () => { - setSubMenuOpenDebounced.clear(); - setSubMenuOpen(subMenuOpen ? null : value); - }; - React.useEffect(() => { return () => { setSubMenuOpenDebounced.clear(); @@ -233,22 +184,19 @@ export default function HeaderNavBar() { Products {({ TransitionProps }) => ( @@ -256,79 +204,55 @@ export default function HeaderNavBar() { variant="outlined" sx={(theme) => ({ mt: 1, - p: 1, minWidth: 498, - overflow: 'hidden', - borderColor: 'grey.200', - bgcolor: 'background.paper', - boxShadow: `0px 4px 16px ${alpha(theme.palette.common.black, 0.15)}`, - '& ul': { - display: 'grid', - gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', - gap: '8px', - margin: 0, - padding: 0, - listStyle: 'none', - }, - '& a': { textDecoration: 'none' }, + overflow: 'clip', + boxShadow: `0 4px 16px ${alpha(theme.palette.common.black, 0.15)}`, ...theme.applyDarkStyles({ bgcolor: 'primaryDark.900', - boxShadow: `0px 4px 16px ${alpha(theme.palette.common.black, 0.8)}`, }), })} > -
    -
  • - + + } + icon={} name="Material UI" description="Ready-to-use foundational React components." /> -
  • -
  • - } + icon={} name="Base UI" description="Unstyled components and hooks." /> -
  • -
  • - } + icon={} name="Pigment CSS" description="Zero-runtime CSS-in-JS." /> -
  • -
  • - } - name="Joy UI" - description="Beautiful foudational React components." - /> -
  • -
  • - } - name="MUI X" - description="Advanced components for complex use cases." - /> -
  • -
  • - } + icon={} name="Toolpad" + description="Low-code admin builder." chip={ } - description="Low-code admin builder." /> -
  • -
  • - } + name="MUI X" + description="Advanced components for complex use cases." + /> + } + name="Joy UI" + description="Beautiful foudational React components." + /> + } name="Templates" description="Fully built Material UI templates." /> -
  • -
  • - } name="Design Kits" description="Material UI in your favorite design tool." /> -
  • -
+ +
)} diff --git a/docs/src/modules/components/MuiProductSelector.tsx b/docs/src/modules/components/MuiProductSelector.tsx index 75f312404da3a1..d523be53a0b170 100644 --- a/docs/src/modules/components/MuiProductSelector.tsx +++ b/docs/src/modules/components/MuiProductSelector.tsx @@ -1,17 +1,16 @@ import * as React from 'react'; -import NextLink from 'next/link'; -import { styled, alpha, Theme } from '@mui/material/styles'; +import { styled, Theme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Chip from '@mui/material/Chip'; import Divider from '@mui/material/Divider'; import MenuList, { MenuListProps } from '@mui/material/MenuList'; -import MenuItem, { MenuItemProps } from '@mui/material/MenuItem'; import ROUTES from 'docs/src/route'; import PageContext from 'docs/src/modules/components/PageContext'; import SvgMuiLogomark from 'docs/src/icons/SvgMuiLogomark'; import SvgBaseUiLogo from 'docs/src/icons/SvgBaseUiLogo'; import SvgToolpadLogo from 'docs/src/icons/SvgToolpadLogo'; +import ProductMenuItem from 'docs/src/components/action/ProductMenuItem'; import BackupTableRoundedIcon from '@mui/icons-material/BackupTableRounded'; import CalendarMonthRoundedIcon from '@mui/icons-material/CalendarMonthRounded'; import AccountTreeRoundedIcon from '@mui/icons-material/AccountTreeRounded'; @@ -41,95 +40,6 @@ const NavLabel = styled(Typography)(({ theme }) => ({ color: (theme.vars || theme).palette.text.tertiary, })); -interface ProductItemProps extends MenuItemProps { - active?: boolean; - chip?: React.ReactNode; - description?: string; - href: string; - icon?: React.ReactNode; - name: string; -} - -function ProductItem({ - active, - chip, - description, - href, - icon, - name, - sx = [], - ...other -}: ProductItemProps) { - return ( - ({ - p: 1, - pl: '6px', - display: 'flex', - alignItems: 'start', - gap: '8px', - flexGrow: 1, - backgroundColor: active ? alpha(theme.palette.primary[50], 0.8) : undefined, - border: '1px solid', - borderColor: active ? 'primary.100' : 'transparent', - borderRadius: '8px', - transition: '100ms ease-in background-color, border', - textDecorationLine: 'none', - '&:hover': { - backgroundColor: active ? alpha(theme.palette.primary[50], 0.8) : 'grey.50', - borderColor: 'divider', - }, - '&.Mui-focusVisible': { - backgroundColor: active ? (theme.vars || theme).palette.primary[50] : 'transparent', - }, - ...theme.applyDarkStyles({ - backgroundColor: active ? alpha(theme.palette.primary[900], 0.2) : undefined, - borderColor: active ? alpha(theme.palette.primary[300], 0.2) : 'transparent', - '&:hover': { - backgroundColor: active - ? alpha(theme.palette.primary[900], 0.3) - : alpha(theme.palette.primaryDark[700], 0.5), - }, - '&.Mui-focusVisible': { - backgroundColor: active ? alpha(theme.palette.primary[900], 0.5) : 'transparent', - }, - }), - }), - // You cannot spread `sx` directly because `SxProps` (typeof sx) can be an array. - ...(Array.isArray(sx) ? sx : [sx]), - ]} - {...other} - > - - {icon} - -
- - - {name} - - {chip} - - - {description} - -
-
- ); -} - const coreProducts = [ { id: 'material-ui', @@ -214,13 +124,14 @@ const MuiProductSelector = React.forwardRef(function MuiProductSelector( }} > {coreProducts.map((product) => ( - ))} MUI X Components {advancedProducts.map((product) => ( - ))} - } description="A self-hosted, low-code internal tool builder." active={pageContext.productId === 'toolpad-core'} + docs chip={