From 1c342d43cc0d5d9e84d80af1a762a6d15bdfe4f6 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Thu, 18 Jun 2026 04:23:47 +0800 Subject: [PATCH 01/19] menu preview --- docs/data/material/components/menus/menus.md | 2 +- docs/data/material/pagesApi.js | 16 + docs/pages/experiments/menu-preview.tsx | 371 +++++++ .../menu-preview-checkbox-item-indicator.js | 16 + .../menu-preview-checkbox-item-indicator.json | 51 + .../api/menu-preview-checkbox-item.js | 16 + .../api/menu-preview-checkbox-item.json | 39 + .../api/menu-preview-group-label.js | 16 + .../api/menu-preview-group-label.json | 27 + .../material-ui/api/menu-preview-group.js | 16 + .../material-ui/api/menu-preview-group.json | 28 + .../material-ui/api/menu-preview-item.js | 16 + .../material-ui/api/menu-preview-item.json | 36 + .../material-ui/api/menu-preview-link-item.js | 16 + .../api/menu-preview-link-item.json | 35 + .../material-ui/api/menu-preview-popup.js | 16 + .../material-ui/api/menu-preview-popup.json | 141 +++ .../api/menu-preview-radio-group.js | 16 + .../api/menu-preview-radio-group.json | 39 + .../api/menu-preview-radio-item-indicator.js | 16 + .../menu-preview-radio-item-indicator.json | 51 + .../api/menu-preview-radio-item.js | 16 + .../api/menu-preview-radio-item.json | 37 + .../material-ui/api/menu-preview-separator.js | 16 + .../api/menu-preview-separator.json | 31 + .../api/menu-preview-submenu-popup.js | 16 + .../api/menu-preview-submenu-popup.json | 141 +++ .../api/menu-preview-submenu-root.js | 16 + .../api/menu-preview-submenu-root.json | 24 + .../api/menu-preview-submenu-trigger.js | 16 + .../api/menu-preview-submenu-trigger.json | 38 + .../material-ui/api/menu-preview-trigger.js | 16 + .../material-ui/api/menu-preview-trigger.json | 45 + docs/pages/material-ui/api/menu-preview.js | 16 + docs/pages/material-ui/api/menu-preview.json | 27 + .../menu-preview-checkbox-item-indicator.json | 35 + .../menu-preview-checkbox-item.json | 44 + .../menu-preview-group-label.json | 16 + .../menu-preview-group.json | 17 + .../menu-preview-item/menu-preview-item.json | 35 + .../menu-preview-link-item.json | 32 + .../menu-preview-popup.json | 55 + .../menu-preview-radio-group.json | 31 + .../menu-preview-radio-item-indicator.json | 35 + .../menu-preview-radio-item.json | 36 + .../menu-preview-separator.json | 17 + .../menu-preview-submenu-popup.json | 55 + .../menu-preview-submenu-root.json | 24 + .../menu-preview-submenu-trigger.json | 43 + .../menu-preview-trigger.json | 40 + .../api-docs/menu-preview/menu-preview.json | 25 + .../materialUi/projectSettings.ts | 40 +- packages-internal/core-docs/package.json | 2 +- packages/mui-material/package.json | 1 + .../src/MenuPreview/MenuPreview.spec.tsx | 204 ++++ .../src/MenuPreview/MenuPreview.test.tsx | 972 ++++++++++++++++++ .../src/MenuPreview/MenuPreview.tsx | 142 +++ .../mui-material/src/MenuPreview/apiDocs.d.ts | 49 + .../mui-material/src/MenuPreview/index.d.ts | 66 ++ .../mui-material/src/MenuPreview/index.js | 66 ++ .../src/MenuPreview/menuPreviewClasses.ts | 280 +++++ .../src/MenuPreview/menuPreviewItemShared.tsx | 244 +++++ .../MenuPreview/menuPreviewPopupShared.tsx | 368 +++++++ .../MenuPreview/menuPreviewSharedStyles.ts | 172 ++++ .../src/MenuPreview/menuPreviewUtils.ts | 76 ++ .../MenuPreviewCheckboxItem.tsx | 308 ++++++ .../src/MenuPreviewCheckboxItem/index.d.ts | 10 + .../src/MenuPreviewCheckboxItem/index.js | 6 + .../MenuPreviewCheckboxItemIndicator.tsx | 224 ++++ .../index.d.ts | 10 + .../MenuPreviewCheckboxItemIndicator/index.js | 6 + .../src/MenuPreviewGroup/MenuPreviewGroup.tsx | 174 ++++ .../src/MenuPreviewGroup/index.d.ts | 10 + .../src/MenuPreviewGroup/index.js | 6 + .../MenuPreviewGroupLabel.tsx | 176 ++++ .../src/MenuPreviewGroupLabel/index.d.ts | 10 + .../src/MenuPreviewGroupLabel/index.js | 6 + .../src/MenuPreviewItem/MenuPreviewItem.tsx | 241 +++++ .../src/MenuPreviewItem/index.d.ts | 10 + .../mui-material/src/MenuPreviewItem/index.js | 6 + .../MenuPreviewLinkItem.tsx | 233 +++++ .../src/MenuPreviewLinkItem/index.d.ts | 10 + .../src/MenuPreviewLinkItem/index.js | 6 + .../src/MenuPreviewPopup/MenuPreviewPopup.tsx | 389 +++++++ .../src/MenuPreviewPopup/index.d.ts | 10 + .../src/MenuPreviewPopup/index.js | 6 + .../MenuPreviewRadioGroup.tsx | 228 ++++ .../src/MenuPreviewRadioGroup/index.d.ts | 10 + .../src/MenuPreviewRadioGroup/index.js | 6 + .../MenuPreviewRadioItem.tsx | 256 +++++ .../src/MenuPreviewRadioItem/index.d.ts | 10 + .../src/MenuPreviewRadioItem/index.js | 6 + .../MenuPreviewRadioItemIndicator.tsx | 223 ++++ .../MenuPreviewRadioItemIndicator/index.d.ts | 10 + .../MenuPreviewRadioItemIndicator/index.js | 6 + .../MenuPreviewSeparator.tsx | 184 ++++ .../src/MenuPreviewSeparator/index.d.ts | 10 + .../src/MenuPreviewSeparator/index.js | 6 + .../MenuPreviewSubmenuPopup.tsx | 389 +++++++ .../src/MenuPreviewSubmenuPopup/index.d.ts | 10 + .../src/MenuPreviewSubmenuPopup/index.js | 6 + .../MenuPreviewSubmenuRoot.tsx | 132 +++ .../src/MenuPreviewSubmenuRoot/index.d.ts | 2 + .../src/MenuPreviewSubmenuRoot/index.js | 2 + .../MenuPreviewSubmenuTrigger.tsx | 274 +++++ .../src/MenuPreviewSubmenuTrigger/index.d.ts | 10 + .../src/MenuPreviewSubmenuTrigger/index.js | 6 + .../MenuPreviewTrigger/MenuPreviewTrigger.tsx | 270 +++++ .../src/MenuPreviewTrigger/index.d.ts | 10 + .../src/MenuPreviewTrigger/index.js | 6 + .../mui-material/src/styles/components.ts | 110 ++ packages/mui-material/src/styles/overrides.ts | 30 + packages/mui-material/src/styles/props.ts | 32 + pnpm-lock.yaml | 385 ++++--- pnpm-workspace.yaml | 2 +- 115 files changed, 9012 insertions(+), 133 deletions(-) create mode 100644 docs/pages/experiments/menu-preview.tsx create mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js create mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json create mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item.js create mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item.json create mode 100644 docs/pages/material-ui/api/menu-preview-group-label.js create mode 100644 docs/pages/material-ui/api/menu-preview-group-label.json create mode 100644 docs/pages/material-ui/api/menu-preview-group.js create mode 100644 docs/pages/material-ui/api/menu-preview-group.json create mode 100644 docs/pages/material-ui/api/menu-preview-item.js create mode 100644 docs/pages/material-ui/api/menu-preview-item.json create mode 100644 docs/pages/material-ui/api/menu-preview-link-item.js create mode 100644 docs/pages/material-ui/api/menu-preview-link-item.json create mode 100644 docs/pages/material-ui/api/menu-preview-popup.js create mode 100644 docs/pages/material-ui/api/menu-preview-popup.json create mode 100644 docs/pages/material-ui/api/menu-preview-radio-group.js create mode 100644 docs/pages/material-ui/api/menu-preview-radio-group.json create mode 100644 docs/pages/material-ui/api/menu-preview-radio-item-indicator.js create mode 100644 docs/pages/material-ui/api/menu-preview-radio-item-indicator.json create mode 100644 docs/pages/material-ui/api/menu-preview-radio-item.js create mode 100644 docs/pages/material-ui/api/menu-preview-radio-item.json create mode 100644 docs/pages/material-ui/api/menu-preview-separator.js create mode 100644 docs/pages/material-ui/api/menu-preview-separator.json create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-popup.js create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-popup.json create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-root.js create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-root.json create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-trigger.js create mode 100644 docs/pages/material-ui/api/menu-preview-submenu-trigger.json create mode 100644 docs/pages/material-ui/api/menu-preview-trigger.js create mode 100644 docs/pages/material-ui/api/menu-preview-trigger.json create mode 100644 docs/pages/material-ui/api/menu-preview.js create mode 100644 docs/pages/material-ui/api/menu-preview.json create mode 100644 docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json create mode 100644 docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json create mode 100644 docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json create mode 100644 docs/translations/api-docs/menu-preview-group/menu-preview-group.json create mode 100644 docs/translations/api-docs/menu-preview-item/menu-preview-item.json create mode 100644 docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json create mode 100644 docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json create mode 100644 docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json create mode 100644 docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json create mode 100644 docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json create mode 100644 docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json create mode 100644 docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json create mode 100644 docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json create mode 100644 docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json create mode 100644 docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json create mode 100644 docs/translations/api-docs/menu-preview/menu-preview.json create mode 100644 packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx create mode 100644 packages/mui-material/src/MenuPreview/MenuPreview.test.tsx create mode 100644 packages/mui-material/src/MenuPreview/MenuPreview.tsx create mode 100644 packages/mui-material/src/MenuPreview/apiDocs.d.ts create mode 100644 packages/mui-material/src/MenuPreview/index.d.ts create mode 100644 packages/mui-material/src/MenuPreview/index.js create mode 100644 packages/mui-material/src/MenuPreview/menuPreviewClasses.ts create mode 100644 packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx create mode 100644 packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx create mode 100644 packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts create mode 100644 packages/mui-material/src/MenuPreview/menuPreviewUtils.ts create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItem/index.js create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js create mode 100644 packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx create mode 100644 packages/mui-material/src/MenuPreviewGroup/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewGroup/index.js create mode 100644 packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx create mode 100644 packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewGroupLabel/index.js create mode 100644 packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx create mode 100644 packages/mui-material/src/MenuPreviewItem/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewItem/index.js create mode 100644 packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx create mode 100644 packages/mui-material/src/MenuPreviewLinkItem/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewLinkItem/index.js create mode 100644 packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx create mode 100644 packages/mui-material/src/MenuPreviewPopup/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewPopup/index.js create mode 100644 packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx create mode 100644 packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewRadioGroup/index.js create mode 100644 packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx create mode 100644 packages/mui-material/src/MenuPreviewRadioItem/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewRadioItem/index.js create mode 100644 packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx create mode 100644 packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js create mode 100644 packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx create mode 100644 packages/mui-material/src/MenuPreviewSeparator/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewSeparator/index.js create mode 100644 packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx create mode 100644 packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewSubmenuPopup/index.js create mode 100644 packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx create mode 100644 packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewSubmenuRoot/index.js create mode 100644 packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx create mode 100644 packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js create mode 100644 packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx create mode 100644 packages/mui-material/src/MenuPreviewTrigger/index.d.ts create mode 100644 packages/mui-material/src/MenuPreviewTrigger/index.js diff --git a/docs/data/material/components/menus/menus.md b/docs/data/material/components/menus/menus.md index 9ae8536ade0a43..a6a90edaae7be2 100644 --- a/docs/data/material/components/menus/menus.md +++ b/docs/data/material/components/menus/menus.md @@ -1,7 +1,7 @@ --- productId: material-ui title: React Menu component -components: Menu, MenuItem, MenuList, ClickAwayListener, Popover, Popper +components: Menu, MenuItem, MenuList, MenuPreview, MenuPreviewTrigger, MenuPreviewPopup, MenuPreviewSubmenuPopup, MenuPreviewItem, MenuPreviewLinkItem, MenuPreviewCheckboxItem, MenuPreviewCheckboxItemIndicator, MenuPreviewRadioGroup, MenuPreviewRadioItem, MenuPreviewRadioItemIndicator, MenuPreviewGroup, MenuPreviewGroupLabel, MenuPreviewSeparator, MenuPreviewSubmenuRoot, MenuPreviewSubmenuTrigger, ClickAwayListener, Popover, Popper githubLabel: 'scope: menu' materialDesign: https://m2.material.io/components/menus waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ diff --git a/docs/data/material/pagesApi.js b/docs/data/material/pagesApi.js index ee76034af9e54a..5ca508d6d9e9eb 100644 --- a/docs/data/material/pagesApi.js +++ b/docs/data/material/pagesApi.js @@ -73,6 +73,22 @@ export default [ { pathname: '/material-ui/api/menu' }, { pathname: '/material-ui/api/menu-item' }, { pathname: '/material-ui/api/menu-list' }, + { pathname: '/material-ui/api/menu-preview' }, + { pathname: '/material-ui/api/menu-preview-checkbox-item' }, + { pathname: '/material-ui/api/menu-preview-checkbox-item-indicator' }, + { pathname: '/material-ui/api/menu-preview-group' }, + { pathname: '/material-ui/api/menu-preview-group-label' }, + { pathname: '/material-ui/api/menu-preview-item' }, + { pathname: '/material-ui/api/menu-preview-link-item' }, + { pathname: '/material-ui/api/menu-preview-popup' }, + { pathname: '/material-ui/api/menu-preview-radio-group' }, + { pathname: '/material-ui/api/menu-preview-radio-item' }, + { pathname: '/material-ui/api/menu-preview-radio-item-indicator' }, + { pathname: '/material-ui/api/menu-preview-separator' }, + { pathname: '/material-ui/api/menu-preview-submenu-popup' }, + { pathname: '/material-ui/api/menu-preview-submenu-root' }, + { pathname: '/material-ui/api/menu-preview-submenu-trigger' }, + { pathname: '/material-ui/api/menu-preview-trigger' }, { pathname: '/material-ui/api/mobile-stepper' }, { pathname: '/material-ui/api/modal' }, { pathname: '/material-ui/api/native-select' }, diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx new file mode 100644 index 00000000000000..44756c616a4809 --- /dev/null +++ b/docs/pages/experiments/menu-preview.tsx @@ -0,0 +1,371 @@ +import * as React from 'react'; +import Container from '@mui/material/Container'; +import CssBaseline from '@mui/material/CssBaseline'; +import Stack from '@mui/material/Stack'; +import Tooltip, { type TooltipProps } from '@mui/material/Tooltip'; +import Typography from '@mui/material/Typography'; +import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; +import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import Menu, { + CheckboxItem, + CheckboxItemIndicator, + Group, + GroupLabel, + Item, + LinkItem, + Popup, + RadioGroup, + RadioItem, + RadioItemIndicator, + Separator, + SubmenuPopup, + SubmenuRoot, + SubmenuTrigger, + Trigger, +} from '@mui/material/MenuPreview'; +import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; + +interface MenuSettings { + modal: boolean; + disabled: boolean; + openOnHover: boolean; +} + +const theme = createTheme({}); + +const defaultSettings: MenuSettings = { + modal: true, + disabled: false, + openOnHover: false, +}; + +const horizontalTooltipProps = { + placement: 'right', + slotProps: { + popper: { + popperOptions: { + modifiers: [ + { + name: 'flip', + options: { + fallbackPlacements: ['left', 'right'], + }, + }, + ], + }, + }, + }, +} satisfies Partial; + +interface MenuTooltipChildProps { + onClickCapture?: React.MouseEventHandler; +} + +function MenuTooltip(props: { + title: string; + children: React.ReactElement; +}) { + const { title, children } = props; + const [open, setOpen] = React.useState(false); + + const handleOpen = React.useCallback(() => { + setOpen(true); + }, []); + + const handleClose = React.useCallback(() => { + setOpen(false); + }, []); + + const child = React.cloneElement(children, { + onClickCapture: (event: React.MouseEvent) => { + setOpen(false); + children.props.onClickCapture?.(event); + }, + }); + + return ( + + {child} + + ); +} + +function DisabledTooltip(props: { title: string; children: React.ReactElement }) { + const { title, children } = props; + + return ( + + {/* Disabled menu items need a wrapper for pointer events. This means aria-describedby + is attached to the wrapper, not the disabled menuitem itself. */} + {children} + + ); +} + +function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { + const handleItemClick = React.useCallback((event: React.MouseEvent) => { + // eslint-disable-next-line no-console + console.log(`${event.currentTarget.textContent} clicked`); + }, []); + + return ( + + } + openOnHover={settings.openOnHover} + > + File + + + New document + Open… + Template gallery + Recent documents + Docs help center + Make a copy + + + Rename document + + + Offline editing unavailable + + + + + + View options + + + + + Document display + + + + 100% + + + + Fit + + + + Page width + + + + Custom zoom unavailable + + + + + + + + Show + + + Ruler + + + + Document outline + + + + Line numbers + + + + Page breaks unavailable + + + + + + + + More tools + + + + Word count + Dictionary + Accessibility settings + + + + + + + + Download + + + + Microsoft Word (.docx) + PDF document (.pdf) + Plain text (.txt) + + + + + + Add-ons unavailable + + + + Marketplace + + + + + ); +} + +function MenuPreviewWithTooltipsDemo() { + return ( + + }> + Tools + + + + New document + + + Open recent + + + Make a copy + + + Import from Drive + + + Share with people + + + + + + + View options + + + + + + Show + + + + Comments + + + + + + Page breaks + + + + + + + + Zoom + + + + + Fit + + + + + + Custom + + + + + + + + + ); +} + +export default function MenuPreviewExperiment() { + const [settings, setSettings] = React.useState(defaultSettings); + + const handleCheckboxChange = (setting: keyof MenuSettings) => { + return (event: React.ChangeEvent) => { + setSettings((currentSettings) => ({ + ...currentSettings, + [setting]: event.target.checked, + })); + }; + }; + + return ( + + + + + + + Menu Preview + +
+ Demo controls + + + +
+
+

Fully-featured menu with submenus, links, radio groups, and checkbox items.

+ +
+
+

Material UI Tooltip integrated with every menu item.

+ +
+ Base UI Menu API +
+
+
+ ); +} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js new file mode 100644 index 00000000000000..dc73f4f5df5157 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json'; +import jsonPageContent from './menu-preview-checkbox-item-indicator.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json new file mode 100644 index 00000000000000..49c213f68f5fef --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json @@ -0,0 +1,51 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "keepMounted": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewCheckboxItemIndicator", + "imports": [ + "import MenuPreviewCheckboxItemIndicator from '@mui/material/MenuPreviewCheckboxItemIndicator';" + ], + "slots": [ + { "name": "root", "description": "", "class": "MuiMenuPreviewCheckboxItemIndicator-root" } + ], + "classes": [ + { + "key": "checked", + "className": "Mui-checked", + "description": "State class applied to the root element if `checked={true}`.", + "isGlobal": true + }, + { + "key": "disabled", + "className": "Mui-disabled", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": true + }, + { + "key": "highlighted", + "className": "MuiMenuPreviewCheckboxItemIndicator-highlighted", + "description": "State class applied to the root element if highlighted.", + "isGlobal": false + } + ], + "muiName": "MuiMenuPreviewCheckboxItemIndicator", + "filename": "/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.js b/docs/pages/material-ui/api/menu-preview-checkbox-item.js new file mode 100644 index 00000000000000..273dae896d9732 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json'; +import jsonPageContent from './menu-preview-checkbox-item.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.json b/docs/pages/material-ui/api/menu-preview-checkbox-item.json new file mode 100644 index 00000000000000..e1ee7b88166402 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item.json @@ -0,0 +1,39 @@ +{ + "props": { + "checked": { "type": { "name": "bool" } }, + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, + "component": { "type": { "name": "elementType" } }, + "defaultChecked": { "type": { "name": "bool" }, "default": "false" }, + "dense": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "disableGutters": { "type": { "name": "bool" }, "default": "false" }, + "divider": { "type": { "name": "bool" }, "default": "false" }, + "label": { "type": { "name": "string" } }, + "nativeButton": { "type": { "name": "bool" } }, + "onChange": { "type": { "name": "func" } }, + "selected": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewCheckboxItem", + "imports": ["import MenuPreviewCheckboxItem from '@mui/material/MenuPreviewCheckboxItem';"], + "slots": [{ "name": "root", "description": "", "class": null }], + "classes": [], + "muiName": "MuiMenuPreviewCheckboxItem", + "filename": "/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-group-label.js b/docs/pages/material-ui/api/menu-preview-group-label.js new file mode 100644 index 00000000000000..b27a7aea1cbebf --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-group-label.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json'; +import jsonPageContent from './menu-preview-group-label.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-group-label.json b/docs/pages/material-ui/api/menu-preview-group-label.json new file mode 100644 index 00000000000000..15ac51dd170114 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-group-label.json @@ -0,0 +1,27 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewGroupLabel", + "imports": ["import MenuPreviewGroupLabel from '@mui/material/MenuPreviewGroupLabel';"], + "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewGroupLabel-root" }], + "classes": [], + "muiName": "MuiMenuPreviewGroupLabel", + "filename": "/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-group.js b/docs/pages/material-ui/api/menu-preview-group.js new file mode 100644 index 00000000000000..dd950c267f3918 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-group.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-group/menu-preview-group.json'; +import jsonPageContent from './menu-preview-group.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-group.json b/docs/pages/material-ui/api/menu-preview-group.json new file mode 100644 index 00000000000000..0bbc5b7c39d31d --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-group.json @@ -0,0 +1,28 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewGroup", + "imports": ["import MenuPreviewGroup from '@mui/material/MenuPreviewGroup';"], + "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewGroup-root" }], + "classes": [], + "muiName": "MuiMenuPreviewGroup", + "filename": "/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-item.js b/docs/pages/material-ui/api/menu-preview-item.js new file mode 100644 index 00000000000000..874e8da3b3785d --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-item.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-item/menu-preview-item.json'; +import jsonPageContent from './menu-preview-item.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-item.json b/docs/pages/material-ui/api/menu-preview-item.json new file mode 100644 index 00000000000000..80b360babe0bcc --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-item.json @@ -0,0 +1,36 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeOnClick": { "type": { "name": "bool" }, "default": "true" }, + "component": { "type": { "name": "elementType" } }, + "dense": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "disableGutters": { "type": { "name": "bool" }, "default": "false" }, + "divider": { "type": { "name": "bool" }, "default": "false" }, + "label": { "type": { "name": "string" } }, + "nativeButton": { "type": { "name": "bool" } }, + "selected": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewItem", + "imports": ["import MenuPreviewItem from '@mui/material/MenuPreviewItem';"], + "slots": [{ "name": "root", "description": "", "class": null }], + "classes": [], + "muiName": "MuiMenuPreviewItem", + "filename": "/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-link-item.js b/docs/pages/material-ui/api/menu-preview-link-item.js new file mode 100644 index 00000000000000..a4d03702e1b16c --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-link-item.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json'; +import jsonPageContent from './menu-preview-link-item.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-link-item.json b/docs/pages/material-ui/api/menu-preview-link-item.json new file mode 100644 index 00000000000000..c1a50c2e21e20b --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-link-item.json @@ -0,0 +1,35 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, + "component": { "type": { "name": "elementType" } }, + "dense": { "type": { "name": "bool" }, "default": "false" }, + "disableGutters": { "type": { "name": "bool" }, "default": "false" }, + "divider": { "type": { "name": "bool" }, "default": "false" }, + "href": { "type": { "name": "string" } }, + "label": { "type": { "name": "string" } }, + "selected": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewLinkItem", + "imports": ["import MenuPreviewLinkItem from '@mui/material/MenuPreviewLinkItem';"], + "slots": [{ "name": "root", "description": "", "class": null }], + "classes": [], + "muiName": "MuiMenuPreviewLinkItem", + "filename": "/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-popup.js b/docs/pages/material-ui/api/menu-preview-popup.js new file mode 100644 index 00000000000000..1a9fcae0e09e49 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-popup.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json'; +import jsonPageContent from './menu-preview-popup.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-popup.json b/docs/pages/material-ui/api/menu-preview-popup.json new file mode 100644 index 00000000000000..74825d504428e0 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-popup.json @@ -0,0 +1,141 @@ +{ + "props": { + "align": { + "type": { + "name": "enum", + "description": "'center'
| 'end'
| 'start'" + }, + "default": "'start'" + }, + "alignOffset": { + "type": { "name": "union", "description": "func
| number" }, + "default": "0" + }, + "anchor": { + "type": { + "name": "union", + "description": "HTML element
| object
| func" + } + }, + "arrowPadding": { "type": { "name": "number" }, "default": "5" }, + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "collisionAvoidance": { + "type": { + "name": "union", + "description": "{ align?: 'flip'
| 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'flip'
| 'none' }
| { align?: 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'none'
| 'shift' }" + } + }, + "collisionBoundary": { + "type": { + "name": "union", + "description": "'clipping-ancestors'
| HTML element
| Array<HTML element>
| { height: number, width: number, x: number, y: number }" + }, + "default": "'clipping-ancestors'" + }, + "collisionPadding": { + "type": { + "name": "union", + "description": "number
| { bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "5" + }, + "container": { + "type": { + "name": "union", + "description": "HTML element
| object
| func" + } + }, + "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, + "finalFocus": { + "type": { + "name": "union", + "description": "func
| { current?: HTML element }
| bool" + } + }, + "keepMounted": { "type": { "name": "bool" }, "default": "false" }, + "positionMethod": { + "type": { "name": "enum", "description": "'absolute'
| 'fixed'" }, + "default": "'absolute'" + }, + "side": { + "type": { + "name": "enum", + "description": "'bottom'
| 'inline-end'
| 'inline-start'
| 'left'
| 'right'
| 'top'" + }, + "default": "'bottom'" + }, + "sideOffset": { + "type": { "name": "union", "description": "func
| number" }, + "default": "0" + }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ list?: func
| object, paper?: func
| object, popup?: func
| object, portal?: func
| object, positioner?: func
| object }" + } + }, + "slots": { + "type": { + "name": "shape", + "description": "{ list?: elementType, paper?: elementType, popup?: elementType, portal?: elementType, positioner?: elementType }" + } + }, + "sticky": { "type": { "name": "bool" }, "default": "false" }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewPopup", + "imports": ["import MenuPreviewPopup from '@mui/material/MenuPreviewPopup';"], + "slots": [ + { + "name": "portal", + "description": "The component used for the portal.", + "default": "BaseMenu.Portal", + "class": null + }, + { + "name": "positioner", + "description": "The component used for the positioner.", + "default": "BaseMenu.Positioner", + "class": null + }, + { + "name": "popup", + "description": "The component rendered by the Base UI popup.", + "default": "'div'", + "class": null + }, + { + "name": "paper", + "description": "The component used for the Material surface.", + "default": "Paper", + "class": "MuiMenuPreviewPopup-paper" + }, + { + "name": "list", + "description": "The component used for the presentational list wrapper.", + "default": "List", + "class": "MuiMenuPreviewPopup-list" + } + ], + "classes": [ + { + "key": "root", + "className": "MuiMenuPreviewPopup-root", + "description": "Styles applied to the root element.", + "isGlobal": false + } + ], + "muiName": "MuiMenuPreviewPopup", + "filename": "/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.js b/docs/pages/material-ui/api/menu-preview-radio-group.js new file mode 100644 index 00000000000000..3b9f531b0034b3 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-group.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json'; +import jsonPageContent from './menu-preview-radio-group.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.json b/docs/pages/material-ui/api/menu-preview-radio-group.json new file mode 100644 index 00000000000000..c972d5644f3576 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-group.json @@ -0,0 +1,39 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "defaultValue": { "type": { "name": "any" } }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "onChange": { "type": { "name": "func" } }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + }, + "value": { "type": { "name": "any" } } + }, + "name": "MenuPreviewRadioGroup", + "imports": ["import MenuPreviewRadioGroup from '@mui/material/MenuPreviewRadioGroup';"], + "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewRadioGroup-root" }], + "classes": [ + { + "key": "disabled", + "className": "Mui-disabled", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": true + } + ], + "muiName": "MuiMenuPreviewRadioGroup", + "filename": "/packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js new file mode 100644 index 00000000000000..d44d1d01f14efc --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json'; +import jsonPageContent from './menu-preview-radio-item-indicator.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json new file mode 100644 index 00000000000000..cc0278e7e2ba7d --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json @@ -0,0 +1,51 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "keepMounted": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewRadioItemIndicator", + "imports": [ + "import MenuPreviewRadioItemIndicator from '@mui/material/MenuPreviewRadioItemIndicator';" + ], + "slots": [ + { "name": "root", "description": "", "class": "MuiMenuPreviewRadioItemIndicator-root" } + ], + "classes": [ + { + "key": "checked", + "className": "Mui-checked", + "description": "State class applied to the root element if `checked={true}`.", + "isGlobal": true + }, + { + "key": "disabled", + "className": "Mui-disabled", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": true + }, + { + "key": "highlighted", + "className": "MuiMenuPreviewRadioItemIndicator-highlighted", + "description": "State class applied to the root element if highlighted.", + "isGlobal": false + } + ], + "muiName": "MuiMenuPreviewRadioItemIndicator", + "filename": "/packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.js b/docs/pages/material-ui/api/menu-preview-radio-item.js new file mode 100644 index 00000000000000..fc06a00097e28e --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-item.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json'; +import jsonPageContent from './menu-preview-radio-item.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.json b/docs/pages/material-ui/api/menu-preview-radio-item.json new file mode 100644 index 00000000000000..bb264de79c2630 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-radio-item.json @@ -0,0 +1,37 @@ +{ + "props": { + "value": { "type": { "name": "any" }, "required": true }, + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, + "component": { "type": { "name": "elementType" } }, + "dense": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "disableGutters": { "type": { "name": "bool" }, "default": "false" }, + "divider": { "type": { "name": "bool" }, "default": "false" }, + "label": { "type": { "name": "string" } }, + "nativeButton": { "type": { "name": "bool" } }, + "selected": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewRadioItem", + "imports": ["import MenuPreviewRadioItem from '@mui/material/MenuPreviewRadioItem';"], + "slots": [{ "name": "root", "description": "", "class": null }], + "classes": [], + "muiName": "MuiMenuPreviewRadioItem", + "filename": "/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-separator.js b/docs/pages/material-ui/api/menu-preview-separator.js new file mode 100644 index 00000000000000..24e4dee90d1c5d --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-separator.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json'; +import jsonPageContent from './menu-preview-separator.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-separator.json b/docs/pages/material-ui/api/menu-preview-separator.json new file mode 100644 index 00000000000000..f601b79a6a6eba --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-separator.json @@ -0,0 +1,31 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "component": { "type": { "name": "elementType" } }, + "orientation": { + "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, + "default": "'horizontal'" + }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewSeparator", + "imports": ["import MenuPreviewSeparator from '@mui/material/MenuPreviewSeparator';"], + "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewSeparator-root" }], + "classes": [], + "muiName": "MuiMenuPreviewSeparator", + "filename": "/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.js b/docs/pages/material-ui/api/menu-preview-submenu-popup.js new file mode 100644 index 00000000000000..1feb03c4f823ef --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-popup.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json'; +import jsonPageContent from './menu-preview-submenu-popup.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.json b/docs/pages/material-ui/api/menu-preview-submenu-popup.json new file mode 100644 index 00000000000000..edb42f9acb55e1 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-popup.json @@ -0,0 +1,141 @@ +{ + "props": { + "align": { + "type": { + "name": "enum", + "description": "'center'
| 'end'
| 'start'" + }, + "default": "'start'" + }, + "alignOffset": { + "type": { "name": "union", "description": "func
| number" }, + "default": "0" + }, + "anchor": { + "type": { + "name": "union", + "description": "HTML element
| object
| func" + } + }, + "arrowPadding": { "type": { "name": "number" }, "default": "5" }, + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "collisionAvoidance": { + "type": { + "name": "union", + "description": "{ align?: 'flip'
| 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'flip'
| 'none' }
| { align?: 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'none'
| 'shift' }" + } + }, + "collisionBoundary": { + "type": { + "name": "union", + "description": "'clipping-ancestors'
| HTML element
| Array<HTML element>
| { height: number, width: number, x: number, y: number }" + }, + "default": "'clipping-ancestors'" + }, + "collisionPadding": { + "type": { + "name": "union", + "description": "number
| { bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "5" + }, + "container": { + "type": { + "name": "union", + "description": "HTML element
| object
| func" + } + }, + "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, + "finalFocus": { + "type": { + "name": "union", + "description": "func
| { current?: HTML element }
| bool" + } + }, + "keepMounted": { "type": { "name": "bool" }, "default": "false" }, + "positionMethod": { + "type": { "name": "enum", "description": "'absolute'
| 'fixed'" }, + "default": "'absolute'" + }, + "side": { + "type": { + "name": "enum", + "description": "'bottom'
| 'inline-end'
| 'inline-start'
| 'left'
| 'right'
| 'top'" + }, + "default": "'inline-end'" + }, + "sideOffset": { + "type": { "name": "union", "description": "func
| number" }, + "default": "0" + }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ list?: func
| object, paper?: func
| object, popup?: func
| object, portal?: func
| object, positioner?: func
| object }" + } + }, + "slots": { + "type": { + "name": "shape", + "description": "{ list?: elementType, paper?: elementType, popup?: elementType, portal?: elementType, positioner?: elementType }" + } + }, + "sticky": { "type": { "name": "bool" }, "default": "false" }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewSubmenuPopup", + "imports": ["import MenuPreviewSubmenuPopup from '@mui/material/MenuPreviewSubmenuPopup';"], + "slots": [ + { + "name": "portal", + "description": "The component used for the portal.", + "default": "BaseMenu.Portal", + "class": null + }, + { + "name": "positioner", + "description": "The component used for the positioner.", + "default": "BaseMenu.Positioner", + "class": null + }, + { + "name": "popup", + "description": "The component rendered by the Base UI popup.", + "default": "'div'", + "class": null + }, + { + "name": "paper", + "description": "The component used for the Material surface.", + "default": "Paper", + "class": "MuiMenuPreviewSubmenuPopup-paper" + }, + { + "name": "list", + "description": "The component used for the presentational list wrapper.", + "default": "List", + "class": "MuiMenuPreviewSubmenuPopup-list" + } + ], + "classes": [ + { + "key": "root", + "className": "MuiMenuPreviewSubmenuPopup-root", + "description": "Styles applied to the root element.", + "isGlobal": false + } + ], + "muiName": "MuiMenuPreviewSubmenuPopup", + "filename": "/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-root.js b/docs/pages/material-ui/api/menu-preview-submenu-root.js new file mode 100644 index 00000000000000..5f12e3a1db11de --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-root.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json'; +import jsonPageContent from './menu-preview-submenu-root.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-root.json b/docs/pages/material-ui/api/menu-preview-submenu-root.json new file mode 100644 index 00000000000000..3ec88efda62789 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-root.json @@ -0,0 +1,24 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "closeParentOnEsc": { "type": { "name": "bool" }, "default": "false" }, + "defaultOpen": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "highlightItemOnHover": { "type": { "name": "bool" }, "default": "true" }, + "loopFocus": { "type": { "name": "bool" }, "default": "true" }, + "onOpenChange": { "type": { "name": "func" } }, + "onOpenChangeComplete": { "type": { "name": "func" } }, + "open": { "type": { "name": "bool" } }, + "orientation": { + "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, + "default": "'vertical'" + } + }, + "name": "MenuPreviewSubmenuRoot", + "imports": ["import MenuPreviewSubmenuRoot from '@mui/material/MenuPreviewSubmenuRoot';"], + "classes": [], + "muiName": "MuiMenuPreviewSubmenuRoot", + "filename": "/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.js b/docs/pages/material-ui/api/menu-preview-submenu-trigger.js new file mode 100644 index 00000000000000..4d4d3bcc553a57 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-trigger.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json'; +import jsonPageContent from './menu-preview-submenu-trigger.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.json b/docs/pages/material-ui/api/menu-preview-submenu-trigger.json new file mode 100644 index 00000000000000..387fe73eb537b9 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-submenu-trigger.json @@ -0,0 +1,38 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeDelay": { "type": { "name": "number" }, "default": "0" }, + "component": { "type": { "name": "elementType" } }, + "delay": { "type": { "name": "number" }, "default": "100" }, + "dense": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "disableGutters": { "type": { "name": "bool" }, "default": "false" }, + "divider": { "type": { "name": "bool" }, "default": "false" }, + "label": { "type": { "name": "string" } }, + "nativeButton": { "type": { "name": "bool" } }, + "openOnHover": { "type": { "name": "bool" } }, + "selected": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewSubmenuTrigger", + "imports": ["import MenuPreviewSubmenuTrigger from '@mui/material/MenuPreviewSubmenuTrigger';"], + "slots": [{ "name": "root", "description": "", "class": null }], + "classes": [], + "muiName": "MuiMenuPreviewSubmenuTrigger", + "filename": "/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview-trigger.js b/docs/pages/material-ui/api/menu-preview-trigger.js new file mode 100644 index 00000000000000..623e31aedb5914 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-trigger.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json'; +import jsonPageContent from './menu-preview-trigger.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview-trigger.json b/docs/pages/material-ui/api/menu-preview-trigger.json new file mode 100644 index 00000000000000..96aca0e828262b --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview-trigger.json @@ -0,0 +1,45 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "className": { "type": { "name": "string" } }, + "closeDelay": { "type": { "name": "number" }, "default": "0" }, + "component": { "type": { "name": "elementType" } }, + "delay": { "type": { "name": "number" }, "default": "100" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "nativeButton": { "type": { "name": "bool" } }, + "openOnHover": { "type": { "name": "bool" } }, + "slotProps": { + "type": { "name": "shape", "description": "{ root?: func
| object }" } + }, + "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, + "style": { "type": { "name": "object" } }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "MenuPreviewTrigger", + "imports": ["import MenuPreviewTrigger from '@mui/material/MenuPreviewTrigger';"], + "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewTrigger-root" }], + "classes": [ + { + "key": "disabled", + "className": "Mui-disabled", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": true + }, + { + "key": "open", + "className": "Mui-open", + "description": "State class applied to the root element if the menu is open.", + "isGlobal": true + } + ], + "muiName": "MuiMenuPreviewTrigger", + "filename": "/packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/pages/material-ui/api/menu-preview.js b/docs/pages/material-ui/api/menu-preview.js new file mode 100644 index 00000000000000..3cb1b61dd994ee --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview.js @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { ApiPage } from '@mui/internal-core-docs/ApiPage'; +import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; +import translation from 'docs/translations/api-docs/menu-preview/menu-preview.json'; +import jsonPageContent from './menu-preview.json'; + +export default function Page(props) { + const { descriptions } = props; + return ; +} + +export async function getStaticProps() { + const descriptions = mapApiPageTranslation(translation); + + return { props: { descriptions } }; +} diff --git a/docs/pages/material-ui/api/menu-preview.json b/docs/pages/material-ui/api/menu-preview.json new file mode 100644 index 00000000000000..084edc4242f118 --- /dev/null +++ b/docs/pages/material-ui/api/menu-preview.json @@ -0,0 +1,27 @@ +{ + "props": { + "children": { "type": { "name": "node" } }, + "closeParentOnEsc": { "type": { "name": "bool" }, "default": "false" }, + "defaultOpen": { "type": { "name": "bool" }, "default": "false" }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "highlightItemOnHover": { "type": { "name": "bool" }, "default": "true" }, + "loopFocus": { "type": { "name": "bool" }, "default": "true" }, + "modal": { "type": { "name": "bool" }, "default": "true" }, + "onOpenChange": { "type": { "name": "func" } }, + "onOpenChangeComplete": { "type": { "name": "func" } }, + "open": { "type": { "name": "bool" } }, + "orientation": { + "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, + "default": "'vertical'" + } + }, + "name": "MenuPreview", + "imports": ["import MenuPreview from '@mui/material/MenuPreview';"], + "classes": [], + "spread": true, + "themeDefaultProps": null, + "muiName": "MuiMenuPreview", + "filename": "/packages/mui-material/src/MenuPreview/MenuPreview.tsx", + "inheritance": null, + "demos": "" +} diff --git a/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json b/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json new file mode 100644 index 00000000000000..2849161fdc3460 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json @@ -0,0 +1,35 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "keepMounted": { + "description": "Whether to keep the HTML element in the DOM when the checkbox item is not checked." + }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { + "checked": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "checked={true}" + }, + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + }, + "highlighted": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "highlighted" + } + }, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json b/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json new file mode 100644 index 00000000000000..fc921efed518e8 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json @@ -0,0 +1,44 @@ +{ + "componentDescription": "", + "propDescriptions": { + "checked": { + "description": "Whether the checkbox item is currently ticked.
To render an uncontrolled checkbox item, use the defaultChecked prop instead." + }, + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, + "component": { "description": "The component used for the root node." }, + "defaultChecked": { + "description": "Whether the checkbox item is initially ticked.
To render a controlled checkbox item, use the checked prop instead." + }, + "dense": { + "description": "If true, compact vertical padding designed for keyboard and mouse input is used." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "disableGutters": { + "description": "If true, the left and right padding is removed." + }, + "divider": { + "description": "If true, a 1px light border is added to the bottom of the menu item." + }, + "label": { + "description": "Overrides the text label to use when the item is matched during keyboard text navigation." + }, + "nativeButton": { + "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." + }, + "onChange": { + "description": "Event handler called when the checkbox item is ticked or unticked." + }, + "selected": { "description": "If true, the component is selected." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json b/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json new file mode 100644 index 00000000000000..8c92b392b4a3d8 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json @@ -0,0 +1,16 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-group/menu-preview-group.json b/docs/translations/api-docs/menu-preview-group/menu-preview-group.json new file mode 100644 index 00000000000000..8743d2e26e1f58 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-group/menu-preview-group.json @@ -0,0 +1,17 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-item/menu-preview-item.json b/docs/translations/api-docs/menu-preview-item/menu-preview-item.json new file mode 100644 index 00000000000000..552a6e719d95ae --- /dev/null +++ b/docs/translations/api-docs/menu-preview-item/menu-preview-item.json @@ -0,0 +1,35 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, + "component": { "description": "The component used for the root node." }, + "dense": { + "description": "If true, compact vertical padding designed for keyboard and mouse input is used." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "disableGutters": { + "description": "If true, the left and right padding is removed." + }, + "divider": { + "description": "If true, a 1px light border is added to the bottom of the menu item." + }, + "label": { + "description": "Overrides the text label to use when the item is matched during keyboard text navigation." + }, + "nativeButton": { + "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." + }, + "selected": { "description": "If true, the component is selected." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json b/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json new file mode 100644 index 00000000000000..79bd5fbfce859a --- /dev/null +++ b/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json @@ -0,0 +1,32 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, + "component": { "description": "The component used for the root node." }, + "dense": { + "description": "If true, compact vertical padding designed for keyboard and mouse input is used." + }, + "disableGutters": { + "description": "If true, the left and right padding is removed." + }, + "divider": { + "description": "If true, a 1px light border is added to the bottom of the menu item." + }, + "href": { "description": "The URL that the link item points to." }, + "label": { + "description": "Overrides the text label to use when the item is matched during keyboard text navigation." + }, + "selected": { "description": "If true, the component is selected." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json b/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json new file mode 100644 index 00000000000000..3c92b01c458026 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json @@ -0,0 +1,55 @@ +{ + "componentDescription": "", + "propDescriptions": { + "align": { "description": "How to align the popup relative to the specified side." }, + "alignOffset": { "description": "Additional offset along the alignment axis in pixels." }, + "anchor": { + "description": "An element to position the popup against.
By default, the popup is positioned against the trigger." + }, + "arrowPadding": { + "description": "Minimum distance to maintain between the arrow and the edges of the popup." + }, + "children": { "description": "The menu items." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the Base UI popup element." }, + "collisionAvoidance": { + "description": "Determines how to handle collisions when positioning the popup." + }, + "collisionBoundary": { + "description": "An element or a rectangle that delimits the area that the popup is confined to." + }, + "collisionPadding": { + "description": "Additional space to maintain from the edge of the collision boundary." + }, + "container": { "description": "The container element to portal the popup into." }, + "disableAnchorTracking": { + "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." + }, + "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, + "keepMounted": { + "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." + }, + "positionMethod": { + "description": "Determines which CSS position property to use." + }, + "side": { "description": "Which side of the anchor element to align the popup against." }, + "sideOffset": { "description": "Distance between the anchor and the popup in pixels." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "sticky": { + "description": "Whether to maintain the popup in the viewport after the anchor element was scrolled out of view." + }, + "style": { "description": "Styles applied to the Base UI popup element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, + "slotDescriptions": { + "list": "The component used for the presentational list wrapper.", + "paper": "The component used for the Material surface.", + "popup": "The component rendered by the Base UI popup.", + "portal": "The component used for the portal.", + "positioner": "The component used for the positioner." + } +} diff --git a/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json b/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json new file mode 100644 index 00000000000000..8d6137b9a1f39a --- /dev/null +++ b/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json @@ -0,0 +1,31 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "defaultValue": { + "description": "The uncontrolled value of the radio item that should be initially selected." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "onChange": { "description": "Function called when the selected value changes." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + }, + "value": { + "description": "The controlled value of the radio item that should be currently selected." + } + }, + "classDescriptions": { + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + } + }, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json b/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json new file mode 100644 index 00000000000000..faa8d5c3697420 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json @@ -0,0 +1,35 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "keepMounted": { + "description": "Whether to keep the HTML element in the DOM when the radio item is inactive." + }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { + "checked": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "checked={true}" + }, + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + }, + "highlighted": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "highlighted" + } + }, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json b/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json new file mode 100644 index 00000000000000..03866a7e3ba528 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json @@ -0,0 +1,36 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, + "component": { "description": "The component used for the root node." }, + "dense": { + "description": "If true, compact vertical padding designed for keyboard and mouse input is used." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "disableGutters": { + "description": "If true, the left and right padding is removed." + }, + "divider": { + "description": "If true, a 1px light border is added to the bottom of the menu item." + }, + "label": { + "description": "Overrides the text label to use when the item is matched during keyboard text navigation." + }, + "nativeButton": { + "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." + }, + "selected": { "description": "If true, the component is selected." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + }, + "value": { "description": "Value of the radio item." } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json b/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json new file mode 100644 index 00000000000000..39ea1cd2fc4e6b --- /dev/null +++ b/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json @@ -0,0 +1,17 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "component": { "description": "The component used for the root node." }, + "orientation": { "description": "The orientation of the separator." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json b/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json new file mode 100644 index 00000000000000..24661184a10744 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json @@ -0,0 +1,55 @@ +{ + "componentDescription": "", + "propDescriptions": { + "align": { "description": "How to align the popup relative to the specified side." }, + "alignOffset": { "description": "Additional offset along the alignment axis in pixels." }, + "anchor": { + "description": "An element to position the popup against.
By default, the popup is positioned against the submenu trigger." + }, + "arrowPadding": { + "description": "Minimum distance to maintain between the arrow and the edges of the popup." + }, + "children": { "description": "The submenu items." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the Base UI popup element." }, + "collisionAvoidance": { + "description": "Determines how to handle collisions when positioning the popup." + }, + "collisionBoundary": { + "description": "An element or a rectangle that delimits the area that the popup is confined to." + }, + "collisionPadding": { + "description": "Additional space to maintain from the edge of the collision boundary." + }, + "container": { "description": "The container element to portal the popup into." }, + "disableAnchorTracking": { + "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." + }, + "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, + "keepMounted": { + "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." + }, + "positionMethod": { + "description": "Determines which CSS position property to use." + }, + "side": { "description": "Which side of the anchor element to align the popup against." }, + "sideOffset": { "description": "Distance between the anchor and the popup in pixels." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "sticky": { + "description": "Whether to maintain the popup in the viewport after the anchor element was scrolled out of view." + }, + "style": { "description": "Styles applied to the Base UI popup element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, + "slotDescriptions": { + "list": "The component used for the presentational list wrapper.", + "paper": "The component used for the Material surface.", + "popup": "The component rendered by the Base UI popup.", + "portal": "The component used for the portal.", + "positioner": "The component used for the positioner." + } +} diff --git a/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json b/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json new file mode 100644 index 00000000000000..ff6614738e49c7 --- /dev/null +++ b/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json @@ -0,0 +1,24 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the submenu." }, + "closeParentOnEsc": { + "description": "When in a submenu, determines whether pressing the Escape key closes the entire menu." + }, + "defaultOpen": { + "description": "Whether the submenu is initially open.
To render a controlled submenu, use the open prop instead." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "highlightItemOnHover": { + "description": "Whether moving the pointer over items should highlight them." + }, + "loopFocus": { "description": "Whether to loop keyboard focus back to the first item." }, + "onOpenChange": { "description": "Event handler called when the submenu is opened or closed." }, + "onOpenChangeComplete": { + "description": "Event handler called after any animations complete when the submenu is opened or closed." + }, + "open": { "description": "Whether the submenu is currently open." }, + "orientation": { "description": "The visual orientation of the submenu." } + }, + "classDescriptions": {} +} diff --git a/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json b/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json new file mode 100644 index 00000000000000..e577f74db637bf --- /dev/null +++ b/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json @@ -0,0 +1,43 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeDelay": { + "description": "How long to wait before closing the submenu that was opened on hover, in milliseconds.
Requires the openOnHover prop." + }, + "component": { "description": "The component used for the root node." }, + "delay": { + "description": "How long to wait before the submenu may be opened on hover, in milliseconds.
Requires the openOnHover prop." + }, + "dense": { + "description": "If true, compact vertical padding designed for keyboard and mouse input is used." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "disableGutters": { + "description": "If true, the left and right padding is removed." + }, + "divider": { + "description": "If true, a 1px light border is added to the bottom of the menu item." + }, + "label": { + "description": "Overrides the text label to use when the item is matched during keyboard text navigation." + }, + "nativeButton": { + "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." + }, + "openOnHover": { + "description": "Whether the submenu should also open when the trigger is hovered." + }, + "selected": { "description": "If true, the component is selected." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": {}, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json b/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json new file mode 100644 index 00000000000000..1b289124ab482c --- /dev/null +++ b/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json @@ -0,0 +1,40 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { "description": "Override or extend the styles applied to the component." }, + "className": { "description": "CSS class applied to the element." }, + "closeDelay": { + "description": "How long to wait before closing the menu that was opened on hover, in milliseconds.
Requires the openOnHover prop." + }, + "component": { "description": "The component used for the root node." }, + "delay": { + "description": "How long to wait before the menu may be opened on hover, in milliseconds.
Requires the openOnHover prop." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "nativeButton": { + "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." + }, + "openOnHover": { + "description": "Whether the menu should also open when the trigger is hovered." + }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "style": { "description": "Styles applied to the root element." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + }, + "open": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "the menu is open" + } + }, + "slotDescriptions": { "root": "" } +} diff --git a/docs/translations/api-docs/menu-preview/menu-preview.json b/docs/translations/api-docs/menu-preview/menu-preview.json new file mode 100644 index 00000000000000..31e7124fe6bf7f --- /dev/null +++ b/docs/translations/api-docs/menu-preview/menu-preview.json @@ -0,0 +1,25 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the menu." }, + "closeParentOnEsc": { + "description": "When in a submenu, determines whether pressing the Escape key closes the entire menu." + }, + "defaultOpen": { + "description": "Whether the menu is initially open.
To render a controlled menu, use the open prop instead." + }, + "disabled": { "description": "Whether the component should ignore user interaction." }, + "highlightItemOnHover": { + "description": "Whether moving the pointer over items should highlight them." + }, + "loopFocus": { "description": "Whether to loop keyboard focus back to the first item." }, + "modal": { "description": "Determines if the menu enters a modal state when open." }, + "onOpenChange": { "description": "Event handler called when the menu is opened or closed." }, + "onOpenChangeComplete": { + "description": "Event handler called after any animations complete when the menu is opened or closed." + }, + "open": { "description": "Whether the menu is currently open." }, + "orientation": { "description": "The visual orientation of the menu." } + }, + "classDescriptions": {} +} diff --git a/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts b/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts index a52477fe5e64cd..e1a85bdc147ba5 100644 --- a/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts +++ b/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts @@ -1,6 +1,6 @@ import path from 'path'; import { LANGUAGES } from '@mui/internal-core-docs/constants'; -import { ProjectSettings, findApiPages } from '@mui/internal-api-docs-builder'; +import { ProjectSettings, findApiPages, toGitHubPath } from '@mui/internal-api-docs-builder'; import generateUtilityClass, { isGlobalState } from '@mui/utils/generateUtilityClass'; import { getMaterialUiComponentInfo } from './getMaterialUiComponentInfo'; @@ -13,6 +13,42 @@ const generateClassName = (componentName: string, slot: string, globalStatePrefi return generateUtilityClass(componentName, slot, globalStatePrefix); }; +const getComponentImports = (name: string, filename: string) => { + const githubPath = toGitHubPath(filename); + const directory = githubPath.match(/\/packages\/mui-material\/src\/([^/]+)\//)?.[1]; + + if (directory?.startsWith('MenuPreview')) { + return [`import ${name} from '@mui/material/${directory}';`]; + } + + const rootImportPath = githubPath.replace( + /\/packages\/mui(?:-(.+?))?\/src\/.*/, + (match, pkg) => `@mui/${pkg}`, + ); + + const subdirectoryImportPath = githubPath.replace( + /\/packages\/mui(?:-(.+?))?\/src\/([^\\/]+)\/.*/, + (match, pkg, subdirectory) => `@mui/${pkg}/${subdirectory}`, + ); + + let namedImportName = name; + const defaultImportName = name; + + if (githubPath.includes('Unstable_')) { + namedImportName = `Unstable_${name} as ${name}`; + } + + const useNamedImports = rootImportPath === '@mui/base'; + + const subpathImport = useNamedImports + ? `import { ${namedImportName} } from '${subdirectoryImportPath}';` + : `import ${defaultImportName} from '${subdirectoryImportPath}';`; + + const rootImport = `import { ${namedImportName} } from '${rootImportPath}';`; + + return [subpathImport, rootImport]; +}; + export const projectSettings: ProjectSettings = { output: { apiManifestPath: path.join(process.cwd(), 'docs/data/material/pagesApi.js'), @@ -23,6 +59,7 @@ export const projectSettings: ProjectSettings = { rootPath: path.join(process.cwd(), 'packages/mui-material'), entryPointPath: [ 'src/index.d.ts', + 'src/MenuPreview/apiDocs.d.ts', 'src/PigmentStack/PigmentStack.tsx', 'src/PigmentContainer/PigmentContainer.tsx', 'src/PigmentGrid/PigmentGrid.tsx', @@ -41,6 +78,7 @@ export const projectSettings: ProjectSettings = { return filename.match(/(ThemeProvider|CssVarsProvider|DefaultPropsProvider)/) !== null; }, translationPagesDirectory: 'docs/translations/api-docs', + getComponentImports, generateClassName, isGlobalClassName: isGlobalState, // #host-reference diff --git a/packages-internal/core-docs/package.json b/packages-internal/core-docs/package.json index 5efc02b42742bf..009ba5bff1d84d 100644 --- a/packages-internal/core-docs/package.json +++ b/packages-internal/core-docs/package.json @@ -53,7 +53,7 @@ "next": "15.5.18" }, "peerDependencies": { - "@base-ui/react": "^1", + "@base-ui/react": "^1.5.0", "@docsearch/react": "catalog:docs", "@emotion/cache": "catalog:docs", "@emotion/react": "catalog:docs", diff --git a/packages/mui-material/package.json b/packages/mui-material/package.json index cb64fd02e9c477..0c7f5375c5ef03 100644 --- a/packages/mui-material/package.json +++ b/packages/mui-material/package.json @@ -34,6 +34,7 @@ }, "dependencies": { "@babel/runtime": "^7.29.2", + "@base-ui/react": "^1.5.0", "@mui/core-downloads-tracker": "workspace:^", "@mui/system": "workspace:^", "@mui/types": "workspace:^", diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx new file mode 100644 index 00000000000000..70b8079302e562 --- /dev/null +++ b/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx @@ -0,0 +1,204 @@ +import * as React from 'react'; +import { expectType } from '@mui/types'; +import Menu, { + CheckboxItem, + CheckboxItemIndicator, + Group, + GroupLabel, + Item, + LinkItem, + Popup, + RadioGroup, + RadioItem, + RadioItemIndicator, + Separator, + SubmenuPopup, + SubmenuRoot, + SubmenuTrigger, + Trigger, +} from '@mui/material/MenuPreview'; +import { createTheme } from '@mui/material/styles'; +// @ts-expect-error MenuPreview is intentionally not exported from the root barrel for this POC. +import { MenuPreview as RootBarrelMenuPreview } from '@mui/material'; + +function MenuPreviewComposition() { + return ( + { + expectType(open); + eventDetails.cancel(); + eventDetails.preventUnmountOnClose(); + }} + > + + Options + + + + Group + + Item + + Profile + { + expectType(event); + expectType(checked); + eventDetails.cancel(); + }} + > + + Checkbox + + { + expectType(event); + expectType(value); + eventDetails.cancel(); + }} + > + + + One + + + + { + expectType(open); + eventDetails.cancel(); + }} + > + + More + + + Nested + + + + + + ); +} + +createTheme({ + components: { + MuiMenuPreview: { + defaultProps: { + modal: false, + }, + }, + MuiMenuPreviewSubmenuRoot: { + defaultProps: { + defaultOpen: false, + }, + }, + MuiMenuPreviewItem: { + defaultProps: { + dense: true, + }, + styleOverrides: { + root: {}, + highlighted: {}, + }, + variants: [ + { + props: { selected: true }, + style: {}, + }, + ], + }, + MuiMenuPreviewPopup: { + defaultProps: { + align: 'start', + }, + styleOverrides: { + root: {}, + paper: {}, + list: {}, + }, + variants: [ + { + props: { align: 'start' }, + style: {}, + }, + ], + }, + MuiMenuPreviewRadioItem: { + variants: [ + { + props: { value: 'small' }, + style: {}, + }, + ], + }, + MuiMenuPreviewLinkItem: { + variants: [ + { + props: { href: '/profile' }, + style: {}, + }, + ], + }, + }, +}); + +; + +; + +} +> + Options +; + + + Options +; + +, + }, + }} +/>; diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx new file mode 100644 index 00000000000000..7b7155e31b7ec4 --- /dev/null +++ b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx @@ -0,0 +1,972 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { spy } from 'sinon'; +import { createRenderer, isJsdom, screen, waitFor } from '@mui/internal-test-utils'; +import Tooltip from '@mui/material/Tooltip'; +import MenuPreview, { + MenuPreviewCheckboxItem, + MenuPreviewCheckboxItemIndicator, + MenuPreviewGroup, + MenuPreviewGroupLabel, + MenuPreviewItem, + MenuPreviewLinkItem, + MenuPreviewPopup, + MenuPreviewRadioGroup, + MenuPreviewRadioItem, + MenuPreviewRadioItemIndicator, + MenuPreviewSeparator, + MenuPreviewSubmenuPopup, + MenuPreviewSubmenuRoot, + MenuPreviewSubmenuTrigger, + MenuPreviewTrigger, + menuPreviewCheckboxItemClasses, + menuPreviewItemClasses, + menuPreviewPopupClasses, + menuPreviewTriggerClasses, +} from '@mui/material/MenuPreview'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; + +describe('', () => { + const { render } = createRenderer(); + type User = ReturnType['user']; + + async function expectTooltipOnHover(user: User, element: Element, title: string) { + await user.hover(element); + + expect(await screen.findByRole('tooltip')).to.have.text(title); + + await user.unhover(element); + + await waitFor(() => { + expect(screen.queryByRole('tooltip')).to.equal(null); + }); + } + + it('opens from the trigger and keeps Menu.Popup as the semantic menu root', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + const trigger = screen.getByRole('button', { name: 'Options' }); + expect(trigger).to.have.class(menuPreviewTriggerClasses.root); + + await user.click(trigger); + + const menu = await screen.findByRole('menu'); + expect(menu).to.have.class(menuPreviewPopupClasses.root); + expect(screen.getByTestId('paper')).to.have.class(menuPreviewPopupClasses.paper); + + const list = screen.getByTestId('paper').querySelector(`.${menuPreviewPopupClasses.list}`); + expect(list).not.to.equal(null); + expect(list!.tagName).to.equal('DIV'); + + expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class( + menuPreviewItemClasses.root, + ); + }); + + it('does not render the trigger as a link when href is passed by a JS caller', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + const trigger = screen.getByRole('button', { name: 'Options' }); + expect(trigger.tagName).to.equal('BUTTON'); + expect(trigger).not.to.have.attribute('href'); + + await user.click(trigger); + + expect(await screen.findByRole('menu')).not.to.equal(null); + }); + + it('supports component props, slotProps, classes, styleOverrides, and variants', async () => { + const theme = createTheme({ + components: { + MuiMenuPreviewTrigger: { + defaultProps: { + variant: 'outlined', + }, + }, + MuiMenuPreviewPopup: { + styleOverrides: { + paper: { + minWidth: 128, + }, + }, + variants: [ + { + props: { align: 'start' }, + style: { + '--MenuPreviewPopup-variant': '"applied"', + }, + }, + ], + }, + MuiMenuPreviewItem: { + variants: [ + { + props: { selected: true }, + style: { + fontWeight: 700, + }, + }, + { + props: { disabled: true }, + style: { + '--MenuPreviewItem-disabledVariant': '"applied"', + }, + }, + ], + }, + MuiMenuPreviewCheckboxItem: { + variants: [ + { + props: { checked: true }, + style: { + '--MenuPreviewCheckboxItem-checkedVariant': '"applied"', + }, + }, + ], + }, + MuiMenuPreviewRadioItem: { + variants: [ + { + props: { value: 'small' }, + style: { + '--MenuPreviewRadioItem-valueVariant': '"applied"', + }, + }, + ], + }, + MuiMenuPreviewLinkItem: { + variants: [ + { + props: { href: '/profile' }, + style: { + '--MenuPreviewLinkItem-hrefVariant': '"applied"', + }, + }, + ], + }, + }, + }); + + const { user } = render( + + + Options + + + Profile + + Disabled profile + Checked profile + + Small + + Link profile + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(screen.getByRole('button', { name: 'Options' })).to.have.class('custom-trigger'); + expect(await screen.findByTestId('list')).to.have.class('custom-list'); + expect( + window + .getComputedStyle(screen.getByRole('menu')) + .getPropertyValue('--MenuPreviewPopup-variant'), + ).to.equal('"applied"'); + expect(await screen.findByRole('menuitem', { name: 'Profile' })).to.have.class('custom-item'); + expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class( + menuPreviewItemClasses.selected, + ); + expect(screen.getByRole('menuitem', { name: 'Disabled profile' })).to.have.class( + menuPreviewItemClasses.disabled, + ); + expect( + window + .getComputedStyle(screen.getByRole('menuitem', { name: 'Disabled profile' })) + .getPropertyValue('--MenuPreviewItem-disabledVariant'), + ).to.equal('"applied"'); + expect(screen.getByRole('menuitemcheckbox', { name: 'Checked profile' })).to.have.class( + menuPreviewCheckboxItemClasses.checked, + ); + expect( + window + .getComputedStyle(screen.getByRole('menuitemcheckbox', { name: 'Checked profile' })) + .getPropertyValue('--MenuPreviewCheckboxItem-checkedVariant'), + ).to.equal('"applied"'); + expect( + window + .getComputedStyle(screen.getByRole('menuitemradio', { name: 'Small' })) + .getPropertyValue('--MenuPreviewRadioItem-valueVariant'), + ).to.equal('"applied"'); + expect( + window + .getComputedStyle(screen.getByRole('menuitem', { name: 'Link profile' })) + .getPropertyValue('--MenuPreviewLinkItem-hrefVariant'), + ).to.equal('"applied"'); + }); + + it('composes popup class names', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + const menu = await screen.findByRole('menu'); + expect(menu).to.have.class('popup-open'); + expect(menu).to.have.class('popup-side-bottom'); + expect(menu).to.have.class(menuPreviewPopupClasses.root); + }); + + it('does not pass ownerState to host popup slots', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(await screen.findByTestId('popup')).not.to.have.attribute('ownerState'); + }); + + it('derives native button behavior from host root slots', async () => { + const error = vi.spyOn(console, 'error').mockImplementation(() => {}); + + try { + const { user } = render( + + Options + + + Native item + + + Native checkbox + + + + Native radio + + + + + Native submenu trigger + + + Nested + + + + , + ); + + const trigger = screen.getByRole('button', { name: 'Options' }); + expect(trigger.tagName).to.equal('DIV'); + + trigger.focus(); + await user.keyboard('[Enter]'); + + expect(await screen.findByRole('menuitem', { name: 'Native item' })).to.have.property( + 'tagName', + 'BUTTON', + ); + expect(screen.getByRole('menuitemcheckbox', { name: 'Native checkbox' })).to.have.property( + 'tagName', + 'BUTTON', + ); + expect(screen.getByRole('menuitemradio', { name: 'Native radio' })).to.have.property( + 'tagName', + 'BUTTON', + ); + expect(screen.getByRole('menuitem', { name: 'Native submenu trigger' })).to.have.property( + 'tagName', + 'BUTTON', + ); + expect( + error.mock.calls.some(([message]) => String(message).includes('nativeButton')), + ).to.equal(false); + } finally { + error.mockRestore(); + } + }); + + it('allows nativeButton to override root slot inference for custom slots', async () => { + const error = vi.spyOn(console, 'error').mockImplementation(() => {}); + const CustomDivRoot = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef<'div'> & { ownerState?: unknown } + >(function CustomDivRoot({ ownerState: _ownerState, ...props }, ref) { + return
; + }); + const CustomButtonRoot = React.forwardRef< + HTMLButtonElement, + React.ComponentPropsWithoutRef<'button'> & { ownerState?: unknown } + >(function CustomButtonRoot({ ownerState: _ownerState, ...props }, ref) { + return + + Options + + Profile + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + await user.click(await screen.findByRole('menuitem', { name: 'Profile' })); + + await waitFor(() => { + expect(document.activeElement).to.equal(finalFocusRef.current); + }); + }); + + it('returns focus to the trigger on Escape and closes on outside press', async () => { + const { user } = render( + + + + Options + + Profile + + + , + ); + + const trigger = screen.getByRole('button', { name: 'Options' }); + await user.click(trigger); + await screen.findByRole('menu'); + + await user.keyboard('[Escape]'); + await waitFor(() => { + expect(screen.queryByRole('menu')).to.equal(null); + }); + expect(document.activeElement).to.equal(trigger); + + await user.click(trigger); + await screen.findByRole('menu'); + await user.click(screen.getByRole('button', { name: 'Outside' })); + + await waitFor(() => { + expect(screen.queryByRole('menu')).to.equal(null); + }); + }); + + it('supports touch trigger interactions', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.pointer({ + keys: '[TouchA]', + target: screen.getByRole('button', { name: 'Options' }), + }); + + expect(await screen.findByRole('menu')).not.to.equal(null); + }); + + it('supports modal backdrop behavior', async () => { + const { user } = render( + + + Modal menu + + Profile + + + + Non-modal menu + + Settings + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Modal menu' })); + await screen.findByRole('menu'); + expect(screen.getByTestId('modal-positioner').previousElementSibling).to.have.attribute( + 'role', + 'presentation', + ); + + await user.keyboard('[Escape]'); + await waitFor(() => { + expect(screen.queryByRole('menu')).to.equal(null); + }); + + await user.click(screen.getByRole('button', { name: 'Non-modal menu' })); + await screen.findByRole('menu'); + expect(screen.getByTestId('non-modal-positioner').previousElementSibling).to.equal(null); + }); + + it('opens in an RTL tree', async () => { + const { user } = render( +
+ + Options + + Profile + + +
, + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(await screen.findByRole('menu')).not.to.equal(null); + }); + + it.skipIf(isJsdom())('applies Base UI positioning attributes in the browser', async () => { + const { user } = render( +
+ + Options + + Profile + + +
, + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + const positioner = await screen.findByTestId('positioner'); + expect(positioner).to.have.attribute('data-side', 'bottom'); + expect(positioner).to.have.attribute('data-align', 'start'); + expect(positioner.style.transform).not.to.equal(''); + }); + + it('supports checkbox and radio item state', async () => { + const handleCheckboxChange = spy((event: Event, checked: boolean, eventDetails: any) => { + expect(event).to.be.instanceOf(Event); + expect(checked).to.equal(true); + expect(eventDetails.reason).to.equal('item-press'); + }); + const handleRadioChange = spy((event: Event, value: string, eventDetails: any) => { + expect(event).to.be.instanceOf(Event); + expect(value).to.equal('large'); + expect(eventDetails.reason).to.equal('item-press'); + }); + + const { user } = render( + + Options + + + + Show hidden files + + + + + Small + + + + Large + + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + const checkbox = await screen.findByRole('menuitemcheckbox', { name: /show hidden files/i }); + expect(checkbox).to.have.attribute('aria-checked', 'false'); + + await user.click(checkbox); + + expect(checkbox).to.have.attribute('aria-checked', 'true'); + expect(checkbox).to.have.class(menuPreviewCheckboxItemClasses.checked); + expect(handleCheckboxChange.callCount).to.equal(1); + + expect(screen.getByRole('menuitemradio', { name: /small/i })).to.have.attribute( + 'aria-checked', + 'true', + ); + expect(screen.getByRole('menuitemradio', { name: /large/i })).to.have.attribute( + 'aria-checked', + 'false', + ); + + await user.click(screen.getByRole('menuitemradio', { name: /large/i })); + + expect(screen.getByRole('menuitemradio', { name: /large/i })).to.have.attribute( + 'aria-checked', + 'true', + ); + expect(handleRadioChange.callCount).to.equal(1); + }); + + it('keeps mounted unchecked indicator marks hidden', () => { + render( + + Options + + + + Show hidden files + + + + + Small + + + + Large + + + + , + ); + + const checkboxIndicator = screen.getByTestId('checkbox-indicator'); + const checkboxIcon = checkboxIndicator.querySelector('[data-mui-menu-preview-indicator-icon]'); + const checkboxMark = checkboxIndicator.querySelector('[data-mui-menu-preview-indicator-mark]'); + expect(checkboxIndicator).to.have.attribute('data-unchecked', ''); + expect(window.getComputedStyle(checkboxIndicator).visibility).to.equal('visible'); + expect(checkboxIcon).not.to.equal(null); + expect(window.getComputedStyle(checkboxIcon!).visibility).to.equal('visible'); + expect(checkboxMark).not.to.equal(null); + expect(window.getComputedStyle(checkboxMark!).visibility).to.equal('hidden'); + + const checkedRadioIndicator = screen.getByTestId('checked-radio-indicator'); + const checkedRadioIcon = checkedRadioIndicator.querySelector( + '[data-mui-menu-preview-indicator-icon]', + ); + const checkedRadioMark = checkedRadioIndicator.querySelector( + '[data-mui-menu-preview-indicator-mark]', + ); + expect(checkedRadioIndicator).to.have.attribute('data-checked', ''); + expect(checkedRadioIcon).not.to.equal(null); + expect(window.getComputedStyle(checkedRadioIcon!).visibility).to.equal('visible'); + expect(checkedRadioMark).not.to.equal(null); + expect(window.getComputedStyle(checkedRadioMark!).visibility).to.equal('visible'); + + const uncheckedRadioIndicator = screen.getByTestId('unchecked-radio-indicator'); + const uncheckedRadioIcon = uncheckedRadioIndicator.querySelector( + '[data-mui-menu-preview-indicator-icon]', + ); + const uncheckedRadioMark = uncheckedRadioIndicator.querySelector( + '[data-mui-menu-preview-indicator-mark]', + ); + expect(uncheckedRadioIndicator).to.have.attribute('data-unchecked', ''); + expect(window.getComputedStyle(uncheckedRadioIndicator).visibility).to.equal('visible'); + expect(uncheckedRadioIcon).not.to.equal(null); + expect(window.getComputedStyle(uncheckedRadioIcon!).visibility).to.equal('visible'); + expect(uncheckedRadioMark).not.to.equal(null); + expect(window.getComputedStyle(uncheckedRadioMark!).visibility).to.equal('hidden'); + }); + + it('supports groups, labels, separators, link items, and submenus', async () => { + const { user } = render( + + Options + + + Account + Profile + + + + More + + Archive + + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(await screen.findByText('Account')).not.to.equal(null); + expect(screen.getByRole('separator')).not.to.equal(null); + expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.attribute('href', '/profile'); + expect(screen.getByRole('menuitem', { name: 'More' })).to.not.equal(null); + expect(screen.getByRole('menuitem', { name: 'Archive' })).to.not.equal(null); + }); + + it('supports Material UI Tooltip on enabled item flavors', async () => { + const { user } = render( + + Options + + + New document + + + + + Comments + + + + + + + Fit + + + + + , + ); + + await expectTooltipOnHover( + user, + screen.getByRole('menuitem', { name: 'New document' }), + 'Create a blank document', + ); + await expectTooltipOnHover( + user, + screen.getByRole('menuitemcheckbox', { name: 'Comments' }), + 'Toggle comments', + ); + await expectTooltipOnHover( + user, + screen.getByRole('menuitemradio', { name: 'Fit' }), + 'Fit to viewport', + ); + }); + + it('can close a controlled Material UI Tooltip when a submenu trigger opens', async () => { + interface TooltipChildProps { + onClickCapture?: React.MouseEventHandler; + } + + function ClickClosingTooltip(props: { + title: string; + children: React.ReactElement; + }) { + const { title, children } = props; + const [open, setOpen] = React.useState(false); + + const child = React.cloneElement(children, { + onClickCapture: (event: React.MouseEvent) => { + setOpen(false); + children.props.onClickCapture?.(event); + }, + }); + + return ( + setOpen(true)} + onClose={() => setOpen(false)} + > + {child} + + ); + } + + const { user } = render( + + Options + + + + + View options + + + + Comments + + + + , + ); + + const submenuTrigger = screen.getByRole('menuitem', { name: 'View options' }); + + await user.hover(submenuTrigger); + expect(await screen.findByRole('tooltip')).to.have.text('Open view settings'); + + await user.click(submenuTrigger); + + expect(await screen.findByRole('menuitem', { name: 'Comments' })).not.to.equal(null); + await waitFor(() => { + expect(screen.queryByRole('tooltip')).to.equal(null); + }); + }); + + it('supports Material UI Tooltip on disabled items through a non-disabled wrapper', async () => { + const { user } = render( + + Options + + + + Import from Drive + + + + , + ); + + expect(screen.getByRole('menuitem', { name: 'Import from Drive' })).to.have.attribute( + 'aria-disabled', + 'true', + ); + await expectTooltipOnHover( + user, + screen.getByTestId('disabled-item-tooltip-target'), + 'Unavailable while offline', + ); + }); + + it('does not warn when a submenu trigger is disabled', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + try { + const { user } = render( + + Options + + + Add-ons unavailable + + Marketplace + + + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + const submenuTrigger = await screen.findByRole('menuitem', { + name: 'Add-ons unavailable', + }); + expect(submenuTrigger).to.have.attribute('aria-disabled', 'true'); + expect( + warn.mock.calls.some(([message]) => + String(message).includes('A disabled element was detected on '), + ), + ).to.equal(false); + } finally { + warn.mockRestore(); + } + }); +}); diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.tsx new file mode 100644 index 00000000000000..6ea6ec15b4d7fd --- /dev/null +++ b/packages/mui-material/src/MenuPreview/MenuPreview.tsx @@ -0,0 +1,142 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { useDefaultProps } from '../DefaultPropsProvider'; + +export interface MenuPreviewProps { + /** + * The content of the menu. + */ + children?: React.ReactNode; + /** + * Whether the menu is initially open. + * + * To render a controlled menu, use the `open` prop instead. + * @default false + */ + defaultOpen?: boolean | undefined; + /** + * Whether the menu is currently open. + */ + open?: boolean | undefined; + /** + * Event handler called when the menu is opened or closed. + */ + onOpenChange?: BaseMenu.Root.Props['onOpenChange']; + /** + * Event handler called after any animations complete when the menu is opened or closed. + */ + onOpenChangeComplete?: BaseMenu.Root.Props['onOpenChangeComplete']; + /** + * Determines if the menu enters a modal state when open. + * @default true + */ + modal?: boolean | undefined; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Whether to loop keyboard focus back to the first item. + * @default true + */ + loopFocus?: boolean | undefined; + /** + * Whether moving the pointer over items should highlight them. + * @default true + */ + highlightItemOnHover?: boolean | undefined; + /** + * The visual orientation of the menu. + * @default 'vertical' + */ + orientation?: 'horizontal' | 'vertical' | undefined; + /** + * When in a submenu, determines whether pressing the Escape key closes the entire menu. + * @default false + */ + closeParentOnEsc?: boolean | undefined; +} + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreview API](https://mui.com/material-ui/api/menu-preview/) + */ +function MenuPreview(props: MenuPreviewProps): React.JSX.Element { + const themedProps = useDefaultProps({ + props, + name: 'MuiMenuPreview', + }); + + return ; +} + +MenuPreview.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the menu. + */ + children: PropTypes.node, + /** + * When in a submenu, determines whether pressing the Escape key closes the entire menu. + * @default false + */ + closeParentOnEsc: PropTypes.bool, + /** + * Whether the menu is initially open. + * + * To render a controlled menu, use the `open` prop instead. + * @default false + */ + defaultOpen: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * Whether moving the pointer over items should highlight them. + * @default true + */ + highlightItemOnHover: PropTypes.bool, + /** + * Whether to loop keyboard focus back to the first item. + * @default true + */ + loopFocus: PropTypes.bool, + /** + * Determines if the menu enters a modal state when open. + * @default true + */ + modal: PropTypes.bool, + /** + * Event handler called when the menu is opened or closed. + */ + onOpenChange: PropTypes.func, + /** + * Event handler called after any animations complete when the menu is opened or closed. + */ + onOpenChangeComplete: PropTypes.func, + /** + * Whether the menu is currently open. + */ + open: PropTypes.bool, + /** + * The visual orientation of the menu. + * @default 'vertical' + */ + orientation: PropTypes.oneOf(['horizontal', 'vertical']), +} as any; + +export default MenuPreview; diff --git a/packages/mui-material/src/MenuPreview/apiDocs.d.ts b/packages/mui-material/src/MenuPreview/apiDocs.d.ts new file mode 100644 index 00000000000000..6c939d59992308 --- /dev/null +++ b/packages/mui-material/src/MenuPreview/apiDocs.d.ts @@ -0,0 +1,49 @@ +export { default as MenuPreview } from './MenuPreview'; +export * from './MenuPreview'; + +export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; +export * from '../MenuPreviewTrigger'; + +export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; +export * from '../MenuPreviewPopup'; + +export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; +export * from '../MenuPreviewSubmenuPopup'; + +export { default as MenuPreviewItem } from '../MenuPreviewItem'; +export * from '../MenuPreviewItem'; + +export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; +export * from '../MenuPreviewLinkItem'; + +export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; +export * from '../MenuPreviewCheckboxItem'; + +export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; +export * from '../MenuPreviewCheckboxItemIndicator'; + +export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; +export * from '../MenuPreviewRadioGroup'; + +export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; +export * from '../MenuPreviewRadioItem'; + +export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; +export * from '../MenuPreviewRadioItemIndicator'; + +export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; +export * from '../MenuPreviewGroup'; + +export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; +export * from '../MenuPreviewGroupLabel'; + +export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; +export * from '../MenuPreviewSeparator'; + +export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; +export * from '../MenuPreviewSubmenuRoot'; + +export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; +export * from '../MenuPreviewSubmenuTrigger'; + +export * from './menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreview/index.d.ts b/packages/mui-material/src/MenuPreview/index.d.ts new file mode 100644 index 00000000000000..43bb7637dc085b --- /dev/null +++ b/packages/mui-material/src/MenuPreview/index.d.ts @@ -0,0 +1,66 @@ +export { default } from './MenuPreview'; +export { default as MenuPreview } from './MenuPreview'; +export { default as Root } from './MenuPreview'; +export * from './MenuPreview'; + +export { default as Trigger } from '../MenuPreviewTrigger'; +export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; +export * from '../MenuPreviewTrigger'; + +export { default as Popup } from '../MenuPreviewPopup'; +export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; +export * from '../MenuPreviewPopup'; + +export { default as SubmenuPopup } from '../MenuPreviewSubmenuPopup'; +export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; +export * from '../MenuPreviewSubmenuPopup'; + +export { default as Item } from '../MenuPreviewItem'; +export { default as MenuPreviewItem } from '../MenuPreviewItem'; +export * from '../MenuPreviewItem'; + +export { default as LinkItem } from '../MenuPreviewLinkItem'; +export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; +export * from '../MenuPreviewLinkItem'; + +export { default as CheckboxItem } from '../MenuPreviewCheckboxItem'; +export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; +export * from '../MenuPreviewCheckboxItem'; + +export { default as CheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; +export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; +export * from '../MenuPreviewCheckboxItemIndicator'; + +export { default as RadioGroup } from '../MenuPreviewRadioGroup'; +export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; +export * from '../MenuPreviewRadioGroup'; + +export { default as RadioItem } from '../MenuPreviewRadioItem'; +export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; +export * from '../MenuPreviewRadioItem'; + +export { default as RadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; +export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; +export * from '../MenuPreviewRadioItemIndicator'; + +export { default as Group } from '../MenuPreviewGroup'; +export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; +export * from '../MenuPreviewGroup'; + +export { default as GroupLabel } from '../MenuPreviewGroupLabel'; +export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; +export * from '../MenuPreviewGroupLabel'; + +export { default as Separator } from '../MenuPreviewSeparator'; +export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; +export * from '../MenuPreviewSeparator'; + +export { default as SubmenuRoot } from '../MenuPreviewSubmenuRoot'; +export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; +export * from '../MenuPreviewSubmenuRoot'; + +export { default as SubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; +export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; +export * from '../MenuPreviewSubmenuTrigger'; + +export * from './menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreview/index.js b/packages/mui-material/src/MenuPreview/index.js new file mode 100644 index 00000000000000..43bb7637dc085b --- /dev/null +++ b/packages/mui-material/src/MenuPreview/index.js @@ -0,0 +1,66 @@ +export { default } from './MenuPreview'; +export { default as MenuPreview } from './MenuPreview'; +export { default as Root } from './MenuPreview'; +export * from './MenuPreview'; + +export { default as Trigger } from '../MenuPreviewTrigger'; +export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; +export * from '../MenuPreviewTrigger'; + +export { default as Popup } from '../MenuPreviewPopup'; +export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; +export * from '../MenuPreviewPopup'; + +export { default as SubmenuPopup } from '../MenuPreviewSubmenuPopup'; +export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; +export * from '../MenuPreviewSubmenuPopup'; + +export { default as Item } from '../MenuPreviewItem'; +export { default as MenuPreviewItem } from '../MenuPreviewItem'; +export * from '../MenuPreviewItem'; + +export { default as LinkItem } from '../MenuPreviewLinkItem'; +export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; +export * from '../MenuPreviewLinkItem'; + +export { default as CheckboxItem } from '../MenuPreviewCheckboxItem'; +export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; +export * from '../MenuPreviewCheckboxItem'; + +export { default as CheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; +export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; +export * from '../MenuPreviewCheckboxItemIndicator'; + +export { default as RadioGroup } from '../MenuPreviewRadioGroup'; +export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; +export * from '../MenuPreviewRadioGroup'; + +export { default as RadioItem } from '../MenuPreviewRadioItem'; +export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; +export * from '../MenuPreviewRadioItem'; + +export { default as RadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; +export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; +export * from '../MenuPreviewRadioItemIndicator'; + +export { default as Group } from '../MenuPreviewGroup'; +export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; +export * from '../MenuPreviewGroup'; + +export { default as GroupLabel } from '../MenuPreviewGroupLabel'; +export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; +export * from '../MenuPreviewGroupLabel'; + +export { default as Separator } from '../MenuPreviewSeparator'; +export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; +export * from '../MenuPreviewSeparator'; + +export { default as SubmenuRoot } from '../MenuPreviewSubmenuRoot'; +export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; +export * from '../MenuPreviewSubmenuRoot'; + +export { default as SubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; +export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; +export * from '../MenuPreviewSubmenuTrigger'; + +export * from './menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts b/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts new file mode 100644 index 00000000000000..5c32722a12a4d0 --- /dev/null +++ b/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts @@ -0,0 +1,280 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface MenuPreviewTriggerClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if the menu is open. */ + open: string; +} + +export type MenuPreviewTriggerClassKey = keyof MenuPreviewTriggerClasses; + +export function getMenuPreviewTriggerUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewTrigger', slot); +} + +export const menuPreviewTriggerClasses: MenuPreviewTriggerClasses = generateUtilityClasses( + 'MuiMenuPreviewTrigger', + ['root', 'disabled', 'open'], +); + +export interface MenuPreviewPopupClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the Material Paper element. */ + paper: string; + /** Styles applied to the Material List element. */ + list: string; +} + +export type MenuPreviewPopupClassKey = keyof MenuPreviewPopupClasses; + +export function getMenuPreviewPopupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewPopup', slot); +} + +export const menuPreviewPopupClasses: MenuPreviewPopupClasses = generateUtilityClasses( + 'MuiMenuPreviewPopup', + ['root', 'paper', 'list'], +); + +export interface MenuPreviewSubmenuPopupClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the Material Paper element. */ + paper: string; + /** Styles applied to the Material List element. */ + list: string; +} + +export type MenuPreviewSubmenuPopupClassKey = keyof MenuPreviewSubmenuPopupClasses; + +export function getMenuPreviewSubmenuPopupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewSubmenuPopup', slot); +} + +export const menuPreviewSubmenuPopupClasses: MenuPreviewSubmenuPopupClasses = + generateUtilityClasses('MuiMenuPreviewSubmenuPopup', ['root', 'paper', 'list']); + +export interface MenuPreviewItemClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** Styles applied to the root element if `dense={true}`. */ + dense: string; + /** Styles applied to the root element if `divider={true}`. */ + divider: string; + /** Styles applied to the root element unless `disableGutters={true}`. */ + gutters: string; + /** State class applied to the root element if `selected={true}`. */ + selected: string; +} + +export type MenuPreviewItemClassKey = keyof MenuPreviewItemClasses; + +export function getMenuPreviewItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewItem', slot); +} + +export const menuPreviewItemClasses: MenuPreviewItemClasses = generateUtilityClasses( + 'MuiMenuPreviewItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected'], +); + +export interface MenuPreviewLinkItemClasses extends MenuPreviewItemClasses {} + +export type MenuPreviewLinkItemClassKey = keyof MenuPreviewLinkItemClasses; + +export function getMenuPreviewLinkItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewLinkItem', slot); +} + +export const menuPreviewLinkItemClasses: MenuPreviewLinkItemClasses = generateUtilityClasses( + 'MuiMenuPreviewLinkItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected'], +); + +export interface MenuPreviewCheckboxItemClasses extends MenuPreviewItemClasses { + /** State class applied to the root element if `checked={true}`. */ + checked: string; +} + +export type MenuPreviewCheckboxItemClassKey = keyof MenuPreviewCheckboxItemClasses; + +export function getMenuPreviewCheckboxItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewCheckboxItem', slot); +} + +export const menuPreviewCheckboxItemClasses: MenuPreviewCheckboxItemClasses = + generateUtilityClasses('MuiMenuPreviewCheckboxItem', [ + 'root', + 'highlighted', + 'disabled', + 'dense', + 'divider', + 'gutters', + 'selected', + 'checked', + ]); + +export interface MenuPreviewCheckboxItemIndicatorClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `checked={true}`. */ + checked: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; +} + +export type MenuPreviewCheckboxItemIndicatorClassKey = + keyof MenuPreviewCheckboxItemIndicatorClasses; + +export function getMenuPreviewCheckboxItemIndicatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewCheckboxItemIndicator', slot); +} + +export const menuPreviewCheckboxItemIndicatorClasses: MenuPreviewCheckboxItemIndicatorClasses = + generateUtilityClasses('MuiMenuPreviewCheckboxItemIndicator', [ + 'root', + 'checked', + 'disabled', + 'highlighted', + ]); + +export interface MenuPreviewRadioGroupClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; +} + +export type MenuPreviewRadioGroupClassKey = keyof MenuPreviewRadioGroupClasses; + +export function getMenuPreviewRadioGroupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewRadioGroup', slot); +} + +export const menuPreviewRadioGroupClasses: MenuPreviewRadioGroupClasses = generateUtilityClasses( + 'MuiMenuPreviewRadioGroup', + ['root', 'disabled'], +); + +export interface MenuPreviewRadioItemClasses extends MenuPreviewItemClasses { + /** State class applied to the root element if `checked={true}`. */ + checked: string; +} + +export type MenuPreviewRadioItemClassKey = keyof MenuPreviewRadioItemClasses; + +export function getMenuPreviewRadioItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewRadioItem', slot); +} + +export const menuPreviewRadioItemClasses: MenuPreviewRadioItemClasses = generateUtilityClasses( + 'MuiMenuPreviewRadioItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected', 'checked'], +); + +export interface MenuPreviewRadioItemIndicatorClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `checked={true}`. */ + checked: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; +} + +export type MenuPreviewRadioItemIndicatorClassKey = keyof MenuPreviewRadioItemIndicatorClasses; + +export function getMenuPreviewRadioItemIndicatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewRadioItemIndicator', slot); +} + +export const menuPreviewRadioItemIndicatorClasses: MenuPreviewRadioItemIndicatorClasses = + generateUtilityClasses('MuiMenuPreviewRadioItemIndicator', [ + 'root', + 'checked', + 'disabled', + 'highlighted', + ]); + +export interface MenuPreviewGroupClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MenuPreviewGroupClassKey = keyof MenuPreviewGroupClasses; + +export function getMenuPreviewGroupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewGroup', slot); +} + +export const menuPreviewGroupClasses: MenuPreviewGroupClasses = generateUtilityClasses( + 'MuiMenuPreviewGroup', + ['root'], +); + +export interface MenuPreviewGroupLabelClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MenuPreviewGroupLabelClassKey = keyof MenuPreviewGroupLabelClasses; + +export function getMenuPreviewGroupLabelUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewGroupLabel', slot); +} + +export const menuPreviewGroupLabelClasses: MenuPreviewGroupLabelClasses = generateUtilityClasses( + 'MuiMenuPreviewGroupLabel', + ['root'], +); + +export interface MenuPreviewSeparatorClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type MenuPreviewSeparatorClassKey = keyof MenuPreviewSeparatorClasses; + +export function getMenuPreviewSeparatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewSeparator', slot); +} + +export const menuPreviewSeparatorClasses: MenuPreviewSeparatorClasses = generateUtilityClasses( + 'MuiMenuPreviewSeparator', + ['root'], +); + +export interface MenuPreviewSubmenuTriggerClasses extends MenuPreviewItemClasses { + /** State class applied to the root element if the submenu is open. */ + open: string; +} + +export type MenuPreviewSubmenuTriggerClassKey = keyof MenuPreviewSubmenuTriggerClasses; + +export function getMenuPreviewSubmenuTriggerUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenuPreviewSubmenuTrigger', slot); +} + +export const menuPreviewSubmenuTriggerClasses: MenuPreviewSubmenuTriggerClasses = + generateUtilityClasses('MuiMenuPreviewSubmenuTrigger', [ + 'root', + 'highlighted', + 'disabled', + 'dense', + 'divider', + 'gutters', + 'selected', + 'open', + ]); diff --git a/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx new file mode 100644 index 00000000000000..904641b370bfbb --- /dev/null +++ b/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx @@ -0,0 +1,244 @@ +'use client'; +import * as React from 'react'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { CSSInterpolation, SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + StateClassName, + mergeStateClassName, +} from './menuPreviewUtils'; + +export interface MenuPreviewItemOwnerState { + checked?: boolean | undefined; + dense: boolean; + disabled: boolean; + divider: boolean; + disableGutters: boolean; + selected: boolean; +} + +export interface MenuPreviewItemVisualProps< + Classes, + Slots = MenuPreviewRootSlots, + SlotProps = MenuPreviewRootSlotProps, +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * The components used for each slot inside. + */ + slots?: Slots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: SlotProps | undefined; + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense?: boolean | undefined; + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters?: boolean | undefined; + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider?: boolean | undefined; + /** + * If `true`, the component is selected. + * @default false + */ + selected?: boolean | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +export interface MenuPreviewItemBaseProps { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default true + */ + closeOnClick?: boolean | undefined; +} + +export interface MenuPreviewLinkItemBaseProps { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * The URL that the link item points to. + */ + href?: string | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick?: boolean | undefined; +} + +export interface MenuPreviewSubmenuTriggerBaseProps { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * How long to wait before the submenu may be opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 100 + */ + delay?: number | undefined; + /** + * How long to wait before closing the submenu that was opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 0 + */ + closeDelay?: number | undefined; + /** + * Whether the submenu should also open when the trigger is hovered. + */ + openOnHover?: boolean | undefined; +} + +export interface MenuPreviewBaseItemState { + disabled?: boolean; + highlighted?: boolean; +} + +export function menuPreviewItemOverridesResolver( + props: { ownerState: MenuPreviewItemOwnerState }, + styles: Record, +) { + const { ownerState } = props; + + return [ + styles.root, + ownerState.dense && styles.dense, + ownerState.divider && styles.divider, + !ownerState.disableGutters && styles.gutters, + ] as CSSInterpolation; +} + +export function getMenuPreviewItemOwnerState( + props: MenuPreviewItemVisualProps & { + checked?: boolean | undefined; + disabled?: boolean | undefined; + }, +): MenuPreviewItemOwnerState { + return { + checked: props.checked, + dense: props.dense ?? false, + disabled: props.disabled ?? false, + divider: props.divider ?? false, + disableGutters: props.disableGutters ?? false, + selected: props.selected ?? false, + }; +} + +export function useMenuPreviewItemUtilityClasses( + ownerState: MenuPreviewItemOwnerState & { + classes?: Partial; + checked?: boolean; + open?: boolean; + }, + getUtilityClass: (slot: string) => string, +) { + const { dense, disabled, divider, disableGutters, selected, checked, open, classes } = ownerState; + const slots = { + root: [ + 'root', + dense && 'dense', + disabled && 'disabled', + !disableGutters && 'gutters', + divider && 'divider', + selected && 'selected', + checked && 'checked', + open && 'open', + ], + highlighted: ['highlighted'], + disabled: ['disabled'], + checked: ['checked'], + open: ['open'], + }; + + return { + ...classes, + ...composeClasses(slots, getUtilityClass, classes as Record | undefined), + } as Classes; +} + +export function getMenuPreviewItemClassName( + classes: Partial>, + ownerState: MenuPreviewItemOwnerState, + state: State, +) { + return clsx( + classes.root, + state.highlighted && classes.highlighted, + state.disabled && !ownerState.disabled && classes.disabled, + ); +} + +export function mergeMenuPreviewItemClassName( + className: StateClassName, + classes: Partial>, + ownerState: MenuPreviewItemOwnerState, +) { + return mergeStateClassName(className, (state) => + getMenuPreviewItemClassName(classes, ownerState, state), + ); +} diff --git a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx new file mode 100644 index 00000000000000..2e5bc2a049171e --- /dev/null +++ b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx @@ -0,0 +1,368 @@ +'use client'; +import * as React from 'react'; +import clsx from 'clsx'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import appendOwnerState from '@mui/utils/appendOwnerState'; +import isHostComponent from '@mui/utils/isHostComponent'; +import { SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { PaperProps } from '../Paper'; +import { ListProps } from '../List'; +import { resolveSlotProps, SlotProps } from './menuPreviewUtils'; + +type ExternalSlotProps = Omit, 'className' | 'render' | 'style'> & { + className?: string | undefined; + render?: never; + style?: React.CSSProperties | undefined; +} & Record; + +function mergeSx(...sx: Array | undefined>) { + return sx.flatMap((style) => (Array.isArray(style) ? style : [style])).filter(Boolean); +} + +function setDefinedProp(props: Record, key: string, value: unknown) { + if (value !== undefined) { + props[key] = value; + } +} + +function omitProps | undefined>( + props: Props, + keys: readonly string[], +): Props { + if (props == null) { + return props; + } + + const result = { ...props }; + keys.forEach((key) => { + delete result[key]; + }); + + return result as Props; +} + +function getSlotProps>( + Slot: ElementType, + props: Props, + hostOmittedProps: readonly string[], +) { + return isHostComponent(Slot) ? omitProps(props, hostOmittedProps) : props; +} + +const portalHostOmittedProps = ['container', 'keepMounted'] as const; +const positionerHostOmittedProps = [ + 'align', + 'alignOffset', + 'anchor', + 'arrowPadding', + 'collisionAvoidance', + 'collisionBoundary', + 'collisionPadding', + 'disableAnchorTracking', + 'positionMethod', + 'render', + 'side', + 'sideOffset', + 'sticky', +] as const; +const paperHostOmittedProps = [ + 'classes', + 'component', + 'elevation', + 'square', + 'sx', + 'variant', +] as const; +const listHostOmittedProps = [ + 'classes', + 'component', + 'dense', + 'disablePadding', + 'subheader', + 'sx', +] as const; + +export interface MenuPreviewPopupSharedSlots { + /** + * The component used for the portal. + * @default BaseMenu.Portal + */ + portal?: React.ElementType; + /** + * The component used for the positioner. + * @default BaseMenu.Positioner + */ + positioner?: React.ElementType; + /** + * The component rendered by the Base UI popup. + * @default 'div' + */ + popup?: React.ElementType; + /** + * The component used for the Material surface. + * @default Paper + */ + paper?: React.ElementType; + /** + * The component used for the presentational list wrapper. + * @default List + */ + list?: React.ElementType; +} + +export interface MenuPreviewPopupSharedSlotProps { + portal?: SlotProps, OwnerState>; + positioner?: SlotProps, OwnerState>; + popup?: SlotProps, OwnerState>; + paper?: SlotProps, OwnerState>; + list?: SlotProps, OwnerState>; +} + +type MenuPreviewPositionerProps = BaseMenu.Positioner.Props; +type MenuPreviewPortalProps = BaseMenu.Portal.Props; + +export type MenuPreviewPopupState = BaseMenu.Popup.State; +export type MenuPreviewPopupSide = NonNullable; +export type MenuPreviewPopupAlign = NonNullable; +export type MenuPreviewPopupOffset = NonNullable; +export type MenuPreviewPopupAnchor = MenuPreviewPositionerProps['anchor']; +export type MenuPreviewPopupPositionMethod = MenuPreviewPositionerProps['positionMethod']; +export type MenuPreviewPopupCollisionBoundary = MenuPreviewPositionerProps['collisionBoundary']; +export type MenuPreviewPopupCollisionPadding = MenuPreviewPositionerProps['collisionPadding']; +export type MenuPreviewPopupCollisionAvoidance = MenuPreviewPositionerProps['collisionAvoidance']; +export type MenuPreviewPopupContainer = MenuPreviewPortalProps['container']; +export type MenuPreviewPopupFinalFocus = BaseMenu.Popup.Props['finalFocus']; + +export interface MenuPreviewPopupPublicProps { + /** + * The menu items. + */ + children?: React.ReactNode; + /** + * CSS class applied to the Base UI popup element. + */ + className?: string | undefined; + /** + * Styles applied to the Base UI popup element. + */ + style?: React.CSSProperties | undefined; + /** + * An element to position the popup against. + * + * By default, the popup is positioned against the trigger. + */ + anchor?: MenuPreviewPopupAnchor; + /** + * Determines which CSS `position` property to use. + * @default 'absolute' + */ + positionMethod?: MenuPreviewPopupPositionMethod; + /** + * Which side of the anchor element to align the popup against. + * @default 'bottom' + */ + side?: MenuPreviewPopupSide | undefined; + /** + * Distance between the anchor and the popup in pixels. + * @default 0 + */ + sideOffset?: MenuPreviewPopupOffset | undefined; + /** + * How to align the popup relative to the specified side. + * @default 'start' + */ + align?: MenuPreviewPopupAlign | undefined; + /** + * Additional offset along the alignment axis in pixels. + * @default 0 + */ + alignOffset?: MenuPreviewPopupOffset | undefined; + /** + * An element or a rectangle that delimits the area that the popup is confined to. + * @default 'clipping-ancestors' + */ + collisionBoundary?: MenuPreviewPopupCollisionBoundary; + /** + * Additional space to maintain from the edge of the collision boundary. + * @default 5 + */ + collisionPadding?: MenuPreviewPopupCollisionPadding; + /** + * Minimum distance to maintain between the arrow and the edges of the popup. + * @default 5 + */ + arrowPadding?: MenuPreviewPositionerProps['arrowPadding']; + /** + * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. + * @default false + */ + sticky?: MenuPreviewPositionerProps['sticky']; + /** + * Whether to disable the popup from tracking layout shifts of its positioning anchor. + * @default false + */ + disableAnchorTracking?: MenuPreviewPositionerProps['disableAnchorTracking']; + /** + * Determines how to handle collisions when positioning the popup. + */ + collisionAvoidance?: MenuPreviewPopupCollisionAvoidance; + /** + * The container element to portal the popup into. + */ + container?: MenuPreviewPopupContainer; + /** + * Whether to keep the portal mounted in the DOM while the popup is hidden. + * @default false + */ + keepMounted?: MenuPreviewPortalProps['keepMounted']; + /** + * Determines the element to focus when the menu is closed. + */ + finalFocus?: MenuPreviewPopupFinalFocus; +} + +export interface MenuPreviewPopupSharedProps + extends + Omit, + MenuPreviewPopupPublicProps { + classes?: Partial>; + ownerState: OwnerState; + slots?: MenuPreviewPopupSharedSlots; + slotProps?: MenuPreviewPopupSharedSlotProps; + defaultSlots: { + popup: React.ElementType; + paper: React.ElementType; + list: React.ElementType; + }; + defaultPositionerProps?: Partial; + sx?: SxProps; +} + +export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBase( + props: MenuPreviewPopupSharedProps, + ref: React.ForwardedRef, +) { + const { + children, + className, + classes, + ownerState, + slots, + slotProps, + defaultSlots, + defaultPositionerProps, + sx, + container, + keepMounted, + anchor, + positionMethod, + side, + sideOffset, + align, + alignOffset, + collisionBoundary, + collisionPadding, + arrowPadding, + sticky, + disableAnchorTracking, + collisionAvoidance, + id, + finalFocus, + style, + ...other + } = props; + + const PortalSlot = slots?.portal ?? BaseMenu.Portal; + const PositionerSlot = slots?.positioner ?? BaseMenu.Positioner; + const PopupSlot = slots?.popup ?? defaultSlots.popup; + const PaperSlot = slots?.paper ?? defaultSlots.paper; + const ListSlot = slots?.list ?? defaultSlots.list; + + const resolvedPortalProps = resolveSlotProps(slotProps?.portal, ownerState); + const resolvedPositionerProps = resolveSlotProps(slotProps?.positioner, ownerState); + const resolvedPopupProps = resolveSlotProps(slotProps?.popup, ownerState); + const resolvedPaperProps = resolveSlotProps(slotProps?.paper, ownerState); + const resolvedListProps = resolveSlotProps(slotProps?.list, ownerState); + const { className: resolvedPopupClassName, ...resolvedPopupOtherProps } = + resolvedPopupProps ?? {}; + const positionerProps = { + ...defaultPositionerProps, + }; + + setDefinedProp(positionerProps, 'anchor', anchor); + setDefinedProp(positionerProps, 'positionMethod', positionMethod); + setDefinedProp(positionerProps, 'side', side); + setDefinedProp(positionerProps, 'sideOffset', sideOffset); + setDefinedProp(positionerProps, 'align', align); + setDefinedProp(positionerProps, 'alignOffset', alignOffset); + setDefinedProp(positionerProps, 'collisionBoundary', collisionBoundary); + setDefinedProp(positionerProps, 'collisionPadding', collisionPadding); + setDefinedProp(positionerProps, 'arrowPadding', arrowPadding); + setDefinedProp(positionerProps, 'sticky', sticky); + setDefinedProp(positionerProps, 'disableAnchorTracking', disableAnchorTracking); + setDefinedProp(positionerProps, 'collisionAvoidance', collisionAvoidance); + + const popupClassName = clsx(classes?.root, className, resolvedPopupClassName); + const popupRender = ; + const portalSlotProps = getSlotProps( + PortalSlot, + { + container, + keepMounted, + ...resolvedPortalProps, + }, + portalHostOmittedProps, + ); + const positionerSlotProps = getSlotProps( + PositionerSlot, + { + ...positionerProps, + ...resolvedPositionerProps, + }, + positionerHostOmittedProps, + ); + const paperSlotProps = getSlotProps( + PaperSlot, + { + elevation: 8, + ...resolvedPaperProps, + className: clsx(classes?.paper, resolvedPaperProps?.className), + sx: mergeSx(sx, resolvedPaperProps?.sx), + }, + paperHostOmittedProps, + ); + const listSlotProps = getSlotProps( + ListSlot, + { + component: 'div', + disablePadding: true, + ...resolvedListProps, + className: clsx(classes?.list, resolvedListProps?.className), + }, + listHostOmittedProps, + ); + + return ( + + + + + {children} + + + + + ); +}) as ( + props: MenuPreviewPopupSharedProps & React.RefAttributes, +) => React.JSX.Element; diff --git a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts new file mode 100644 index 00000000000000..75295e4ea2b1fb --- /dev/null +++ b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts @@ -0,0 +1,172 @@ +import { CSSInterpolation } from '@mui/system'; +import { dividerClasses } from '../Divider'; +import { listItemIconClasses } from '../ListItemIcon'; +import { listItemTextClasses } from '../ListItemText'; +import memoTheme from '../utils/memoTheme'; +import { Theme } from '../styles'; + +export interface SharedMenuPreviewItemClasses { + highlighted: string; + disabled: string; + dense: string; + divider: string; + gutters: string; + selected: string; +} + +interface MenuPreviewItemVariantOwnerState { + dense: boolean; + divider: boolean; + disableGutters: boolean; +} + +export function getMenuPreviewItemStyles( + theme: Theme, + classes: SharedMenuPreviewItemClasses, +): CSSInterpolation { + return { + ...theme.typography.body1, + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + position: 'relative', + textDecoration: 'none', + minHeight: 48, + paddingTop: 6, + paddingBottom: 6, + boxSizing: 'border-box', + whiteSpace: 'nowrap', + cursor: 'default', + userSelect: 'none', + outline: 0, + '&:hover': { + textDecoration: 'none', + backgroundColor: (theme.vars || theme).palette.action.hover, + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + [`&.${classes.selected}`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + (theme.vars || theme).palette.action.selectedOpacity, + ), + [`&.${classes.highlighted}`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + `${(theme.vars || theme).palette.action.selectedOpacity} + ${ + (theme.vars || theme).palette.action.focusOpacity + }`, + ), + }, + }, + [`&.${classes.selected}:hover`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + `${(theme.vars || theme).palette.action.selectedOpacity} + ${ + (theme.vars || theme).palette.action.hoverOpacity + }`, + ), + '@media (hover: none)': { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + (theme.vars || theme).palette.action.selectedOpacity, + ), + }, + }, + [`&.${classes.highlighted}`]: { + backgroundColor: (theme.vars || theme).palette.action.focus, + }, + [`&.${classes.disabled}`]: { + opacity: (theme.vars || theme).palette.action.disabledOpacity, + pointerEvents: 'none', + }, + [`& + .${dividerClasses.root}`]: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + [`& + .${dividerClasses.inset}`]: { + marginLeft: 52, + }, + [`& .${listItemTextClasses.root}`]: { + marginTop: 0, + marginBottom: 0, + }, + [`& .${listItemTextClasses.inset}`]: { + paddingLeft: 36, + }, + [`& .${listItemIconClasses.root}`]: { + minWidth: 36, + }, + variants: [ + { + props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => + !ownerState.disableGutters, + style: { + paddingLeft: 16, + paddingRight: 16, + }, + }, + { + props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => + ownerState.divider, + style: { + borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundClip: 'padding-box', + }, + }, + { + props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => + !ownerState.dense, + style: { + [theme.breakpoints.up('sm')]: { + minHeight: 'auto', + }, + }, + }, + { + props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => + ownerState.dense, + style: { + minHeight: 32, + paddingTop: 4, + paddingBottom: 4, + ...theme.typography.body2, + [`& .${listItemIconClasses.root} svg`]: { + fontSize: '1.25rem', + }, + }, + }, + ], + }; +} + +export const menuPreviewPopupPaperStyles: CSSInterpolation = { + maxHeight: 'calc(100% - 96px)', + WebkitOverflowScrolling: 'touch', +}; + +export const menuPreviewPopupListStyles: CSSInterpolation = { + outline: 0, +}; + +export const menuPreviewIndicatorStyles = memoTheme(({ theme }) => ({ + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + minWidth: 36, + color: (theme.vars || theme).palette.action.active, + '& [data-mui-menu-preview-indicator-icon]': { + display: 'inline-block', + flexShrink: 0, + width: '1.25rem', + height: '1.25rem', + fill: 'currentColor', + }, + '& [data-mui-menu-preview-checkbox-checkmark]': { + fill: (theme.vars || theme).palette.background.paper, + }, + '&[data-unchecked] [data-mui-menu-preview-indicator-mark]': { + visibility: 'hidden', + }, +})); diff --git a/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts b/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts new file mode 100644 index 00000000000000..bfb185ae77ba5e --- /dev/null +++ b/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts @@ -0,0 +1,76 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import appendOwnerState from '@mui/utils/appendOwnerState'; +import isHostComponent from '@mui/utils/isHostComponent'; + +export type StateClassName = string | ((state: State) => string | undefined) | undefined; + +export function resolveStateClassName( + className: StateClassName, + state: State, +): string | undefined { + return typeof className === 'function' ? className(state) : className; +} + +export function mergeStateClassName( + className: StateClassName, + getClassName: (state: State) => string | undefined, +) { + return (state: State) => clsx(getClassName(state), resolveStateClassName(className, state)); +} + +export type SlotProps = + | SlotPropsValue + | ((ownerState: OwnerState) => SlotPropsValue) + | undefined; + +export function resolveSlotProps( + slotProps: SlotProps, + ownerState: OwnerState, +): SlotPropsValue | undefined { + return typeof slotProps === 'function' + ? (slotProps as (ownerState: OwnerState) => SlotPropsValue)(ownerState) + : slotProps; +} + +export interface MenuPreviewRootSlots { + root?: React.ElementType; +} + +export interface MenuPreviewRootSlotProps { + root?: SlotProps, OwnerState>; +} + +export function getMenuPreviewRootRender( + RootSlot: React.ElementType, + ownerState: OwnerState, + props?: Record, +) { + if (isHostComponent(RootSlot)) { + const hostProps = { ...(props ?? {}) }; + delete hostProps.as; + delete hostProps.component; + delete hostProps.ownerState; + delete hostProps.sx; + + return React.createElement(RootSlot, hostProps); + } + + return React.createElement(RootSlot, appendOwnerState(RootSlot, props ?? {}, ownerState)); +} + +export function isMenuPreviewRootNativeButton( + RootSlot: React.ElementType, + component: React.ElementType | undefined, + defaultNativeButton = false, +) { + if (isHostComponent(RootSlot)) { + return RootSlot === 'button'; + } + + if (component != null) { + return component === 'button'; + } + + return defaultNativeButton; +} diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx b/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx new file mode 100644 index 00000000000000..bbfad0b75225b9 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx @@ -0,0 +1,308 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import ListContext from '../List/ListContext'; +import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + isMenuPreviewRootNativeButton, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewItemClassName, + getMenuPreviewItemOwnerState, + MenuPreviewItemBaseProps, + MenuPreviewItemOwnerState, + MenuPreviewItemVisualProps, + menuPreviewItemOverridesResolver, + useMenuPreviewItemUtilityClasses, +} from '../MenuPreview/menuPreviewItemShared'; +import { + getMenuPreviewCheckboxItemUtilityClass, + menuPreviewCheckboxItemClasses, + MenuPreviewCheckboxItemClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewCheckboxItemSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewCheckboxItemSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewCheckboxItemProps + extends + Omit< + BaseMenu.CheckboxItem.Props, + 'className' | 'nativeButton' | 'onChange' | 'onCheckedChange' | 'render' | 'style' + >, + MenuPreviewItemBaseProps, + MenuPreviewItemVisualProps< + MenuPreviewCheckboxItemClasses, + MenuPreviewCheckboxItemSlots, + MenuPreviewCheckboxItemSlotProps + > { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Whether the checkbox item is currently ticked. + * + * To render an uncontrolled checkbox item, use the `defaultChecked` prop instead. + */ + checked?: boolean | undefined; + /** + * Whether the checkbox item is initially ticked. + * + * To render a controlled checkbox item, use the `checked` prop instead. + * @default false + */ + defaultChecked?: boolean | undefined; + /** + * Event handler called when the checkbox item is ticked or unticked. + */ + onChange?: + | (( + event: Event, + checked: boolean, + eventDetails: BaseMenu.CheckboxItem.ChangeEventDetails, + ) => void) + | undefined; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick?: boolean | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; +} + +const MenuPreviewCheckboxItemRoot = styled('div', { + name: 'MuiMenuPreviewCheckboxItem', + slot: 'Root', + overridesResolver: menuPreviewItemOverridesResolver, +})<{ ownerState: MenuPreviewItemOwnerState }>( + memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewCheckboxItemClasses)), +); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewCheckboxItem API](https://mui.com/material-ui/api/menu-preview-checkbox-item/) + */ +const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxItem( + inProps: MenuPreviewCheckboxItemProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewCheckboxItem', + }); + + const { + checked, + className, + classes: classesProp, + component, + dense = false, + disabled = false, + disableGutters = false, + divider = false, + nativeButton: nativeButtonProp, + onChange, + selected = false, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + ...getMenuPreviewItemOwnerState({ + checked, + dense, + disabled, + disableGutters, + divider, + selected, + }), + classes: classesProp, + }; + const classes = useMenuPreviewItemUtilityClasses( + ownerState, + getMenuPreviewCheckboxItemUtilityClass, + ); + const childContext = React.useMemo( + () => ({ + dense, + disableGutters, + }), + [dense, disableGutters], + ); + const handleCheckedChange = React.useCallback( + (newChecked: boolean, eventDetails: BaseMenu.CheckboxItem.ChangeEventDetails) => { + onChange?.(eventDetails.event, newChecked, eventDetails); + }, + [onChange], + ); + const RootSlot = slots?.root ?? MenuPreviewCheckboxItemRoot; + + return ( + + + clsx( + className, + getMenuPreviewItemClassName(classes, ownerState, state), + state.checked && classes.checked, + ) + } + checked={checked} + disabled={disabled} + nativeButton={nativeButtonProp ?? isMenuPreviewRootNativeButton(RootSlot, component)} + onCheckedChange={handleCheckedChange} + style={style} + {...other} + /> + + ); +}); + +MenuPreviewCheckboxItem.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * Whether the checkbox item is currently ticked. + * + * To render an uncontrolled checkbox item, use the `defaultChecked` prop instead. + */ + checked: PropTypes.bool, + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick: PropTypes.bool, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * Whether the checkbox item is initially ticked. + * + * To render a controlled checkbox item, use the `checked` prop instead. + * @default false + */ + defaultChecked: PropTypes.bool, + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider: PropTypes.bool, + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label: PropTypes.string, + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton: PropTypes.bool, + /** + * Event handler called when the checkbox item is ticked or unticked. + */ + onChange: PropTypes.func, + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewCheckboxItem; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts b/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts new file mode 100644 index 00000000000000..4372d2838e90bb --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewCheckboxItem'; +export * from './MenuPreviewCheckboxItem'; +export { + menuPreviewCheckboxItemClasses, + getMenuPreviewCheckboxItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewCheckboxItemClasses, + MenuPreviewCheckboxItemClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/index.js b/packages/mui-material/src/MenuPreviewCheckboxItem/index.js new file mode 100644 index 00000000000000..d453dbcbd0a5ab --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItem/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewCheckboxItem'; +export * from './MenuPreviewCheckboxItem'; +export { + menuPreviewCheckboxItemClasses, + getMenuPreviewCheckboxItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx new file mode 100644 index 00000000000000..f133331c8c69d6 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx @@ -0,0 +1,224 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { menuPreviewIndicatorStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewCheckboxItemIndicatorUtilityClass, + MenuPreviewCheckboxItemIndicatorClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewCheckboxItemIndicatorSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewCheckboxItemIndicatorSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewCheckboxItemIndicatorProps extends Omit< + BaseMenu.CheckboxItemIndicator.Props, + 'className' | 'render' | 'style' +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Whether to keep the HTML element in the DOM when the checkbox item is not checked. + * @default false + */ + keepMounted?: boolean | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewCheckboxItemIndicatorSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewCheckboxItemIndicatorSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewCheckboxItemIndicatorProps) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + checked: ['checked'], + disabled: ['disabled'], + highlighted: ['highlighted'], + }; + + return { + ...classes, + ...composeClasses(slots, getMenuPreviewCheckboxItemIndicatorUtilityClass, classes), + }; +}; + +const MenuPreviewCheckboxItemIndicatorRoot = styled('span', { + name: 'MuiMenuPreviewCheckboxItemIndicator', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})(menuPreviewIndicatorStyles) as any; + +function DefaultCheckboxIndicatorIcon() { + return ( + + ); +} + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewCheckboxItemIndicator API](https://mui.com/material-ui/api/menu-preview-checkbox-item-indicator/) + */ +const MenuPreviewCheckboxItemIndicator = React.forwardRef(function MenuPreviewCheckboxItemIndicator( + inProps: MenuPreviewCheckboxItemIndicatorProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewCheckboxItemIndicator', + }); + + const { + children, + className, + classes: classesProp, + component, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + clsx( + className, + classes.root, + state.checked && classes.checked, + state.disabled && classes.disabled, + state.highlighted && classes.highlighted, + ) + } + style={style} + {...other} + > + {children ?? } + + ); +}); + +MenuPreviewCheckboxItemIndicator.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * Whether to keep the HTML element in the DOM when the checkbox item is not checked. + * @default false + */ + keepMounted: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewCheckboxItemIndicator; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts new file mode 100644 index 00000000000000..4e0c311cd37242 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewCheckboxItemIndicator'; +export * from './MenuPreviewCheckboxItemIndicator'; +export { + menuPreviewCheckboxItemIndicatorClasses, + getMenuPreviewCheckboxItemIndicatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewCheckboxItemIndicatorClasses, + MenuPreviewCheckboxItemIndicatorClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js new file mode 100644 index 00000000000000..fb24b5bf809efc --- /dev/null +++ b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewCheckboxItemIndicator'; +export * from './MenuPreviewCheckboxItemIndicator'; +export { + menuPreviewCheckboxItemIndicatorClasses, + getMenuPreviewCheckboxItemIndicatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx b/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx new file mode 100644 index 00000000000000..863f2309232f1b --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx @@ -0,0 +1,174 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewGroupUtilityClass, + MenuPreviewGroupClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewGroupSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewGroupSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewGroupProps extends Omit< + BaseMenu.Group.Props, + 'className' | 'render' | 'style' +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewGroupSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewGroupSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewGroupProps) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMenuPreviewGroupUtilityClass, classes); +}; + +const MenuPreviewGroupRoot = styled('div', { + name: 'MuiMenuPreviewGroup', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({}) as any; + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewGroup API](https://mui.com/material-ui/api/menu-preview-group/) + */ +const MenuPreviewGroup = React.forwardRef(function MenuPreviewGroup( + inProps: MenuPreviewGroupProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewGroup', + }); + + const { + className, + classes: classesProp, + component, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +MenuPreviewGroup.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewGroup; diff --git a/packages/mui-material/src/MenuPreviewGroup/index.d.ts b/packages/mui-material/src/MenuPreviewGroup/index.d.ts new file mode 100644 index 00000000000000..73a72486bb167f --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroup/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewGroup'; +export * from './MenuPreviewGroup'; +export { + menuPreviewGroupClasses, + getMenuPreviewGroupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewGroupClasses, + MenuPreviewGroupClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroup/index.js b/packages/mui-material/src/MenuPreviewGroup/index.js new file mode 100644 index 00000000000000..49e6742bc562e4 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroup/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewGroup'; +export * from './MenuPreviewGroup'; +export { + menuPreviewGroupClasses, + getMenuPreviewGroupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx b/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx new file mode 100644 index 00000000000000..c640acd889bd07 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx @@ -0,0 +1,176 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import ListSubheader from '../ListSubheader'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewGroupLabelUtilityClass, + MenuPreviewGroupLabelClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewGroupLabelSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewGroupLabelSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewGroupLabelProps extends Omit< + BaseMenu.GroupLabel.Props, + 'className' | 'render' | 'style' +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewGroupLabelSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewGroupLabelSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewGroupLabelProps) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMenuPreviewGroupLabelUtilityClass, classes); +}; + +const MenuPreviewGroupLabelRoot = styled(ListSubheader, { + name: 'MuiMenuPreviewGroupLabel', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({}) as any; + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewGroupLabel API](https://mui.com/material-ui/api/menu-preview-group-label/) + */ +const MenuPreviewGroupLabel = React.forwardRef(function MenuPreviewGroupLabel( + inProps: MenuPreviewGroupLabelProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewGroupLabel', + }); + + const { + className, + classes: classesProp, + component, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +MenuPreviewGroupLabel.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewGroupLabel; diff --git a/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts b/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts new file mode 100644 index 00000000000000..6b9ddf97eea6cf --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewGroupLabel'; +export * from './MenuPreviewGroupLabel'; +export { + menuPreviewGroupLabelClasses, + getMenuPreviewGroupLabelUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewGroupLabelClasses, + MenuPreviewGroupLabelClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroupLabel/index.js b/packages/mui-material/src/MenuPreviewGroupLabel/index.js new file mode 100644 index 00000000000000..30bf6f4677fed1 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewGroupLabel/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewGroupLabel'; +export * from './MenuPreviewGroupLabel'; +export { + menuPreviewGroupLabelClasses, + getMenuPreviewGroupLabelUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx b/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx new file mode 100644 index 00000000000000..6571355efac4e7 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx @@ -0,0 +1,241 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import ListContext from '../List/ListContext'; +import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewItemOwnerState, + MenuPreviewItemBaseProps, + MenuPreviewItemOwnerState, + MenuPreviewItemVisualProps, + menuPreviewItemOverridesResolver, + mergeMenuPreviewItemClassName, + useMenuPreviewItemUtilityClasses, +} from '../MenuPreview/menuPreviewItemShared'; +import { + getMenuPreviewRootRender, + isMenuPreviewRootNativeButton, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewItemUtilityClass, + menuPreviewItemClasses, + MenuPreviewItemClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewItemSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewItemSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewItemProps + extends + Omit, + MenuPreviewItemBaseProps, + MenuPreviewItemVisualProps< + MenuPreviewItemClasses, + MenuPreviewItemSlots, + MenuPreviewItemSlotProps + > { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default true + */ + closeOnClick?: boolean | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; +} + +const MenuPreviewItemRoot = styled('div', { + name: 'MuiMenuPreviewItem', + slot: 'Root', + overridesResolver: menuPreviewItemOverridesResolver, +})<{ ownerState: MenuPreviewItemOwnerState }>( + memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewItemClasses)), +); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewItem API](https://mui.com/material-ui/api/menu-preview-item/) + */ +const MenuPreviewItem = React.forwardRef(function MenuPreviewItem( + inProps: MenuPreviewItemProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewItem', + }); + + const { + className, + classes: classesProp, + component, + dense = false, + disabled = false, + disableGutters = false, + divider = false, + nativeButton: nativeButtonProp, + selected = false, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + ...getMenuPreviewItemOwnerState({ dense, disabled, disableGutters, divider, selected }), + classes: classesProp, + }; + const classes = useMenuPreviewItemUtilityClasses( + ownerState, + getMenuPreviewItemUtilityClass, + ); + const childContext = React.useMemo( + () => ({ + dense, + disableGutters, + }), + [dense, disableGutters], + ); + const RootSlot = slots?.root ?? MenuPreviewItemRoot; + + return ( + + + + ); +}); + +MenuPreviewItem.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * Whether to close the menu when the item is clicked. + * @default true + */ + closeOnClick: PropTypes.bool, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider: PropTypes.bool, + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label: PropTypes.string, + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton: PropTypes.bool, + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewItem; diff --git a/packages/mui-material/src/MenuPreviewItem/index.d.ts b/packages/mui-material/src/MenuPreviewItem/index.d.ts new file mode 100644 index 00000000000000..febb5776db5b46 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewItem/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewItem'; +export * from './MenuPreviewItem'; +export { + menuPreviewItemClasses, + getMenuPreviewItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewItemClasses, + MenuPreviewItemClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewItem/index.js b/packages/mui-material/src/MenuPreviewItem/index.js new file mode 100644 index 00000000000000..f2296b0ea1f44f --- /dev/null +++ b/packages/mui-material/src/MenuPreviewItem/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewItem'; +export * from './MenuPreviewItem'; +export { + menuPreviewItemClasses, + getMenuPreviewItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx b/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx new file mode 100644 index 00000000000000..9f8e8978ff8b2e --- /dev/null +++ b/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx @@ -0,0 +1,233 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import ListContext from '../List/ListContext'; +import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewItemOwnerState, + MenuPreviewLinkItemBaseProps, + MenuPreviewItemOwnerState, + MenuPreviewItemVisualProps, + menuPreviewItemOverridesResolver, + mergeMenuPreviewItemClassName, + useMenuPreviewItemUtilityClasses, +} from '../MenuPreview/menuPreviewItemShared'; +import { + getMenuPreviewLinkItemUtilityClass, + menuPreviewLinkItemClasses, + MenuPreviewLinkItemClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewLinkItemSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewLinkItemSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewLinkItemProps + extends + Omit, + MenuPreviewLinkItemBaseProps, + MenuPreviewItemVisualProps< + MenuPreviewLinkItemClasses, + MenuPreviewLinkItemSlots, + MenuPreviewLinkItemSlotProps + > { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * The URL that the link item points to. + */ + href?: string | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick?: boolean | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; +} + +const MenuPreviewLinkItemRoot = styled('a', { + name: 'MuiMenuPreviewLinkItem', + slot: 'Root', + overridesResolver: menuPreviewItemOverridesResolver, +})<{ ownerState: MenuPreviewItemOwnerState }>( + memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewLinkItemClasses)), +); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewLinkItem API](https://mui.com/material-ui/api/menu-preview-link-item/) + */ +const MenuPreviewLinkItem = React.forwardRef(function MenuPreviewLinkItem( + inProps: MenuPreviewLinkItemProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewLinkItem', + }); + + const { + className, + classes: classesProp, + component, + dense = false, + disableGutters = false, + divider = false, + selected = false, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + ...getMenuPreviewItemOwnerState({ + dense, + disabled: false, + disableGutters, + divider, + selected, + }), + classes: classesProp, + }; + const classes = useMenuPreviewItemUtilityClasses( + ownerState, + getMenuPreviewLinkItemUtilityClass, + ); + const childContext = React.useMemo( + () => ({ + dense, + disableGutters, + }), + [dense, disableGutters], + ); + + return ( + + + + ); +}); + +MenuPreviewLinkItem.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick: PropTypes.bool, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense: PropTypes.bool, + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider: PropTypes.bool, + /** + * The URL that the link item points to. + */ + href: PropTypes.string, + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label: PropTypes.string, + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewLinkItem; diff --git a/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts b/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts new file mode 100644 index 00000000000000..d67220bdee8596 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewLinkItem'; +export * from './MenuPreviewLinkItem'; +export { + menuPreviewLinkItemClasses, + getMenuPreviewLinkItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewLinkItemClasses, + MenuPreviewLinkItemClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewLinkItem/index.js b/packages/mui-material/src/MenuPreviewLinkItem/index.js new file mode 100644 index 00000000000000..7a69a871b02fdc --- /dev/null +++ b/packages/mui-material/src/MenuPreviewLinkItem/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewLinkItem'; +export * from './MenuPreviewLinkItem'; +export { + menuPreviewLinkItemClasses, + getMenuPreviewLinkItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx new file mode 100644 index 00000000000000..cafc2212940014 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx @@ -0,0 +1,389 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import composeClasses from '@mui/utils/composeClasses'; +import HTMLElementType from '@mui/utils/HTMLElementType'; +import { SxProps } from '@mui/system'; +import Paper from '../Paper'; +import List from '../List'; +import { styled } from '../zero-styled'; +import { Theme } from '../styles'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + MenuPreviewPopupBase, + MenuPreviewPopupPublicProps, + MenuPreviewPopupSharedProps, + MenuPreviewPopupSharedSlotProps, + MenuPreviewPopupSharedSlots, +} from '../MenuPreview/menuPreviewPopupShared'; +import { + menuPreviewPopupListStyles, + menuPreviewPopupPaperStyles, +} from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewPopupUtilityClass, + MenuPreviewPopupClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewPopupProps extends Omit< + MenuPreviewPopupSharedProps, + | 'classes' + | 'defaultPositionerProps' + | 'defaultSlots' + | 'ownerState' + | keyof MenuPreviewPopupPublicProps +> { + /** + * The menu items. + */ + children?: React.ReactNode; + /** + * CSS class applied to the Base UI popup element. + */ + className?: MenuPreviewPopupPublicProps['className']; + /** + * Styles applied to the Base UI popup element. + */ + style?: MenuPreviewPopupPublicProps['style']; + /** + * An element to position the popup against. + * + * By default, the popup is positioned against the trigger. + */ + anchor?: MenuPreviewPopupPublicProps['anchor']; + /** + * Determines which CSS `position` property to use. + * @default 'absolute' + */ + positionMethod?: MenuPreviewPopupPublicProps['positionMethod']; + /** + * Which side of the anchor element to align the popup against. + * @default 'bottom' + */ + side?: MenuPreviewPopupPublicProps['side']; + /** + * Distance between the anchor and the popup in pixels. + * @default 0 + */ + sideOffset?: MenuPreviewPopupPublicProps['sideOffset']; + /** + * How to align the popup relative to the specified side. + * @default 'start' + */ + align?: MenuPreviewPopupPublicProps['align']; + /** + * Additional offset along the alignment axis in pixels. + * @default 0 + */ + alignOffset?: MenuPreviewPopupPublicProps['alignOffset']; + /** + * An element or a rectangle that delimits the area that the popup is confined to. + * @default 'clipping-ancestors' + */ + collisionBoundary?: MenuPreviewPopupPublicProps['collisionBoundary']; + /** + * Additional space to maintain from the edge of the collision boundary. + * @default 5 + */ + collisionPadding?: MenuPreviewPopupPublicProps['collisionPadding']; + /** + * Minimum distance to maintain between the arrow and the edges of the popup. + * @default 5 + */ + arrowPadding?: MenuPreviewPopupPublicProps['arrowPadding']; + /** + * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. + * @default false + */ + sticky?: MenuPreviewPopupPublicProps['sticky']; + /** + * Whether to disable the popup from tracking layout shifts of its positioning anchor. + * @default false + */ + disableAnchorTracking?: MenuPreviewPopupPublicProps['disableAnchorTracking']; + /** + * Determines how to handle collisions when positioning the popup. + */ + collisionAvoidance?: MenuPreviewPopupPublicProps['collisionAvoidance']; + /** + * The container element to portal the popup into. + */ + container?: MenuPreviewPopupPublicProps['container']; + /** + * Whether to keep the portal mounted in the DOM while the popup is hidden. + * @default false + */ + keepMounted?: MenuPreviewPopupPublicProps['keepMounted']; + /** + * Determines the element to focus when the menu is closed. + */ + finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewPopupSlotProps | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewPopupSlots | undefined; +} + +export interface MenuPreviewPopupOwnerState extends MenuPreviewPopupProps {} + +export interface MenuPreviewPopupSlots extends MenuPreviewPopupSharedSlots {} + +export interface MenuPreviewPopupSlotProps extends MenuPreviewPopupSharedSlotProps {} + +const useUtilityClasses = (ownerState: MenuPreviewPopupOwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + paper: ['paper'], + list: ['list'], + }; + + return composeClasses(slots, getMenuPreviewPopupUtilityClass, classes); +}; + +const MenuPreviewPopupRoot = styled('div', { + name: 'MuiMenuPreviewPopup', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({ + outline: 0, +}); + +const MenuPreviewPopupPaper = styled(Paper, { + name: 'MuiMenuPreviewPopup', + slot: 'Paper', + overridesResolver: (props, styles) => styles.paper, +})(menuPreviewPopupPaperStyles); + +const MenuPreviewPopupList = styled(List, { + name: 'MuiMenuPreviewPopup', + slot: 'List', + overridesResolver: (props, styles) => styles.list, +})(menuPreviewPopupListStyles); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewPopup API](https://mui.com/material-ui/api/menu-preview-popup/) + */ +const MenuPreviewPopup = React.forwardRef(function MenuPreviewPopup( + inProps: MenuPreviewPopupProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewPopup', + }); + + const ownerState: MenuPreviewPopupOwnerState = { + side: 'bottom', + align: 'start', + ...props, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +MenuPreviewPopup.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * How to align the popup relative to the specified side. + * @default 'start' + */ + align: PropTypes.oneOf(['center', 'end', 'start']), + /** + * Additional offset along the alignment axis in pixels. + * @default 0 + */ + alignOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), + /** + * An element to position the popup against. + * + * By default, the popup is positioned against the trigger. + */ + anchor: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + HTMLElementType, + PropTypes.object, + PropTypes.func, + ]), + /** + * Minimum distance to maintain between the arrow and the edges of the popup. + * @default 5 + */ + arrowPadding: PropTypes.number, + /** + * The menu items. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the Base UI popup element. + */ + className: PropTypes.string, + /** + * Determines how to handle collisions when positioning the popup. + */ + collisionAvoidance: PropTypes.oneOfType([ + PropTypes.shape({ + align: PropTypes.oneOf(['flip', 'none', 'shift']), + fallbackAxisSide: PropTypes.oneOf(['end', 'none', 'start']), + side: PropTypes.oneOf(['flip', 'none']), + }), + PropTypes.shape({ + align: PropTypes.oneOf(['none', 'shift']), + fallbackAxisSide: PropTypes.oneOf(['end', 'none', 'start']), + side: PropTypes.oneOf(['none', 'shift']), + }), + ]), + /** + * An element or a rectangle that delimits the area that the popup is confined to. + * @default 'clipping-ancestors' + */ + collisionBoundary: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['clipping-ancestors']), + HTMLElementType, + PropTypes.arrayOf(HTMLElementType), + PropTypes.shape({ + height: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + }), + ]), + /** + * Additional space to maintain from the edge of the collision boundary. + * @default 5 + */ + collisionPadding: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + bottom: PropTypes.number, + left: PropTypes.number, + right: PropTypes.number, + top: PropTypes.number, + }), + ]), + /** + * The container element to portal the popup into. + */ + container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + HTMLElementType, + PropTypes.object, + PropTypes.func, + ]), + /** + * Whether to disable the popup from tracking layout shifts of its positioning anchor. + * @default false + */ + disableAnchorTracking: PropTypes.bool, + /** + * Determines the element to focus when the menu is closed. + */ + finalFocus: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: HTMLElementType, + }), + PropTypes.bool, + ]), + /** + * Whether to keep the portal mounted in the DOM while the popup is hidden. + * @default false + */ + keepMounted: PropTypes.bool, + /** + * Determines which CSS `position` property to use. + * @default 'absolute' + */ + positionMethod: PropTypes.oneOf(['absolute', 'fixed']), + /** + * Which side of the anchor element to align the popup against. + * @default 'bottom' + */ + side: PropTypes.oneOf(['bottom', 'inline-end', 'inline-start', 'left', 'right', 'top']), + /** + * Distance between the anchor and the popup in pixels. + * @default 0 + */ + sideOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + list: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + popup: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + portal: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + positioner: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + list: PropTypes.elementType, + paper: PropTypes.elementType, + popup: PropTypes.elementType, + portal: PropTypes.elementType, + positioner: PropTypes.elementType, + }), + /** + * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. + * @default false + */ + sticky: PropTypes.bool, + /** + * Styles applied to the Base UI popup element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewPopup; diff --git a/packages/mui-material/src/MenuPreviewPopup/index.d.ts b/packages/mui-material/src/MenuPreviewPopup/index.d.ts new file mode 100644 index 00000000000000..82ce5ac8e7311f --- /dev/null +++ b/packages/mui-material/src/MenuPreviewPopup/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewPopup'; +export * from './MenuPreviewPopup'; +export { + menuPreviewPopupClasses, + getMenuPreviewPopupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewPopupClasses, + MenuPreviewPopupClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewPopup/index.js b/packages/mui-material/src/MenuPreviewPopup/index.js new file mode 100644 index 00000000000000..25a0b859273605 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewPopup/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewPopup'; +export * from './MenuPreviewPopup'; +export { + menuPreviewPopupClasses, + getMenuPreviewPopupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx b/packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx new file mode 100644 index 00000000000000..2ccf9ab360f89b --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx @@ -0,0 +1,228 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewRadioGroupUtilityClass, + MenuPreviewRadioGroupClasses, +} from '../MenuPreview/menuPreviewClasses'; + +interface MenuPreviewRadioGroupOwnerState extends MenuPreviewRadioGroupProps {} + +export interface MenuPreviewRadioGroupSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewRadioGroupSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewRadioGroupProps extends Omit< + BaseMenu.RadioGroup.Props, + 'className' | 'onChange' | 'onValueChange' | 'render' | 'style' +> { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * The controlled value of the radio item that should be currently selected. + */ + value?: any; + /** + * The uncontrolled value of the radio item that should be initially selected. + */ + defaultValue?: any; + /** + * Function called when the selected value changes. + */ + onChange?: + | ((event: Event, value: any, eventDetails: BaseMenu.RadioGroup.ChangeEventDetails) => void) + | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewRadioGroupSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewRadioGroupSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewRadioGroupOwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + disabled: ['disabled'], + }; + + return { + ...classes, + ...composeClasses(slots, getMenuPreviewRadioGroupUtilityClass, classes), + }; +}; + +const MenuPreviewRadioGroupRoot = styled('div', { + name: 'MuiMenuPreviewRadioGroup', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({}) as any; + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewRadioGroup API](https://mui.com/material-ui/api/menu-preview-radio-group/) + */ +const MenuPreviewRadioGroup = React.forwardRef(function MenuPreviewRadioGroup( + inProps: MenuPreviewRadioGroupProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewRadioGroup', + }); + + const { + className, + classes: classesProp, + component, + onChange, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + }; + const classes = useUtilityClasses(ownerState); + const handleValueChange = React.useCallback( + (newValue: any, eventDetails: BaseMenu.RadioGroup.ChangeEventDetails) => { + onChange?.(eventDetails.event, newValue, eventDetails); + }, + [onChange], + ); + + return ( + clsx(className, classes.root, state.disabled && classes.disabled)} + onValueChange={handleValueChange} + style={style} + {...other} + /> + ); +}); + +MenuPreviewRadioGroup.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * The uncontrolled value of the radio item that should be initially selected. + */ + defaultValue: PropTypes.any, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * Function called when the selected value changes. + */ + onChange: PropTypes.func, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * The controlled value of the radio item that should be currently selected. + */ + value: PropTypes.any, +} as any; + +export default MenuPreviewRadioGroup; diff --git a/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts b/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts new file mode 100644 index 00000000000000..f4e14f49d9bc87 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewRadioGroup'; +export * from './MenuPreviewRadioGroup'; +export { + menuPreviewRadioGroupClasses, + getMenuPreviewRadioGroupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewRadioGroupClasses, + MenuPreviewRadioGroupClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioGroup/index.js b/packages/mui-material/src/MenuPreviewRadioGroup/index.js new file mode 100644 index 00000000000000..a5e718613154f1 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioGroup/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewRadioGroup'; +export * from './MenuPreviewRadioGroup'; +export { + menuPreviewRadioGroupClasses, + getMenuPreviewRadioGroupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx b/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx new file mode 100644 index 00000000000000..5a76496c40712d --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx @@ -0,0 +1,256 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import ListContext from '../List/ListContext'; +import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + isMenuPreviewRootNativeButton, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewItemClassName, + getMenuPreviewItemOwnerState, + MenuPreviewItemBaseProps, + MenuPreviewItemOwnerState, + MenuPreviewItemVisualProps, + menuPreviewItemOverridesResolver, + useMenuPreviewItemUtilityClasses, +} from '../MenuPreview/menuPreviewItemShared'; +import { + getMenuPreviewRadioItemUtilityClass, + menuPreviewRadioItemClasses, + MenuPreviewRadioItemClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewRadioItemSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewRadioItemSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewRadioItemProps + extends + Omit, + MenuPreviewItemBaseProps, + MenuPreviewItemVisualProps< + MenuPreviewRadioItemClasses, + MenuPreviewRadioItemSlots, + MenuPreviewRadioItemSlotProps + > { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Value of the radio item. + */ + value: any; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick?: boolean | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; +} + +const MenuPreviewRadioItemRoot = styled('div', { + name: 'MuiMenuPreviewRadioItem', + slot: 'Root', + overridesResolver: menuPreviewItemOverridesResolver, +})<{ ownerState: MenuPreviewItemOwnerState }>( + memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewRadioItemClasses)), +); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewRadioItem API](https://mui.com/material-ui/api/menu-preview-radio-item/) + */ +const MenuPreviewRadioItem = React.forwardRef(function MenuPreviewRadioItem( + inProps: MenuPreviewRadioItemProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewRadioItem', + }); + + const { + className, + classes: classesProp, + component, + dense = false, + disabled = false, + disableGutters = false, + divider = false, + nativeButton: nativeButtonProp, + selected = false, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + ...getMenuPreviewItemOwnerState({ dense, disabled, disableGutters, divider, selected }), + classes: classesProp, + }; + const classes = useMenuPreviewItemUtilityClasses( + ownerState, + getMenuPreviewRadioItemUtilityClass, + ); + const childContext = React.useMemo( + () => ({ + dense, + disableGutters, + }), + [dense, disableGutters], + ); + const RootSlot = slots?.root ?? MenuPreviewRadioItemRoot; + + return ( + + + clsx( + className, + getMenuPreviewItemClassName(classes, ownerState, state), + state.checked && classes.checked, + ) + } + disabled={disabled} + nativeButton={nativeButtonProp ?? isMenuPreviewRootNativeButton(RootSlot, component)} + style={style} + {...other} + /> + + ); +}); + +MenuPreviewRadioItem.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * Whether to close the menu when the item is clicked. + * @default false + */ + closeOnClick: PropTypes.bool, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider: PropTypes.bool, + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label: PropTypes.string, + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton: PropTypes.bool, + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * Value of the radio item. + */ + value: PropTypes.any.isRequired, +} as any; + +export default MenuPreviewRadioItem; diff --git a/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts b/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts new file mode 100644 index 00000000000000..05366886438934 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewRadioItem'; +export * from './MenuPreviewRadioItem'; +export { + menuPreviewRadioItemClasses, + getMenuPreviewRadioItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewRadioItemClasses, + MenuPreviewRadioItemClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItem/index.js b/packages/mui-material/src/MenuPreviewRadioItem/index.js new file mode 100644 index 00000000000000..fa1950c32d8e36 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItem/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewRadioItem'; +export * from './MenuPreviewRadioItem'; +export { + menuPreviewRadioItemClasses, + getMenuPreviewRadioItemUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx b/packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx new file mode 100644 index 00000000000000..09c7affeb4424d --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx @@ -0,0 +1,223 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { menuPreviewIndicatorStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewRadioItemIndicatorUtilityClass, + MenuPreviewRadioItemIndicatorClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewRadioItemIndicatorSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewRadioItemIndicatorSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewRadioItemIndicatorProps extends Omit< + BaseMenu.RadioItemIndicator.Props, + 'className' | 'render' | 'style' +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Whether to keep the HTML element in the DOM when the radio item is inactive. + * @default false + */ + keepMounted?: boolean | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewRadioItemIndicatorSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewRadioItemIndicatorSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewRadioItemIndicatorProps) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + checked: ['checked'], + disabled: ['disabled'], + highlighted: ['highlighted'], + }; + + return { + ...classes, + ...composeClasses(slots, getMenuPreviewRadioItemIndicatorUtilityClass, classes), + }; +}; + +const MenuPreviewRadioItemIndicatorRoot = styled('span', { + name: 'MuiMenuPreviewRadioItemIndicator', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})(menuPreviewIndicatorStyles) as any; + +function DefaultRadioIndicatorIcon() { + return ( + + ); +} + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewRadioItemIndicator API](https://mui.com/material-ui/api/menu-preview-radio-item-indicator/) + */ +const MenuPreviewRadioItemIndicator = React.forwardRef(function MenuPreviewRadioItemIndicator( + inProps: MenuPreviewRadioItemIndicatorProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewRadioItemIndicator', + }); + + const { + children, + className, + classes: classesProp, + component, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + clsx( + className, + classes.root, + state.checked && classes.checked, + state.disabled && classes.disabled, + state.highlighted && classes.highlighted, + ) + } + style={style} + {...other} + > + {children ?? } + + ); +}); + +MenuPreviewRadioItemIndicator.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * Whether to keep the HTML element in the DOM when the radio item is inactive. + * @default false + */ + keepMounted: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewRadioItemIndicator; diff --git a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts new file mode 100644 index 00000000000000..4fa829e8dedd1c --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewRadioItemIndicator'; +export * from './MenuPreviewRadioItemIndicator'; +export { + menuPreviewRadioItemIndicatorClasses, + getMenuPreviewRadioItemIndicatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewRadioItemIndicatorClasses, + MenuPreviewRadioItemIndicatorClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js new file mode 100644 index 00000000000000..32b270dcd36da5 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewRadioItemIndicator'; +export * from './MenuPreviewRadioItemIndicator'; +export { + menuPreviewRadioItemIndicatorClasses, + getMenuPreviewRadioItemIndicatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx b/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx new file mode 100644 index 00000000000000..6a01bce0a07c73 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx @@ -0,0 +1,184 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Separator as BaseSeparator } from '@base-ui/react/separator'; +import { SxProps } from '@mui/system'; +import Divider from '../Divider'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + getMenuPreviewRootRender, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewSeparatorUtilityClass, + MenuPreviewSeparatorClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewSeparatorSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewSeparatorSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewSeparatorProps extends Omit< + BaseSeparator.Props, + 'className' | 'render' | 'style' +> { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewSeparatorSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewSeparatorSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +const useUtilityClasses = (ownerState: MenuPreviewSeparatorProps) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getMenuPreviewSeparatorUtilityClass, classes); +}; + +const MenuPreviewSeparatorRoot = styled(Divider, { + name: 'MuiMenuPreviewSeparator', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({}) as any; + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewSeparator API](https://mui.com/material-ui/api/menu-preview-separator/) + */ +const MenuPreviewSeparator = React.forwardRef(function MenuPreviewSeparator( + inProps: MenuPreviewSeparatorProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewSeparator', + }); + + const { + className, + classes: classesProp, + component, + orientation = 'horizontal', + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + classes: classesProp, + orientation, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +MenuPreviewSeparator.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * The orientation of the separator. + * @default 'horizontal' + */ + orientation: PropTypes.oneOf(['horizontal', 'vertical']), + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewSeparator; diff --git a/packages/mui-material/src/MenuPreviewSeparator/index.d.ts b/packages/mui-material/src/MenuPreviewSeparator/index.d.ts new file mode 100644 index 00000000000000..d39696b5ea6141 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSeparator/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewSeparator'; +export * from './MenuPreviewSeparator'; +export { + menuPreviewSeparatorClasses, + getMenuPreviewSeparatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewSeparatorClasses, + MenuPreviewSeparatorClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSeparator/index.js b/packages/mui-material/src/MenuPreviewSeparator/index.js new file mode 100644 index 00000000000000..b610767aee6389 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSeparator/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewSeparator'; +export * from './MenuPreviewSeparator'; +export { + menuPreviewSeparatorClasses, + getMenuPreviewSeparatorUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx new file mode 100644 index 00000000000000..752bd5347ad8d7 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx @@ -0,0 +1,389 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import composeClasses from '@mui/utils/composeClasses'; +import HTMLElementType from '@mui/utils/HTMLElementType'; +import { SxProps } from '@mui/system'; +import Paper from '../Paper'; +import List from '../List'; +import { styled } from '../zero-styled'; +import { Theme } from '../styles'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + MenuPreviewPopupBase, + MenuPreviewPopupPublicProps, + MenuPreviewPopupSharedProps, + MenuPreviewPopupSharedSlotProps, + MenuPreviewPopupSharedSlots, +} from '../MenuPreview/menuPreviewPopupShared'; +import { + menuPreviewPopupListStyles, + menuPreviewPopupPaperStyles, +} from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewSubmenuPopupUtilityClass, + MenuPreviewSubmenuPopupClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewSubmenuPopupProps extends Omit< + MenuPreviewPopupSharedProps, + | 'classes' + | 'defaultPositionerProps' + | 'defaultSlots' + | 'ownerState' + | keyof MenuPreviewPopupPublicProps +> { + /** + * The submenu items. + */ + children?: React.ReactNode; + /** + * CSS class applied to the Base UI popup element. + */ + className?: MenuPreviewPopupPublicProps['className']; + /** + * Styles applied to the Base UI popup element. + */ + style?: MenuPreviewPopupPublicProps['style']; + /** + * An element to position the popup against. + * + * By default, the popup is positioned against the submenu trigger. + */ + anchor?: MenuPreviewPopupPublicProps['anchor']; + /** + * Determines which CSS `position` property to use. + * @default 'absolute' + */ + positionMethod?: MenuPreviewPopupPublicProps['positionMethod']; + /** + * Which side of the anchor element to align the popup against. + * @default 'inline-end' + */ + side?: MenuPreviewPopupPublicProps['side']; + /** + * Distance between the anchor and the popup in pixels. + * @default 0 + */ + sideOffset?: MenuPreviewPopupPublicProps['sideOffset']; + /** + * How to align the popup relative to the specified side. + * @default 'start' + */ + align?: MenuPreviewPopupPublicProps['align']; + /** + * Additional offset along the alignment axis in pixels. + * @default 0 + */ + alignOffset?: MenuPreviewPopupPublicProps['alignOffset']; + /** + * An element or a rectangle that delimits the area that the popup is confined to. + * @default 'clipping-ancestors' + */ + collisionBoundary?: MenuPreviewPopupPublicProps['collisionBoundary']; + /** + * Additional space to maintain from the edge of the collision boundary. + * @default 5 + */ + collisionPadding?: MenuPreviewPopupPublicProps['collisionPadding']; + /** + * Minimum distance to maintain between the arrow and the edges of the popup. + * @default 5 + */ + arrowPadding?: MenuPreviewPopupPublicProps['arrowPadding']; + /** + * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. + * @default false + */ + sticky?: MenuPreviewPopupPublicProps['sticky']; + /** + * Whether to disable the popup from tracking layout shifts of its positioning anchor. + * @default false + */ + disableAnchorTracking?: MenuPreviewPopupPublicProps['disableAnchorTracking']; + /** + * Determines how to handle collisions when positioning the popup. + */ + collisionAvoidance?: MenuPreviewPopupPublicProps['collisionAvoidance']; + /** + * The container element to portal the popup into. + */ + container?: MenuPreviewPopupPublicProps['container']; + /** + * Whether to keep the portal mounted in the DOM while the popup is hidden. + * @default false + */ + keepMounted?: MenuPreviewPopupPublicProps['keepMounted']; + /** + * Determines the element to focus when the menu is closed. + */ + finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewSubmenuPopupSlotProps | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewSubmenuPopupSlots | undefined; +} + +export interface MenuPreviewSubmenuPopupOwnerState extends MenuPreviewSubmenuPopupProps {} + +export interface MenuPreviewSubmenuPopupSlots extends MenuPreviewPopupSharedSlots {} + +export interface MenuPreviewSubmenuPopupSlotProps extends MenuPreviewPopupSharedSlotProps {} + +const useUtilityClasses = (ownerState: MenuPreviewSubmenuPopupOwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + paper: ['paper'], + list: ['list'], + }; + + return composeClasses(slots, getMenuPreviewSubmenuPopupUtilityClass, classes); +}; + +const MenuPreviewSubmenuPopupRoot = styled('div', { + name: 'MuiMenuPreviewSubmenuPopup', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({ + outline: 0, +}); + +const MenuPreviewSubmenuPopupPaper = styled(Paper, { + name: 'MuiMenuPreviewSubmenuPopup', + slot: 'Paper', + overridesResolver: (props, styles) => styles.paper, +})(menuPreviewPopupPaperStyles); + +const MenuPreviewSubmenuPopupList = styled(List, { + name: 'MuiMenuPreviewSubmenuPopup', + slot: 'List', + overridesResolver: (props, styles) => styles.list, +})(menuPreviewPopupListStyles); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewSubmenuPopup API](https://mui.com/material-ui/api/menu-preview-submenu-popup/) + */ +const MenuPreviewSubmenuPopup = React.forwardRef(function MenuPreviewSubmenuPopup( + inProps: MenuPreviewSubmenuPopupProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewSubmenuPopup', + }); + + const ownerState: MenuPreviewSubmenuPopupOwnerState = { + side: 'inline-end', + align: 'start', + ...props, + }; + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +MenuPreviewSubmenuPopup.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * How to align the popup relative to the specified side. + * @default 'start' + */ + align: PropTypes.oneOf(['center', 'end', 'start']), + /** + * Additional offset along the alignment axis in pixels. + * @default 0 + */ + alignOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), + /** + * An element to position the popup against. + * + * By default, the popup is positioned against the submenu trigger. + */ + anchor: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + HTMLElementType, + PropTypes.object, + PropTypes.func, + ]), + /** + * Minimum distance to maintain between the arrow and the edges of the popup. + * @default 5 + */ + arrowPadding: PropTypes.number, + /** + * The submenu items. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the Base UI popup element. + */ + className: PropTypes.string, + /** + * Determines how to handle collisions when positioning the popup. + */ + collisionAvoidance: PropTypes.oneOfType([ + PropTypes.shape({ + align: PropTypes.oneOf(['flip', 'none', 'shift']), + fallbackAxisSide: PropTypes.oneOf(['end', 'none', 'start']), + side: PropTypes.oneOf(['flip', 'none']), + }), + PropTypes.shape({ + align: PropTypes.oneOf(['none', 'shift']), + fallbackAxisSide: PropTypes.oneOf(['end', 'none', 'start']), + side: PropTypes.oneOf(['none', 'shift']), + }), + ]), + /** + * An element or a rectangle that delimits the area that the popup is confined to. + * @default 'clipping-ancestors' + */ + collisionBoundary: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['clipping-ancestors']), + HTMLElementType, + PropTypes.arrayOf(HTMLElementType), + PropTypes.shape({ + height: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + }), + ]), + /** + * Additional space to maintain from the edge of the collision boundary. + * @default 5 + */ + collisionPadding: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + bottom: PropTypes.number, + left: PropTypes.number, + right: PropTypes.number, + top: PropTypes.number, + }), + ]), + /** + * The container element to portal the popup into. + */ + container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + HTMLElementType, + PropTypes.object, + PropTypes.func, + ]), + /** + * Whether to disable the popup from tracking layout shifts of its positioning anchor. + * @default false + */ + disableAnchorTracking: PropTypes.bool, + /** + * Determines the element to focus when the menu is closed. + */ + finalFocus: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: HTMLElementType, + }), + PropTypes.bool, + ]), + /** + * Whether to keep the portal mounted in the DOM while the popup is hidden. + * @default false + */ + keepMounted: PropTypes.bool, + /** + * Determines which CSS `position` property to use. + * @default 'absolute' + */ + positionMethod: PropTypes.oneOf(['absolute', 'fixed']), + /** + * Which side of the anchor element to align the popup against. + * @default 'inline-end' + */ + side: PropTypes.oneOf(['bottom', 'inline-end', 'inline-start', 'left', 'right', 'top']), + /** + * Distance between the anchor and the popup in pixels. + * @default 0 + */ + sideOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + list: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + popup: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + portal: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + positioner: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + list: PropTypes.elementType, + paper: PropTypes.elementType, + popup: PropTypes.elementType, + portal: PropTypes.elementType, + positioner: PropTypes.elementType, + }), + /** + * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. + * @default false + */ + sticky: PropTypes.bool, + /** + * Styles applied to the Base UI popup element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewSubmenuPopup; diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts new file mode 100644 index 00000000000000..ee1f99c4e671e0 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewSubmenuPopup'; +export * from './MenuPreviewSubmenuPopup'; +export { + menuPreviewSubmenuPopupClasses, + getMenuPreviewSubmenuPopupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewSubmenuPopupClasses, + MenuPreviewSubmenuPopupClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js new file mode 100644 index 00000000000000..0941a947811103 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewSubmenuPopup'; +export * from './MenuPreviewSubmenuPopup'; +export { + menuPreviewSubmenuPopupClasses, + getMenuPreviewSubmenuPopupUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx b/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx new file mode 100644 index 00000000000000..11ee567dbc9571 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx @@ -0,0 +1,132 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { useDefaultProps } from '../DefaultPropsProvider'; + +export interface MenuPreviewSubmenuRootProps { + /** + * The content of the submenu. + */ + children?: React.ReactNode; + /** + * Whether the submenu is initially open. + * + * To render a controlled submenu, use the `open` prop instead. + * @default false + */ + defaultOpen?: boolean | undefined; + /** + * Whether the submenu is currently open. + */ + open?: boolean | undefined; + /** + * Event handler called when the submenu is opened or closed. + */ + onOpenChange?: BaseMenu.SubmenuRoot.Props['onOpenChange']; + /** + * Event handler called after any animations complete when the submenu is opened or closed. + */ + onOpenChangeComplete?: BaseMenu.SubmenuRoot.Props['onOpenChangeComplete']; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Whether to loop keyboard focus back to the first item. + * @default true + */ + loopFocus?: boolean | undefined; + /** + * Whether moving the pointer over items should highlight them. + * @default true + */ + highlightItemOnHover?: boolean | undefined; + /** + * The visual orientation of the submenu. + * @default 'vertical' + */ + orientation?: 'horizontal' | 'vertical' | undefined; + /** + * When in a submenu, determines whether pressing the Escape key closes the entire menu. + * @default false + */ + closeParentOnEsc?: boolean | undefined; +} + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewSubmenuRoot API](https://mui.com/material-ui/api/menu-preview-submenu-root/) + */ +function MenuPreviewSubmenuRoot(props: MenuPreviewSubmenuRootProps): React.JSX.Element { + const themedProps = useDefaultProps({ + props, + name: 'MuiMenuPreviewSubmenuRoot', + }); + + return ; +} + +MenuPreviewSubmenuRoot.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the submenu. + */ + children: PropTypes.node, + /** + * When in a submenu, determines whether pressing the Escape key closes the entire menu. + * @default false + */ + closeParentOnEsc: PropTypes.bool, + /** + * Whether the submenu is initially open. + * + * To render a controlled submenu, use the `open` prop instead. + * @default false + */ + defaultOpen: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * Whether moving the pointer over items should highlight them. + * @default true + */ + highlightItemOnHover: PropTypes.bool, + /** + * Whether to loop keyboard focus back to the first item. + * @default true + */ + loopFocus: PropTypes.bool, + /** + * Event handler called when the submenu is opened or closed. + */ + onOpenChange: PropTypes.func, + /** + * Event handler called after any animations complete when the submenu is opened or closed. + */ + onOpenChangeComplete: PropTypes.func, + /** + * Whether the submenu is currently open. + */ + open: PropTypes.bool, + /** + * The visual orientation of the submenu. + * @default 'vertical' + */ + orientation: PropTypes.oneOf(['horizontal', 'vertical']), +} as any; + +export default MenuPreviewSubmenuRoot; diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts new file mode 100644 index 00000000000000..df51cc07534051 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts @@ -0,0 +1,2 @@ +export { default } from './MenuPreviewSubmenuRoot'; +export * from './MenuPreviewSubmenuRoot'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js new file mode 100644 index 00000000000000..df51cc07534051 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js @@ -0,0 +1,2 @@ +export { default } from './MenuPreviewSubmenuRoot'; +export * from './MenuPreviewSubmenuRoot'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx b/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx new file mode 100644 index 00000000000000..66c56f538bc352 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx @@ -0,0 +1,274 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import ListContext from '../List/ListContext'; +import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { + getMenuPreviewRootRender, + isMenuPreviewRootNativeButton, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewItemClassName, + getMenuPreviewItemOwnerState, + MenuPreviewItemOwnerState, + MenuPreviewItemVisualProps, + MenuPreviewSubmenuTriggerBaseProps, + menuPreviewItemOverridesResolver, + useMenuPreviewItemUtilityClasses, +} from '../MenuPreview/menuPreviewItemShared'; +import { + getMenuPreviewSubmenuTriggerUtilityClass, + menuPreviewSubmenuTriggerClasses, + MenuPreviewSubmenuTriggerClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewSubmenuTriggerSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewSubmenuTriggerSlotProps extends MenuPreviewRootSlotProps {} + +export interface MenuPreviewSubmenuTriggerProps + extends + Omit, + MenuPreviewSubmenuTriggerBaseProps, + MenuPreviewItemVisualProps< + MenuPreviewSubmenuTriggerClasses, + MenuPreviewSubmenuTriggerSlots, + MenuPreviewSubmenuTriggerSlotProps + > { + /** + * The content of the component. + */ + children?: React.ReactNode; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label?: string | undefined; + /** + * How long to wait before the submenu may be opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 100 + */ + delay?: number | undefined; + /** + * How long to wait before closing the submenu that was opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 0 + */ + closeDelay?: number | undefined; + /** + * Whether the submenu should also open when the trigger is hovered. + */ + openOnHover?: boolean | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; +} + +const MenuPreviewSubmenuTriggerRoot = styled('div', { + name: 'MuiMenuPreviewSubmenuTrigger', + slot: 'Root', + overridesResolver: menuPreviewItemOverridesResolver, +})<{ ownerState: MenuPreviewItemOwnerState }>( + memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewSubmenuTriggerClasses)), +); + +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewSubmenuTrigger API](https://mui.com/material-ui/api/menu-preview-submenu-trigger/) + */ +const MenuPreviewSubmenuTrigger = React.forwardRef(function MenuPreviewSubmenuTrigger( + inProps: MenuPreviewSubmenuTriggerProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewSubmenuTrigger', + }); + + const { + className, + classes: classesProp, + component, + dense = false, + disabled = false, + disableGutters = false, + divider = false, + nativeButton: nativeButtonProp, + selected = false, + slotProps, + slots, + sx, + style, + ...other + } = props; + const ownerState = { + ...props, + ...getMenuPreviewItemOwnerState({ dense, disabled, disableGutters, divider, selected }), + classes: classesProp, + }; + const classes = useMenuPreviewItemUtilityClasses( + ownerState, + getMenuPreviewSubmenuTriggerUtilityClass, + ); + const childContext = React.useMemo( + () => ({ + dense, + disableGutters, + }), + [dense, disableGutters], + ); + const RootSlot = slots?.root ?? MenuPreviewSubmenuTriggerRoot; + + return ( + + + clsx( + className, + getMenuPreviewItemClassName(classes, ownerState, state), + state.open && classes.open, + ) + } + disabled={disabled} + nativeButton={nativeButtonProp ?? isMenuPreviewRootNativeButton(RootSlot, component)} + style={style} + {...other} + /> + + ); +}); + +MenuPreviewSubmenuTrigger.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * How long to wait before closing the submenu that was opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 0 + */ + closeDelay: PropTypes.number, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * How long to wait before the submenu may be opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 100 + */ + delay: PropTypes.number, + /** + * If `true`, compact vertical padding designed for keyboard and mouse input is used. + * @default false + */ + dense: PropTypes.bool, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, the left and right padding is removed. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, a 1px light border is added to the bottom of the menu item. + * @default false + */ + divider: PropTypes.bool, + /** + * Overrides the text label to use when the item is matched during keyboard text navigation. + */ + label: PropTypes.string, + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton: PropTypes.bool, + /** + * Whether the submenu should also open when the trigger is hovered. + */ + openOnHover: PropTypes.bool, + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewSubmenuTrigger; diff --git a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts new file mode 100644 index 00000000000000..c635b6f0a1818e --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewSubmenuTrigger'; +export * from './MenuPreviewSubmenuTrigger'; +export { + menuPreviewSubmenuTriggerClasses, + getMenuPreviewSubmenuTriggerUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewSubmenuTriggerClasses, + MenuPreviewSubmenuTriggerClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js new file mode 100644 index 00000000000000..21cf2aafd9dc18 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewSubmenuTrigger'; +export * from './MenuPreviewSubmenuTrigger'; +export { + menuPreviewSubmenuTriggerClasses, + getMenuPreviewSubmenuTriggerUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx b/packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx new file mode 100644 index 00000000000000..309c8a58b37bb6 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx @@ -0,0 +1,270 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { SxProps } from '@mui/system'; +import Button, { ButtonProps } from '../Button'; +import { Theme } from '../styles'; +import { styled } from '../zero-styled'; +import { useDefaultProps } from '../DefaultPropsProvider'; +import { + getMenuPreviewRootRender, + isMenuPreviewRootNativeButton, + MenuPreviewRootSlotProps, + MenuPreviewRootSlots, + resolveSlotProps, +} from '../MenuPreview/menuPreviewUtils'; +import { + getMenuPreviewTriggerUtilityClass, + MenuPreviewTriggerClasses, +} from '../MenuPreview/menuPreviewClasses'; + +export interface MenuPreviewTriggerSlots extends MenuPreviewRootSlots {} + +export interface MenuPreviewTriggerProps + extends + Omit< + BaseMenu.Trigger.Props, + 'className' | 'handle' | 'nativeButton' | 'payload' | 'render' | 'style' + >, + Omit< + ButtonProps, + keyof BaseMenu.Trigger.Props | 'classes' | 'component' | 'disabled' | 'href' | 'style' + > { + /** + * The component used for the root node. + */ + component?: React.ElementType | undefined; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial | undefined; + /** + * CSS class applied to the element. + */ + className?: string | undefined; + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled?: boolean | undefined; + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton?: boolean | undefined; + /** + * How long to wait before the menu may be opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 100 + */ + delay?: number | undefined; + /** + * How long to wait before closing the menu that was opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 0 + */ + closeDelay?: number | undefined; + /** + * Whether the menu should also open when the trigger is hovered. + */ + openOnHover?: boolean | undefined; + /** + * The components used for each slot inside. + */ + slots?: MenuPreviewTriggerSlots | undefined; + /** + * The props used for each slot inside. + */ + slotProps?: MenuPreviewTriggerSlotProps | undefined; + /** + * Styles applied to the root element. + */ + style?: React.CSSProperties | undefined; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps | undefined; +} + +interface MenuPreviewTriggerOwnerState extends MenuPreviewTriggerProps { + disabled: boolean; +} + +export interface MenuPreviewTriggerSlotProps extends MenuPreviewRootSlotProps {} + +const useUtilityClasses = (ownerState: MenuPreviewTriggerOwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + disabled: ['disabled'], + open: ['open'], + }; + + return { + ...classes, + ...composeClasses(slots, getMenuPreviewTriggerUtilityClass, classes), + }; +}; + +const MenuPreviewTriggerRoot = styled(Button, { + name: 'MuiMenuPreviewTrigger', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({}) as any; + +const BaseMenuTrigger = BaseMenu.Trigger as any; +/** + * + * Demos: + * + * - [Menu](https://mui.com/material-ui/react-menu/) + * + * API: + * + * - [MenuPreviewTrigger API](https://mui.com/material-ui/api/menu-preview-trigger/) + */ +const MenuPreviewTrigger = React.forwardRef(function MenuPreviewTrigger( + inProps: MenuPreviewTriggerProps, + ref: React.ForwardedRef, +) { + const props = useDefaultProps({ + props: inProps, + name: 'MuiMenuPreviewTrigger', + }); + + const { href: ignoredHref, ...propsWithoutHref } = props as MenuPreviewTriggerProps & { + href?: unknown; + }; + void ignoredHref; + + const { + className, + classes: classesProp, + component, + disabled = false, + nativeButton: nativeButtonProp, + slotProps, + slots, + sx, + style, + ...other + } = propsWithoutHref; + const ownerState = { + ...propsWithoutHref, + classes: classesProp, + disabled, + }; + const classes = useUtilityClasses(ownerState); + const RootSlot = slots?.root ?? MenuPreviewTriggerRoot; + + return ( + + clsx( + className, + classes.root, + state.open && classes.open, + state.disabled && classes.disabled, + ) + } + nativeButton={nativeButtonProp ?? isMenuPreviewRootNativeButton(RootSlot, component, true)} + style={style} + {...other} + /> + ); +}) as ((props: MenuPreviewTriggerProps & React.RefAttributes) => React.JSX.Element) & { + propTypes?: any; +}; + +MenuPreviewTrigger.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * CSS class applied to the element. + */ + className: PropTypes.string, + /** + * How long to wait before closing the menu that was opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 0 + */ + closeDelay: PropTypes.number, + /** + * The component used for the root node. + */ + component: PropTypes.elementType, + /** + * How long to wait before the menu may be opened on hover, in milliseconds. + * + * Requires the `openOnHover` prop. + * @default 100 + */ + delay: PropTypes.number, + /** + * Whether the component should ignore user interaction. + * @default false + */ + disabled: PropTypes.bool, + /** + * Whether the component is rendered as a native button. + * + * By default, this is inferred from the root slot and `component` prop. + */ + nativeButton: PropTypes.bool, + /** + * Whether the menu should also open when the trigger is hovered. + */ + openOnHover: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * Styles applied to the root element. + */ + style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export default MenuPreviewTrigger; diff --git a/packages/mui-material/src/MenuPreviewTrigger/index.d.ts b/packages/mui-material/src/MenuPreviewTrigger/index.d.ts new file mode 100644 index 00000000000000..ea8d0c4a9ab413 --- /dev/null +++ b/packages/mui-material/src/MenuPreviewTrigger/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './MenuPreviewTrigger'; +export * from './MenuPreviewTrigger'; +export { + menuPreviewTriggerClasses, + getMenuPreviewTriggerUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; +export type { + MenuPreviewTriggerClasses, + MenuPreviewTriggerClassKey, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewTrigger/index.js b/packages/mui-material/src/MenuPreviewTrigger/index.js new file mode 100644 index 00000000000000..0b40dbfdcb13dd --- /dev/null +++ b/packages/mui-material/src/MenuPreviewTrigger/index.js @@ -0,0 +1,6 @@ +export { default } from './MenuPreviewTrigger'; +export * from './MenuPreviewTrigger'; +export { + menuPreviewTriggerClasses, + getMenuPreviewTriggerUtilityClass, +} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/styles/components.ts b/packages/mui-material/src/styles/components.ts index 3404eaee7c94f0..3e859ef603969e 100644 --- a/packages/mui-material/src/styles/components.ts +++ b/packages/mui-material/src/styles/components.ts @@ -477,6 +477,116 @@ export interface Components { variants?: ComponentsVariants['MuiMenuList'] | undefined; } | undefined; + MuiMenuPreview?: + | { + defaultProps?: ComponentsProps['MuiMenuPreview'] | undefined; + } + | undefined; + MuiMenuPreviewCheckboxItem?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewCheckboxItem'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewCheckboxItem'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewCheckboxItem'] | undefined; + } + | undefined; + MuiMenuPreviewCheckboxItemIndicator?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewCheckboxItemIndicator'] | undefined; + styleOverrides?: + | ComponentsOverrides['MuiMenuPreviewCheckboxItemIndicator'] + | undefined; + variants?: ComponentsVariants['MuiMenuPreviewCheckboxItemIndicator'] | undefined; + } + | undefined; + MuiMenuPreviewGroup?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewGroup'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewGroup'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewGroup'] | undefined; + } + | undefined; + MuiMenuPreviewGroupLabel?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewGroupLabel'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewGroupLabel'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewGroupLabel'] | undefined; + } + | undefined; + MuiMenuPreviewItem?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewItem'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewItem'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewItem'] | undefined; + } + | undefined; + MuiMenuPreviewLinkItem?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewLinkItem'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewLinkItem'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewLinkItem'] | undefined; + } + | undefined; + MuiMenuPreviewPopup?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewPopup'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewPopup'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewPopup'] | undefined; + } + | undefined; + MuiMenuPreviewRadioGroup?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewRadioGroup'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewRadioGroup'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewRadioGroup'] | undefined; + } + | undefined; + MuiMenuPreviewRadioItem?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewRadioItem'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewRadioItem'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewRadioItem'] | undefined; + } + | undefined; + MuiMenuPreviewRadioItemIndicator?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewRadioItemIndicator'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewRadioItemIndicator'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewRadioItemIndicator'] | undefined; + } + | undefined; + MuiMenuPreviewSeparator?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewSeparator'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewSeparator'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewSeparator'] | undefined; + } + | undefined; + MuiMenuPreviewSubmenuPopup?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewSubmenuPopup'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewSubmenuPopup'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewSubmenuPopup'] | undefined; + } + | undefined; + MuiMenuPreviewSubmenuRoot?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewSubmenuRoot'] | undefined; + } + | undefined; + MuiMenuPreviewSubmenuTrigger?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewSubmenuTrigger'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewSubmenuTrigger'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewSubmenuTrigger'] | undefined; + } + | undefined; + MuiMenuPreviewTrigger?: + | { + defaultProps?: ComponentsProps['MuiMenuPreviewTrigger'] | undefined; + styleOverrides?: ComponentsOverrides['MuiMenuPreviewTrigger'] | undefined; + variants?: ComponentsVariants['MuiMenuPreviewTrigger'] | undefined; + } + | undefined; MuiMobileStepper?: | { defaultProps?: ComponentsProps['MuiMobileStepper'] | undefined; diff --git a/packages/mui-material/src/styles/overrides.ts b/packages/mui-material/src/styles/overrides.ts index fa9e23fd15ab1a..0006aaddee54a5 100644 --- a/packages/mui-material/src/styles/overrides.ts +++ b/packages/mui-material/src/styles/overrides.ts @@ -67,6 +67,22 @@ import { ListSubheaderClassKey } from '../ListSubheader'; import { MenuClassKey } from '../Menu'; import { MenuItemClassKey } from '../MenuItem'; import { MenuListClassKey } from '../MenuList'; +import { + MenuPreviewCheckboxItemClassKey, + MenuPreviewCheckboxItemIndicatorClassKey, + MenuPreviewGroupClassKey, + MenuPreviewGroupLabelClassKey, + MenuPreviewItemClassKey, + MenuPreviewLinkItemClassKey, + MenuPreviewPopupClassKey, + MenuPreviewRadioGroupClassKey, + MenuPreviewRadioItemClassKey, + MenuPreviewRadioItemIndicatorClassKey, + MenuPreviewSeparatorClassKey, + MenuPreviewSubmenuPopupClassKey, + MenuPreviewSubmenuTriggerClassKey, + MenuPreviewTriggerClassKey, +} from '../MenuPreview'; import { MobileStepperClassKey } from '../MobileStepper'; import { ModalClassKey } from '../Modal'; import { NativeSelectClassKey } from '../NativeSelect'; @@ -211,6 +227,20 @@ export interface ComponentNameToClassKey { MuiMenu: MenuClassKey; MuiMenuItem: MenuItemClassKey; MuiMenuList: MenuListClassKey; + MuiMenuPreviewCheckboxItem: MenuPreviewCheckboxItemClassKey; + MuiMenuPreviewCheckboxItemIndicator: MenuPreviewCheckboxItemIndicatorClassKey; + MuiMenuPreviewGroup: MenuPreviewGroupClassKey; + MuiMenuPreviewGroupLabel: MenuPreviewGroupLabelClassKey; + MuiMenuPreviewItem: MenuPreviewItemClassKey; + MuiMenuPreviewLinkItem: MenuPreviewLinkItemClassKey; + MuiMenuPreviewPopup: MenuPreviewPopupClassKey; + MuiMenuPreviewRadioGroup: MenuPreviewRadioGroupClassKey; + MuiMenuPreviewRadioItem: MenuPreviewRadioItemClassKey; + MuiMenuPreviewRadioItemIndicator: MenuPreviewRadioItemIndicatorClassKey; + MuiMenuPreviewSeparator: MenuPreviewSeparatorClassKey; + MuiMenuPreviewSubmenuPopup: MenuPreviewSubmenuPopupClassKey; + MuiMenuPreviewSubmenuTrigger: MenuPreviewSubmenuTriggerClassKey; + MuiMenuPreviewTrigger: MenuPreviewTriggerClassKey; MuiMobileStepper: MobileStepperClassKey; MuiModal: ModalClassKey; MuiNativeSelect: NativeSelectClassKey; diff --git a/packages/mui-material/src/styles/props.ts b/packages/mui-material/src/styles/props.ts index 35d8b8783307af..c60edf2923a04f 100644 --- a/packages/mui-material/src/styles/props.ts +++ b/packages/mui-material/src/styles/props.ts @@ -64,6 +64,22 @@ import { ListProps } from '../List'; import { ListSubheaderProps } from '../ListSubheader'; import { MenuItemProps } from '../MenuItem'; import { MenuListProps } from '../MenuList'; +import { MenuPreviewProps } from '../MenuPreview'; +import { MenuPreviewCheckboxItemProps } from '../MenuPreviewCheckboxItem'; +import { MenuPreviewCheckboxItemIndicatorProps } from '../MenuPreviewCheckboxItemIndicator'; +import { MenuPreviewGroupProps } from '../MenuPreviewGroup'; +import { MenuPreviewGroupLabelProps } from '../MenuPreviewGroupLabel'; +import { MenuPreviewItemProps } from '../MenuPreviewItem'; +import { MenuPreviewLinkItemProps } from '../MenuPreviewLinkItem'; +import { MenuPreviewPopupProps } from '../MenuPreviewPopup'; +import { MenuPreviewRadioGroupProps } from '../MenuPreviewRadioGroup'; +import { MenuPreviewRadioItemProps } from '../MenuPreviewRadioItem'; +import { MenuPreviewRadioItemIndicatorProps } from '../MenuPreviewRadioItemIndicator'; +import { MenuPreviewSeparatorProps } from '../MenuPreviewSeparator'; +import { MenuPreviewSubmenuPopupProps } from '../MenuPreviewSubmenuPopup'; +import { MenuPreviewSubmenuRootProps } from '../MenuPreviewSubmenuRoot'; +import { MenuPreviewSubmenuTriggerProps } from '../MenuPreviewSubmenuTrigger'; +import { MenuPreviewTriggerProps } from '../MenuPreviewTrigger'; import { MenuProps } from '../Menu'; import { MobileStepperProps } from '../MobileStepper'; import { ModalProps } from '../Modal'; @@ -189,6 +205,22 @@ export interface ComponentsPropsList { MuiMenu: MenuProps; MuiMenuItem: MenuItemProps; MuiMenuList: MenuListProps; + MuiMenuPreview: MenuPreviewProps; + MuiMenuPreviewCheckboxItem: MenuPreviewCheckboxItemProps; + MuiMenuPreviewCheckboxItemIndicator: MenuPreviewCheckboxItemIndicatorProps; + MuiMenuPreviewGroup: MenuPreviewGroupProps; + MuiMenuPreviewGroupLabel: MenuPreviewGroupLabelProps; + MuiMenuPreviewItem: MenuPreviewItemProps; + MuiMenuPreviewLinkItem: MenuPreviewLinkItemProps; + MuiMenuPreviewPopup: MenuPreviewPopupProps; + MuiMenuPreviewRadioGroup: MenuPreviewRadioGroupProps; + MuiMenuPreviewRadioItem: MenuPreviewRadioItemProps; + MuiMenuPreviewRadioItemIndicator: MenuPreviewRadioItemIndicatorProps; + MuiMenuPreviewSeparator: MenuPreviewSeparatorProps; + MuiMenuPreviewSubmenuPopup: MenuPreviewSubmenuPopupProps; + MuiMenuPreviewSubmenuRoot: MenuPreviewSubmenuRootProps; + MuiMenuPreviewSubmenuTrigger: MenuPreviewSubmenuTriggerProps; + MuiMenuPreviewTrigger: MenuPreviewTriggerProps; MuiMobileStepper: MobileStepperProps; MuiModal: ModalProps; MuiNativeSelect: NativeSelectProps; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f6083fb3521de..463a3ac3257662 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,8 +7,8 @@ settings: catalogs: docs: '@base-ui/react': - specifier: ^1.4.1 - version: 1.4.1 + specifier: ^1.5.0 + version: 1.5.0 '@docsearch/react': specifier: ^3.9.0 version: 3.9.0 @@ -256,7 +256,7 @@ importers: version: 7.29.2 '@base-ui/react': specifier: catalog:docs - version: 1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.5.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@docsearch/react': specifier: ^3.9.0 version: 3.9.0(@algolia/client-search@5.18.0)(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(search-insights@2.13.0) @@ -697,8 +697,8 @@ importers: specifier: ^7.29.2 version: 7.29.2 '@base-ui/react': - specifier: ^1 - version: 1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^1.5.0 + version: 1.5.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@docsearch/react': specifier: catalog:docs version: 3.9.0(@algolia/client-search@5.18.0)(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(search-insights@2.13.0) @@ -1111,6 +1111,9 @@ importers: '@babel/runtime': specifier: ^7.29.2 version: 7.29.2 + '@base-ui/react': + specifier: ^1.5.0 + version: 1.5.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@emotion/react': specifier: ^11.5.0 version: 11.14.0(@types/react@19.2.14)(react@19.2.6) @@ -2404,8 +2407,8 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@base-ui/react@1.4.1': - resolution: {integrity: sha512-Ab5/LIhcmL8BQcsBUYiOfkSDRdLpvgUBzMK30cu684JPcLclYlztharvCZyNNgzJtbAiREzI9q0pI5erHCMgCw==} + '@base-ui/react@1.5.0': + resolution: {integrity: sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==} engines: {node: '>=14.0.0'} peerDependencies: '@date-fns/tz': ^1.2.0 @@ -2421,16 +2424,6 @@ packages: date-fns: optional: true - '@base-ui/utils@0.2.8': - resolution: {integrity: sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ==} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - '@base-ui/utils@0.2.9': resolution: {integrity: sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw==} peerDependencies: @@ -4374,10 +4367,10 @@ packages: resolution: {integrity: sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==} engines: {node: ^20.17.0 || >=22.9.0} - '@nx/devkit@22.7.5': - resolution: {integrity: sha512-/63ziS7kdHXYTLLhwWBu9hFwoFFT8xf+PkcQjsNdPqc5JmkYkSew0cE/vp5ORgBpGLWWnFPJgmfqjbJoO2C7jA==} + '@nx/devkit@21.6.8': + resolution: {integrity: sha512-N0cj0NqdxY2pcI0IJV+fAu362B6tppdv2ohSBNGacNeSqxfAlJxO5TFZePDmxX5nt0t9hAqT+iasfu4BSYGfZw==} peerDependencies: - nx: '>= 21 <= 23 || ^22.0.0-0' + nx: '>= 20 <= 22' '@nx/nx-darwin-arm64@22.7.5': resolution: {integrity: sha512-eoPtwx0qZqvRUD+VVOHm150AlSYwYoPxkDHBBGqKCn5nzPspb0lLWw8q83crM/L1M928YgK0WmGf3C++7eqsTA==} @@ -5926,6 +5919,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-flatten@3.0.0: + resolution: {integrity: sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==} + array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -5990,6 +5986,9 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -6108,8 +6107,8 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + body-parser@2.0.2: + resolution: {integrity: sha512-SNMk0OONlQ01uk8EPeiBvTW7W4ovpL5b1O3t1sjpPgfxOQ6BqQJ6XjxinDPR79Z6HdcD5zBBwr5ssiTlgdNztQ==} engines: {node: '>=18'} boolbase@1.0.0: @@ -6475,10 +6474,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - content-type@2.0.0: - resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} - engines: {node: '>=18'} - conventional-changelog-angular@7.0.0: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} @@ -6524,8 +6519,8 @@ packages: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} cookie@1.1.1: @@ -6737,6 +6732,14 @@ packages: supports-color: optional: true + debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6745,6 +6748,15 @@ packages: supports-color: optional: true + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -6829,6 +6841,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -6902,6 +6918,11 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + ejs@5.0.1: resolution: {integrity: sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==} engines: {node: '>=0.12.18'} @@ -6926,6 +6947,10 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -7285,8 +7310,8 @@ packages: exponential-backoff@3.1.2: resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + express@5.0.1: + resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} engines: {node: '>= 18'} extend@3.0.2: @@ -7373,6 +7398,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -7381,9 +7409,9 @@ packages: resolution: {integrity: sha512-Ohygw2lDgc2HNynfUu82Jp5U45+OLLeBQcwWbrex1IAbQw0uNaFMUbm5dhYCF6y7jcORgl5tg19/1zYxlJtLmg==} engines: {node: '>=18'} - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} + finalhandler@2.0.0: + resolution: {integrity: sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==} + engines: {node: '>= 0.8'} find-babel-config@2.1.1: resolution: {integrity: sha512-5Ji+EAysHGe1OipH7GN4qDjok5Z1uw5KAwDCbicU/4wyTZY7CqOCzcWbG7J5ad9mazq67k89fXlbc1MuIfl9uA==} @@ -7474,6 +7502,10 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -7617,8 +7649,8 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@11.1.0: - resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true @@ -7825,8 +7857,8 @@ packages: http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} http-proxy-agent@7.0.2: @@ -7852,6 +7884,10 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + iconv-lite@0.5.2: + resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==} + engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -8255,6 +8291,11 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + java-properties@1.0.2: resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} engines: {node: '>= 0.6.0'} @@ -8820,6 +8861,10 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -8843,6 +8888,10 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -9005,8 +9054,16 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@8.0.7: - resolution: {integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.9: @@ -9071,6 +9128,9 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -9653,8 +9713,9 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-to-regexp@8.1.0: + resolution: {integrity: sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==} + engines: {node: '>=16'} path-type@3.0.0: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} @@ -9924,8 +9985,8 @@ packages: resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} engines: {node: '>=20'} - qs@6.15.2: - resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -9946,9 +10007,9 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} raw-loader@4.0.2: resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} @@ -10349,9 +10410,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} + router@2.0.0: + resolution: {integrity: sha512-dIM5zVoG8xhC6rnSN8uoAgFARwTE7BQs8YwHEvK0VCmfxQXMaOuA1uiR1IPwsW7JyK5iTt7Od/TC9StasS2NPQ==} + engines: {node: '>= 0.10'} rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -10444,15 +10505,15 @@ packages: engines: {node: '>=10'} hasBin: true - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + send@1.1.0: + resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==} engines: {node: '>= 18'} serve-handler@6.1.7: resolution: {integrity: sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==} - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + serve-static@2.1.0: + resolution: {integrity: sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==} engines: {node: '>= 18'} serve@14.2.6: @@ -10653,6 +10714,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -11131,9 +11196,13 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} - engines: {node: '>= 18'} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.0.0: + resolution: {integrity: sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==} + engines: {node: '>= 0.6'} typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} @@ -11315,6 +11384,10 @@ packages: util@0.10.4: resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + v8flags@3.2.0: resolution: {integrity: sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==} engines: {node: '>= 0.10'} @@ -12755,10 +12828,10 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.4.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@base-ui/react@1.5.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.8(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@base-ui/utils': 0.2.9(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@floating-ui/utils': 0.2.11 react: 19.2.6 @@ -12767,17 +12840,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@base-ui/utils@0.2.8(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@babel/runtime': 7.29.2 - '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - reselect: 5.2.0 - use-sync-external-store: 1.6.0(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.14 - '@base-ui/utils@0.2.9(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 @@ -14685,7 +14747,7 @@ snapshots: '@npmcli/package-json@7.0.2': dependencies: '@npmcli/git': 7.0.1 - glob: 11.1.0 + glob: 11.0.3 hosted-git-info: 9.0.2 json-parse-even-better-errors: 5.0.0 proc-log: 6.0.0 @@ -14721,12 +14783,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@nx/devkit@22.7.5(nx@22.7.5)': + '@nx/devkit@21.6.8(nx@22.7.5)': dependencies: - '@zkochan/js-yaml': 0.0.7 - ejs: 5.0.1 + ejs: 3.1.10 enquirer: 2.3.6 - minimatch: 10.2.5 + ignore: 5.3.2 + minimatch: 9.0.3 nx: 22.7.5 semver: 7.8.2 tslib: 2.8.1 @@ -15191,9 +15253,9 @@ snapshots: '@slack/web-api': 7.15.1 '@types/express': 5.0.0 axios: 1.16.0 - express: 5.2.1 - path-to-regexp: 8.4.2 - raw-body: 3.0.2 + express: 5.0.1 + path-to-regexp: 8.1.0 + raw-body: 3.0.0 tsscmp: 1.0.6 transitivePeerDependencies: - bufferutil @@ -16243,6 +16305,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-flatten@3.0.0: {} + array-ify@1.0.0: {} array-includes@3.1.9: @@ -16341,6 +16405,8 @@ snapshots: astral-regex@2.0.0: {} + async@3.2.6: {} + asynckit@0.4.0: {} autosuggest-highlight@3.3.4: @@ -16474,17 +16540,18 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@2.2.2: + body-parser@2.0.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.0 + debug: 3.1.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.5.2 on-finished: 2.4.1 - qs: 6.15.2 - raw-body: 3.0.2 - type-is: 2.1.0 + qs: 6.13.0 + raw-body: 3.0.0 + type-is: 1.6.18 transitivePeerDependencies: - supports-color @@ -16555,7 +16622,7 @@ snapshots: dependencies: '@npmcli/fs': 4.0.0 fs-minipass: 3.0.3 - glob: 11.1.0 + glob: 11.0.3 lru-cache: 11.5.1 minipass: 7.1.3 minipass-collect: 2.0.1 @@ -16844,8 +16911,6 @@ snapshots: content-type@1.0.5: {} - content-type@2.0.0: {} - conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 @@ -16911,7 +16976,7 @@ snapshots: cookie-signature@1.2.2: {} - cookie@0.7.2: {} + cookie@0.7.1: {} cookie@1.1.1: {} @@ -17130,10 +17195,18 @@ snapshots: dependencies: ms: 2.0.0 + debug@3.1.0: + dependencies: + ms: 2.0.0 + debug@3.2.7: dependencies: ms: 2.1.3 + debug@4.3.6: + dependencies: + ms: 2.1.2 + debug@4.4.3: dependencies: ms: 2.1.3 @@ -17196,6 +17269,8 @@ snapshots: dequal@2.0.3: {} + destroy@1.2.0: {} + detect-libc@2.1.2: {} devlop@1.1.0: @@ -17269,6 +17344,10 @@ snapshots: ee-first@1.1.1: {} + ejs@3.1.10: + dependencies: + jake: 10.9.2 + ejs@5.0.1: {} electron-to-chromium@1.5.368: {} @@ -17283,6 +17362,8 @@ snapshots: emojis-list@3.0.0: {} + encodeurl@1.0.2: {} + encodeurl@2.0.0: {} encoding@0.1.13: @@ -17847,35 +17928,39 @@ snapshots: exponential-backoff@3.1.2: {} - express@5.2.1: + express@5.0.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.2 + body-parser: 2.0.2 content-disposition: 1.0.0 content-type: 1.0.5 - cookie: 0.7.2 + cookie: 0.7.1 cookie-signature: 1.2.2 - debug: 4.4.3 + debug: 4.3.6 depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1 + finalhandler: 2.0.0 fresh: 2.0.0 - http-errors: 2.0.1 + http-errors: 2.0.0 merge-descriptors: 2.0.0 + methods: 1.1.2 mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.2 + qs: 6.13.0 range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.1.0 + router: 2.0.0 + safe-buffer: 5.2.1 + send: 1.1.0 + serve-static: 2.1.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 2.0.0 + utils-merge: 1.0.1 vary: 1.1.2 transitivePeerDependencies: - supports-color @@ -17961,6 +18046,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + filelist@1.0.4: + dependencies: + minimatch: 5.1.9 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -17969,14 +18058,15 @@ snapshots: dependencies: '@babel/runtime': 7.29.2 - finalhandler@2.1.1: + finalhandler@2.0.0: dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 + debug: 2.6.9 + encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 - statuses: 2.0.2 + statuses: 2.0.1 + unpipe: 1.0.0 transitivePeerDependencies: - supports-color @@ -18065,6 +18155,8 @@ snapshots: forwarded@0.2.0: {} + fresh@0.5.2: {} + fresh@2.0.0: {} fs-constants@1.0.0: {} @@ -18217,7 +18309,7 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@11.1.0: + glob@11.0.3: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 @@ -18244,7 +18336,7 @@ snapshots: glob@9.3.5: dependencies: fs.realpath: 1.0.0 - minimatch: 8.0.7 + minimatch: 8.0.4 minipass: 4.2.8 path-scurry: 1.11.1 @@ -18440,12 +18532,12 @@ snapshots: http-cache-semantics@4.1.1: {} - http-errors@2.0.1: + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 - statuses: 2.0.2 + statuses: 2.0.1 toidentifier: 1.0.1 http-proxy-agent@7.0.2: @@ -18470,6 +18562,10 @@ snapshots: hyphenate-style-name@1.1.0: {} + iconv-lite@0.5.2: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -18810,6 +18906,13 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.5 + java-properties@1.0.2: {} jest-diff@30.2.0: @@ -19058,7 +19161,7 @@ snapshots: '@npmcli/arborist': 9.1.6 '@npmcli/package-json': 7.0.2 '@npmcli/run-script': 10.0.3 - '@nx/devkit': 22.7.5(nx@22.7.5) + '@nx/devkit': 21.6.8(nx@22.7.5) '@octokit/plugin-enterprise-rest': 6.0.1 '@octokit/rest': 20.1.2 aproba: 2.0.0 @@ -19584,6 +19687,8 @@ snapshots: mdn-data@2.27.1: {} + media-typer@0.3.0: {} + media-typer@1.1.0: {} meow@14.1.0: {} @@ -19608,6 +19713,8 @@ snapshots: merge2@1.4.1: {} + methods@1.1.2: {} + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.0.2 @@ -19924,7 +20031,15 @@ snapshots: dependencies: brace-expansion: 1.1.15 - minimatch@8.0.7: + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + + minimatch@8.0.4: + dependencies: + brace-expansion: 2.1.1 + + minimatch@9.0.3: dependencies: brace-expansion: 2.1.1 @@ -19984,6 +20099,8 @@ snapshots: ms@2.0.0: {} + ms@2.1.2: {} + ms@2.1.3: {} multipipe@1.0.2: @@ -20765,7 +20882,7 @@ snapshots: path-to-regexp@3.3.0: {} - path-to-regexp@8.4.2: {} + path-to-regexp@8.1.0: {} path-type@3.0.0: dependencies: @@ -20997,7 +21114,7 @@ snapshots: dependencies: hookified: 1.15.0 - qs@6.15.2: + qs@6.13.0: dependencies: side-channel: 1.1.0 @@ -21011,11 +21128,11 @@ snapshots: range-parser@1.2.1: {} - raw-body@3.0.2: + raw-body@3.0.0: dependencies: bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 unpipe: 1.0.0 raw-loader@4.0.2(webpack@5.107.1(esbuild@0.27.7)(lightningcss@1.32.0)(postcss@8.5.15)): @@ -21629,15 +21746,15 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 - router@2.2.0: + router@2.0.0: dependencies: - debug: 4.4.3 - depd: 2.0.0 + array-flatten: 3.0.0 is-promise: 4.0.0 + methods: 1.1.2 parseurl: 1.3.3 - path-to-regexp: 8.4.2 - transitivePeerDependencies: - - supports-color + path-to-regexp: 8.1.0 + setprototypeof: 1.2.0 + utils-merge: 1.0.1 rrweb-cssom@0.8.0: {} @@ -21722,15 +21839,16 @@ snapshots: semver@7.8.2: {} - send@1.2.1: + send@1.1.0: dependencies: debug: 4.4.3 + destroy: 1.2.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 + fresh: 0.5.2 + http-errors: 2.0.0 + mime-types: 2.1.35 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -21748,12 +21866,12 @@ snapshots: path-to-regexp: 3.3.0 range-parser: 1.2.0 - serve-static@2.2.1: + serve-static@2.1.0: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1 + send: 1.1.0 transitivePeerDependencies: - supports-color @@ -22006,6 +22124,8 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: {} + statuses@2.0.2: {} std-env@4.1.0: {} @@ -22472,9 +22592,14 @@ snapshots: type-fest@3.13.1: {} - type-is@2.1.0: + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.0.0: dependencies: - content-type: 2.0.0 + content-type: 1.0.5 media-typer: 1.1.0 mime-types: 3.0.2 @@ -22725,6 +22850,8 @@ snapshots: dependencies: inherits: 2.0.3 + utils-merge@1.0.1: {} + v8flags@3.2.0: dependencies: homedir-polyfill: 1.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b8dd8576572781..9ae8ef3988a95d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,7 +27,7 @@ trustPolicyIgnoreAfter: 525600 catalogs: docs: stylis: '4.2.0' - '@base-ui/react': '^1.4.1' + '@base-ui/react': '^1.5.0' '@docsearch/react': '^3.9.0' '@emotion/cache': '^11.14.0' '@emotion/react': '^11.14.0' From e591fa63090273dedac4b5fa5906e4ad800ede23 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Thu, 18 Jun 2026 06:52:26 +0800 Subject: [PATCH 02/19] add preview card with popover --- docs/pages/experiments/menu-preview.tsx | 227 ++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 17 deletions(-) diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index 44756c616a4809..ed1892ea0eeb47 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import Container from '@mui/material/Container'; import CssBaseline from '@mui/material/CssBaseline'; +import Popover from '@mui/material/Popover'; import Stack from '@mui/material/Stack'; import Tooltip, { type TooltipProps } from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; @@ -29,7 +30,7 @@ import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; interface MenuSettings { modal: boolean; disabled: boolean; - openOnHover: boolean; + submenusOpenOnHover: boolean; } const theme = createTheme({}); @@ -37,9 +38,54 @@ const theme = createTheme({}); const defaultSettings: MenuSettings = { modal: true, disabled: false, - openOnHover: false, + submenusOpenOnHover: false, }; +interface PreviewCardItem { + id: string; + label: string; + description: string; + footer: string; +} + +const rootPreviewCardItems: PreviewCardItem[] = [ + { + id: 'template-gallery', + label: 'Template gallery', + description: 'Start from a polished document layout for notes, proposals, and project plans.', + footer: 'Opens the template picker', + }, + { + id: 'publish-web', + label: 'Publish to web', + description: 'Create a public read-only page that updates when this document changes.', + footer: 'Requires sharing permission', + }, +]; + +const versionHistoryPreviewCardItems: PreviewCardItem[] = [ + { + id: 'named-versions', + label: 'Named versions', + description: 'Create and manage named checkpoints for important document milestones.', + footer: 'Keeps the current version history', + }, + { + id: 'compare-changes', + label: 'Compare changes', + description: 'Review edits between two versions and inspect who changed each section.', + footer: 'Opens in a side-by-side view', + }, + { + id: 'restore-version', + label: 'Restore version', + description: 'Replace the current document with a selected earlier version.', + footer: 'Creates a new restore checkpoint', + }, +]; + +const previewCardItems = [...rootPreviewCardItems, ...versionHistoryPreviewCardItems]; + const horizontalTooltipProps = { placement: 'right', slotProps: { @@ -98,6 +144,68 @@ function MenuTooltip(props: { ); } +function MaterialPreviewCard(props: { + id: string | undefined; + item: PreviewCardItem | null; + anchorEl: HTMLElement | null; +}) { + const { id, item, anchorEl } = props; + const open = Boolean(item && anchorEl); + + return ( + + {item ? ( + + + {item.label} + + + {item.description} + + + {item.footer} + + + ) : null} + + ); +} + function DisabledTooltip(props: { title: string; children: React.ReactElement }) { const { title, children } = props; @@ -110,6 +218,91 @@ function DisabledTooltip(props: { title: string; children: React.ReactElement }) ); } +function MenuPreviewWithPreviewCardsDemo({ + submenusOpenOnHover, +}: { + submenusOpenOnHover: boolean; +}) { + const previewCardIdPrefix = React.useId(); + const [activeItemId, setActiveItemId] = React.useState(null); + const [anchorEl, setAnchorEl] = React.useState(null); + const activeItem = + previewCardItems.find((previewCardItem) => previewCardItem.id === activeItemId) ?? null; + const activePreviewCardId = activeItem + ? `${previewCardIdPrefix}-${activeItem.id}-preview-card` + : undefined; + + const clearActiveItem = () => { + setActiveItemId(null); + setAnchorEl(null); + }; + + const getPreviewCardProps = (item: PreviewCardItem) => { + const setActiveItem = (element: HTMLElement) => { + setActiveItemId(item.id); + setAnchorEl(element); + }; + + return { + 'aria-describedby': + activeItemId === item.id ? `${previewCardIdPrefix}-${item.id}-preview-card` : undefined, + onFocus: (event: React.FocusEvent) => { + setActiveItem(event.currentTarget); + }, + onMouseEnter: (event: React.MouseEvent) => { + setActiveItem(event.currentTarget); + }, + }; + }; + + return ( + { + if (!open) { + setActiveItemId(null); + setAnchorEl(null); + } + }} + > + }> + Help cards + + + + {rootPreviewCardItems[0].label} + + + + Version history + + + + {versionHistoryPreviewCardItems.map((item) => ( + + {item.label} + + ))} + + + + {rootPreviewCardItems[1].label} + + + + + ); +} + function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { const handleItemClick = React.useCallback((event: React.MouseEvent) => { // eslint-disable-next-line no-console @@ -118,11 +311,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { return ( - } - openOnHover={settings.openOnHover} - > + }> File @@ -142,7 +331,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { - + View options @@ -194,7 +383,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { - + More tools @@ -208,7 +397,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { - + Download @@ -220,7 +409,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { - + Add-ons unavailable @@ -233,7 +422,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { ); } -function MenuPreviewWithTooltipsDemo() { +function MenuPreviewWithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: boolean }) { return ( }> @@ -259,7 +448,7 @@ function MenuPreviewWithTooltipsDemo() { - + View options @@ -349,10 +538,10 @@ export default function MenuPreviewExperiment() {
@@ -361,7 +550,11 @@ export default function MenuPreviewExperiment() {

Material UI Tooltip integrated with every menu item.

- + +
+
+

Material UI Popover used as a PreviewCard-style menu item help card.

+
Base UI Menu API From edcecab3f5a3fa48d67a4b601366610b5ed77756 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Fri, 19 Jun 2026 07:49:10 +0800 Subject: [PATCH 03/19] do not flip submenu tooltips --- docs/pages/experiments/menu-preview.tsx | 34 ++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index ed1892ea0eeb47..57fdde8267c223 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -7,7 +7,7 @@ import Tooltip, { type TooltipProps } from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; +import { ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; import Menu, { CheckboxItem, CheckboxItemIndicator, @@ -111,8 +111,9 @@ interface MenuTooltipChildProps { function MenuTooltip(props: { title: string; children: React.ReactElement; + tooltipProps?: Partial; }) { - const { title, children } = props; + const { title, children, tooltipProps = horizontalTooltipProps } = props; const [open, setOpen] = React.useState(false); const handleOpen = React.useCallback(() => { @@ -132,7 +133,7 @@ function MenuTooltip(props: { return ( >( + () => ({ + placement: direction === 'rtl' ? 'right' : 'left', + slotProps: { + popper: { + popperOptions: { + modifiers: [ + { + // Submenus default to inline-end, so keep this tooltip on + // inline-start instead of letting Popper flip it onto the submenu. + name: 'flip', + enabled: false, + }, + ], + }, + }, + }, + }), + [direction], + ); + return ( }> @@ -447,7 +470,7 @@ function MenuPreviewWithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHo - + View options @@ -545,14 +568,17 @@ export default function MenuPreviewExperiment() {
+

Fully-featured menu with submenus, links, radio groups, and checkbox items.

+

Material UI Tooltip integrated with every menu item.

+

Material UI Popover used as a PreviewCard-style menu item help card.

From a922a5f2f0c7b7892c4f4f7f325b79868eecb6a6 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 22 Jun 2026 08:13:26 +0800 Subject: [PATCH 04/19] dedupe styles --- packages/mui-material/src/Menu/Menu.js | 15 +- packages/mui-material/src/Menu/menuStyles.js | 15 ++ .../mui-material/src/MenuItem/MenuItem.js | 123 +------------- .../src/MenuItem/menuItemStyles.js | 137 +++++++++++++++ .../src/MenuPreview/MenuPreview.test.tsx | 2 + .../MenuPreview/menuPreviewPopupShared.tsx | 2 +- .../MenuPreview/menuPreviewSharedStyles.ts | 158 ++++-------------- 7 files changed, 195 insertions(+), 257 deletions(-) create mode 100644 packages/mui-material/src/Menu/menuStyles.js create mode 100644 packages/mui-material/src/MenuItem/menuItemStyles.js diff --git a/packages/mui-material/src/Menu/Menu.js b/packages/mui-material/src/Menu/Menu.js index 0b7831588c0dea..b683260516ff00 100644 --- a/packages/mui-material/src/Menu/Menu.js +++ b/packages/mui-material/src/Menu/Menu.js @@ -12,6 +12,7 @@ import { styled } from '../zero-styled'; import { useDefaultProps } from '../DefaultPropsProvider'; import { getMenuUtilityClass } from './menuClasses'; import useSlot from '../utils/useSlot'; +import { menuListStyles, menuPaperStyles } from './menuStyles'; const RTL_ORIGIN = { vertical: 'top', @@ -44,22 +45,12 @@ const MenuRoot = styled(Popover, { export const MenuPaper = styled(PopoverPaper, { name: 'MuiMenu', slot: 'Paper', -})({ - // specZ: The maximum height of a simple menu should be one or more rows less than the view - // height. This ensures a tappable area outside of the simple menu with which to dismiss - // the menu. - maxHeight: 'calc(100% - 96px)', - // Add iOS momentum scrolling for iOS < 13.0 - WebkitOverflowScrolling: 'touch', -}); +})(menuPaperStyles); const MenuMenuList = styled(MenuList, { name: 'MuiMenu', slot: 'List', -})({ - // We disable the focus ring for mouse, touch and keyboard users. - outline: 0, -}); +})(menuListStyles); const Menu = React.forwardRef(function Menu(inProps, ref) { const props = useDefaultProps({ props: inProps, name: 'MuiMenu' }); diff --git a/packages/mui-material/src/Menu/menuStyles.js b/packages/mui-material/src/Menu/menuStyles.js new file mode 100644 index 00000000000000..7a7b2cd6e57311 --- /dev/null +++ b/packages/mui-material/src/Menu/menuStyles.js @@ -0,0 +1,15 @@ +/** @type {import('@mui/system').CSSInterpolation} */ +export const menuPaperStyles = { + // specZ: The maximum height of a simple menu should be one or more rows less than the view + // height. This ensures a tappable area outside of the simple menu with which to dismiss + // the menu. + maxHeight: 'calc(100% - 96px)', + // Add iOS momentum scrolling for iOS < 13.0 + WebkitOverflowScrolling: 'touch', +}; + +/** @type {import('@mui/system').CSSInterpolation} */ +export const menuListStyles = { + // We disable the focus ring for mouse, touch and keyboard users. + outline: 0, +}; diff --git a/packages/mui-material/src/MenuItem/MenuItem.js b/packages/mui-material/src/MenuItem/MenuItem.js index 68df44938b4404..538639bccd98c7 100644 --- a/packages/mui-material/src/MenuItem/MenuItem.js +++ b/packages/mui-material/src/MenuItem/MenuItem.js @@ -14,23 +14,12 @@ import focusWithVisible from '../utils/focusWithVisible'; import useForkRef from '../utils/useForkRef'; import useId from '../utils/useId'; import { useRovingTabIndexItem } from '../utils/useRovingTabIndex'; -import { dividerClasses } from '../Divider'; -import { listItemIconClasses } from '../ListItemIcon'; -import { listItemTextClasses } from '../ListItemText'; import { useMenuListContext } from '../MenuList/MenuListContext'; import { useSelectFocusSource } from '../Select/utils'; import menuItemClasses, { getMenuItemUtilityClass } from './menuItemClasses'; +import { getMenuItemRootStyles, menuItemOverridesResolver } from './menuItemStyles'; -export const overridesResolver = (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.dense && styles.dense, - ownerState.divider && styles.divider, - !ownerState.disableGutters && styles.gutters, - ]; -}; +export const overridesResolver = menuItemOverridesResolver; const useUtilityClasses = (ownerState) => { const { disabled, dense, divider, disableGutters, selected, classes } = ownerState; @@ -58,113 +47,7 @@ const MenuItemRoot = styled(ButtonBase, { name: 'MuiMenuItem', slot: 'Root', overridesResolver, -})( - memoTheme(({ theme }) => ({ - ...theme.typography.body1, - display: 'flex', - justifyContent: 'flex-start', - alignItems: 'center', - position: 'relative', - textDecoration: 'none', - minHeight: 48, - paddingTop: 6, - paddingBottom: 6, - boxSizing: 'border-box', - whiteSpace: 'nowrap', - '&:hover': { - textDecoration: 'none', - backgroundColor: (theme.vars || theme).palette.action.hover, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${menuItemClasses.selected}`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - (theme.vars || theme).palette.action.selectedOpacity, - ), - [`&.${menuItemClasses.focusVisible}`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - `${(theme.vars || theme).palette.action.selectedOpacity} + ${(theme.vars || theme).palette.action.focusOpacity}`, - ), - }, - }, - [`&.${menuItemClasses.selected}:hover`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - `${(theme.vars || theme).palette.action.selectedOpacity} + ${(theme.vars || theme).palette.action.hoverOpacity}`, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - (theme.vars || theme).palette.action.selectedOpacity, - ), - }, - }, - [`&.${menuItemClasses.focusVisible}`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, - }, - [`&.${menuItemClasses.disabled}`]: { - opacity: (theme.vars || theme).palette.action.disabledOpacity, - }, - [`& + .${dividerClasses.root}`]: { - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - [`& + .${dividerClasses.inset}`]: { - marginLeft: 52, - }, - [`& .${listItemTextClasses.root}`]: { - marginTop: 0, - marginBottom: 0, - }, - [`& .${listItemTextClasses.inset}`]: { - paddingLeft: 36, - }, - [`& .${listItemIconClasses.root}`]: { - minWidth: 36, - }, - variants: [ - { - props: ({ ownerState }) => !ownerState.disableGutters, - style: { - paddingLeft: 16, - paddingRight: 16, - }, - }, - { - props: ({ ownerState }) => ownerState.divider, - style: { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }, - }, - { - props: ({ ownerState }) => !ownerState.dense, - style: { - [theme.breakpoints.up('sm')]: { - minHeight: 'auto', - }, - }, - }, - { - props: ({ ownerState }) => ownerState.dense, - style: { - minHeight: 32, // https://m2.material.io/components/menus#specs > Dense - paddingTop: 4, - paddingBottom: 4, - ...theme.typography.body2, - [`& .${listItemIconClasses.root} svg`]: { - fontSize: '1.25rem', - }, - }, - }, - ], - })), -); +})(memoTheme(({ theme }) => getMenuItemRootStyles(theme, menuItemClasses))); const MenuItem = React.forwardRef(function MenuItem(inProps, ref) { const props = useDefaultProps({ props: inProps, name: 'MuiMenuItem' }); diff --git a/packages/mui-material/src/MenuItem/menuItemStyles.js b/packages/mui-material/src/MenuItem/menuItemStyles.js new file mode 100644 index 00000000000000..d51264edc4a55a --- /dev/null +++ b/packages/mui-material/src/MenuItem/menuItemStyles.js @@ -0,0 +1,137 @@ +import { dividerClasses } from '../Divider'; +import { listItemIconClasses } from '../ListItemIcon'; +import { listItemTextClasses } from '../ListItemText'; + +export const menuItemOverridesResolver = (props, styles) => { + const { ownerState } = props; + + return [ + styles.root, + ownerState.dense && styles.dense, + ownerState.divider && styles.divider, + !ownerState.disableGutters && styles.gutters, + ]; +}; + +export function getMenuItemRootStyles(theme, classes, options = {}) { + const focusVisibleClass = options.focusVisibleClass ?? classes.focusVisible; + const disabledPointerEvents = options.disabledPointerEvents ?? false; + + return { + ...theme.typography.body1, + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + position: 'relative', + textDecoration: 'none', + minHeight: 48, + paddingTop: 6, + paddingBottom: 6, + boxSizing: 'border-box', + whiteSpace: 'nowrap', + '&:hover': { + textDecoration: 'none', + backgroundColor: (theme.vars || theme).palette.action.hover, + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + [`&.${classes.selected}`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + (theme.vars || theme).palette.action.selectedOpacity, + ), + ...(focusVisibleClass && { + [`&.${focusVisibleClass}`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + `${(theme.vars || theme).palette.action.selectedOpacity} + ${ + (theme.vars || theme).palette.action.focusOpacity + }`, + ), + }, + }), + }, + [`&.${classes.selected}:hover`]: { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + `${(theme.vars || theme).palette.action.selectedOpacity} + ${ + (theme.vars || theme).palette.action.hoverOpacity + }`, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: theme.alpha( + (theme.vars || theme).palette.primary.main, + (theme.vars || theme).palette.action.selectedOpacity, + ), + }, + }, + ...(focusVisibleClass && { + [`&.${focusVisibleClass}`]: { + backgroundColor: (theme.vars || theme).palette.action.focus, + }, + }), + [`&.${classes.disabled}`]: { + opacity: (theme.vars || theme).palette.action.disabledOpacity, + ...(disabledPointerEvents && { + pointerEvents: 'none', + cursor: 'default', + }), + }, + [`& + .${dividerClasses.root}`]: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + [`& + .${dividerClasses.inset}`]: { + marginLeft: 52, + }, + [`& .${listItemTextClasses.root}`]: { + marginTop: 0, + marginBottom: 0, + }, + [`& .${listItemTextClasses.inset}`]: { + paddingLeft: 36, + }, + [`& .${listItemIconClasses.root}`]: { + minWidth: 36, + }, + variants: [ + { + props: ({ ownerState }) => !ownerState.disableGutters, + style: { + paddingLeft: 16, + paddingRight: 16, + }, + }, + { + props: ({ ownerState }) => ownerState.divider, + style: { + borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundClip: 'padding-box', + }, + }, + { + props: ({ ownerState }) => !ownerState.dense, + style: { + [theme.breakpoints.up('sm')]: { + minHeight: 'auto', + }, + }, + }, + { + props: ({ ownerState }) => ownerState.dense, + style: { + minHeight: 32, // https://m2.material.io/components/menus#specs > Dense + paddingTop: 4, + paddingBottom: 4, + ...theme.typography.body2, + [`& .${listItemIconClasses.root} svg`]: { + fontSize: '1.25rem', + }, + }, + }, + ], + }; +} diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx index 7b7155e31b7ec4..3dac7e6dd93801 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx +++ b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { createRenderer, isJsdom, screen, waitFor } from '@mui/internal-test-utils'; +import { listClasses } from '@mui/material/List'; import Tooltip from '@mui/material/Tooltip'; import MenuPreview, { MenuPreviewCheckboxItem, @@ -64,6 +65,7 @@ describe('', () => { const list = screen.getByTestId('paper').querySelector(`.${menuPreviewPopupClasses.list}`); expect(list).not.to.equal(null); expect(list!.tagName).to.equal('DIV'); + expect(list!).to.have.class(listClasses.padding); expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class( menuPreviewItemClasses.root, diff --git a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx index 2e5bc2a049171e..e41db8fd1aa674 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx +++ b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx @@ -336,7 +336,7 @@ export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBa ListSlot, { component: 'div', - disablePadding: true, + disablePadding: false, ...resolvedListProps, className: clsx(classes?.list, resolvedListProps?.className), }, diff --git a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts index 75295e4ea2b1fb..5621e902dddd89 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts +++ b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts @@ -1,9 +1,8 @@ import { CSSInterpolation } from '@mui/system'; -import { dividerClasses } from '../Divider'; -import { listItemIconClasses } from '../ListItemIcon'; -import { listItemTextClasses } from '../ListItemText'; import memoTheme from '../utils/memoTheme'; import { Theme } from '../styles'; +import { menuListStyles, menuPaperStyles } from '../Menu/menuStyles'; +import { getMenuItemRootStyles } from '../MenuItem/menuItemStyles'; export interface SharedMenuPreviewItemClasses { highlighted: string; @@ -12,143 +11,54 @@ export interface SharedMenuPreviewItemClasses { divider: string; gutters: string; selected: string; -} - -interface MenuPreviewItemVariantOwnerState { - dense: boolean; - divider: boolean; - disableGutters: boolean; + open?: string; } export function getMenuPreviewItemStyles( theme: Theme, classes: SharedMenuPreviewItemClasses, ): CSSInterpolation { + const selectedFocusBackgroundColor = theme.alpha( + (theme.vars || theme).palette.primary.main, + `${(theme.vars || theme).palette.action.selectedOpacity} + ${ + (theme.vars || theme).palette.action.focusOpacity + }`, + ); + return { - ...theme.typography.body1, - display: 'flex', - justifyContent: 'flex-start', - alignItems: 'center', - position: 'relative', - textDecoration: 'none', - minHeight: 48, - paddingTop: 6, - paddingBottom: 6, - boxSizing: 'border-box', - whiteSpace: 'nowrap', - cursor: 'default', + WebkitTapHighlightColor: 'transparent', + backgroundColor: 'transparent', + border: 0, + margin: 0, + borderRadius: 0, + color: 'inherit', + cursor: 'pointer', userSelect: 'none', + verticalAlign: 'middle', + MozAppearance: 'none', + WebkitAppearance: 'none', outline: 0, - '&:hover': { - textDecoration: 'none', - backgroundColor: (theme.vars || theme).palette.action.hover, - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${classes.selected}`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - (theme.vars || theme).palette.action.selectedOpacity, - ), - [`&.${classes.highlighted}`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - `${(theme.vars || theme).palette.action.selectedOpacity} + ${ - (theme.vars || theme).palette.action.focusOpacity - }`, - ), - }, - }, - [`&.${classes.selected}:hover`]: { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - `${(theme.vars || theme).palette.action.selectedOpacity} + ${ - (theme.vars || theme).palette.action.hoverOpacity - }`, - ), - '@media (hover: none)': { - backgroundColor: theme.alpha( - (theme.vars || theme).palette.primary.main, - (theme.vars || theme).palette.action.selectedOpacity, - ), - }, - }, - [`&.${classes.highlighted}`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, + '&::-moz-focus-inner': { + borderStyle: 'none', }, - [`&.${classes.disabled}`]: { - opacity: (theme.vars || theme).palette.action.disabledOpacity, - pointerEvents: 'none', - }, - [`& + .${dividerClasses.root}`]: { - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - [`& + .${dividerClasses.inset}`]: { - marginLeft: 52, - }, - [`& .${listItemTextClasses.root}`]: { - marginTop: 0, - marginBottom: 0, - }, - [`& .${listItemTextClasses.inset}`]: { - paddingLeft: 36, - }, - [`& .${listItemIconClasses.root}`]: { - minWidth: 36, - }, - variants: [ - { - props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => - !ownerState.disableGutters, - style: { - paddingLeft: 16, - paddingRight: 16, - }, - }, - { - props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => - ownerState.divider, - style: { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }, - }, - { - props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => - !ownerState.dense, - style: { - [theme.breakpoints.up('sm')]: { - minHeight: 'auto', - }, - }, + ...getMenuItemRootStyles(theme, classes, { + focusVisibleClass: classes.highlighted, + disabledPointerEvents: true, + }), + ...(classes.open && { + [`&.${classes.open}`]: { + backgroundColor: (theme.vars || theme).palette.action.focus, }, - { - props: ({ ownerState }: { ownerState: MenuPreviewItemVariantOwnerState }) => - ownerState.dense, - style: { - minHeight: 32, - paddingTop: 4, - paddingBottom: 4, - ...theme.typography.body2, - [`& .${listItemIconClasses.root} svg`]: { - fontSize: '1.25rem', - }, - }, + [`&.${classes.selected}.${classes.open}`]: { + backgroundColor: selectedFocusBackgroundColor, }, - ], + }), }; } -export const menuPreviewPopupPaperStyles: CSSInterpolation = { - maxHeight: 'calc(100% - 96px)', - WebkitOverflowScrolling: 'touch', -}; +export const menuPreviewPopupPaperStyles: CSSInterpolation = menuPaperStyles; -export const menuPreviewPopupListStyles: CSSInterpolation = { - outline: 0, -}; +export const menuPreviewPopupListStyles: CSSInterpolation = menuListStyles; export const menuPreviewIndicatorStyles = memoTheme(({ theme }) => ({ display: 'inline-flex', From 65c477725b100a119b448d22337b7a21f80ac3fa Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 29 Jun 2026 18:47:46 +0800 Subject: [PATCH 05/19] fixes --- .../menu-preview-checkbox-item-indicator.js | 12 ++----- .../api/menu-preview-checkbox-item.js | 12 ++----- .../api/menu-preview-group-label.js | 12 ++----- .../material-ui/api/menu-preview-group.js | 12 ++----- .../material-ui/api/menu-preview-item.js | 12 ++----- .../material-ui/api/menu-preview-link-item.js | 12 ++----- .../material-ui/api/menu-preview-popup.js | 12 ++----- .../material-ui/api/menu-preview-popup.json | 1 + .../api/menu-preview-radio-group.js | 12 ++----- .../api/menu-preview-radio-item-indicator.js | 12 ++----- .../api/menu-preview-radio-item.js | 12 ++----- .../material-ui/api/menu-preview-separator.js | 12 ++----- .../api/menu-preview-submenu-popup.js | 12 ++----- .../api/menu-preview-submenu-popup.json | 1 + .../api/menu-preview-submenu-root.js | 12 ++----- .../api/menu-preview-submenu-trigger.js | 12 ++----- .../material-ui/api/menu-preview-trigger.js | 12 ++----- docs/pages/material-ui/api/menu-preview.js | 12 ++----- .../menu-preview-popup.json | 1 + .../menu-preview-submenu-popup.json | 1 + .../src/MenuPreview/MenuPreview.test.tsx | 31 +++++++++++++++++++ .../MenuPreview/menuPreviewPopupShared.tsx | 8 ++++- .../src/MenuPreviewPopup/MenuPreviewPopup.tsx | 10 ++++++ .../MenuPreviewSubmenuPopup.tsx | 10 ++++++ 24 files changed, 94 insertions(+), 161 deletions(-) diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js index dc73f4f5df5157..0fc1bd2331944b 100644 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json'; import jsonPageContent from './menu-preview-checkbox-item-indicator.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.js b/docs/pages/material-ui/api/menu-preview-checkbox-item.js index 273dae896d9732..0f2c3bb92fb575 100644 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item.js +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json'; import jsonPageContent from './menu-preview-checkbox-item.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-group-label.js b/docs/pages/material-ui/api/menu-preview-group-label.js index b27a7aea1cbebf..8c2b8cbdec3e7b 100644 --- a/docs/pages/material-ui/api/menu-preview-group-label.js +++ b/docs/pages/material-ui/api/menu-preview-group-label.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json'; import jsonPageContent from './menu-preview-group-label.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-group.js b/docs/pages/material-ui/api/menu-preview-group.js index dd950c267f3918..e4ba5947235e0a 100644 --- a/docs/pages/material-ui/api/menu-preview-group.js +++ b/docs/pages/material-ui/api/menu-preview-group.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-group/menu-preview-group.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-group/menu-preview-group.json'; import jsonPageContent from './menu-preview-group.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-item.js b/docs/pages/material-ui/api/menu-preview-item.js index 874e8da3b3785d..9b5ba881bc9d25 100644 --- a/docs/pages/material-ui/api/menu-preview-item.js +++ b/docs/pages/material-ui/api/menu-preview-item.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-item/menu-preview-item.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-item/menu-preview-item.json'; import jsonPageContent from './menu-preview-item.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-link-item.js b/docs/pages/material-ui/api/menu-preview-link-item.js index a4d03702e1b16c..9f12334dbbafee 100644 --- a/docs/pages/material-ui/api/menu-preview-link-item.js +++ b/docs/pages/material-ui/api/menu-preview-link-item.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json'; import jsonPageContent from './menu-preview-link-item.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-popup.js b/docs/pages/material-ui/api/menu-preview-popup.js index 1a9fcae0e09e49..127dc9feed6913 100644 --- a/docs/pages/material-ui/api/menu-preview-popup.js +++ b/docs/pages/material-ui/api/menu-preview-popup.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json'; import jsonPageContent from './menu-preview-popup.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-popup.json b/docs/pages/material-ui/api/menu-preview-popup.json index 74825d504428e0..eae28eec44cda5 100644 --- a/docs/pages/material-ui/api/menu-preview-popup.json +++ b/docs/pages/material-ui/api/menu-preview-popup.json @@ -48,6 +48,7 @@ } }, "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, + "elevation": { "type": { "name": "number" }, "default": "8" }, "finalFocus": { "type": { "name": "union", diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.js b/docs/pages/material-ui/api/menu-preview-radio-group.js index 3b9f531b0034b3..dd1bbd6501522b 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-group.js +++ b/docs/pages/material-ui/api/menu-preview-radio-group.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json'; import jsonPageContent from './menu-preview-radio-group.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js index d44d1d01f14efc..700de197f4f9bb 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js +++ b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json'; import jsonPageContent from './menu-preview-radio-item-indicator.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.js b/docs/pages/material-ui/api/menu-preview-radio-item.js index fc06a00097e28e..8f58a8bd490986 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-item.js +++ b/docs/pages/material-ui/api/menu-preview-radio-item.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json'; import jsonPageContent from './menu-preview-radio-item.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-separator.js b/docs/pages/material-ui/api/menu-preview-separator.js index 24e4dee90d1c5d..46e88927598939 100644 --- a/docs/pages/material-ui/api/menu-preview-separator.js +++ b/docs/pages/material-ui/api/menu-preview-separator.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json'; import jsonPageContent from './menu-preview-separator.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.js b/docs/pages/material-ui/api/menu-preview-submenu-popup.js index 1feb03c4f823ef..b9e2198291d25d 100644 --- a/docs/pages/material-ui/api/menu-preview-submenu-popup.js +++ b/docs/pages/material-ui/api/menu-preview-submenu-popup.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json'; import jsonPageContent from './menu-preview-submenu-popup.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.json b/docs/pages/material-ui/api/menu-preview-submenu-popup.json index edb42f9acb55e1..44e03df304c3bc 100644 --- a/docs/pages/material-ui/api/menu-preview-submenu-popup.json +++ b/docs/pages/material-ui/api/menu-preview-submenu-popup.json @@ -48,6 +48,7 @@ } }, "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, + "elevation": { "type": { "name": "number" }, "default": "8" }, "finalFocus": { "type": { "name": "union", diff --git a/docs/pages/material-ui/api/menu-preview-submenu-root.js b/docs/pages/material-ui/api/menu-preview-submenu-root.js index 5f12e3a1db11de..71117c38788e95 100644 --- a/docs/pages/material-ui/api/menu-preview-submenu-root.js +++ b/docs/pages/material-ui/api/menu-preview-submenu-root.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json'; import jsonPageContent from './menu-preview-submenu-root.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.js b/docs/pages/material-ui/api/menu-preview-submenu-trigger.js index 4d4d3bcc553a57..64d99de2e3afc7 100644 --- a/docs/pages/material-ui/api/menu-preview-submenu-trigger.js +++ b/docs/pages/material-ui/api/menu-preview-submenu-trigger.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json'; import jsonPageContent from './menu-preview-submenu-trigger.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview-trigger.js b/docs/pages/material-ui/api/menu-preview-trigger.js index 623e31aedb5914..445e0f871bd7be 100644 --- a/docs/pages/material-ui/api/menu-preview-trigger.js +++ b/docs/pages/material-ui/api/menu-preview-trigger.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json'; +import descriptions from 'docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json'; import jsonPageContent from './menu-preview-trigger.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/pages/material-ui/api/menu-preview.js b/docs/pages/material-ui/api/menu-preview.js index 3cb1b61dd994ee..9ed07a5cc58816 100644 --- a/docs/pages/material-ui/api/menu-preview.js +++ b/docs/pages/material-ui/api/menu-preview.js @@ -1,16 +1,8 @@ import * as React from 'react'; import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations'; -import translation from 'docs/translations/api-docs/menu-preview/menu-preview.json'; +import descriptions from 'docs/translations/api-docs/menu-preview/menu-preview.json'; import jsonPageContent from './menu-preview.json'; -export default function Page(props) { - const { descriptions } = props; +export default function Page() { return ; } - -export async function getStaticProps() { - const descriptions = mapApiPageTranslation(translation); - - return { props: { descriptions } }; -} diff --git a/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json b/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json index 3c92b01c458026..b7b59afbc03738 100644 --- a/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json +++ b/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json @@ -25,6 +25,7 @@ "disableAnchorTracking": { "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." }, + "elevation": { "description": "The elevation of the menu surface." }, "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, "keepMounted": { "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." diff --git a/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json b/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json index 24661184a10744..e8c18a9115d7cd 100644 --- a/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json +++ b/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json @@ -25,6 +25,7 @@ "disableAnchorTracking": { "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." }, + "elevation": { "description": "The elevation of the menu surface." }, "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, "keepMounted": { "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx index 3dac7e6dd93801..319651a86943d9 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx +++ b/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx @@ -3,6 +3,7 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { createRenderer, isJsdom, screen, waitFor } from '@mui/internal-test-utils'; import { listClasses } from '@mui/material/List'; +import { paperClasses } from '@mui/material/Paper'; import Tooltip from '@mui/material/Tooltip'; import MenuPreview, { MenuPreviewCheckboxItem, @@ -432,6 +433,36 @@ describe('', () => { expect(list).not.to.have.attribute('sx'); }); + it('defaults the popup surface elevation to 8', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(await screen.findByTestId('paper')).to.have.class(paperClasses.elevation8); + }); + + it('forwards a custom elevation to the popup surface', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + expect(await screen.findByTestId('paper')).to.have.class(paperClasses.elevation4); + }); + it('supports controlled open state and Base UI cancellation details', async () => { const handleOpenChange = spy((open: boolean, eventDetails: any) => { expect(open).to.equal(true); diff --git a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx index e41db8fd1aa674..771522fa73cd3b 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx +++ b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx @@ -220,6 +220,11 @@ export interface MenuPreviewPopupPublicProps { * Determines the element to focus when the menu is closed. */ finalFocus?: MenuPreviewPopupFinalFocus; + /** + * The elevation of the menu surface. + * @default 8 + */ + elevation?: number | undefined; } export interface MenuPreviewPopupSharedProps @@ -269,6 +274,7 @@ export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBa collisionAvoidance, id, finalFocus, + elevation, style, ...other } = props; @@ -325,7 +331,7 @@ export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBa const paperSlotProps = getSlotProps( PaperSlot, { - elevation: 8, + elevation: elevation ?? 8, ...resolvedPaperProps, className: clsx(classes?.paper, resolvedPaperProps?.className), sx: mergeSx(sx, resolvedPaperProps?.sx), diff --git a/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx index cafc2212940014..3eb6c741f8666c 100644 --- a/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx +++ b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx @@ -118,6 +118,11 @@ export interface MenuPreviewPopupProps extends Omit< * Determines the element to focus when the menu is closed. */ finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + /** + * The elevation of the menu surface. + * @default 8 + */ + elevation?: MenuPreviewPopupPublicProps['elevation']; /** * Override or extend the styles applied to the component. */ @@ -317,6 +322,11 @@ MenuPreviewPopup.propTypes /* remove-proptypes */ = { * @default false */ disableAnchorTracking: PropTypes.bool, + /** + * The elevation of the menu surface. + * @default 8 + */ + elevation: PropTypes.number, /** * Determines the element to focus when the menu is closed. */ diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx index 752bd5347ad8d7..bb171f81d1afce 100644 --- a/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx +++ b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx @@ -118,6 +118,11 @@ export interface MenuPreviewSubmenuPopupProps extends Omit< * Determines the element to focus when the menu is closed. */ finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + /** + * The elevation of the menu surface. + * @default 8 + */ + elevation?: MenuPreviewPopupPublicProps['elevation']; /** * Override or extend the styles applied to the component. */ @@ -317,6 +322,11 @@ MenuPreviewSubmenuPopup.propTypes /* remove-proptypes */ = { * @default false */ disableAnchorTracking: PropTypes.bool, + /** + * The elevation of the menu surface. + * @default 8 + */ + elevation: PropTypes.number, /** * Determines the element to focus when the menu is closed. */ From 6bdd72c0a69178982e3f65a119ba02af3e569ae7 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 29 Jun 2026 19:05:34 +0800 Subject: [PATCH 06/19] Add context menu recipe --- docs/pages/experiments/menu-preview.tsx | 91 +++++++++++++++++++ .../src/MenuPreview/MenuPreview.tsx | 4 +- .../src/MenuPreview/menuPreviewItemShared.tsx | 10 +- .../MenuPreview/menuPreviewPopupShared.tsx | 54 +++++------ .../MenuPreview/menuPreviewSharedStyles.ts | 2 +- .../src/MenuPreview/menuPreviewUtils.ts | 2 +- .../src/MenuPreviewPopup/MenuPreviewPopup.tsx | 36 ++++---- .../MenuPreviewSubmenuPopup.tsx | 36 ++++---- .../MenuPreviewSubmenuRoot.tsx | 4 +- 9 files changed, 165 insertions(+), 74 deletions(-) diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index 57fdde8267c223..9f08660603a43d 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -41,6 +41,19 @@ const defaultSettings: MenuSettings = { submenusOpenOnHover: false, }; +function createVirtualAnchor(mouseX: number, mouseY: number) { + return { + getBoundingClientRect() { + return DOMRect.fromRect({ + x: mouseX, + y: mouseY, + width: 0, + height: 0, + }); + }, + }; +} + interface PreviewCardItem { id: string; label: string; @@ -519,6 +532,79 @@ function MenuPreviewWithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHo ); } +function MenuPreviewContextMenuRecipe() { + const [anchor, setAnchor] = React.useState | null>(null); + const open = anchor !== null; + + const handleContextMenu = (event: React.MouseEvent) => { + event.preventDefault(); + + setAnchor( + anchor === null + ? createVirtualAnchor(event.clientX + 2, event.clientY - 6) + : // Keep the old Material recipe behavior: a repeated contextmenu event while + // open closes the menu instead of relocating it through the backdrop. + null, + ); + + // Preserve selected text after opening the context menu in Safari and Firefox. + const selection = document.getSelection(); + if (selection && selection.rangeCount > 0) { + const range = selection.getRangeAt(0); + + setTimeout(() => { + selection.addRange(range); + }); + } + }; + + const handleClose = () => { + setAnchor(null); + }; + + const handleOpenChange: React.ComponentProps['onOpenChange'] = ( + nextOpen, + eventDetails, + ) => { + if (nextOpen) { + return; + } + + if ( + eventDetails.reason === 'item-press' || + eventDetails.reason === 'outside-press' || + eventDetails.reason === 'escape-key' + ) { + handleClose(); + return; + } + + eventDetails.cancel(); + }; + + return ( +
+ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ipsum purus, bibendum sit + amet vulputate eget, porta semper ligula. Donec bibendum vulputate erat, ac fringilla mi + finibus nec. Donec ac dolor sed dolor porttitor blandit vel vel purus. Fusce vel malesuada + ligula. Nam quis vehicula ante, eu finibus est. Proin ullamcorper fermentum orci, quis + finibus massa. Nunc lobortis, massa ut rutrum ultrices, metus metus finibus ex, sit amet + facilisis neque enim sed neque. Quisque accumsan metus vel maximus consequat. Suspendisse + lacinia tellus a libero volutpat maximus. + + + + Copy + Print + Highlight + Email + + +
+ ); +} + export default function MenuPreviewExperiment() { const [settings, setSettings] = React.useState(defaultSettings); @@ -582,6 +668,11 @@ export default function MenuPreviewExperiment() {

Material UI Popover used as a PreviewCard-style menu item help card.

+
+ +

Right-click the text to open a cursor-positioned MenuPreview popup.

+ +
Base UI Menu API diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.tsx index 6ea6ec15b4d7fd..b47beb7f169753 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.tsx +++ b/packages/mui-material/src/MenuPreview/MenuPreview.tsx @@ -23,11 +23,11 @@ export interface MenuPreviewProps { /** * Event handler called when the menu is opened or closed. */ - onOpenChange?: BaseMenu.Root.Props['onOpenChange']; + onOpenChange?: (BaseMenu.Root.Props['onOpenChange']) | undefined; /** * Event handler called after any animations complete when the menu is opened or closed. */ - onOpenChangeComplete?: BaseMenu.Root.Props['onOpenChangeComplete']; + onOpenChangeComplete?: (BaseMenu.Root.Props['onOpenChangeComplete']) | undefined; /** * Determines if the menu enters a modal state when open. * @default true diff --git a/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx index 904641b370bfbb..62f72cde410274 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx +++ b/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx @@ -155,8 +155,8 @@ export interface MenuPreviewSubmenuTriggerBaseProps { } export interface MenuPreviewBaseItemState { - disabled?: boolean; - highlighted?: boolean; + disabled?: (boolean) | undefined; + highlighted?: (boolean) | undefined; } export function menuPreviewItemOverridesResolver( @@ -191,9 +191,9 @@ export function getMenuPreviewItemOwnerState( export function useMenuPreviewItemUtilityClasses( ownerState: MenuPreviewItemOwnerState & { - classes?: Partial; - checked?: boolean; - open?: boolean; + classes?: (Partial) | undefined; + checked?: (boolean) | undefined; + open?: (boolean) | undefined; }, getUtilityClass: (slot: string) => string, ) { diff --git a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx index 771522fa73cd3b..7848b0cfe2a790 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx +++ b/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx @@ -12,7 +12,7 @@ import { resolveSlotProps, SlotProps } from './menuPreviewUtils'; type ExternalSlotProps = Omit, 'className' | 'render' | 'style'> & { className?: string | undefined; - render?: never; + render?: (never) | undefined; style?: React.CSSProperties | undefined; } & Record; @@ -88,35 +88,35 @@ export interface MenuPreviewPopupSharedSlots { * The component used for the portal. * @default BaseMenu.Portal */ - portal?: React.ElementType; + portal?: (React.ElementType) | undefined; /** * The component used for the positioner. * @default BaseMenu.Positioner */ - positioner?: React.ElementType; + positioner?: (React.ElementType) | undefined; /** * The component rendered by the Base UI popup. * @default 'div' */ - popup?: React.ElementType; + popup?: (React.ElementType) | undefined; /** * The component used for the Material surface. * @default Paper */ - paper?: React.ElementType; + paper?: (React.ElementType) | undefined; /** * The component used for the presentational list wrapper. * @default List */ - list?: React.ElementType; + list?: (React.ElementType) | undefined; } export interface MenuPreviewPopupSharedSlotProps { - portal?: SlotProps, OwnerState>; - positioner?: SlotProps, OwnerState>; - popup?: SlotProps, OwnerState>; - paper?: SlotProps, OwnerState>; - list?: SlotProps, OwnerState>; + portal?: (SlotProps, OwnerState>) | undefined; + positioner?: (SlotProps, OwnerState>) | undefined; + popup?: (SlotProps, OwnerState>) | undefined; + paper?: (SlotProps, OwnerState>) | undefined; + list?: (SlotProps, OwnerState>) | undefined; } type MenuPreviewPositionerProps = BaseMenu.Positioner.Props; @@ -152,12 +152,12 @@ export interface MenuPreviewPopupPublicProps { * * By default, the popup is positioned against the trigger. */ - anchor?: MenuPreviewPopupAnchor; + anchor?: (MenuPreviewPopupAnchor) | undefined; /** * Determines which CSS `position` property to use. * @default 'absolute' */ - positionMethod?: MenuPreviewPopupPositionMethod; + positionMethod?: (MenuPreviewPopupPositionMethod) | undefined; /** * Which side of the anchor element to align the popup against. * @default 'bottom' @@ -182,44 +182,44 @@ export interface MenuPreviewPopupPublicProps { * An element or a rectangle that delimits the area that the popup is confined to. * @default 'clipping-ancestors' */ - collisionBoundary?: MenuPreviewPopupCollisionBoundary; + collisionBoundary?: (MenuPreviewPopupCollisionBoundary) | undefined; /** * Additional space to maintain from the edge of the collision boundary. * @default 5 */ - collisionPadding?: MenuPreviewPopupCollisionPadding; + collisionPadding?: (MenuPreviewPopupCollisionPadding) | undefined; /** * Minimum distance to maintain between the arrow and the edges of the popup. * @default 5 */ - arrowPadding?: MenuPreviewPositionerProps['arrowPadding']; + arrowPadding?: (MenuPreviewPositionerProps['arrowPadding']) | undefined; /** * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. * @default false */ - sticky?: MenuPreviewPositionerProps['sticky']; + sticky?: (MenuPreviewPositionerProps['sticky']) | undefined; /** * Whether to disable the popup from tracking layout shifts of its positioning anchor. * @default false */ - disableAnchorTracking?: MenuPreviewPositionerProps['disableAnchorTracking']; + disableAnchorTracking?: (MenuPreviewPositionerProps['disableAnchorTracking']) | undefined; /** * Determines how to handle collisions when positioning the popup. */ - collisionAvoidance?: MenuPreviewPopupCollisionAvoidance; + collisionAvoidance?: (MenuPreviewPopupCollisionAvoidance) | undefined; /** * The container element to portal the popup into. */ - container?: MenuPreviewPopupContainer; + container?: (MenuPreviewPopupContainer) | undefined; /** * Whether to keep the portal mounted in the DOM while the popup is hidden. * @default false */ - keepMounted?: MenuPreviewPortalProps['keepMounted']; + keepMounted?: (MenuPreviewPortalProps['keepMounted']) | undefined; /** * Determines the element to focus when the menu is closed. */ - finalFocus?: MenuPreviewPopupFinalFocus; + finalFocus?: (MenuPreviewPopupFinalFocus) | undefined; /** * The elevation of the menu surface. * @default 8 @@ -231,17 +231,17 @@ export interface MenuPreviewPopupSharedProps extends Omit, MenuPreviewPopupPublicProps { - classes?: Partial>; + classes?: (Partial>) | undefined; ownerState: OwnerState; - slots?: MenuPreviewPopupSharedSlots; - slotProps?: MenuPreviewPopupSharedSlotProps; + slots?: (MenuPreviewPopupSharedSlots) | undefined; + slotProps?: (MenuPreviewPopupSharedSlotProps) | undefined; defaultSlots: { popup: React.ElementType; paper: React.ElementType; list: React.ElementType; }; - defaultPositionerProps?: Partial; - sx?: SxProps; + defaultPositionerProps?: (Partial) | undefined; + sx?: (SxProps) | undefined; } export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBase( diff --git a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts index 5621e902dddd89..bc63a096c86d88 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts +++ b/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts @@ -11,7 +11,7 @@ export interface SharedMenuPreviewItemClasses { divider: string; gutters: string; selected: string; - open?: string; + open?: (string) | undefined; } export function getMenuPreviewItemStyles( diff --git a/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts b/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts index bfb185ae77ba5e..6ba6b353ea620e 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts +++ b/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts @@ -34,7 +34,7 @@ export function resolveSlotProps( } export interface MenuPreviewRootSlots { - root?: React.ElementType; + root?: (React.ElementType) | undefined; } export interface MenuPreviewRootSlotProps { diff --git a/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx index 3eb6c741f8666c..282103a24e3bc2 100644 --- a/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx +++ b/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx @@ -40,89 +40,89 @@ export interface MenuPreviewPopupProps extends Omit< /** * CSS class applied to the Base UI popup element. */ - className?: MenuPreviewPopupPublicProps['className']; + className?: (MenuPreviewPopupPublicProps['className']) | undefined; /** * Styles applied to the Base UI popup element. */ - style?: MenuPreviewPopupPublicProps['style']; + style?: (MenuPreviewPopupPublicProps['style']) | undefined; /** * An element to position the popup against. * * By default, the popup is positioned against the trigger. */ - anchor?: MenuPreviewPopupPublicProps['anchor']; + anchor?: (MenuPreviewPopupPublicProps['anchor']) | undefined; /** * Determines which CSS `position` property to use. * @default 'absolute' */ - positionMethod?: MenuPreviewPopupPublicProps['positionMethod']; + positionMethod?: (MenuPreviewPopupPublicProps['positionMethod']) | undefined; /** * Which side of the anchor element to align the popup against. * @default 'bottom' */ - side?: MenuPreviewPopupPublicProps['side']; + side?: (MenuPreviewPopupPublicProps['side']) | undefined; /** * Distance between the anchor and the popup in pixels. * @default 0 */ - sideOffset?: MenuPreviewPopupPublicProps['sideOffset']; + sideOffset?: (MenuPreviewPopupPublicProps['sideOffset']) | undefined; /** * How to align the popup relative to the specified side. * @default 'start' */ - align?: MenuPreviewPopupPublicProps['align']; + align?: (MenuPreviewPopupPublicProps['align']) | undefined; /** * Additional offset along the alignment axis in pixels. * @default 0 */ - alignOffset?: MenuPreviewPopupPublicProps['alignOffset']; + alignOffset?: (MenuPreviewPopupPublicProps['alignOffset']) | undefined; /** * An element or a rectangle that delimits the area that the popup is confined to. * @default 'clipping-ancestors' */ - collisionBoundary?: MenuPreviewPopupPublicProps['collisionBoundary']; + collisionBoundary?: (MenuPreviewPopupPublicProps['collisionBoundary']) | undefined; /** * Additional space to maintain from the edge of the collision boundary. * @default 5 */ - collisionPadding?: MenuPreviewPopupPublicProps['collisionPadding']; + collisionPadding?: (MenuPreviewPopupPublicProps['collisionPadding']) | undefined; /** * Minimum distance to maintain between the arrow and the edges of the popup. * @default 5 */ - arrowPadding?: MenuPreviewPopupPublicProps['arrowPadding']; + arrowPadding?: (MenuPreviewPopupPublicProps['arrowPadding']) | undefined; /** * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. * @default false */ - sticky?: MenuPreviewPopupPublicProps['sticky']; + sticky?: (MenuPreviewPopupPublicProps['sticky']) | undefined; /** * Whether to disable the popup from tracking layout shifts of its positioning anchor. * @default false */ - disableAnchorTracking?: MenuPreviewPopupPublicProps['disableAnchorTracking']; + disableAnchorTracking?: (MenuPreviewPopupPublicProps['disableAnchorTracking']) | undefined; /** * Determines how to handle collisions when positioning the popup. */ - collisionAvoidance?: MenuPreviewPopupPublicProps['collisionAvoidance']; + collisionAvoidance?: (MenuPreviewPopupPublicProps['collisionAvoidance']) | undefined; /** * The container element to portal the popup into. */ - container?: MenuPreviewPopupPublicProps['container']; + container?: (MenuPreviewPopupPublicProps['container']) | undefined; /** * Whether to keep the portal mounted in the DOM while the popup is hidden. * @default false */ - keepMounted?: MenuPreviewPopupPublicProps['keepMounted']; + keepMounted?: (MenuPreviewPopupPublicProps['keepMounted']) | undefined; /** * Determines the element to focus when the menu is closed. */ - finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + finalFocus?: (MenuPreviewPopupPublicProps['finalFocus']) | undefined; /** * The elevation of the menu surface. * @default 8 */ - elevation?: MenuPreviewPopupPublicProps['elevation']; + elevation?: (MenuPreviewPopupPublicProps['elevation']) | undefined; /** * Override or extend the styles applied to the component. */ diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx index bb171f81d1afce..4f35324e1646ca 100644 --- a/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx +++ b/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx @@ -40,89 +40,89 @@ export interface MenuPreviewSubmenuPopupProps extends Omit< /** * CSS class applied to the Base UI popup element. */ - className?: MenuPreviewPopupPublicProps['className']; + className?: (MenuPreviewPopupPublicProps['className']) | undefined; /** * Styles applied to the Base UI popup element. */ - style?: MenuPreviewPopupPublicProps['style']; + style?: (MenuPreviewPopupPublicProps['style']) | undefined; /** * An element to position the popup against. * * By default, the popup is positioned against the submenu trigger. */ - anchor?: MenuPreviewPopupPublicProps['anchor']; + anchor?: (MenuPreviewPopupPublicProps['anchor']) | undefined; /** * Determines which CSS `position` property to use. * @default 'absolute' */ - positionMethod?: MenuPreviewPopupPublicProps['positionMethod']; + positionMethod?: (MenuPreviewPopupPublicProps['positionMethod']) | undefined; /** * Which side of the anchor element to align the popup against. * @default 'inline-end' */ - side?: MenuPreviewPopupPublicProps['side']; + side?: (MenuPreviewPopupPublicProps['side']) | undefined; /** * Distance between the anchor and the popup in pixels. * @default 0 */ - sideOffset?: MenuPreviewPopupPublicProps['sideOffset']; + sideOffset?: (MenuPreviewPopupPublicProps['sideOffset']) | undefined; /** * How to align the popup relative to the specified side. * @default 'start' */ - align?: MenuPreviewPopupPublicProps['align']; + align?: (MenuPreviewPopupPublicProps['align']) | undefined; /** * Additional offset along the alignment axis in pixels. * @default 0 */ - alignOffset?: MenuPreviewPopupPublicProps['alignOffset']; + alignOffset?: (MenuPreviewPopupPublicProps['alignOffset']) | undefined; /** * An element or a rectangle that delimits the area that the popup is confined to. * @default 'clipping-ancestors' */ - collisionBoundary?: MenuPreviewPopupPublicProps['collisionBoundary']; + collisionBoundary?: (MenuPreviewPopupPublicProps['collisionBoundary']) | undefined; /** * Additional space to maintain from the edge of the collision boundary. * @default 5 */ - collisionPadding?: MenuPreviewPopupPublicProps['collisionPadding']; + collisionPadding?: (MenuPreviewPopupPublicProps['collisionPadding']) | undefined; /** * Minimum distance to maintain between the arrow and the edges of the popup. * @default 5 */ - arrowPadding?: MenuPreviewPopupPublicProps['arrowPadding']; + arrowPadding?: (MenuPreviewPopupPublicProps['arrowPadding']) | undefined; /** * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. * @default false */ - sticky?: MenuPreviewPopupPublicProps['sticky']; + sticky?: (MenuPreviewPopupPublicProps['sticky']) | undefined; /** * Whether to disable the popup from tracking layout shifts of its positioning anchor. * @default false */ - disableAnchorTracking?: MenuPreviewPopupPublicProps['disableAnchorTracking']; + disableAnchorTracking?: (MenuPreviewPopupPublicProps['disableAnchorTracking']) | undefined; /** * Determines how to handle collisions when positioning the popup. */ - collisionAvoidance?: MenuPreviewPopupPublicProps['collisionAvoidance']; + collisionAvoidance?: (MenuPreviewPopupPublicProps['collisionAvoidance']) | undefined; /** * The container element to portal the popup into. */ - container?: MenuPreviewPopupPublicProps['container']; + container?: (MenuPreviewPopupPublicProps['container']) | undefined; /** * Whether to keep the portal mounted in the DOM while the popup is hidden. * @default false */ - keepMounted?: MenuPreviewPopupPublicProps['keepMounted']; + keepMounted?: (MenuPreviewPopupPublicProps['keepMounted']) | undefined; /** * Determines the element to focus when the menu is closed. */ - finalFocus?: MenuPreviewPopupPublicProps['finalFocus']; + finalFocus?: (MenuPreviewPopupPublicProps['finalFocus']) | undefined; /** * The elevation of the menu surface. * @default 8 */ - elevation?: MenuPreviewPopupPublicProps['elevation']; + elevation?: (MenuPreviewPopupPublicProps['elevation']) | undefined; /** * Override or extend the styles applied to the component. */ diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx b/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx index 11ee567dbc9571..cdd393f1b8ef46 100644 --- a/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx +++ b/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx @@ -23,11 +23,11 @@ export interface MenuPreviewSubmenuRootProps { /** * Event handler called when the submenu is opened or closed. */ - onOpenChange?: BaseMenu.SubmenuRoot.Props['onOpenChange']; + onOpenChange?: (BaseMenu.SubmenuRoot.Props['onOpenChange']) | undefined; /** * Event handler called after any animations complete when the submenu is opened or closed. */ - onOpenChangeComplete?: BaseMenu.SubmenuRoot.Props['onOpenChangeComplete']; + onOpenChangeComplete?: (BaseMenu.SubmenuRoot.Props['onOpenChangeComplete']) | undefined; /** * Whether the component should ignore user interaction. * @default false From 62aa760c54a76610d4f04651f28be047f8983f94 Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 29 Jun 2026 19:46:21 +0800 Subject: [PATCH 07/19] fix ci --- .../menu-preview-checkbox-item-indicator.json | 7 +- .../api/menu-preview-checkbox-item.json | 9 ++- .../api/menu-preview-group-label.json | 9 ++- .../material-ui/api/menu-preview-group.json | 9 ++- .../material-ui/api/menu-preview-item.json | 9 ++- .../api/menu-preview-link-item.json | 9 ++- .../api/menu-preview-radio-group.json | 9 ++- .../menu-preview-radio-item-indicator.json | 7 +- .../api/menu-preview-radio-item.json | 9 ++- .../api/menu-preview-separator.json | 9 ++- .../api/menu-preview-submenu-trigger.json | 9 ++- .../material-ui/api/menu-preview-trigger.json | 9 ++- .../menu-preview-checkbox-item-indicator.json | 2 +- .../menu-preview-checkbox-item.json | 2 +- .../menu-preview-group-label.json | 2 +- .../menu-preview-group.json | 2 +- .../menu-preview-item/menu-preview-item.json | 2 +- .../menu-preview-link-item.json | 2 +- .../menu-preview-radio-group.json | 2 +- .../menu-preview-radio-item-indicator.json | 2 +- .../menu-preview-radio-item.json | 2 +- .../menu-preview-separator.json | 2 +- .../menu-preview-submenu-trigger.json | 2 +- .../menu-preview-trigger.json | 2 +- .../src/MenuPreview/MenuPreview.spec.tsx | 2 +- .../src/MenuPreview/MenuPreview.test.tsx | 2 +- .../src/MenuPreview/MenuPreview.tsx | 4 +- .../mui-material/src/MenuPreview/index.d.ts | 2 - .../mui-material/src/MenuPreview/index.js | 2 - .../src/MenuPreview/menuPreviewItemShared.tsx | 10 +-- .../MenuPreview/menuPreviewPopupShared.tsx | 54 +++++++-------- .../MenuPreview/menuPreviewSharedStyles.ts | 2 +- .../src/MenuPreview/menuPreviewUtils.ts | 2 +- .../MenuPreviewCheckboxItem.tsx | 9 ++- .../MenuPreviewCheckboxItemIndicator.tsx | 9 ++- .../src/MenuPreviewGroup/MenuPreviewGroup.tsx | 9 ++- .../MenuPreviewGroupLabel.tsx | 9 ++- .../src/MenuPreviewItem/MenuPreviewItem.tsx | 9 ++- .../MenuPreviewLinkItem.tsx | 9 ++- .../src/MenuPreviewPopup/MenuPreviewPopup.tsx | 65 +++++++++++++------ .../MenuPreviewRadioGroup.tsx | 9 ++- .../MenuPreviewRadioItem.tsx | 9 ++- .../MenuPreviewRadioItemIndicator.tsx | 9 ++- .../MenuPreviewSeparator.tsx | 9 ++- .../MenuPreviewSubmenuPopup.tsx | 65 +++++++++++++------ .../MenuPreviewSubmenuRoot.tsx | 4 +- .../MenuPreviewSubmenuTrigger.tsx | 9 ++- .../MenuPreviewTrigger/MenuPreviewTrigger.tsx | 9 ++- 48 files changed, 318 insertions(+), 132 deletions(-) diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json index 49c213f68f5fef..578f4099507f42 100644 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json @@ -22,7 +22,12 @@ "import MenuPreviewCheckboxItemIndicator from '@mui/material/MenuPreviewCheckboxItemIndicator';" ], "slots": [ - { "name": "root", "description": "", "class": "MuiMenuPreviewCheckboxItemIndicator-root" } + { + "name": "root", + "description": "The component that renders the root.", + "default": "'span'", + "class": "MuiMenuPreviewCheckboxItemIndicator-root" + } ], "classes": [ { diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.json b/docs/pages/material-ui/api/menu-preview-checkbox-item.json index e1ee7b88166402..c111984ae17e80 100644 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item.json +++ b/docs/pages/material-ui/api/menu-preview-checkbox-item.json @@ -30,7 +30,14 @@ }, "name": "MenuPreviewCheckboxItem", "imports": ["import MenuPreviewCheckboxItem from '@mui/material/MenuPreviewCheckboxItem';"], - "slots": [{ "name": "root", "description": "", "class": null }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": null + } + ], "classes": [], "muiName": "MuiMenuPreviewCheckboxItem", "filename": "/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-group-label.json b/docs/pages/material-ui/api/menu-preview-group-label.json index 15ac51dd170114..05e7ee00cc3678 100644 --- a/docs/pages/material-ui/api/menu-preview-group-label.json +++ b/docs/pages/material-ui/api/menu-preview-group-label.json @@ -18,7 +18,14 @@ }, "name": "MenuPreviewGroupLabel", "imports": ["import MenuPreviewGroupLabel from '@mui/material/MenuPreviewGroupLabel';"], - "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewGroupLabel-root" }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "ListSubheader", + "class": "MuiMenuPreviewGroupLabel-root" + } + ], "classes": [], "muiName": "MuiMenuPreviewGroupLabel", "filename": "/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-group.json b/docs/pages/material-ui/api/menu-preview-group.json index 0bbc5b7c39d31d..75a9f51a590117 100644 --- a/docs/pages/material-ui/api/menu-preview-group.json +++ b/docs/pages/material-ui/api/menu-preview-group.json @@ -19,7 +19,14 @@ }, "name": "MenuPreviewGroup", "imports": ["import MenuPreviewGroup from '@mui/material/MenuPreviewGroup';"], - "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewGroup-root" }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": "MuiMenuPreviewGroup-root" + } + ], "classes": [], "muiName": "MuiMenuPreviewGroup", "filename": "/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-item.json b/docs/pages/material-ui/api/menu-preview-item.json index 80b360babe0bcc..85f2432722027e 100644 --- a/docs/pages/material-ui/api/menu-preview-item.json +++ b/docs/pages/material-ui/api/menu-preview-item.json @@ -27,7 +27,14 @@ }, "name": "MenuPreviewItem", "imports": ["import MenuPreviewItem from '@mui/material/MenuPreviewItem';"], - "slots": [{ "name": "root", "description": "", "class": null }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": null + } + ], "classes": [], "muiName": "MuiMenuPreviewItem", "filename": "/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-link-item.json b/docs/pages/material-ui/api/menu-preview-link-item.json index c1a50c2e21e20b..2f2aa34b194857 100644 --- a/docs/pages/material-ui/api/menu-preview-link-item.json +++ b/docs/pages/material-ui/api/menu-preview-link-item.json @@ -26,7 +26,14 @@ }, "name": "MenuPreviewLinkItem", "imports": ["import MenuPreviewLinkItem from '@mui/material/MenuPreviewLinkItem';"], - "slots": [{ "name": "root", "description": "", "class": null }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'a'", + "class": null + } + ], "classes": [], "muiName": "MuiMenuPreviewLinkItem", "filename": "/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.json b/docs/pages/material-ui/api/menu-preview-radio-group.json index c972d5644f3576..0c709d5d214111 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-group.json +++ b/docs/pages/material-ui/api/menu-preview-radio-group.json @@ -23,7 +23,14 @@ }, "name": "MenuPreviewRadioGroup", "imports": ["import MenuPreviewRadioGroup from '@mui/material/MenuPreviewRadioGroup';"], - "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewRadioGroup-root" }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": "MuiMenuPreviewRadioGroup-root" + } + ], "classes": [ { "key": "disabled", diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json index cc0278e7e2ba7d..0ba43b87d70be3 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json +++ b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json @@ -22,7 +22,12 @@ "import MenuPreviewRadioItemIndicator from '@mui/material/MenuPreviewRadioItemIndicator';" ], "slots": [ - { "name": "root", "description": "", "class": "MuiMenuPreviewRadioItemIndicator-root" } + { + "name": "root", + "description": "The component that renders the root.", + "default": "'span'", + "class": "MuiMenuPreviewRadioItemIndicator-root" + } ], "classes": [ { diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.json b/docs/pages/material-ui/api/menu-preview-radio-item.json index bb264de79c2630..f5e4735f558304 100644 --- a/docs/pages/material-ui/api/menu-preview-radio-item.json +++ b/docs/pages/material-ui/api/menu-preview-radio-item.json @@ -28,7 +28,14 @@ }, "name": "MenuPreviewRadioItem", "imports": ["import MenuPreviewRadioItem from '@mui/material/MenuPreviewRadioItem';"], - "slots": [{ "name": "root", "description": "", "class": null }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": null + } + ], "classes": [], "muiName": "MuiMenuPreviewRadioItem", "filename": "/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-separator.json b/docs/pages/material-ui/api/menu-preview-separator.json index f601b79a6a6eba..9c121e406aa332 100644 --- a/docs/pages/material-ui/api/menu-preview-separator.json +++ b/docs/pages/material-ui/api/menu-preview-separator.json @@ -22,7 +22,14 @@ }, "name": "MenuPreviewSeparator", "imports": ["import MenuPreviewSeparator from '@mui/material/MenuPreviewSeparator';"], - "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewSeparator-root" }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "Divider", + "class": "MuiMenuPreviewSeparator-root" + } + ], "classes": [], "muiName": "MuiMenuPreviewSeparator", "filename": "/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.json b/docs/pages/material-ui/api/menu-preview-submenu-trigger.json index 387fe73eb537b9..1de0b51f8fe169 100644 --- a/docs/pages/material-ui/api/menu-preview-submenu-trigger.json +++ b/docs/pages/material-ui/api/menu-preview-submenu-trigger.json @@ -29,7 +29,14 @@ }, "name": "MenuPreviewSubmenuTrigger", "imports": ["import MenuPreviewSubmenuTrigger from '@mui/material/MenuPreviewSubmenuTrigger';"], - "slots": [{ "name": "root", "description": "", "class": null }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": null + } + ], "classes": [], "muiName": "MuiMenuPreviewSubmenuTrigger", "filename": "/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx", diff --git a/docs/pages/material-ui/api/menu-preview-trigger.json b/docs/pages/material-ui/api/menu-preview-trigger.json index 96aca0e828262b..385c860b98acb7 100644 --- a/docs/pages/material-ui/api/menu-preview-trigger.json +++ b/docs/pages/material-ui/api/menu-preview-trigger.json @@ -23,7 +23,14 @@ }, "name": "MenuPreviewTrigger", "imports": ["import MenuPreviewTrigger from '@mui/material/MenuPreviewTrigger';"], - "slots": [{ "name": "root", "description": "", "class": "MuiMenuPreviewTrigger-root" }], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "Button", + "class": "MuiMenuPreviewTrigger-root" + } + ], "classes": [ { "key": "disabled", diff --git a/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json b/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json index 2849161fdc3460..5d76236eb0765d 100644 --- a/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json +++ b/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json @@ -31,5 +31,5 @@ "conditions": "highlighted" } }, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json b/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json index fc921efed518e8..d166f10000526b 100644 --- a/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json +++ b/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json @@ -40,5 +40,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json b/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json index 8c92b392b4a3d8..84f2fc365ca341 100644 --- a/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json +++ b/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json @@ -12,5 +12,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-group/menu-preview-group.json b/docs/translations/api-docs/menu-preview-group/menu-preview-group.json index 8743d2e26e1f58..5b609b8b501add 100644 --- a/docs/translations/api-docs/menu-preview-group/menu-preview-group.json +++ b/docs/translations/api-docs/menu-preview-group/menu-preview-group.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-item/menu-preview-item.json b/docs/translations/api-docs/menu-preview-item/menu-preview-item.json index 552a6e719d95ae..f7a24beb397a62 100644 --- a/docs/translations/api-docs/menu-preview-item/menu-preview-item.json +++ b/docs/translations/api-docs/menu-preview-item/menu-preview-item.json @@ -31,5 +31,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json b/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json index 79bd5fbfce859a..56fc025de29c66 100644 --- a/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json +++ b/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json @@ -28,5 +28,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json b/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json index 8d6137b9a1f39a..2660a224a7df17 100644 --- a/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json +++ b/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json @@ -27,5 +27,5 @@ "conditions": "disabled={true}" } }, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json b/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json index faa8d5c3697420..df183e63479557 100644 --- a/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json +++ b/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json @@ -31,5 +31,5 @@ "conditions": "highlighted" } }, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json b/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json index 03866a7e3ba528..b3eb6029c79fd2 100644 --- a/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json +++ b/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json @@ -32,5 +32,5 @@ "value": { "description": "Value of the radio item." } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json b/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json index 39ea1cd2fc4e6b..87b20cb20534de 100644 --- a/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json +++ b/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json b/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json index e577f74db637bf..63b8e9926e33af 100644 --- a/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json +++ b/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json @@ -39,5 +39,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json b/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json index 1b289124ab482c..de06b7a775229e 100644 --- a/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json +++ b/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json @@ -36,5 +36,5 @@ "conditions": "the menu is open" } }, - "slotDescriptions": { "root": "" } + "slotDescriptions": { "root": "The component that renders the root." } } diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx b/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx index 70b8079302e562..6ca3ce1f3d2a11 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx +++ b/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx @@ -182,7 +182,7 @@ createTheme({ } + render={ + setClassicAnchorEl(null)} + elevation={settings.elevation} + > + {parityItems.map((item) => ( + setClassicAnchorEl(null)} + > + {item.label} + + ))} + +
+ + }> + Successor + + + {parityItems.map((item) => ( + + {item.label} + + ))} + + + + ); +} + +function ControlledAnchorDemo() { + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const handleOpenChange: MenuProps['onOpenChange'] = (nextOpen) => { + if (!nextOpen) { + setAnchorEl(null); + } + }; + + return ( +
+ + + + setAnchorEl(null)}>Profile + setAnchorEl(null)}>My account + setAnchorEl(null)}>Logout + + +
+ ); +} + +const typeaheadEntries = [ + 'Argentina', + 'Australia', + 'Austria', + 'Belgium', + 'Brazil', + 'Canada', + 'Chile', + 'Colombia', + 'Czechia', + 'Denmark', + 'Estonia', + 'Finland', + 'France', + 'Germany', + 'Greece', + 'Hungary', + 'Iceland', + 'India', + 'Ireland', + 'Italy', + 'Japan', + 'Lithuania', + 'Mexico', + 'Netherlands', + 'New Zealand', + 'Norway', + 'Poland', + 'Portugal', + 'Spain', + 'Sweden', + 'Switzerland', + 'United Kingdom', +]; + +function TypeaheadScrollDemo() { + return ( + + }> + Country + + + {typeaheadEntries.map((entry) => ( + + {entry} + + ))} + + + ); +} + +function SettingsPanel({ + settings, + onChange, +}: { + settings: PlaygroundSettings; + onChange: React.Dispatch>; +}) { + const setSetting = ( + key: Key, + value: PlaygroundSettings[Key], + ) => { + onChange((currentSettings) => ({ ...currentSettings, [key]: value })); + }; + + const renderCheckbox = (key: keyof PlaygroundSettings, label: string) => ( + + ); + + const renderNumber = (key: keyof PlaygroundSettings, label: string, step = 50) => ( + + ); + + return ( + + Playground knobs +
+ Root behavior + {renderCheckbox('modal', 'modal')} + {renderCheckbox('loopFocus', 'loopFocus')} + {renderCheckbox('highlightItemOnHover', 'highlightItemOnHover')} +
+
+ Submenus + {renderCheckbox('submenusOpenOnHover', 'openOnHover')} + {renderNumber('submenuDelay', 'delay (ms)')} + {renderNumber('submenuCloseDelay', 'closeDelay (ms)')} + {renderCheckbox('closeParentOnEsc', 'closeParentOnEsc')} +
+
+ Positioning + + + {renderNumber('sideOffset', 'sideOffset', 4)} + {renderNumber('alignOffset', 'alignOffset', 4)} + {renderCheckbox('keepMounted', 'keepMounted')} +
+
+ Appearance (RFC open questions) + + + {renderCheckbox('dense', 'dense items')} + {renderCheckbox('dividers', 'item dividers')} + {renderCheckbox('rtl', 'RTL direction')} +
+
+ ); +} + +export default function MenuRfcExperiment() { + const [settings, setSettings] = React.useState(defaultSettings); + const [log, setLog] = React.useState([]); + + const pushLog = React.useCallback((entry: string) => { + setLog((currentLog) => [...currentLog.slice(-11), entry]); + }, []); + + const playgroundTheme = settings.rtl ? rtlTheme : theme; + + return ( + + + + + + + Menu RFC playground + + + Companion experiment for the Menu successor RFC. Every knob maps to a prop or an RFC + open question. See also the{' '} + MenuPreview experiment for + Tooltip, PreviewCard, and ContextMenu recipes. + + + + +
+

Kitchen sink

+

+ Nested submenus (three levels), groups with labels, checkbox and radio items, a + disabled item, a visual-only selected item, and a link item. All knobs apply. +

+ + + + + + + + + {log.length === 0 + ? 'Event log: interact with the menu to see onOpenChange reasons.' + : log.join('\n')} + + +
+ +
+

Classic vs successor

+

+ The same item set rendered by the classic Menu and the successor, for visual parity + checks (dense, dividers, selected, disabled, elevation knobs apply to both). Note the + classic top-level elevation prop vs{' '} + slotProps.paper.elevation on the successor (RFC open question). +

+ +
+ +
+

Classic-style controlled usage

+

+ No Trigger part: external anchor element plus controlled open /{' '} + onOpenChange, approximating the classic anchorEl pattern. +

+ +
+ +
+

Typeahead and scrolling

+

+ Open the menu and type to jump between items (for example type "sw"). The + popup constrains height via slotProps.paper. +

+ +
+ + Base UI Menu API +
+
+
+ ); +} From 1650ef471c19b4db2bc1fd192e55ba4af51a9be1 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 11:00:54 +0300 Subject: [PATCH 09/19] Remove docs/API tooling special-casing for the menu experiment Per the agreed standard, docs tooling is a fixed constraint: drop the projectSettings/getComponentImports carve-out, the apiDocs.d.ts entry point, the generated API reference pages and translations, and restore the menus.md frontmatter. The experiment is exercised via the non-public /experiments pages instead. Co-Authored-By: Claude Fable 5 --- docs/data/material/components/menus/menus.md | 2 +- .../menu-preview-checkbox-item-indicator.json | 56 ------- .../api/menu-preview-checkbox-item.json | 46 ------ .../api/menu-preview-group-label.json | 34 ----- .../material-ui/api/menu-preview-group.json | 35 ----- .../material-ui/api/menu-preview-item.json | 43 ------ .../api/menu-preview-link-item.json | 42 ------ .../material-ui/api/menu-preview-popup.json | 142 ------------------ .../api/menu-preview-radio-group.json | 46 ------ .../menu-preview-radio-item-indicator.json | 56 ------- .../api/menu-preview-radio-item.json | 44 ------ .../api/menu-preview-separator.json | 38 ----- .../api/menu-preview-submenu-popup.json | 142 ------------------ .../api/menu-preview-submenu-root.json | 24 --- .../api/menu-preview-submenu-trigger.json | 45 ------ .../material-ui/api/menu-preview-trigger.json | 52 ------- docs/pages/material-ui/api/menu-preview.json | 27 ---- .../menu-preview-checkbox-item-indicator.json | 35 ----- .../menu-preview-checkbox-item.json | 44 ------ .../menu-preview-group-label.json | 16 -- .../menu-preview-group.json | 17 --- .../menu-preview-item/menu-preview-item.json | 35 ----- .../menu-preview-link-item.json | 32 ---- .../menu-preview-popup.json | 56 ------- .../menu-preview-radio-group.json | 31 ---- .../menu-preview-radio-item-indicator.json | 35 ----- .../menu-preview-radio-item.json | 36 ----- .../menu-preview-separator.json | 17 --- .../menu-preview-submenu-popup.json | 56 ------- .../menu-preview-submenu-root.json | 24 --- .../menu-preview-submenu-trigger.json | 43 ------ .../menu-preview-trigger.json | 40 ----- .../api-docs/menu-preview/menu-preview.json | 25 --- .../materialUi/projectSettings.ts | 40 +---- .../mui-material/src/MenuPreview/apiDocs.d.ts | 49 ------ 35 files changed, 2 insertions(+), 1503 deletions(-) delete mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json delete mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item.json delete mode 100644 docs/pages/material-ui/api/menu-preview-group-label.json delete mode 100644 docs/pages/material-ui/api/menu-preview-group.json delete mode 100644 docs/pages/material-ui/api/menu-preview-item.json delete mode 100644 docs/pages/material-ui/api/menu-preview-link-item.json delete mode 100644 docs/pages/material-ui/api/menu-preview-popup.json delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-group.json delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-item-indicator.json delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-item.json delete mode 100644 docs/pages/material-ui/api/menu-preview-separator.json delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-popup.json delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-root.json delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-trigger.json delete mode 100644 docs/pages/material-ui/api/menu-preview-trigger.json delete mode 100644 docs/pages/material-ui/api/menu-preview.json delete mode 100644 docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json delete mode 100644 docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json delete mode 100644 docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json delete mode 100644 docs/translations/api-docs/menu-preview-group/menu-preview-group.json delete mode 100644 docs/translations/api-docs/menu-preview-item/menu-preview-item.json delete mode 100644 docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json delete mode 100644 docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json delete mode 100644 docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json delete mode 100644 docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json delete mode 100644 docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json delete mode 100644 docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json delete mode 100644 docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json delete mode 100644 docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json delete mode 100644 docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json delete mode 100644 docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json delete mode 100644 docs/translations/api-docs/menu-preview/menu-preview.json delete mode 100644 packages/mui-material/src/MenuPreview/apiDocs.d.ts diff --git a/docs/data/material/components/menus/menus.md b/docs/data/material/components/menus/menus.md index 93abb6bb4d2a9b..0ae32bfd270d10 100644 --- a/docs/data/material/components/menus/menus.md +++ b/docs/data/material/components/menus/menus.md @@ -1,7 +1,7 @@ --- productId: material-ui title: React Menu component -components: Menu, MenuItem, MenuList, MenuPreview, MenuPreviewTrigger, MenuPreviewPopup, MenuPreviewSubmenuPopup, MenuPreviewItem, MenuPreviewLinkItem, MenuPreviewCheckboxItem, MenuPreviewCheckboxItemIndicator, MenuPreviewRadioGroup, MenuPreviewRadioItem, MenuPreviewRadioItemIndicator, MenuPreviewGroup, MenuPreviewGroupLabel, MenuPreviewSeparator, MenuPreviewSubmenuRoot, MenuPreviewSubmenuTrigger, ClickAwayListener, Popover, Popper +components: Menu, MenuItem, MenuList, ClickAwayListener, Popover, Popper githubLabel: 'scope: menu' materialDesign: https://m2.material.io/components/menus waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json deleted file mode 100644 index 578f4099507f42..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "props": { - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "keepMounted": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewCheckboxItemIndicator", - "imports": [ - "import MenuPreviewCheckboxItemIndicator from '@mui/material/MenuPreviewCheckboxItemIndicator';" - ], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'span'", - "class": "MuiMenuPreviewCheckboxItemIndicator-root" - } - ], - "classes": [ - { - "key": "checked", - "className": "Mui-checked", - "description": "State class applied to the root element if `checked={true}`.", - "isGlobal": true - }, - { - "key": "disabled", - "className": "Mui-disabled", - "description": "State class applied to the root element if `disabled={true}`.", - "isGlobal": true - }, - { - "key": "highlighted", - "className": "MuiMenuPreviewCheckboxItemIndicator-highlighted", - "description": "State class applied to the root element if highlighted.", - "isGlobal": false - } - ], - "muiName": "MuiMenuPreviewCheckboxItemIndicator", - "filename": "/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.json b/docs/pages/material-ui/api/menu-preview-checkbox-item.json deleted file mode 100644 index c111984ae17e80..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "props": { - "checked": { "type": { "name": "bool" } }, - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, - "component": { "type": { "name": "elementType" } }, - "defaultChecked": { "type": { "name": "bool" }, "default": "false" }, - "dense": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "disableGutters": { "type": { "name": "bool" }, "default": "false" }, - "divider": { "type": { "name": "bool" }, "default": "false" }, - "label": { "type": { "name": "string" } }, - "nativeButton": { "type": { "name": "bool" } }, - "onChange": { "type": { "name": "func" } }, - "selected": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewCheckboxItem", - "imports": ["import MenuPreviewCheckboxItem from '@mui/material/MenuPreviewCheckboxItem';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": null - } - ], - "classes": [], - "muiName": "MuiMenuPreviewCheckboxItem", - "filename": "/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-group-label.json b/docs/pages/material-ui/api/menu-preview-group-label.json deleted file mode 100644 index 05e7ee00cc3678..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-group-label.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "props": { - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewGroupLabel", - "imports": ["import MenuPreviewGroupLabel from '@mui/material/MenuPreviewGroupLabel';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "ListSubheader", - "class": "MuiMenuPreviewGroupLabel-root" - } - ], - "classes": [], - "muiName": "MuiMenuPreviewGroupLabel", - "filename": "/packages/mui-material/src/MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-group.json b/docs/pages/material-ui/api/menu-preview-group.json deleted file mode 100644 index 75a9f51a590117..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-group.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewGroup", - "imports": ["import MenuPreviewGroup from '@mui/material/MenuPreviewGroup';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": "MuiMenuPreviewGroup-root" - } - ], - "classes": [], - "muiName": "MuiMenuPreviewGroup", - "filename": "/packages/mui-material/src/MenuPreviewGroup/MenuPreviewGroup.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-item.json b/docs/pages/material-ui/api/menu-preview-item.json deleted file mode 100644 index 85f2432722027e..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-item.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeOnClick": { "type": { "name": "bool" }, "default": "true" }, - "component": { "type": { "name": "elementType" } }, - "dense": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "disableGutters": { "type": { "name": "bool" }, "default": "false" }, - "divider": { "type": { "name": "bool" }, "default": "false" }, - "label": { "type": { "name": "string" } }, - "nativeButton": { "type": { "name": "bool" } }, - "selected": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewItem", - "imports": ["import MenuPreviewItem from '@mui/material/MenuPreviewItem';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": null - } - ], - "classes": [], - "muiName": "MuiMenuPreviewItem", - "filename": "/packages/mui-material/src/MenuPreviewItem/MenuPreviewItem.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-link-item.json b/docs/pages/material-ui/api/menu-preview-link-item.json deleted file mode 100644 index 2f2aa34b194857..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-link-item.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, - "component": { "type": { "name": "elementType" } }, - "dense": { "type": { "name": "bool" }, "default": "false" }, - "disableGutters": { "type": { "name": "bool" }, "default": "false" }, - "divider": { "type": { "name": "bool" }, "default": "false" }, - "href": { "type": { "name": "string" } }, - "label": { "type": { "name": "string" } }, - "selected": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewLinkItem", - "imports": ["import MenuPreviewLinkItem from '@mui/material/MenuPreviewLinkItem';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'a'", - "class": null - } - ], - "classes": [], - "muiName": "MuiMenuPreviewLinkItem", - "filename": "/packages/mui-material/src/MenuPreviewLinkItem/MenuPreviewLinkItem.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-popup.json b/docs/pages/material-ui/api/menu-preview-popup.json deleted file mode 100644 index eae28eec44cda5..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-popup.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "props": { - "align": { - "type": { - "name": "enum", - "description": "'center'
| 'end'
| 'start'" - }, - "default": "'start'" - }, - "alignOffset": { - "type": { "name": "union", "description": "func
| number" }, - "default": "0" - }, - "anchor": { - "type": { - "name": "union", - "description": "HTML element
| object
| func" - } - }, - "arrowPadding": { "type": { "name": "number" }, "default": "5" }, - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "collisionAvoidance": { - "type": { - "name": "union", - "description": "{ align?: 'flip'
| 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'flip'
| 'none' }
| { align?: 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'none'
| 'shift' }" - } - }, - "collisionBoundary": { - "type": { - "name": "union", - "description": "'clipping-ancestors'
| HTML element
| Array<HTML element>
| { height: number, width: number, x: number, y: number }" - }, - "default": "'clipping-ancestors'" - }, - "collisionPadding": { - "type": { - "name": "union", - "description": "number
| { bottom?: number, left?: number, right?: number, top?: number }" - }, - "default": "5" - }, - "container": { - "type": { - "name": "union", - "description": "HTML element
| object
| func" - } - }, - "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, - "elevation": { "type": { "name": "number" }, "default": "8" }, - "finalFocus": { - "type": { - "name": "union", - "description": "func
| { current?: HTML element }
| bool" - } - }, - "keepMounted": { "type": { "name": "bool" }, "default": "false" }, - "positionMethod": { - "type": { "name": "enum", "description": "'absolute'
| 'fixed'" }, - "default": "'absolute'" - }, - "side": { - "type": { - "name": "enum", - "description": "'bottom'
| 'inline-end'
| 'inline-start'
| 'left'
| 'right'
| 'top'" - }, - "default": "'bottom'" - }, - "sideOffset": { - "type": { "name": "union", "description": "func
| number" }, - "default": "0" - }, - "slotProps": { - "type": { - "name": "shape", - "description": "{ list?: func
| object, paper?: func
| object, popup?: func
| object, portal?: func
| object, positioner?: func
| object }" - } - }, - "slots": { - "type": { - "name": "shape", - "description": "{ list?: elementType, paper?: elementType, popup?: elementType, portal?: elementType, positioner?: elementType }" - } - }, - "sticky": { "type": { "name": "bool" }, "default": "false" }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewPopup", - "imports": ["import MenuPreviewPopup from '@mui/material/MenuPreviewPopup';"], - "slots": [ - { - "name": "portal", - "description": "The component used for the portal.", - "default": "BaseMenu.Portal", - "class": null - }, - { - "name": "positioner", - "description": "The component used for the positioner.", - "default": "BaseMenu.Positioner", - "class": null - }, - { - "name": "popup", - "description": "The component rendered by the Base UI popup.", - "default": "'div'", - "class": null - }, - { - "name": "paper", - "description": "The component used for the Material surface.", - "default": "Paper", - "class": "MuiMenuPreviewPopup-paper" - }, - { - "name": "list", - "description": "The component used for the presentational list wrapper.", - "default": "List", - "class": "MuiMenuPreviewPopup-list" - } - ], - "classes": [ - { - "key": "root", - "className": "MuiMenuPreviewPopup-root", - "description": "Styles applied to the root element.", - "isGlobal": false - } - ], - "muiName": "MuiMenuPreviewPopup", - "filename": "/packages/mui-material/src/MenuPreviewPopup/MenuPreviewPopup.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.json b/docs/pages/material-ui/api/menu-preview-radio-group.json deleted file mode 100644 index 0c709d5d214111..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-group.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "defaultValue": { "type": { "name": "any" } }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "onChange": { "type": { "name": "func" } }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - }, - "value": { "type": { "name": "any" } } - }, - "name": "MenuPreviewRadioGroup", - "imports": ["import MenuPreviewRadioGroup from '@mui/material/MenuPreviewRadioGroup';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": "MuiMenuPreviewRadioGroup-root" - } - ], - "classes": [ - { - "key": "disabled", - "className": "Mui-disabled", - "description": "State class applied to the root element if `disabled={true}`.", - "isGlobal": true - } - ], - "muiName": "MuiMenuPreviewRadioGroup", - "filename": "/packages/mui-material/src/MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json deleted file mode 100644 index 0ba43b87d70be3..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "props": { - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "keepMounted": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewRadioItemIndicator", - "imports": [ - "import MenuPreviewRadioItemIndicator from '@mui/material/MenuPreviewRadioItemIndicator';" - ], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'span'", - "class": "MuiMenuPreviewRadioItemIndicator-root" - } - ], - "classes": [ - { - "key": "checked", - "className": "Mui-checked", - "description": "State class applied to the root element if `checked={true}`.", - "isGlobal": true - }, - { - "key": "disabled", - "className": "Mui-disabled", - "description": "State class applied to the root element if `disabled={true}`.", - "isGlobal": true - }, - { - "key": "highlighted", - "className": "MuiMenuPreviewRadioItemIndicator-highlighted", - "description": "State class applied to the root element if highlighted.", - "isGlobal": false - } - ], - "muiName": "MuiMenuPreviewRadioItemIndicator", - "filename": "/packages/mui-material/src/MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.json b/docs/pages/material-ui/api/menu-preview-radio-item.json deleted file mode 100644 index f5e4735f558304..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-item.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "props": { - "value": { "type": { "name": "any" }, "required": true }, - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeOnClick": { "type": { "name": "bool" }, "default": "false" }, - "component": { "type": { "name": "elementType" } }, - "dense": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "disableGutters": { "type": { "name": "bool" }, "default": "false" }, - "divider": { "type": { "name": "bool" }, "default": "false" }, - "label": { "type": { "name": "string" } }, - "nativeButton": { "type": { "name": "bool" } }, - "selected": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewRadioItem", - "imports": ["import MenuPreviewRadioItem from '@mui/material/MenuPreviewRadioItem';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": null - } - ], - "classes": [], - "muiName": "MuiMenuPreviewRadioItem", - "filename": "/packages/mui-material/src/MenuPreviewRadioItem/MenuPreviewRadioItem.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-separator.json b/docs/pages/material-ui/api/menu-preview-separator.json deleted file mode 100644 index 9c121e406aa332..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-separator.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "props": { - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "component": { "type": { "name": "elementType" } }, - "orientation": { - "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, - "default": "'horizontal'" - }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewSeparator", - "imports": ["import MenuPreviewSeparator from '@mui/material/MenuPreviewSeparator';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "Divider", - "class": "MuiMenuPreviewSeparator-root" - } - ], - "classes": [], - "muiName": "MuiMenuPreviewSeparator", - "filename": "/packages/mui-material/src/MenuPreviewSeparator/MenuPreviewSeparator.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.json b/docs/pages/material-ui/api/menu-preview-submenu-popup.json deleted file mode 100644 index 44e03df304c3bc..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-popup.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "props": { - "align": { - "type": { - "name": "enum", - "description": "'center'
| 'end'
| 'start'" - }, - "default": "'start'" - }, - "alignOffset": { - "type": { "name": "union", "description": "func
| number" }, - "default": "0" - }, - "anchor": { - "type": { - "name": "union", - "description": "HTML element
| object
| func" - } - }, - "arrowPadding": { "type": { "name": "number" }, "default": "5" }, - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "collisionAvoidance": { - "type": { - "name": "union", - "description": "{ align?: 'flip'
| 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'flip'
| 'none' }
| { align?: 'none'
| 'shift', fallbackAxisSide?: 'end'
| 'none'
| 'start', side?: 'none'
| 'shift' }" - } - }, - "collisionBoundary": { - "type": { - "name": "union", - "description": "'clipping-ancestors'
| HTML element
| Array<HTML element>
| { height: number, width: number, x: number, y: number }" - }, - "default": "'clipping-ancestors'" - }, - "collisionPadding": { - "type": { - "name": "union", - "description": "number
| { bottom?: number, left?: number, right?: number, top?: number }" - }, - "default": "5" - }, - "container": { - "type": { - "name": "union", - "description": "HTML element
| object
| func" - } - }, - "disableAnchorTracking": { "type": { "name": "bool" }, "default": "false" }, - "elevation": { "type": { "name": "number" }, "default": "8" }, - "finalFocus": { - "type": { - "name": "union", - "description": "func
| { current?: HTML element }
| bool" - } - }, - "keepMounted": { "type": { "name": "bool" }, "default": "false" }, - "positionMethod": { - "type": { "name": "enum", "description": "'absolute'
| 'fixed'" }, - "default": "'absolute'" - }, - "side": { - "type": { - "name": "enum", - "description": "'bottom'
| 'inline-end'
| 'inline-start'
| 'left'
| 'right'
| 'top'" - }, - "default": "'inline-end'" - }, - "sideOffset": { - "type": { "name": "union", "description": "func
| number" }, - "default": "0" - }, - "slotProps": { - "type": { - "name": "shape", - "description": "{ list?: func
| object, paper?: func
| object, popup?: func
| object, portal?: func
| object, positioner?: func
| object }" - } - }, - "slots": { - "type": { - "name": "shape", - "description": "{ list?: elementType, paper?: elementType, popup?: elementType, portal?: elementType, positioner?: elementType }" - } - }, - "sticky": { "type": { "name": "bool" }, "default": "false" }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewSubmenuPopup", - "imports": ["import MenuPreviewSubmenuPopup from '@mui/material/MenuPreviewSubmenuPopup';"], - "slots": [ - { - "name": "portal", - "description": "The component used for the portal.", - "default": "BaseMenu.Portal", - "class": null - }, - { - "name": "positioner", - "description": "The component used for the positioner.", - "default": "BaseMenu.Positioner", - "class": null - }, - { - "name": "popup", - "description": "The component rendered by the Base UI popup.", - "default": "'div'", - "class": null - }, - { - "name": "paper", - "description": "The component used for the Material surface.", - "default": "Paper", - "class": "MuiMenuPreviewSubmenuPopup-paper" - }, - { - "name": "list", - "description": "The component used for the presentational list wrapper.", - "default": "List", - "class": "MuiMenuPreviewSubmenuPopup-list" - } - ], - "classes": [ - { - "key": "root", - "className": "MuiMenuPreviewSubmenuPopup-root", - "description": "Styles applied to the root element.", - "isGlobal": false - } - ], - "muiName": "MuiMenuPreviewSubmenuPopup", - "filename": "/packages/mui-material/src/MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-root.json b/docs/pages/material-ui/api/menu-preview-submenu-root.json deleted file mode 100644 index 3ec88efda62789..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-root.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "closeParentOnEsc": { "type": { "name": "bool" }, "default": "false" }, - "defaultOpen": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "highlightItemOnHover": { "type": { "name": "bool" }, "default": "true" }, - "loopFocus": { "type": { "name": "bool" }, "default": "true" }, - "onOpenChange": { "type": { "name": "func" } }, - "onOpenChangeComplete": { "type": { "name": "func" } }, - "open": { "type": { "name": "bool" } }, - "orientation": { - "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, - "default": "'vertical'" - } - }, - "name": "MenuPreviewSubmenuRoot", - "imports": ["import MenuPreviewSubmenuRoot from '@mui/material/MenuPreviewSubmenuRoot';"], - "classes": [], - "muiName": "MuiMenuPreviewSubmenuRoot", - "filename": "/packages/mui-material/src/MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.json b/docs/pages/material-ui/api/menu-preview-submenu-trigger.json deleted file mode 100644 index 1de0b51f8fe169..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-trigger.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeDelay": { "type": { "name": "number" }, "default": "0" }, - "component": { "type": { "name": "elementType" } }, - "delay": { "type": { "name": "number" }, "default": "100" }, - "dense": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "disableGutters": { "type": { "name": "bool" }, "default": "false" }, - "divider": { "type": { "name": "bool" }, "default": "false" }, - "label": { "type": { "name": "string" } }, - "nativeButton": { "type": { "name": "bool" } }, - "openOnHover": { "type": { "name": "bool" } }, - "selected": { "type": { "name": "bool" }, "default": "false" }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewSubmenuTrigger", - "imports": ["import MenuPreviewSubmenuTrigger from '@mui/material/MenuPreviewSubmenuTrigger';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "'div'", - "class": null - } - ], - "classes": [], - "muiName": "MuiMenuPreviewSubmenuTrigger", - "filename": "/packages/mui-material/src/MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview-trigger.json b/docs/pages/material-ui/api/menu-preview-trigger.json deleted file mode 100644 index 385c860b98acb7..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-trigger.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "props": { - "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "className": { "type": { "name": "string" } }, - "closeDelay": { "type": { "name": "number" }, "default": "0" }, - "component": { "type": { "name": "elementType" } }, - "delay": { "type": { "name": "number" }, "default": "100" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "nativeButton": { "type": { "name": "bool" } }, - "openOnHover": { "type": { "name": "bool" } }, - "slotProps": { - "type": { "name": "shape", "description": "{ root?: func
| object }" } - }, - "slots": { "type": { "name": "shape", "description": "{ root?: elementType }" } }, - "style": { "type": { "name": "object" } }, - "sx": { - "type": { - "name": "union", - "description": "Array<func
| object
| bool>
| func
| object" - }, - "additionalInfo": { "sx": true } - } - }, - "name": "MenuPreviewTrigger", - "imports": ["import MenuPreviewTrigger from '@mui/material/MenuPreviewTrigger';"], - "slots": [ - { - "name": "root", - "description": "The component that renders the root.", - "default": "Button", - "class": "MuiMenuPreviewTrigger-root" - } - ], - "classes": [ - { - "key": "disabled", - "className": "Mui-disabled", - "description": "State class applied to the root element if `disabled={true}`.", - "isGlobal": true - }, - { - "key": "open", - "className": "Mui-open", - "description": "State class applied to the root element if the menu is open.", - "isGlobal": true - } - ], - "muiName": "MuiMenuPreviewTrigger", - "filename": "/packages/mui-material/src/MenuPreviewTrigger/MenuPreviewTrigger.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/pages/material-ui/api/menu-preview.json b/docs/pages/material-ui/api/menu-preview.json deleted file mode 100644 index 084edc4242f118..00000000000000 --- a/docs/pages/material-ui/api/menu-preview.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "props": { - "children": { "type": { "name": "node" } }, - "closeParentOnEsc": { "type": { "name": "bool" }, "default": "false" }, - "defaultOpen": { "type": { "name": "bool" }, "default": "false" }, - "disabled": { "type": { "name": "bool" }, "default": "false" }, - "highlightItemOnHover": { "type": { "name": "bool" }, "default": "true" }, - "loopFocus": { "type": { "name": "bool" }, "default": "true" }, - "modal": { "type": { "name": "bool" }, "default": "true" }, - "onOpenChange": { "type": { "name": "func" } }, - "onOpenChangeComplete": { "type": { "name": "func" } }, - "open": { "type": { "name": "bool" } }, - "orientation": { - "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, - "default": "'vertical'" - } - }, - "name": "MenuPreview", - "imports": ["import MenuPreview from '@mui/material/MenuPreview';"], - "classes": [], - "spread": true, - "themeDefaultProps": null, - "muiName": "MuiMenuPreview", - "filename": "/packages/mui-material/src/MenuPreview/MenuPreview.tsx", - "inheritance": null, - "demos": "" -} diff --git a/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json b/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json deleted file mode 100644 index 5d76236eb0765d..00000000000000 --- a/docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "keepMounted": { - "description": "Whether to keep the HTML element in the DOM when the checkbox item is not checked." - }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": { - "checked": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "checked={true}" - }, - "disabled": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "disabled={true}" - }, - "highlighted": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "highlighted" - } - }, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json b/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json deleted file mode 100644 index d166f10000526b..00000000000000 --- a/docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "checked": { - "description": "Whether the checkbox item is currently ticked.
To render an uncontrolled checkbox item, use the defaultChecked prop instead." - }, - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, - "component": { "description": "The component used for the root node." }, - "defaultChecked": { - "description": "Whether the checkbox item is initially ticked.
To render a controlled checkbox item, use the checked prop instead." - }, - "dense": { - "description": "If true, compact vertical padding designed for keyboard and mouse input is used." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "disableGutters": { - "description": "If true, the left and right padding is removed." - }, - "divider": { - "description": "If true, a 1px light border is added to the bottom of the menu item." - }, - "label": { - "description": "Overrides the text label to use when the item is matched during keyboard text navigation." - }, - "nativeButton": { - "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." - }, - "onChange": { - "description": "Event handler called when the checkbox item is ticked or unticked." - }, - "selected": { "description": "If true, the component is selected." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json b/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json deleted file mode 100644 index 84f2fc365ca341..00000000000000 --- a/docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-group/menu-preview-group.json b/docs/translations/api-docs/menu-preview-group/menu-preview-group.json deleted file mode 100644 index 5b609b8b501add..00000000000000 --- a/docs/translations/api-docs/menu-preview-group/menu-preview-group.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-item/menu-preview-item.json b/docs/translations/api-docs/menu-preview-item/menu-preview-item.json deleted file mode 100644 index f7a24beb397a62..00000000000000 --- a/docs/translations/api-docs/menu-preview-item/menu-preview-item.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, - "component": { "description": "The component used for the root node." }, - "dense": { - "description": "If true, compact vertical padding designed for keyboard and mouse input is used." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "disableGutters": { - "description": "If true, the left and right padding is removed." - }, - "divider": { - "description": "If true, a 1px light border is added to the bottom of the menu item." - }, - "label": { - "description": "Overrides the text label to use when the item is matched during keyboard text navigation." - }, - "nativeButton": { - "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." - }, - "selected": { "description": "If true, the component is selected." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json b/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json deleted file mode 100644 index 56fc025de29c66..00000000000000 --- a/docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, - "component": { "description": "The component used for the root node." }, - "dense": { - "description": "If true, compact vertical padding designed for keyboard and mouse input is used." - }, - "disableGutters": { - "description": "If true, the left and right padding is removed." - }, - "divider": { - "description": "If true, a 1px light border is added to the bottom of the menu item." - }, - "href": { "description": "The URL that the link item points to." }, - "label": { - "description": "Overrides the text label to use when the item is matched during keyboard text navigation." - }, - "selected": { "description": "If true, the component is selected." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json b/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json deleted file mode 100644 index b7b59afbc03738..00000000000000 --- a/docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "align": { "description": "How to align the popup relative to the specified side." }, - "alignOffset": { "description": "Additional offset along the alignment axis in pixels." }, - "anchor": { - "description": "An element to position the popup against.
By default, the popup is positioned against the trigger." - }, - "arrowPadding": { - "description": "Minimum distance to maintain between the arrow and the edges of the popup." - }, - "children": { "description": "The menu items." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the Base UI popup element." }, - "collisionAvoidance": { - "description": "Determines how to handle collisions when positioning the popup." - }, - "collisionBoundary": { - "description": "An element or a rectangle that delimits the area that the popup is confined to." - }, - "collisionPadding": { - "description": "Additional space to maintain from the edge of the collision boundary." - }, - "container": { "description": "The container element to portal the popup into." }, - "disableAnchorTracking": { - "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." - }, - "elevation": { "description": "The elevation of the menu surface." }, - "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, - "keepMounted": { - "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." - }, - "positionMethod": { - "description": "Determines which CSS position property to use." - }, - "side": { "description": "Which side of the anchor element to align the popup against." }, - "sideOffset": { "description": "Distance between the anchor and the popup in pixels." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "sticky": { - "description": "Whether to maintain the popup in the viewport after the anchor element was scrolled out of view." - }, - "style": { "description": "Styles applied to the Base UI popup element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, - "slotDescriptions": { - "list": "The component used for the presentational list wrapper.", - "paper": "The component used for the Material surface.", - "popup": "The component rendered by the Base UI popup.", - "portal": "The component used for the portal.", - "positioner": "The component used for the positioner." - } -} diff --git a/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json b/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json deleted file mode 100644 index 2660a224a7df17..00000000000000 --- a/docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "defaultValue": { - "description": "The uncontrolled value of the radio item that should be initially selected." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "onChange": { "description": "Function called when the selected value changes." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - }, - "value": { - "description": "The controlled value of the radio item that should be currently selected." - } - }, - "classDescriptions": { - "disabled": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "disabled={true}" - } - }, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json b/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json deleted file mode 100644 index df183e63479557..00000000000000 --- a/docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "keepMounted": { - "description": "Whether to keep the HTML element in the DOM when the radio item is inactive." - }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": { - "checked": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "checked={true}" - }, - "disabled": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "disabled={true}" - }, - "highlighted": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "highlighted" - } - }, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json b/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json deleted file mode 100644 index b3eb6029c79fd2..00000000000000 --- a/docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeOnClick": { "description": "Whether to close the menu when the item is clicked." }, - "component": { "description": "The component used for the root node." }, - "dense": { - "description": "If true, compact vertical padding designed for keyboard and mouse input is used." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "disableGutters": { - "description": "If true, the left and right padding is removed." - }, - "divider": { - "description": "If true, a 1px light border is added to the bottom of the menu item." - }, - "label": { - "description": "Overrides the text label to use when the item is matched during keyboard text navigation." - }, - "nativeButton": { - "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." - }, - "selected": { "description": "If true, the component is selected." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - }, - "value": { "description": "Value of the radio item." } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json b/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json deleted file mode 100644 index 87b20cb20534de..00000000000000 --- a/docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "component": { "description": "The component used for the root node." }, - "orientation": { "description": "The orientation of the separator." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json b/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json deleted file mode 100644 index e8c18a9115d7cd..00000000000000 --- a/docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "align": { "description": "How to align the popup relative to the specified side." }, - "alignOffset": { "description": "Additional offset along the alignment axis in pixels." }, - "anchor": { - "description": "An element to position the popup against.
By default, the popup is positioned against the submenu trigger." - }, - "arrowPadding": { - "description": "Minimum distance to maintain between the arrow and the edges of the popup." - }, - "children": { "description": "The submenu items." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the Base UI popup element." }, - "collisionAvoidance": { - "description": "Determines how to handle collisions when positioning the popup." - }, - "collisionBoundary": { - "description": "An element or a rectangle that delimits the area that the popup is confined to." - }, - "collisionPadding": { - "description": "Additional space to maintain from the edge of the collision boundary." - }, - "container": { "description": "The container element to portal the popup into." }, - "disableAnchorTracking": { - "description": "Whether to disable the popup from tracking layout shifts of its positioning anchor." - }, - "elevation": { "description": "The elevation of the menu surface." }, - "finalFocus": { "description": "Determines the element to focus when the menu is closed." }, - "keepMounted": { - "description": "Whether to keep the portal mounted in the DOM while the popup is hidden." - }, - "positionMethod": { - "description": "Determines which CSS position property to use." - }, - "side": { "description": "Which side of the anchor element to align the popup against." }, - "sideOffset": { "description": "Distance between the anchor and the popup in pixels." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "sticky": { - "description": "Whether to maintain the popup in the viewport after the anchor element was scrolled out of view." - }, - "style": { "description": "Styles applied to the Base UI popup element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, - "slotDescriptions": { - "list": "The component used for the presentational list wrapper.", - "paper": "The component used for the Material surface.", - "popup": "The component rendered by the Base UI popup.", - "portal": "The component used for the portal.", - "positioner": "The component used for the positioner." - } -} diff --git a/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json b/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json deleted file mode 100644 index ff6614738e49c7..00000000000000 --- a/docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the submenu." }, - "closeParentOnEsc": { - "description": "When in a submenu, determines whether pressing the Escape key closes the entire menu." - }, - "defaultOpen": { - "description": "Whether the submenu is initially open.
To render a controlled submenu, use the open prop instead." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "highlightItemOnHover": { - "description": "Whether moving the pointer over items should highlight them." - }, - "loopFocus": { "description": "Whether to loop keyboard focus back to the first item." }, - "onOpenChange": { "description": "Event handler called when the submenu is opened or closed." }, - "onOpenChangeComplete": { - "description": "Event handler called after any animations complete when the submenu is opened or closed." - }, - "open": { "description": "Whether the submenu is currently open." }, - "orientation": { "description": "The visual orientation of the submenu." } - }, - "classDescriptions": {} -} diff --git a/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json b/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json deleted file mode 100644 index 63b8e9926e33af..00000000000000 --- a/docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the component." }, - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeDelay": { - "description": "How long to wait before closing the submenu that was opened on hover, in milliseconds.
Requires the openOnHover prop." - }, - "component": { "description": "The component used for the root node." }, - "delay": { - "description": "How long to wait before the submenu may be opened on hover, in milliseconds.
Requires the openOnHover prop." - }, - "dense": { - "description": "If true, compact vertical padding designed for keyboard and mouse input is used." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "disableGutters": { - "description": "If true, the left and right padding is removed." - }, - "divider": { - "description": "If true, a 1px light border is added to the bottom of the menu item." - }, - "label": { - "description": "Overrides the text label to use when the item is matched during keyboard text navigation." - }, - "nativeButton": { - "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." - }, - "openOnHover": { - "description": "Whether the submenu should also open when the trigger is hovered." - }, - "selected": { "description": "If true, the component is selected." }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": {}, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json b/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json deleted file mode 100644 index de06b7a775229e..00000000000000 --- a/docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "classes": { "description": "Override or extend the styles applied to the component." }, - "className": { "description": "CSS class applied to the element." }, - "closeDelay": { - "description": "How long to wait before closing the menu that was opened on hover, in milliseconds.
Requires the openOnHover prop." - }, - "component": { "description": "The component used for the root node." }, - "delay": { - "description": "How long to wait before the menu may be opened on hover, in milliseconds.
Requires the openOnHover prop." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "nativeButton": { - "description": "Whether the component is rendered as a native button.
By default, this is inferred from the root slot and component prop." - }, - "openOnHover": { - "description": "Whether the menu should also open when the trigger is hovered." - }, - "slotProps": { "description": "The props used for each slot inside." }, - "slots": { "description": "The components used for each slot inside." }, - "style": { "description": "Styles applied to the root element." }, - "sx": { - "description": "The system prop that allows defining system overrides as well as additional CSS styles." - } - }, - "classDescriptions": { - "disabled": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "disabled={true}" - }, - "open": { - "description": "State class applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "the menu is open" - } - }, - "slotDescriptions": { "root": "The component that renders the root." } -} diff --git a/docs/translations/api-docs/menu-preview/menu-preview.json b/docs/translations/api-docs/menu-preview/menu-preview.json deleted file mode 100644 index 31e7124fe6bf7f..00000000000000 --- a/docs/translations/api-docs/menu-preview/menu-preview.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "componentDescription": "", - "propDescriptions": { - "children": { "description": "The content of the menu." }, - "closeParentOnEsc": { - "description": "When in a submenu, determines whether pressing the Escape key closes the entire menu." - }, - "defaultOpen": { - "description": "Whether the menu is initially open.
To render a controlled menu, use the open prop instead." - }, - "disabled": { "description": "Whether the component should ignore user interaction." }, - "highlightItemOnHover": { - "description": "Whether moving the pointer over items should highlight them." - }, - "loopFocus": { "description": "Whether to loop keyboard focus back to the first item." }, - "modal": { "description": "Determines if the menu enters a modal state when open." }, - "onOpenChange": { "description": "Event handler called when the menu is opened or closed." }, - "onOpenChangeComplete": { - "description": "Event handler called after any animations complete when the menu is opened or closed." - }, - "open": { "description": "Whether the menu is currently open." }, - "orientation": { "description": "The visual orientation of the menu." } - }, - "classDescriptions": {} -} diff --git a/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts b/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts index e1a85bdc147ba5..a52477fe5e64cd 100644 --- a/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts +++ b/packages-internal/api-docs-builder-core/materialUi/projectSettings.ts @@ -1,6 +1,6 @@ import path from 'path'; import { LANGUAGES } from '@mui/internal-core-docs/constants'; -import { ProjectSettings, findApiPages, toGitHubPath } from '@mui/internal-api-docs-builder'; +import { ProjectSettings, findApiPages } from '@mui/internal-api-docs-builder'; import generateUtilityClass, { isGlobalState } from '@mui/utils/generateUtilityClass'; import { getMaterialUiComponentInfo } from './getMaterialUiComponentInfo'; @@ -13,42 +13,6 @@ const generateClassName = (componentName: string, slot: string, globalStatePrefi return generateUtilityClass(componentName, slot, globalStatePrefix); }; -const getComponentImports = (name: string, filename: string) => { - const githubPath = toGitHubPath(filename); - const directory = githubPath.match(/\/packages\/mui-material\/src\/([^/]+)\//)?.[1]; - - if (directory?.startsWith('MenuPreview')) { - return [`import ${name} from '@mui/material/${directory}';`]; - } - - const rootImportPath = githubPath.replace( - /\/packages\/mui(?:-(.+?))?\/src\/.*/, - (match, pkg) => `@mui/${pkg}`, - ); - - const subdirectoryImportPath = githubPath.replace( - /\/packages\/mui(?:-(.+?))?\/src\/([^\\/]+)\/.*/, - (match, pkg, subdirectory) => `@mui/${pkg}/${subdirectory}`, - ); - - let namedImportName = name; - const defaultImportName = name; - - if (githubPath.includes('Unstable_')) { - namedImportName = `Unstable_${name} as ${name}`; - } - - const useNamedImports = rootImportPath === '@mui/base'; - - const subpathImport = useNamedImports - ? `import { ${namedImportName} } from '${subdirectoryImportPath}';` - : `import ${defaultImportName} from '${subdirectoryImportPath}';`; - - const rootImport = `import { ${namedImportName} } from '${rootImportPath}';`; - - return [subpathImport, rootImport]; -}; - export const projectSettings: ProjectSettings = { output: { apiManifestPath: path.join(process.cwd(), 'docs/data/material/pagesApi.js'), @@ -59,7 +23,6 @@ export const projectSettings: ProjectSettings = { rootPath: path.join(process.cwd(), 'packages/mui-material'), entryPointPath: [ 'src/index.d.ts', - 'src/MenuPreview/apiDocs.d.ts', 'src/PigmentStack/PigmentStack.tsx', 'src/PigmentContainer/PigmentContainer.tsx', 'src/PigmentGrid/PigmentGrid.tsx', @@ -78,7 +41,6 @@ export const projectSettings: ProjectSettings = { return filename.match(/(ThemeProvider|CssVarsProvider|DefaultPropsProvider)/) !== null; }, translationPagesDirectory: 'docs/translations/api-docs', - getComponentImports, generateClassName, isGlobalClassName: isGlobalState, // #host-reference diff --git a/packages/mui-material/src/MenuPreview/apiDocs.d.ts b/packages/mui-material/src/MenuPreview/apiDocs.d.ts deleted file mode 100644 index 6c939d59992308..00000000000000 --- a/packages/mui-material/src/MenuPreview/apiDocs.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -export { default as MenuPreview } from './MenuPreview'; -export * from './MenuPreview'; - -export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; -export * from '../MenuPreviewTrigger'; - -export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; -export * from '../MenuPreviewPopup'; - -export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; -export * from '../MenuPreviewSubmenuPopup'; - -export { default as MenuPreviewItem } from '../MenuPreviewItem'; -export * from '../MenuPreviewItem'; - -export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; -export * from '../MenuPreviewLinkItem'; - -export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; -export * from '../MenuPreviewCheckboxItem'; - -export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; -export * from '../MenuPreviewCheckboxItemIndicator'; - -export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; -export * from '../MenuPreviewRadioGroup'; - -export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; -export * from '../MenuPreviewRadioItem'; - -export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; -export * from '../MenuPreviewRadioItemIndicator'; - -export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; -export * from '../MenuPreviewGroup'; - -export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; -export * from '../MenuPreviewGroupLabel'; - -export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; -export * from '../MenuPreviewSeparator'; - -export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; -export * from '../MenuPreviewSubmenuRoot'; - -export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; -export * from '../MenuPreviewSubmenuTrigger'; - -export * from './menuPreviewClasses'; From a9c5e155d31fd847fefe9df9d2896dd785a53d91 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 11:11:49 +0300 Subject: [PATCH 10/19] Rename MenuPreview family to Unstable_Menu2 Aligns the experiment with the agreed naming lifecycle: - public subpaths and directories use Unstable_Menu2* (one component per subpath); the root index no longer re-exports parts or Base UI-style short aliases - internal identifiers and file names use clean Menu2* naming, following the Unstable_TrapFocus/Grid2 precedent enforced by the naming-convention lint rules - theme keys and classes use MuiMenu2* (the name-matches-component lint rule derives keys from component names; matches Grid2, and keys survive the later Unstable_ -> stable rename) - data attributes use data-mui-menu2-* Co-Authored-By: Claude Fable 5 --- docs/pages/experiments/menu-preview.tsx | 86 ++- docs/pages/experiments/menu-rfc.tsx | 54 +- .../mui-material/src/MenuPreview/index.d.ts | 64 -- .../mui-material/src/MenuPreview/index.js | 64 -- .../src/MenuPreview/menuPreviewClasses.ts | 280 -------- .../src/MenuPreviewCheckboxItem/index.d.ts | 10 - .../src/MenuPreviewCheckboxItem/index.js | 6 - .../index.d.ts | 10 - .../MenuPreviewCheckboxItemIndicator/index.js | 6 - .../src/MenuPreviewGroup/index.d.ts | 10 - .../src/MenuPreviewGroup/index.js | 6 - .../src/MenuPreviewGroupLabel/index.d.ts | 10 - .../src/MenuPreviewGroupLabel/index.js | 6 - .../src/MenuPreviewItem/index.d.ts | 10 - .../mui-material/src/MenuPreviewItem/index.js | 6 - .../src/MenuPreviewLinkItem/index.d.ts | 10 - .../src/MenuPreviewLinkItem/index.js | 6 - .../src/MenuPreviewPopup/index.d.ts | 10 - .../src/MenuPreviewPopup/index.js | 6 - .../src/MenuPreviewRadioGroup/index.d.ts | 10 - .../src/MenuPreviewRadioGroup/index.js | 6 - .../src/MenuPreviewRadioItem/index.d.ts | 10 - .../src/MenuPreviewRadioItem/index.js | 6 - .../MenuPreviewRadioItemIndicator/index.d.ts | 10 - .../MenuPreviewRadioItemIndicator/index.js | 6 - .../src/MenuPreviewSeparator/index.d.ts | 10 - .../src/MenuPreviewSeparator/index.js | 6 - .../src/MenuPreviewSubmenuPopup/index.d.ts | 10 - .../src/MenuPreviewSubmenuPopup/index.js | 6 - .../src/MenuPreviewSubmenuRoot/index.d.ts | 2 - .../src/MenuPreviewSubmenuRoot/index.js | 2 - .../src/MenuPreviewSubmenuTrigger/index.d.ts | 10 - .../src/MenuPreviewSubmenuTrigger/index.js | 6 - .../src/MenuPreviewTrigger/index.d.ts | 10 - .../src/MenuPreviewTrigger/index.js | 6 - .../Menu2.spec.tsx} | 53 +- .../Menu2.test.tsx} | 610 +++++++++--------- .../Menu2.tsx} | 12 +- .../src/Unstable_Menu2/index.d.ts | 3 + .../mui-material/src/Unstable_Menu2/index.js | 3 + .../src/Unstable_Menu2/menu2Classes.ts | 273 ++++++++ .../menu2ItemShared.tsx} | 46 +- .../menu2PopupShared.tsx} | 78 +-- .../menu2SharedStyles.ts} | 18 +- .../menu2Utils.ts} | 12 +- .../Menu2CheckboxItem.tsx} | 86 +-- .../src/Unstable_Menu2CheckboxItem/index.d.ts | 10 + .../src/Unstable_Menu2CheckboxItem/index.js | 6 + .../Menu2CheckboxItemIndicator.tsx} | 73 +-- .../index.d.ts | 10 + .../index.js | 6 + .../Menu2Group.tsx} | 45 +- .../src/Unstable_Menu2Group/index.d.ts | 4 + .../src/Unstable_Menu2Group/index.js | 3 + .../Menu2GroupLabel.tsx} | 46 +- .../src/Unstable_Menu2GroupLabel/index.d.ts | 10 + .../src/Unstable_Menu2GroupLabel/index.js | 6 + .../Menu2Item.tsx} | 84 ++- .../src/Unstable_Menu2Item/index.d.ts | 4 + .../src/Unstable_Menu2Item/index.js | 3 + .../Menu2LinkItem.tsx} | 78 ++- .../src/Unstable_Menu2LinkItem/index.d.ts | 4 + .../src/Unstable_Menu2LinkItem/index.js | 3 + .../Menu2Popup.tsx} | 120 ++-- .../src/Unstable_Menu2Popup/index.d.ts | 4 + .../src/Unstable_Menu2Popup/index.js | 3 + .../Menu2RadioGroup.tsx} | 48 +- .../src/Unstable_Menu2RadioGroup/index.d.ts | 10 + .../src/Unstable_Menu2RadioGroup/index.js | 6 + .../Menu2RadioItem.tsx} | 84 ++- .../src/Unstable_Menu2RadioItem/index.d.ts | 7 + .../src/Unstable_Menu2RadioItem/index.js | 6 + .../Menu2RadioItemIndicator.tsx} | 71 +- .../index.d.ts | 10 + .../Unstable_Menu2RadioItemIndicator/index.js | 6 + .../Menu2Separator.tsx} | 46 +- .../src/Unstable_Menu2Separator/index.d.ts | 7 + .../src/Unstable_Menu2Separator/index.js | 6 + .../Menu2SubmenuPopup.tsx} | 121 ++-- .../src/Unstable_Menu2SubmenuPopup/index.d.ts | 10 + .../src/Unstable_Menu2SubmenuPopup/index.js | 6 + .../Menu2SubmenuRoot.tsx} | 12 +- .../src/Unstable_Menu2SubmenuRoot/index.d.ts | 2 + .../src/Unstable_Menu2SubmenuRoot/index.js | 2 + .../Menu2SubmenuTrigger.tsx} | 86 +-- .../Unstable_Menu2SubmenuTrigger/index.d.ts | 10 + .../src/Unstable_Menu2SubmenuTrigger/index.js | 6 + .../Menu2Trigger.tsx} | 57 +- .../src/Unstable_Menu2Trigger/index.d.ts | 4 + .../src/Unstable_Menu2Trigger/index.js | 3 + .../mui-material/src/styles/components.ts | 122 ++-- packages/mui-material/src/styles/overrides.ts | 58 +- packages/mui-material/src/styles/props.ts | 64 +- 93 files changed, 1570 insertions(+), 1792 deletions(-) delete mode 100644 packages/mui-material/src/MenuPreview/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreview/index.js delete mode 100644 packages/mui-material/src/MenuPreview/menuPreviewClasses.ts delete mode 100644 packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewCheckboxItem/index.js delete mode 100644 packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js delete mode 100644 packages/mui-material/src/MenuPreviewGroup/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewGroup/index.js delete mode 100644 packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewGroupLabel/index.js delete mode 100644 packages/mui-material/src/MenuPreviewItem/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewItem/index.js delete mode 100644 packages/mui-material/src/MenuPreviewLinkItem/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewLinkItem/index.js delete mode 100644 packages/mui-material/src/MenuPreviewPopup/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewPopup/index.js delete mode 100644 packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewRadioGroup/index.js delete mode 100644 packages/mui-material/src/MenuPreviewRadioItem/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewRadioItem/index.js delete mode 100644 packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js delete mode 100644 packages/mui-material/src/MenuPreviewSeparator/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewSeparator/index.js delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuPopup/index.js delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuRoot/index.js delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js delete mode 100644 packages/mui-material/src/MenuPreviewTrigger/index.d.ts delete mode 100644 packages/mui-material/src/MenuPreviewTrigger/index.js rename packages/mui-material/src/{MenuPreview/MenuPreview.spec.tsx => Unstable_Menu2/Menu2.spec.tsx} (74%) rename packages/mui-material/src/{MenuPreview/MenuPreview.test.tsx => Unstable_Menu2/Menu2.test.tsx} (63%) rename packages/mui-material/src/{MenuPreview/MenuPreview.tsx => Unstable_Menu2/Menu2.tsx} (93%) create mode 100644 packages/mui-material/src/Unstable_Menu2/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2/index.js create mode 100644 packages/mui-material/src/Unstable_Menu2/menu2Classes.ts rename packages/mui-material/src/{MenuPreview/menuPreviewItemShared.tsx => Unstable_Menu2/menu2ItemShared.tsx} (82%) rename packages/mui-material/src/{MenuPreview/menuPreviewPopupShared.tsx => Unstable_Menu2/menu2PopupShared.tsx} (77%) rename packages/mui-material/src/{MenuPreview/menuPreviewSharedStyles.ts => Unstable_Menu2/menu2SharedStyles.ts} (78%) rename packages/mui-material/src/{MenuPreview/menuPreviewUtils.ts => Unstable_Menu2/menu2Utils.ts} (87%) rename packages/mui-material/src/{MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx => Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx} (76%) create mode 100644 packages/mui-material/src/Unstable_Menu2CheckboxItem/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2CheckboxItem/index.js rename packages/mui-material/src/{MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx => Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx} (69%) create mode 100644 packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/index.js rename packages/mui-material/src/{MenuPreviewGroup/MenuPreviewGroup.tsx => Unstable_Menu2Group/Menu2Group.tsx} (75%) create mode 100644 packages/mui-material/src/Unstable_Menu2Group/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2Group/index.js rename packages/mui-material/src/{MenuPreviewGroupLabel/MenuPreviewGroupLabel.tsx => Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx} (74%) create mode 100644 packages/mui-material/src/Unstable_Menu2GroupLabel/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2GroupLabel/index.js rename packages/mui-material/src/{MenuPreviewItem/MenuPreviewItem.tsx => Unstable_Menu2Item/Menu2Item.tsx} (71%) create mode 100644 packages/mui-material/src/Unstable_Menu2Item/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2Item/index.js rename packages/mui-material/src/{MenuPreviewLinkItem/MenuPreviewLinkItem.tsx => Unstable_Menu2LinkItem/Menu2LinkItem.tsx} (71%) create mode 100644 packages/mui-material/src/Unstable_Menu2LinkItem/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2LinkItem/index.js rename packages/mui-material/src/{MenuPreviewPopup/MenuPreviewPopup.tsx => Unstable_Menu2Popup/Menu2Popup.tsx} (75%) create mode 100644 packages/mui-material/src/Unstable_Menu2Popup/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2Popup/index.js rename packages/mui-material/src/{MenuPreviewRadioGroup/MenuPreviewRadioGroup.tsx => Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx} (79%) create mode 100644 packages/mui-material/src/Unstable_Menu2RadioGroup/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2RadioGroup/index.js rename packages/mui-material/src/{MenuPreviewRadioItem/MenuPreviewRadioItem.tsx => Unstable_Menu2RadioItem/Menu2RadioItem.tsx} (71%) create mode 100644 packages/mui-material/src/Unstable_Menu2RadioItem/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2RadioItem/index.js rename packages/mui-material/src/{MenuPreviewRadioItemIndicator/MenuPreviewRadioItemIndicator.tsx => Unstable_Menu2RadioItemIndicator/Menu2RadioItemIndicator.tsx} (71%) create mode 100644 packages/mui-material/src/Unstable_Menu2RadioItemIndicator/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2RadioItemIndicator/index.js rename packages/mui-material/src/{MenuPreviewSeparator/MenuPreviewSeparator.tsx => Unstable_Menu2Separator/Menu2Separator.tsx} (76%) create mode 100644 packages/mui-material/src/Unstable_Menu2Separator/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2Separator/index.js rename packages/mui-material/src/{MenuPreviewSubmenuPopup/MenuPreviewSubmenuPopup.tsx => Unstable_Menu2SubmenuPopup/Menu2SubmenuPopup.tsx} (73%) create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuPopup/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuPopup/index.js rename packages/mui-material/src/{MenuPreviewSubmenuRoot/MenuPreviewSubmenuRoot.tsx => Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx} (90%) create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuRoot/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuRoot/index.js rename packages/mui-material/src/{MenuPreviewSubmenuTrigger/MenuPreviewSubmenuTrigger.tsx => Unstable_Menu2SubmenuTrigger/Menu2SubmenuTrigger.tsx} (72%) create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuTrigger/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2SubmenuTrigger/index.js rename packages/mui-material/src/{MenuPreviewTrigger/MenuPreviewTrigger.tsx => Unstable_Menu2Trigger/Menu2Trigger.tsx} (79%) create mode 100644 packages/mui-material/src/Unstable_Menu2Trigger/index.d.ts create mode 100644 packages/mui-material/src/Unstable_Menu2Trigger/index.js diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index 9f08660603a43d..7edf5d139a54ff 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -8,25 +8,43 @@ import Typography from '@mui/material/Typography'; import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded'; import { ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; -import Menu, { - CheckboxItem, - CheckboxItemIndicator, - Group, - GroupLabel, - Item, - LinkItem, - Popup, - RadioGroup, - RadioItem, - RadioItemIndicator, - Separator, - SubmenuPopup, - SubmenuRoot, - SubmenuTrigger, - Trigger, -} from '@mui/material/MenuPreview'; +import Unstable_Menu2 from '@mui/material/Unstable_Menu2'; +import Unstable_Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import Unstable_Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Unstable_Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Unstable_Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Unstable_Menu2Item from '@mui/material/Unstable_Menu2Item'; +import Unstable_Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Unstable_Menu2Popup from '@mui/material/Unstable_Menu2Popup'; +import Unstable_Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Unstable_Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Unstable_Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Unstable_Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Unstable_Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Unstable_Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Unstable_Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Unstable_Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; +// Local aliases for demo readability; the import lines above reflect the real +// one-component-per-subpath ergonomics (no Base UI-style short aliases). +const Menu = Unstable_Menu2; +const CheckboxItem = Unstable_Menu2CheckboxItem; +const CheckboxItemIndicator = Unstable_Menu2CheckboxItemIndicator; +const Group = Unstable_Menu2Group; +const GroupLabel = Unstable_Menu2GroupLabel; +const Item = Unstable_Menu2Item; +const LinkItem = Unstable_Menu2LinkItem; +const Popup = Unstable_Menu2Popup; +const RadioGroup = Unstable_Menu2RadioGroup; +const RadioItem = Unstable_Menu2RadioItem; +const RadioItemIndicator = Unstable_Menu2RadioItemIndicator; +const Separator = Unstable_Menu2Separator; +const SubmenuPopup = Unstable_Menu2SubmenuPopup; +const SubmenuRoot = Unstable_Menu2SubmenuRoot; +const SubmenuTrigger = Unstable_Menu2SubmenuTrigger; +const Trigger = Unstable_Menu2Trigger; + interface MenuSettings { modal: boolean; disabled: boolean; @@ -232,11 +250,7 @@ function DisabledTooltip(props: { title: string; children: React.ReactElement }) ); } -function MenuPreviewWithPreviewCardsDemo({ - submenusOpenOnHover, -}: { - submenusOpenOnHover: boolean; -}) { +function Menu2WithPreviewCardsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: boolean }) { const previewCardIdPrefix = React.useId(); const [activeItemId, setActiveItemId] = React.useState(null); const [anchorEl, setAnchorEl] = React.useState(null); @@ -317,7 +331,7 @@ function MenuPreviewWithPreviewCardsDemo({ ); } -function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { +function Menu2Demo({ settings }: { settings: MenuSettings }) { const handleItemClick = React.useCallback((event: React.MouseEvent) => { // eslint-disable-next-line no-console console.log(`${event.currentTarget.textContent} clicked`); @@ -436,7 +450,7 @@ function MenuPreviewDemo({ settings }: { settings: MenuSettings }) { ); } -function MenuPreviewWithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: boolean }) { +function Menu2WithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: boolean }) { const { direction } = useTheme(); const submenuTriggerTooltipProps = React.useMemo>( () => ({ @@ -532,7 +546,7 @@ function MenuPreviewWithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHo ); } -function MenuPreviewContextMenuRecipe() { +function Menu2ContextMenuRecipe() { const [anchor, setAnchor] = React.useState | null>(null); const open = anchor !== null; @@ -605,7 +619,7 @@ function MenuPreviewContextMenuRecipe() { ); } -export default function MenuPreviewExperiment() { +export default function Menu2Experiment() { const [settings, setSettings] = React.useState(defaultSettings); const handleCheckboxChange = (setting: keyof MenuSettings) => { @@ -620,7 +634,7 @@ export default function MenuPreviewExperiment() { return ( - + @@ -656,22 +670,24 @@ export default function MenuPreviewExperiment() {

Fully-featured menu with submenus, links, radio groups, and checkbox items.

- +
- +

Material UI Tooltip integrated with every menu item.

- +
- +

Material UI Popover used as a PreviewCard-style menu item help card.

- +
- -

Right-click the text to open a cursor-positioned MenuPreview popup.

- + +

Right-click the text to open a cursor-positioned Unstable_Menu2 popup.

+
Base UI Menu API
diff --git a/docs/pages/experiments/menu-rfc.tsx b/docs/pages/experiments/menu-rfc.tsx index 319c15c7ba654e..ae8ab2afbeac52 100644 --- a/docs/pages/experiments/menu-rfc.tsx +++ b/docs/pages/experiments/menu-rfc.tsx @@ -13,25 +13,43 @@ import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRigh import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; import { ThemeProvider, createTheme, type SxProps, type Theme } from '@mui/material/styles'; import { DirectionProvider } from '@base-ui/react/direction-provider'; -import Menu, { - CheckboxItem, - CheckboxItemIndicator, - Group, - GroupLabel, - Item, - LinkItem, - Popup, - RadioGroup, - RadioItem, - RadioItemIndicator, - Separator, - SubmenuPopup, - SubmenuRoot, - SubmenuTrigger, - Trigger, -} from '@mui/material/MenuPreview'; +import Unstable_Menu2 from '@mui/material/Unstable_Menu2'; +import Unstable_Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import Unstable_Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Unstable_Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Unstable_Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Unstable_Menu2Item from '@mui/material/Unstable_Menu2Item'; +import Unstable_Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Unstable_Menu2Popup from '@mui/material/Unstable_Menu2Popup'; +import Unstable_Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Unstable_Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Unstable_Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Unstable_Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Unstable_Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Unstable_Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Unstable_Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Unstable_Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; +// Local aliases for demo readability; the import lines above reflect the real +// one-component-per-subpath ergonomics (no Base UI-style short aliases). +const Menu = Unstable_Menu2; +const CheckboxItem = Unstable_Menu2CheckboxItem; +const CheckboxItemIndicator = Unstable_Menu2CheckboxItemIndicator; +const Group = Unstable_Menu2Group; +const GroupLabel = Unstable_Menu2GroupLabel; +const Item = Unstable_Menu2Item; +const LinkItem = Unstable_Menu2LinkItem; +const Popup = Unstable_Menu2Popup; +const RadioGroup = Unstable_Menu2RadioGroup; +const RadioItem = Unstable_Menu2RadioItem; +const RadioItemIndicator = Unstable_Menu2RadioItemIndicator; +const Separator = Unstable_Menu2Separator; +const SubmenuPopup = Unstable_Menu2SubmenuPopup; +const SubmenuRoot = Unstable_Menu2SubmenuRoot; +const SubmenuTrigger = Unstable_Menu2SubmenuTrigger; +const Trigger = Unstable_Menu2Trigger; + type MenuProps = React.ComponentProps; type PopupProps = React.ComponentProps; type PopupSide = NonNullable; @@ -531,7 +549,7 @@ export default function MenuRfcExperiment() { Companion experiment for the Menu successor RFC. Every knob maps to a prop or an RFC open question. See also the{' '} - MenuPreview experiment for + Unstable_Menu2 experiment for Tooltip, PreviewCard, and ContextMenu recipes. diff --git a/packages/mui-material/src/MenuPreview/index.d.ts b/packages/mui-material/src/MenuPreview/index.d.ts deleted file mode 100644 index 1ccbd2605879ee..00000000000000 --- a/packages/mui-material/src/MenuPreview/index.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -export { default } from './MenuPreview'; -export { default as MenuPreview } from './MenuPreview'; -export { default as Root } from './MenuPreview'; -export * from './MenuPreview'; - -export { default as Trigger } from '../MenuPreviewTrigger'; -export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; -export * from '../MenuPreviewTrigger'; - -export { default as Popup } from '../MenuPreviewPopup'; -export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; -export * from '../MenuPreviewPopup'; - -export { default as SubmenuPopup } from '../MenuPreviewSubmenuPopup'; -export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; -export * from '../MenuPreviewSubmenuPopup'; - -export { default as Item } from '../MenuPreviewItem'; -export { default as MenuPreviewItem } from '../MenuPreviewItem'; -export * from '../MenuPreviewItem'; - -export { default as LinkItem } from '../MenuPreviewLinkItem'; -export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; -export * from '../MenuPreviewLinkItem'; - -export { default as CheckboxItem } from '../MenuPreviewCheckboxItem'; -export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; -export * from '../MenuPreviewCheckboxItem'; - -export { default as CheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; -export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; -export * from '../MenuPreviewCheckboxItemIndicator'; - -export { default as RadioGroup } from '../MenuPreviewRadioGroup'; -export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; -export * from '../MenuPreviewRadioGroup'; - -export { default as RadioItem } from '../MenuPreviewRadioItem'; -export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; -export * from '../MenuPreviewRadioItem'; - -export { default as RadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; -export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; -export * from '../MenuPreviewRadioItemIndicator'; - -export { default as Group } from '../MenuPreviewGroup'; -export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; -export * from '../MenuPreviewGroup'; - -export { default as GroupLabel } from '../MenuPreviewGroupLabel'; -export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; -export * from '../MenuPreviewGroupLabel'; - -export { default as Separator } from '../MenuPreviewSeparator'; -export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; -export * from '../MenuPreviewSeparator'; - -export { default as SubmenuRoot } from '../MenuPreviewSubmenuRoot'; -export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; -export * from '../MenuPreviewSubmenuRoot'; - -export { default as SubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; -export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; -export * from '../MenuPreviewSubmenuTrigger'; diff --git a/packages/mui-material/src/MenuPreview/index.js b/packages/mui-material/src/MenuPreview/index.js deleted file mode 100644 index 1ccbd2605879ee..00000000000000 --- a/packages/mui-material/src/MenuPreview/index.js +++ /dev/null @@ -1,64 +0,0 @@ -export { default } from './MenuPreview'; -export { default as MenuPreview } from './MenuPreview'; -export { default as Root } from './MenuPreview'; -export * from './MenuPreview'; - -export { default as Trigger } from '../MenuPreviewTrigger'; -export { default as MenuPreviewTrigger } from '../MenuPreviewTrigger'; -export * from '../MenuPreviewTrigger'; - -export { default as Popup } from '../MenuPreviewPopup'; -export { default as MenuPreviewPopup } from '../MenuPreviewPopup'; -export * from '../MenuPreviewPopup'; - -export { default as SubmenuPopup } from '../MenuPreviewSubmenuPopup'; -export { default as MenuPreviewSubmenuPopup } from '../MenuPreviewSubmenuPopup'; -export * from '../MenuPreviewSubmenuPopup'; - -export { default as Item } from '../MenuPreviewItem'; -export { default as MenuPreviewItem } from '../MenuPreviewItem'; -export * from '../MenuPreviewItem'; - -export { default as LinkItem } from '../MenuPreviewLinkItem'; -export { default as MenuPreviewLinkItem } from '../MenuPreviewLinkItem'; -export * from '../MenuPreviewLinkItem'; - -export { default as CheckboxItem } from '../MenuPreviewCheckboxItem'; -export { default as MenuPreviewCheckboxItem } from '../MenuPreviewCheckboxItem'; -export * from '../MenuPreviewCheckboxItem'; - -export { default as CheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; -export { default as MenuPreviewCheckboxItemIndicator } from '../MenuPreviewCheckboxItemIndicator'; -export * from '../MenuPreviewCheckboxItemIndicator'; - -export { default as RadioGroup } from '../MenuPreviewRadioGroup'; -export { default as MenuPreviewRadioGroup } from '../MenuPreviewRadioGroup'; -export * from '../MenuPreviewRadioGroup'; - -export { default as RadioItem } from '../MenuPreviewRadioItem'; -export { default as MenuPreviewRadioItem } from '../MenuPreviewRadioItem'; -export * from '../MenuPreviewRadioItem'; - -export { default as RadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; -export { default as MenuPreviewRadioItemIndicator } from '../MenuPreviewRadioItemIndicator'; -export * from '../MenuPreviewRadioItemIndicator'; - -export { default as Group } from '../MenuPreviewGroup'; -export { default as MenuPreviewGroup } from '../MenuPreviewGroup'; -export * from '../MenuPreviewGroup'; - -export { default as GroupLabel } from '../MenuPreviewGroupLabel'; -export { default as MenuPreviewGroupLabel } from '../MenuPreviewGroupLabel'; -export * from '../MenuPreviewGroupLabel'; - -export { default as Separator } from '../MenuPreviewSeparator'; -export { default as MenuPreviewSeparator } from '../MenuPreviewSeparator'; -export * from '../MenuPreviewSeparator'; - -export { default as SubmenuRoot } from '../MenuPreviewSubmenuRoot'; -export { default as MenuPreviewSubmenuRoot } from '../MenuPreviewSubmenuRoot'; -export * from '../MenuPreviewSubmenuRoot'; - -export { default as SubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; -export { default as MenuPreviewSubmenuTrigger } from '../MenuPreviewSubmenuTrigger'; -export * from '../MenuPreviewSubmenuTrigger'; diff --git a/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts b/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts deleted file mode 100644 index 5c32722a12a4d0..00000000000000 --- a/packages/mui-material/src/MenuPreview/menuPreviewClasses.ts +++ /dev/null @@ -1,280 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface MenuPreviewTriggerClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if the menu is open. */ - open: string; -} - -export type MenuPreviewTriggerClassKey = keyof MenuPreviewTriggerClasses; - -export function getMenuPreviewTriggerUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewTrigger', slot); -} - -export const menuPreviewTriggerClasses: MenuPreviewTriggerClasses = generateUtilityClasses( - 'MuiMenuPreviewTrigger', - ['root', 'disabled', 'open'], -); - -export interface MenuPreviewPopupClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the Material Paper element. */ - paper: string; - /** Styles applied to the Material List element. */ - list: string; -} - -export type MenuPreviewPopupClassKey = keyof MenuPreviewPopupClasses; - -export function getMenuPreviewPopupUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewPopup', slot); -} - -export const menuPreviewPopupClasses: MenuPreviewPopupClasses = generateUtilityClasses( - 'MuiMenuPreviewPopup', - ['root', 'paper', 'list'], -); - -export interface MenuPreviewSubmenuPopupClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the Material Paper element. */ - paper: string; - /** Styles applied to the Material List element. */ - list: string; -} - -export type MenuPreviewSubmenuPopupClassKey = keyof MenuPreviewSubmenuPopupClasses; - -export function getMenuPreviewSubmenuPopupUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewSubmenuPopup', slot); -} - -export const menuPreviewSubmenuPopupClasses: MenuPreviewSubmenuPopupClasses = - generateUtilityClasses('MuiMenuPreviewSubmenuPopup', ['root', 'paper', 'list']); - -export interface MenuPreviewItemClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if highlighted. */ - highlighted: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `dense={true}`. */ - dense: string; - /** Styles applied to the root element if `divider={true}`. */ - divider: string; - /** Styles applied to the root element unless `disableGutters={true}`. */ - gutters: string; - /** State class applied to the root element if `selected={true}`. */ - selected: string; -} - -export type MenuPreviewItemClassKey = keyof MenuPreviewItemClasses; - -export function getMenuPreviewItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewItem', slot); -} - -export const menuPreviewItemClasses: MenuPreviewItemClasses = generateUtilityClasses( - 'MuiMenuPreviewItem', - ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected'], -); - -export interface MenuPreviewLinkItemClasses extends MenuPreviewItemClasses {} - -export type MenuPreviewLinkItemClassKey = keyof MenuPreviewLinkItemClasses; - -export function getMenuPreviewLinkItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewLinkItem', slot); -} - -export const menuPreviewLinkItemClasses: MenuPreviewLinkItemClasses = generateUtilityClasses( - 'MuiMenuPreviewLinkItem', - ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected'], -); - -export interface MenuPreviewCheckboxItemClasses extends MenuPreviewItemClasses { - /** State class applied to the root element if `checked={true}`. */ - checked: string; -} - -export type MenuPreviewCheckboxItemClassKey = keyof MenuPreviewCheckboxItemClasses; - -export function getMenuPreviewCheckboxItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewCheckboxItem', slot); -} - -export const menuPreviewCheckboxItemClasses: MenuPreviewCheckboxItemClasses = - generateUtilityClasses('MuiMenuPreviewCheckboxItem', [ - 'root', - 'highlighted', - 'disabled', - 'dense', - 'divider', - 'gutters', - 'selected', - 'checked', - ]); - -export interface MenuPreviewCheckboxItemIndicatorClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `checked={true}`. */ - checked: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if highlighted. */ - highlighted: string; -} - -export type MenuPreviewCheckboxItemIndicatorClassKey = - keyof MenuPreviewCheckboxItemIndicatorClasses; - -export function getMenuPreviewCheckboxItemIndicatorUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewCheckboxItemIndicator', slot); -} - -export const menuPreviewCheckboxItemIndicatorClasses: MenuPreviewCheckboxItemIndicatorClasses = - generateUtilityClasses('MuiMenuPreviewCheckboxItemIndicator', [ - 'root', - 'checked', - 'disabled', - 'highlighted', - ]); - -export interface MenuPreviewRadioGroupClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; -} - -export type MenuPreviewRadioGroupClassKey = keyof MenuPreviewRadioGroupClasses; - -export function getMenuPreviewRadioGroupUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewRadioGroup', slot); -} - -export const menuPreviewRadioGroupClasses: MenuPreviewRadioGroupClasses = generateUtilityClasses( - 'MuiMenuPreviewRadioGroup', - ['root', 'disabled'], -); - -export interface MenuPreviewRadioItemClasses extends MenuPreviewItemClasses { - /** State class applied to the root element if `checked={true}`. */ - checked: string; -} - -export type MenuPreviewRadioItemClassKey = keyof MenuPreviewRadioItemClasses; - -export function getMenuPreviewRadioItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewRadioItem', slot); -} - -export const menuPreviewRadioItemClasses: MenuPreviewRadioItemClasses = generateUtilityClasses( - 'MuiMenuPreviewRadioItem', - ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected', 'checked'], -); - -export interface MenuPreviewRadioItemIndicatorClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `checked={true}`. */ - checked: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if highlighted. */ - highlighted: string; -} - -export type MenuPreviewRadioItemIndicatorClassKey = keyof MenuPreviewRadioItemIndicatorClasses; - -export function getMenuPreviewRadioItemIndicatorUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewRadioItemIndicator', slot); -} - -export const menuPreviewRadioItemIndicatorClasses: MenuPreviewRadioItemIndicatorClasses = - generateUtilityClasses('MuiMenuPreviewRadioItemIndicator', [ - 'root', - 'checked', - 'disabled', - 'highlighted', - ]); - -export interface MenuPreviewGroupClasses { - /** Styles applied to the root element. */ - root: string; -} - -export type MenuPreviewGroupClassKey = keyof MenuPreviewGroupClasses; - -export function getMenuPreviewGroupUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewGroup', slot); -} - -export const menuPreviewGroupClasses: MenuPreviewGroupClasses = generateUtilityClasses( - 'MuiMenuPreviewGroup', - ['root'], -); - -export interface MenuPreviewGroupLabelClasses { - /** Styles applied to the root element. */ - root: string; -} - -export type MenuPreviewGroupLabelClassKey = keyof MenuPreviewGroupLabelClasses; - -export function getMenuPreviewGroupLabelUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewGroupLabel', slot); -} - -export const menuPreviewGroupLabelClasses: MenuPreviewGroupLabelClasses = generateUtilityClasses( - 'MuiMenuPreviewGroupLabel', - ['root'], -); - -export interface MenuPreviewSeparatorClasses { - /** Styles applied to the root element. */ - root: string; -} - -export type MenuPreviewSeparatorClassKey = keyof MenuPreviewSeparatorClasses; - -export function getMenuPreviewSeparatorUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewSeparator', slot); -} - -export const menuPreviewSeparatorClasses: MenuPreviewSeparatorClasses = generateUtilityClasses( - 'MuiMenuPreviewSeparator', - ['root'], -); - -export interface MenuPreviewSubmenuTriggerClasses extends MenuPreviewItemClasses { - /** State class applied to the root element if the submenu is open. */ - open: string; -} - -export type MenuPreviewSubmenuTriggerClassKey = keyof MenuPreviewSubmenuTriggerClasses; - -export function getMenuPreviewSubmenuTriggerUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuPreviewSubmenuTrigger', slot); -} - -export const menuPreviewSubmenuTriggerClasses: MenuPreviewSubmenuTriggerClasses = - generateUtilityClasses('MuiMenuPreviewSubmenuTrigger', [ - 'root', - 'highlighted', - 'disabled', - 'dense', - 'divider', - 'gutters', - 'selected', - 'open', - ]); diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts b/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts deleted file mode 100644 index 4372d2838e90bb..00000000000000 --- a/packages/mui-material/src/MenuPreviewCheckboxItem/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewCheckboxItem'; -export * from './MenuPreviewCheckboxItem'; -export { - menuPreviewCheckboxItemClasses, - getMenuPreviewCheckboxItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewCheckboxItemClasses, - MenuPreviewCheckboxItemClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/index.js b/packages/mui-material/src/MenuPreviewCheckboxItem/index.js deleted file mode 100644 index d453dbcbd0a5ab..00000000000000 --- a/packages/mui-material/src/MenuPreviewCheckboxItem/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewCheckboxItem'; -export * from './MenuPreviewCheckboxItem'; -export { - menuPreviewCheckboxItemClasses, - getMenuPreviewCheckboxItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts deleted file mode 100644 index 4e0c311cd37242..00000000000000 --- a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewCheckboxItemIndicator'; -export * from './MenuPreviewCheckboxItemIndicator'; -export { - menuPreviewCheckboxItemIndicatorClasses, - getMenuPreviewCheckboxItemIndicatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewCheckboxItemIndicatorClasses, - MenuPreviewCheckboxItemIndicatorClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js b/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js deleted file mode 100644 index fb24b5bf809efc..00000000000000 --- a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewCheckboxItemIndicator'; -export * from './MenuPreviewCheckboxItemIndicator'; -export { - menuPreviewCheckboxItemIndicatorClasses, - getMenuPreviewCheckboxItemIndicatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroup/index.d.ts b/packages/mui-material/src/MenuPreviewGroup/index.d.ts deleted file mode 100644 index 73a72486bb167f..00000000000000 --- a/packages/mui-material/src/MenuPreviewGroup/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewGroup'; -export * from './MenuPreviewGroup'; -export { - menuPreviewGroupClasses, - getMenuPreviewGroupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewGroupClasses, - MenuPreviewGroupClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroup/index.js b/packages/mui-material/src/MenuPreviewGroup/index.js deleted file mode 100644 index 49e6742bc562e4..00000000000000 --- a/packages/mui-material/src/MenuPreviewGroup/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewGroup'; -export * from './MenuPreviewGroup'; -export { - menuPreviewGroupClasses, - getMenuPreviewGroupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts b/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts deleted file mode 100644 index 6b9ddf97eea6cf..00000000000000 --- a/packages/mui-material/src/MenuPreviewGroupLabel/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewGroupLabel'; -export * from './MenuPreviewGroupLabel'; -export { - menuPreviewGroupLabelClasses, - getMenuPreviewGroupLabelUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewGroupLabelClasses, - MenuPreviewGroupLabelClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewGroupLabel/index.js b/packages/mui-material/src/MenuPreviewGroupLabel/index.js deleted file mode 100644 index 30bf6f4677fed1..00000000000000 --- a/packages/mui-material/src/MenuPreviewGroupLabel/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewGroupLabel'; -export * from './MenuPreviewGroupLabel'; -export { - menuPreviewGroupLabelClasses, - getMenuPreviewGroupLabelUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewItem/index.d.ts b/packages/mui-material/src/MenuPreviewItem/index.d.ts deleted file mode 100644 index febb5776db5b46..00000000000000 --- a/packages/mui-material/src/MenuPreviewItem/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewItem'; -export * from './MenuPreviewItem'; -export { - menuPreviewItemClasses, - getMenuPreviewItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewItemClasses, - MenuPreviewItemClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewItem/index.js b/packages/mui-material/src/MenuPreviewItem/index.js deleted file mode 100644 index f2296b0ea1f44f..00000000000000 --- a/packages/mui-material/src/MenuPreviewItem/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewItem'; -export * from './MenuPreviewItem'; -export { - menuPreviewItemClasses, - getMenuPreviewItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts b/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts deleted file mode 100644 index d67220bdee8596..00000000000000 --- a/packages/mui-material/src/MenuPreviewLinkItem/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewLinkItem'; -export * from './MenuPreviewLinkItem'; -export { - menuPreviewLinkItemClasses, - getMenuPreviewLinkItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewLinkItemClasses, - MenuPreviewLinkItemClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewLinkItem/index.js b/packages/mui-material/src/MenuPreviewLinkItem/index.js deleted file mode 100644 index 7a69a871b02fdc..00000000000000 --- a/packages/mui-material/src/MenuPreviewLinkItem/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewLinkItem'; -export * from './MenuPreviewLinkItem'; -export { - menuPreviewLinkItemClasses, - getMenuPreviewLinkItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewPopup/index.d.ts b/packages/mui-material/src/MenuPreviewPopup/index.d.ts deleted file mode 100644 index 82ce5ac8e7311f..00000000000000 --- a/packages/mui-material/src/MenuPreviewPopup/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewPopup'; -export * from './MenuPreviewPopup'; -export { - menuPreviewPopupClasses, - getMenuPreviewPopupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewPopupClasses, - MenuPreviewPopupClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewPopup/index.js b/packages/mui-material/src/MenuPreviewPopup/index.js deleted file mode 100644 index 25a0b859273605..00000000000000 --- a/packages/mui-material/src/MenuPreviewPopup/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewPopup'; -export * from './MenuPreviewPopup'; -export { - menuPreviewPopupClasses, - getMenuPreviewPopupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts b/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts deleted file mode 100644 index f4e14f49d9bc87..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioGroup/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewRadioGroup'; -export * from './MenuPreviewRadioGroup'; -export { - menuPreviewRadioGroupClasses, - getMenuPreviewRadioGroupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewRadioGroupClasses, - MenuPreviewRadioGroupClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioGroup/index.js b/packages/mui-material/src/MenuPreviewRadioGroup/index.js deleted file mode 100644 index a5e718613154f1..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioGroup/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewRadioGroup'; -export * from './MenuPreviewRadioGroup'; -export { - menuPreviewRadioGroupClasses, - getMenuPreviewRadioGroupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts b/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts deleted file mode 100644 index 05366886438934..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioItem/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewRadioItem'; -export * from './MenuPreviewRadioItem'; -export { - menuPreviewRadioItemClasses, - getMenuPreviewRadioItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewRadioItemClasses, - MenuPreviewRadioItemClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItem/index.js b/packages/mui-material/src/MenuPreviewRadioItem/index.js deleted file mode 100644 index fa1950c32d8e36..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioItem/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewRadioItem'; -export * from './MenuPreviewRadioItem'; -export { - menuPreviewRadioItemClasses, - getMenuPreviewRadioItemUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts deleted file mode 100644 index 4fa829e8dedd1c..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewRadioItemIndicator'; -export * from './MenuPreviewRadioItemIndicator'; -export { - menuPreviewRadioItemIndicatorClasses, - getMenuPreviewRadioItemIndicatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewRadioItemIndicatorClasses, - MenuPreviewRadioItemIndicatorClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js b/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js deleted file mode 100644 index 32b270dcd36da5..00000000000000 --- a/packages/mui-material/src/MenuPreviewRadioItemIndicator/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewRadioItemIndicator'; -export * from './MenuPreviewRadioItemIndicator'; -export { - menuPreviewRadioItemIndicatorClasses, - getMenuPreviewRadioItemIndicatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSeparator/index.d.ts b/packages/mui-material/src/MenuPreviewSeparator/index.d.ts deleted file mode 100644 index d39696b5ea6141..00000000000000 --- a/packages/mui-material/src/MenuPreviewSeparator/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewSeparator'; -export * from './MenuPreviewSeparator'; -export { - menuPreviewSeparatorClasses, - getMenuPreviewSeparatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewSeparatorClasses, - MenuPreviewSeparatorClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSeparator/index.js b/packages/mui-material/src/MenuPreviewSeparator/index.js deleted file mode 100644 index b610767aee6389..00000000000000 --- a/packages/mui-material/src/MenuPreviewSeparator/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewSeparator'; -export * from './MenuPreviewSeparator'; -export { - menuPreviewSeparatorClasses, - getMenuPreviewSeparatorUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts deleted file mode 100644 index ee1f99c4e671e0..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewSubmenuPopup'; -export * from './MenuPreviewSubmenuPopup'; -export { - menuPreviewSubmenuPopupClasses, - getMenuPreviewSubmenuPopupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewSubmenuPopupClasses, - MenuPreviewSubmenuPopupClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js b/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js deleted file mode 100644 index 0941a947811103..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuPopup/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewSubmenuPopup'; -export * from './MenuPreviewSubmenuPopup'; -export { - menuPreviewSubmenuPopupClasses, - getMenuPreviewSubmenuPopupUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts deleted file mode 100644 index df51cc07534051..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './MenuPreviewSubmenuRoot'; -export * from './MenuPreviewSubmenuRoot'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js b/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js deleted file mode 100644 index df51cc07534051..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuRoot/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './MenuPreviewSubmenuRoot'; -export * from './MenuPreviewSubmenuRoot'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts deleted file mode 100644 index c635b6f0a1818e..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewSubmenuTrigger'; -export * from './MenuPreviewSubmenuTrigger'; -export { - menuPreviewSubmenuTriggerClasses, - getMenuPreviewSubmenuTriggerUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewSubmenuTriggerClasses, - MenuPreviewSubmenuTriggerClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js b/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js deleted file mode 100644 index 21cf2aafd9dc18..00000000000000 --- a/packages/mui-material/src/MenuPreviewSubmenuTrigger/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewSubmenuTrigger'; -export * from './MenuPreviewSubmenuTrigger'; -export { - menuPreviewSubmenuTriggerClasses, - getMenuPreviewSubmenuTriggerUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewTrigger/index.d.ts b/packages/mui-material/src/MenuPreviewTrigger/index.d.ts deleted file mode 100644 index ea8d0c4a9ab413..00000000000000 --- a/packages/mui-material/src/MenuPreviewTrigger/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default } from './MenuPreviewTrigger'; -export * from './MenuPreviewTrigger'; -export { - menuPreviewTriggerClasses, - getMenuPreviewTriggerUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; -export type { - MenuPreviewTriggerClasses, - MenuPreviewTriggerClassKey, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreviewTrigger/index.js b/packages/mui-material/src/MenuPreviewTrigger/index.js deleted file mode 100644 index 0b40dbfdcb13dd..00000000000000 --- a/packages/mui-material/src/MenuPreviewTrigger/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default } from './MenuPreviewTrigger'; -export * from './MenuPreviewTrigger'; -export { - menuPreviewTriggerClasses, - getMenuPreviewTriggerUtilityClass, -} from '../MenuPreview/menuPreviewClasses'; diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx similarity index 74% rename from packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx rename to packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx index 6ca3ce1f3d2a11..d312b26a197e23 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.spec.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx @@ -1,27 +1,26 @@ import * as React from 'react'; import { expectType } from '@mui/types'; -import Menu, { - CheckboxItem, - CheckboxItemIndicator, - Group, - GroupLabel, - Item, - LinkItem, - Popup, - RadioGroup, - RadioItem, - RadioItemIndicator, - Separator, - SubmenuPopup, - SubmenuRoot, - SubmenuTrigger, - Trigger, -} from '@mui/material/MenuPreview'; +import Menu from '@mui/material/Unstable_Menu2'; +import CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Group from '@mui/material/Unstable_Menu2Group'; +import GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Item from '@mui/material/Unstable_Menu2Item'; +import LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Popup from '@mui/material/Unstable_Menu2Popup'; +import RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Separator from '@mui/material/Unstable_Menu2Separator'; +import SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Trigger from '@mui/material/Unstable_Menu2Trigger'; import { createTheme } from '@mui/material/styles'; -// @ts-expect-error MenuPreview is intentionally not exported from the root barrel for this POC. -import { MenuPreview as RootBarrelMenuPreview } from '@mui/material'; +// @ts-expect-error Menu2 is intentionally not exported from the root barrel for this POC. +import { Menu2 as RootBarrelMenu2 } from '@mui/material'; -function MenuPreviewComposition() { +function Menu2Composition() { return ( ; Options diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx similarity index 63% rename from packages/mui-material/src/MenuPreview/MenuPreview.test.tsx rename to packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx index 49e9632251ba7d..941158a051fa00 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.test.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx @@ -5,30 +5,27 @@ import { createRenderer, isJsdom, screen, waitFor } from '@mui/internal-test-uti import { listClasses } from '@mui/material/List'; import { paperClasses } from '@mui/material/Paper'; import Tooltip from '@mui/material/Tooltip'; -import MenuPreview, { - MenuPreviewCheckboxItem, - MenuPreviewCheckboxItemIndicator, - MenuPreviewGroup, - MenuPreviewGroupLabel, - MenuPreviewItem, - MenuPreviewLinkItem, - MenuPreviewPopup, - MenuPreviewRadioGroup, - MenuPreviewRadioItem, - MenuPreviewRadioItemIndicator, - MenuPreviewSeparator, - MenuPreviewSubmenuPopup, - MenuPreviewSubmenuRoot, - MenuPreviewSubmenuTrigger, - MenuPreviewTrigger, - menuPreviewCheckboxItemClasses, - menuPreviewItemClasses, - menuPreviewPopupClasses, - menuPreviewTriggerClasses, -} from '@mui/material/MenuPreview'; +import Menu2 from '@mui/material/Unstable_Menu2'; +import Menu2CheckboxItem, { + menu2CheckboxItemClasses, +} from '@mui/material/Unstable_Menu2CheckboxItem'; +import Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Menu2Item, { menu2ItemClasses } from '@mui/material/Unstable_Menu2Item'; +import Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Menu2Popup, { menu2PopupClasses } from '@mui/material/Unstable_Menu2Popup'; +import Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Menu2Trigger, { menu2TriggerClasses } from '@mui/material/Unstable_Menu2Trigger'; import { ThemeProvider, createTheme } from '@mui/material/styles'; -describe('', () => { +describe('', () => { const { render } = createRenderer(); type User = ReturnType['user']; @@ -46,41 +43,39 @@ describe('', () => { it('opens from the trigger and keeps Menu.Popup as the semantic menu root', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); const trigger = screen.getByRole('button', { name: 'Options' }); - expect(trigger).to.have.class(menuPreviewTriggerClasses.root); + expect(trigger).to.have.class(menu2TriggerClasses.root); await user.click(trigger); const menu = await screen.findByRole('menu'); - expect(menu).to.have.class(menuPreviewPopupClasses.root); - expect(screen.getByTestId('paper')).to.have.class(menuPreviewPopupClasses.paper); + expect(menu).to.have.class(menu2PopupClasses.root); + expect(screen.getByTestId('paper')).to.have.class(menu2PopupClasses.paper); - const list = screen.getByTestId('paper').querySelector(`.${menuPreviewPopupClasses.list}`); + const list = screen.getByTestId('paper').querySelector(`.${menu2PopupClasses.list}`); expect(list).not.to.equal(null); expect(list!.tagName).to.equal('DIV'); expect(list!).to.have.class(listClasses.padding); - expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class( - menuPreviewItemClasses.root, - ); + expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class(menu2ItemClasses.root); }); it('does not render the trigger as a link when href is passed by a JS caller', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); const trigger = screen.getByRole('button', { name: 'Options' }); @@ -95,12 +90,12 @@ describe('', () => { it('supports component props, slotProps, classes, styleOverrides, and variants', async () => { const theme = createTheme({ components: { - MuiMenuPreviewTrigger: { + MuiMenu2Trigger: { defaultProps: { variant: 'outlined', }, }, - MuiMenuPreviewPopup: { + MuiMenu2Popup: { styleOverrides: { paper: { minWidth: 128, @@ -110,12 +105,12 @@ describe('', () => { { props: { align: 'start' }, style: { - '--MenuPreviewPopup-variant': '"applied"', + '--Menu2Popup-variant': '"applied"', }, }, ], }, - MuiMenuPreviewItem: { + MuiMenu2Item: { variants: [ { props: { selected: true }, @@ -126,37 +121,37 @@ describe('', () => { { props: { disabled: true }, style: { - '--MenuPreviewItem-disabledVariant': '"applied"', + '--Menu2Item-disabledVariant': '"applied"', }, }, ], }, - MuiMenuPreviewCheckboxItem: { + MuiMenu2CheckboxItem: { variants: [ { props: { checked: true }, style: { - '--MenuPreviewCheckboxItem-checkedVariant': '"applied"', + '--Menu2CheckboxItem-checkedVariant': '"applied"', }, }, ], }, - MuiMenuPreviewRadioItem: { + MuiMenu2RadioItem: { variants: [ { props: { value: 'small' }, style: { - '--MenuPreviewRadioItem-valueVariant': '"applied"', + '--Menu2RadioItem-valueVariant': '"applied"', }, }, ], }, - MuiMenuPreviewLinkItem: { + MuiMenu2LinkItem: { variants: [ { props: { href: '/profile' }, style: { - '--MenuPreviewLinkItem-hrefVariant': '"applied"', + '--Menu2LinkItem-hrefVariant': '"applied"', }, }, ], @@ -166,23 +161,23 @@ describe('', () => { const { user } = render( - - Options - + Options + - + Profile - - Disabled profile - Checked profile - - Small - - Link profile - - + + Disabled profile + Checked profile + + Small + + Link profile + + , ); @@ -191,47 +186,45 @@ describe('', () => { expect(screen.getByRole('button', { name: 'Options' })).to.have.class('custom-trigger'); expect(await screen.findByTestId('list')).to.have.class('custom-list'); expect( - window - .getComputedStyle(screen.getByRole('menu')) - .getPropertyValue('--MenuPreviewPopup-variant'), + window.getComputedStyle(screen.getByRole('menu')).getPropertyValue('--Menu2Popup-variant'), ).to.equal('"applied"'); expect(await screen.findByRole('menuitem', { name: 'Profile' })).to.have.class('custom-item'); expect(screen.getByRole('menuitem', { name: 'Profile' })).to.have.class( - menuPreviewItemClasses.selected, + menu2ItemClasses.selected, ); expect(screen.getByRole('menuitem', { name: 'Disabled profile' })).to.have.class( - menuPreviewItemClasses.disabled, + menu2ItemClasses.disabled, ); expect( window .getComputedStyle(screen.getByRole('menuitem', { name: 'Disabled profile' })) - .getPropertyValue('--MenuPreviewItem-disabledVariant'), + .getPropertyValue('--Menu2Item-disabledVariant'), ).to.equal('"applied"'); expect(screen.getByRole('menuitemcheckbox', { name: 'Checked profile' })).to.have.class( - menuPreviewCheckboxItemClasses.checked, + menu2CheckboxItemClasses.checked, ); expect( window .getComputedStyle(screen.getByRole('menuitemcheckbox', { name: 'Checked profile' })) - .getPropertyValue('--MenuPreviewCheckboxItem-checkedVariant'), + .getPropertyValue('--Menu2CheckboxItem-checkedVariant'), ).to.equal('"applied"'); expect( window .getComputedStyle(screen.getByRole('menuitemradio', { name: 'Small' })) - .getPropertyValue('--MenuPreviewRadioItem-valueVariant'), + .getPropertyValue('--Menu2RadioItem-valueVariant'), ).to.equal('"applied"'); expect( window .getComputedStyle(screen.getByRole('menuitem', { name: 'Link profile' })) - .getPropertyValue('--MenuPreviewLinkItem-hrefVariant'), + .getPropertyValue('--Menu2LinkItem-hrefVariant'), ).to.equal('"applied"'); }); it('composes popup class names', async () => { const { user } = render( - - Options - + Options + ', () => { }, }} > - Profile - - , + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -249,20 +242,17 @@ describe('', () => { const menu = await screen.findByRole('menu'); expect(menu).to.have.class('popup-open'); expect(menu).to.have.class('popup-side-bottom'); - expect(menu).to.have.class(menuPreviewPopupClasses.root); + expect(menu).to.have.class(menu2PopupClasses.root); }); it('does not pass ownerState to host popup slots', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -275,30 +265,30 @@ describe('', () => { try { const { user } = render( - - Options - - + + Options + + Native item - - + + Native checkbox - - - + + + Native radio - - - - + + + + Native submenu trigger - - - Nested - - - - , + + + Nested + + + + , ); const trigger = screen.getByRole('button', { name: 'Options' }); @@ -348,24 +338,24 @@ describe('', () => { try { const { user } = render( - - + + Options - - - + + + Custom native item - - - + + + Custom native submenu trigger - - - Nested - - - - , + + + Nested + + + + , ); const trigger = screen.getByRole('button', { name: 'Options' }); @@ -391,9 +381,9 @@ describe('', () => { it('does not pass internal props to host paper and list slots', async () => { const { user } = render( - - Options - + Options + ', () => { }, }} > - Profile - - , + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -435,12 +425,12 @@ describe('', () => { it('defaults the popup surface elevation to 8', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -450,12 +440,12 @@ describe('', () => { it('forwards a custom elevation to the popup surface', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -472,12 +462,12 @@ describe('', () => { }); const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -490,12 +480,12 @@ describe('', () => { it('does not open when the root is disabled', async () => { render( - - Options - - Profile - - , + + Options + + Profile + + , ); expect(screen.getByRole('button', { name: 'Options' })).to.have.attribute('disabled'); @@ -504,12 +494,12 @@ describe('', () => { it('supports defaultOpen', () => { render( - - Options - - Profile - - , + + Options + + Profile + + , ); expect(screen.getByRole('menu')).not.to.equal(null); @@ -517,12 +507,12 @@ describe('', () => { it('supports keepMounted', () => { render( - - Options - - Profile - - , + + Options + + Profile + + , ); expect(screen.getByText('Profile')).not.to.equal(null); @@ -539,12 +529,12 @@ describe('', () => { - - Options - - Profile - - + + Options + + Profile + + , ); @@ -560,12 +550,12 @@ describe('', () => { const { user } = render( - - Options - - Profile - - + + Options + + Profile + + , ); @@ -590,12 +580,12 @@ describe('', () => { it('supports touch trigger interactions', async () => { const { user } = render( - - Options - - Profile - - , + + Options + + Profile + + , ); await user.pointer({ @@ -609,18 +599,18 @@ describe('', () => { it('supports modal backdrop behavior', async () => { const { user } = render( - - Modal menu - - Profile - - - - Non-modal menu - - Settings - - + + Modal menu + + Profile + + + + Non-modal menu + + Settings + + , ); @@ -644,12 +634,12 @@ describe('', () => { it('opens in an RTL tree', async () => { const { user } = render(
- - Options - - Profile - - + + Options + + Profile + +
, ); @@ -661,17 +651,17 @@ describe('', () => { it.skipIf(isJsdom())('applies Base UI positioning attributes in the browser', async () => { const { user } = render(
- - Options - + Options + - Profile - - + Profile + +
, ); @@ -696,25 +686,25 @@ describe('', () => { }); const { user } = render( - - Options - - - + + Options + + + Show hidden files - - - - + + + + Small - - - + + + Large - - - - , + + + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -725,7 +715,7 @@ describe('', () => { await user.click(checkbox); expect(checkbox).to.have.attribute('aria-checked', 'true'); - expect(checkbox).to.have.class(menuPreviewCheckboxItemClasses.checked); + expect(checkbox).to.have.class(menu2CheckboxItemClasses.checked); expect(handleCheckboxChange.callCount).to.equal(1); expect(screen.getByRole('menuitemradio', { name: /small/i })).to.have.attribute( @@ -748,30 +738,30 @@ describe('', () => { it('keeps mounted unchecked indicator marks hidden', () => { render( - - Options - - - + + Options + + + Show hidden files - - - - + + + + Small - - - + + + Large - - - - , + + + + , ); const checkboxIndicator = screen.getByTestId('checkbox-indicator'); - const checkboxIcon = checkboxIndicator.querySelector('[data-mui-menu-preview-indicator-icon]'); - const checkboxMark = checkboxIndicator.querySelector('[data-mui-menu-preview-indicator-mark]'); + const checkboxIcon = checkboxIndicator.querySelector('[data-mui-menu2-indicator-icon]'); + const checkboxMark = checkboxIndicator.querySelector('[data-mui-menu2-indicator-mark]'); expect(checkboxIndicator).to.have.attribute('data-unchecked', ''); expect(window.getComputedStyle(checkboxIndicator).visibility).to.equal('visible'); expect(checkboxIcon).not.to.equal(null); @@ -780,12 +770,8 @@ describe('', () => { expect(window.getComputedStyle(checkboxMark!).visibility).to.equal('hidden'); const checkedRadioIndicator = screen.getByTestId('checked-radio-indicator'); - const checkedRadioIcon = checkedRadioIndicator.querySelector( - '[data-mui-menu-preview-indicator-icon]', - ); - const checkedRadioMark = checkedRadioIndicator.querySelector( - '[data-mui-menu-preview-indicator-mark]', - ); + const checkedRadioIcon = checkedRadioIndicator.querySelector('[data-mui-menu2-indicator-icon]'); + const checkedRadioMark = checkedRadioIndicator.querySelector('[data-mui-menu2-indicator-mark]'); expect(checkedRadioIndicator).to.have.attribute('data-checked', ''); expect(checkedRadioIcon).not.to.equal(null); expect(window.getComputedStyle(checkedRadioIcon!).visibility).to.equal('visible'); @@ -794,10 +780,10 @@ describe('', () => { const uncheckedRadioIndicator = screen.getByTestId('unchecked-radio-indicator'); const uncheckedRadioIcon = uncheckedRadioIndicator.querySelector( - '[data-mui-menu-preview-indicator-icon]', + '[data-mui-menu2-indicator-icon]', ); const uncheckedRadioMark = uncheckedRadioIndicator.querySelector( - '[data-mui-menu-preview-indicator-mark]', + '[data-mui-menu2-indicator-mark]', ); expect(uncheckedRadioIndicator).to.have.attribute('data-unchecked', ''); expect(window.getComputedStyle(uncheckedRadioIndicator).visibility).to.equal('visible'); @@ -809,22 +795,22 @@ describe('', () => { it('supports groups, labels, separators, link items, and submenus', async () => { const { user } = render( - - Options - - - Account - Profile - - - - More - - Archive - - - - , + + Options + + + Account + Profile + + + + More + + Archive + + + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); @@ -838,28 +824,28 @@ describe('', () => { it('supports Material UI Tooltip on enabled item flavors', async () => { const { user } = render( - - Options - + + Options + - New document + New document - - + + Comments - + - + - - + + Fit - + - - - , + + + , ); await expectTooltipOnHover( @@ -914,21 +900,19 @@ describe('', () => { } const { user } = render( - - Options - - + + Options + + - - View options - + View options - - Comments - - - - , + + Comments + + + + , ); const submenuTrigger = screen.getByRole('menuitem', { name: 'View options' }); @@ -946,16 +930,16 @@ describe('', () => { it('supports Material UI Tooltip on disabled items through a non-disabled wrapper', async () => { const { user } = render( - - Options - + + Options + - Import from Drive + Import from Drive - - , + + , ); expect(screen.getByRole('menuitem', { name: 'Import from Drive' })).to.have.attribute( @@ -974,17 +958,17 @@ describe('', () => { try { const { user } = render( - - Options - - - Add-ons unavailable - - Marketplace - - - - , + + Options + + + Add-ons unavailable + + Marketplace + + + + , ); await user.click(screen.getByRole('button', { name: 'Options' })); diff --git a/packages/mui-material/src/MenuPreview/MenuPreview.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx similarity index 93% rename from packages/mui-material/src/MenuPreview/MenuPreview.tsx rename to packages/mui-material/src/Unstable_Menu2/Menu2.tsx index cf860c31fae48c..0554315c46e9cf 100644 --- a/packages/mui-material/src/MenuPreview/MenuPreview.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { Menu as BaseMenu } from '@base-ui/react/menu'; import { useDefaultProps } from '../DefaultPropsProvider'; -export interface MenuPreviewProps { +export interface Menu2Props { /** * The content of the menu. */ @@ -68,18 +68,18 @@ export interface MenuPreviewProps { * * API: * - * - [MenuPreview API](https://mui.com/material-ui/api/menu-preview/) + * - [Menu2 API](https://mui.com/material-ui/api/menu-preview/) */ -function MenuPreview(props: MenuPreviewProps): React.JSX.Element { +function Menu2(props: Menu2Props): React.JSX.Element { const themedProps = useDefaultProps({ props, - name: 'MuiMenuPreview', + name: 'MuiMenu2', }); return ; } -MenuPreview.propTypes /* remove-proptypes */ = { +Menu2.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ @@ -139,4 +139,4 @@ MenuPreview.propTypes /* remove-proptypes */ = { orientation: PropTypes.oneOf(['horizontal', 'vertical']), } as any; -export default MenuPreview; +export default Menu2; diff --git a/packages/mui-material/src/Unstable_Menu2/index.d.ts b/packages/mui-material/src/Unstable_Menu2/index.d.ts new file mode 100644 index 00000000000000..23dd0c131828d4 --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2/index.d.ts @@ -0,0 +1,3 @@ +export { default } from './Menu2'; +export { default as Unstable_Menu2 } from './Menu2'; +export * from './Menu2'; diff --git a/packages/mui-material/src/Unstable_Menu2/index.js b/packages/mui-material/src/Unstable_Menu2/index.js new file mode 100644 index 00000000000000..23dd0c131828d4 --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2/index.js @@ -0,0 +1,3 @@ +export { default } from './Menu2'; +export { default as Unstable_Menu2 } from './Menu2'; +export * from './Menu2'; diff --git a/packages/mui-material/src/Unstable_Menu2/menu2Classes.ts b/packages/mui-material/src/Unstable_Menu2/menu2Classes.ts new file mode 100644 index 00000000000000..8c2ee021bb0e00 --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2/menu2Classes.ts @@ -0,0 +1,273 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface Menu2TriggerClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if the menu is open. */ + open: string; +} + +export type Menu2TriggerClassKey = keyof Menu2TriggerClasses; + +export function getMenu2TriggerUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2Trigger', slot); +} + +export const menu2TriggerClasses: Menu2TriggerClasses = generateUtilityClasses('MuiMenu2Trigger', [ + 'root', + 'disabled', + 'open', +]); + +export interface Menu2PopupClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the Material Paper element. */ + paper: string; + /** Styles applied to the Material List element. */ + list: string; +} + +export type Menu2PopupClassKey = keyof Menu2PopupClasses; + +export function getMenu2PopupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2Popup', slot); +} + +export const menu2PopupClasses: Menu2PopupClasses = generateUtilityClasses('MuiMenu2Popup', [ + 'root', + 'paper', + 'list', +]); + +export interface Menu2SubmenuPopupClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the Material Paper element. */ + paper: string; + /** Styles applied to the Material List element. */ + list: string; +} + +export type Menu2SubmenuPopupClassKey = keyof Menu2SubmenuPopupClasses; + +export function getMenu2SubmenuPopupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2SubmenuPopup', slot); +} + +export const menu2SubmenuPopupClasses: Menu2SubmenuPopupClasses = generateUtilityClasses( + 'MuiMenu2SubmenuPopup', + ['root', 'paper', 'list'], +); + +export interface Menu2ItemClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** Styles applied to the root element if `dense={true}`. */ + dense: string; + /** Styles applied to the root element if `divider={true}`. */ + divider: string; + /** Styles applied to the root element unless `disableGutters={true}`. */ + gutters: string; + /** State class applied to the root element if `selected={true}`. */ + selected: string; +} + +export type Menu2ItemClassKey = keyof Menu2ItemClasses; + +export function getMenu2ItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2Item', slot); +} + +export const menu2ItemClasses: Menu2ItemClasses = generateUtilityClasses('MuiMenu2Item', [ + 'root', + 'highlighted', + 'disabled', + 'dense', + 'divider', + 'gutters', + 'selected', +]); + +export interface Menu2LinkItemClasses extends Menu2ItemClasses {} + +export type Menu2LinkItemClassKey = keyof Menu2LinkItemClasses; + +export function getMenu2LinkItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2LinkItem', slot); +} + +export const menu2LinkItemClasses: Menu2LinkItemClasses = generateUtilityClasses( + 'MuiMenu2LinkItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected'], +); + +export interface Menu2CheckboxItemClasses extends Menu2ItemClasses { + /** State class applied to the root element if `checked={true}`. */ + checked: string; +} + +export type Menu2CheckboxItemClassKey = keyof Menu2CheckboxItemClasses; + +export function getMenu2CheckboxItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2CheckboxItem', slot); +} + +export const menu2CheckboxItemClasses: Menu2CheckboxItemClasses = generateUtilityClasses( + 'MuiMenu2CheckboxItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected', 'checked'], +); + +export interface Menu2CheckboxItemIndicatorClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `checked={true}`. */ + checked: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; +} + +export type Menu2CheckboxItemIndicatorClassKey = keyof Menu2CheckboxItemIndicatorClasses; + +export function getMenu2CheckboxItemIndicatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2CheckboxItemIndicator', slot); +} + +export const menu2CheckboxItemIndicatorClasses: Menu2CheckboxItemIndicatorClasses = + generateUtilityClasses('MuiMenu2CheckboxItemIndicator', [ + 'root', + 'checked', + 'disabled', + 'highlighted', + ]); + +export interface Menu2RadioGroupClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; +} + +export type Menu2RadioGroupClassKey = keyof Menu2RadioGroupClasses; + +export function getMenu2RadioGroupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2RadioGroup', slot); +} + +export const menu2RadioGroupClasses: Menu2RadioGroupClasses = generateUtilityClasses( + 'MuiMenu2RadioGroup', + ['root', 'disabled'], +); + +export interface Menu2RadioItemClasses extends Menu2ItemClasses { + /** State class applied to the root element if `checked={true}`. */ + checked: string; +} + +export type Menu2RadioItemClassKey = keyof Menu2RadioItemClasses; + +export function getMenu2RadioItemUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2RadioItem', slot); +} + +export const menu2RadioItemClasses: Menu2RadioItemClasses = generateUtilityClasses( + 'MuiMenu2RadioItem', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected', 'checked'], +); + +export interface Menu2RadioItemIndicatorClasses { + /** Styles applied to the root element. */ + root: string; + /** State class applied to the root element if `checked={true}`. */ + checked: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** State class applied to the root element if highlighted. */ + highlighted: string; +} + +export type Menu2RadioItemIndicatorClassKey = keyof Menu2RadioItemIndicatorClasses; + +export function getMenu2RadioItemIndicatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2RadioItemIndicator', slot); +} + +export const menu2RadioItemIndicatorClasses: Menu2RadioItemIndicatorClasses = + generateUtilityClasses('MuiMenu2RadioItemIndicator', [ + 'root', + 'checked', + 'disabled', + 'highlighted', + ]); + +export interface Menu2GroupClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type Menu2GroupClassKey = keyof Menu2GroupClasses; + +export function getMenu2GroupUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2Group', slot); +} + +export const menu2GroupClasses: Menu2GroupClasses = generateUtilityClasses('MuiMenu2Group', [ + 'root', +]); + +export interface Menu2GroupLabelClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type Menu2GroupLabelClassKey = keyof Menu2GroupLabelClasses; + +export function getMenu2GroupLabelUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2GroupLabel', slot); +} + +export const menu2GroupLabelClasses: Menu2GroupLabelClasses = generateUtilityClasses( + 'MuiMenu2GroupLabel', + ['root'], +); + +export interface Menu2SeparatorClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type Menu2SeparatorClassKey = keyof Menu2SeparatorClasses; + +export function getMenu2SeparatorUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2Separator', slot); +} + +export const menu2SeparatorClasses: Menu2SeparatorClasses = generateUtilityClasses( + 'MuiMenu2Separator', + ['root'], +); + +export interface Menu2SubmenuTriggerClasses extends Menu2ItemClasses { + /** State class applied to the root element if the submenu is open. */ + open: string; +} + +export type Menu2SubmenuTriggerClassKey = keyof Menu2SubmenuTriggerClasses; + +export function getMenu2SubmenuTriggerUtilityClass(slot: string): string { + return generateUtilityClass('MuiMenu2SubmenuTrigger', slot); +} + +export const menu2SubmenuTriggerClasses: Menu2SubmenuTriggerClasses = generateUtilityClasses( + 'MuiMenu2SubmenuTrigger', + ['root', 'highlighted', 'disabled', 'dense', 'divider', 'gutters', 'selected', 'open'], +); diff --git a/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx b/packages/mui-material/src/Unstable_Menu2/menu2ItemShared.tsx similarity index 82% rename from packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx rename to packages/mui-material/src/Unstable_Menu2/menu2ItemShared.tsx index 90288780997daa..3192ace44d4d77 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewItemShared.tsx +++ b/packages/mui-material/src/Unstable_Menu2/menu2ItemShared.tsx @@ -5,13 +5,13 @@ import composeClasses from '@mui/utils/composeClasses'; import { CSSInterpolation, SxProps } from '@mui/system'; import { Theme } from '../styles'; import { - MenuPreviewRootSlotProps, - MenuPreviewRootSlots, + Menu2RootSlotProps, + Menu2RootSlots, StateClassName, mergeStateClassName, -} from './menuPreviewUtils'; +} from './menu2Utils'; -export interface MenuPreviewItemOwnerState { +export interface Menu2ItemOwnerState { checked?: boolean | undefined; dense: boolean; disabled: boolean; @@ -20,10 +20,10 @@ export interface MenuPreviewItemOwnerState { selected: boolean; } -export interface MenuPreviewItemVisualProps< +export interface Menu2ItemVisualProps< Classes, - Slots = MenuPreviewRootSlots, - SlotProps = MenuPreviewRootSlotProps, + Slots = Menu2RootSlots, + SlotProps = Menu2RootSlotProps, > { /** * The component used for the root node. @@ -67,7 +67,7 @@ export interface MenuPreviewItemVisualProps< sx?: SxProps | undefined; } -export interface MenuPreviewItemBaseProps { +export interface Menu2ItemBaseProps { /** * The content of the component. */ @@ -94,7 +94,7 @@ export interface MenuPreviewItemBaseProps { closeOnClick?: boolean | undefined; } -export interface MenuPreviewLinkItemBaseProps { +export interface Menu2LinkItemBaseProps { /** * The content of the component. */ @@ -114,7 +114,7 @@ export interface MenuPreviewLinkItemBaseProps { closeOnClick?: boolean | undefined; } -export interface MenuPreviewSubmenuTriggerBaseProps { +export interface Menu2SubmenuTriggerBaseProps { /** * The content of the component. */ @@ -154,13 +154,13 @@ export interface MenuPreviewSubmenuTriggerBaseProps { openOnHover?: boolean | undefined; } -export interface MenuPreviewBaseItemState { +export interface Menu2BaseItemState { disabled?: boolean | undefined; highlighted?: boolean | undefined; } -export function menuPreviewItemOverridesResolver( - props: { ownerState: MenuPreviewItemOwnerState }, +export function menu2ItemOverridesResolver( + props: { ownerState: Menu2ItemOwnerState }, styles: Record, ) { const { ownerState } = props; @@ -173,12 +173,12 @@ export function menuPreviewItemOverridesResolver( ] as CSSInterpolation; } -export function getMenuPreviewItemOwnerState( - props: MenuPreviewItemVisualProps & { +export function getMenu2ItemOwnerState( + props: Menu2ItemVisualProps & { checked?: boolean | undefined; disabled?: boolean | undefined; }, -): MenuPreviewItemOwnerState { +): Menu2ItemOwnerState { return { checked: props.checked, dense: props.dense ?? false, @@ -189,8 +189,8 @@ export function getMenuPreviewItemOwnerState( }; } -export function useMenuPreviewItemUtilityClasses( - ownerState: MenuPreviewItemOwnerState & { +export function useMenu2ItemUtilityClasses( + ownerState: Menu2ItemOwnerState & { classes?: Partial | undefined; checked?: boolean | undefined; open?: boolean | undefined; @@ -221,9 +221,9 @@ export function useMenuPreviewItemUtilityClasses( } as Classes; } -export function getMenuPreviewItemClassName( +export function getMenu2ItemClassName( classes: Partial>, - ownerState: MenuPreviewItemOwnerState, + ownerState: Menu2ItemOwnerState, state: State, ) { return clsx( @@ -233,12 +233,12 @@ export function getMenuPreviewItemClassName( +export function mergeMenu2ItemClassName( className: StateClassName, classes: Partial>, - ownerState: MenuPreviewItemOwnerState, + ownerState: Menu2ItemOwnerState, ) { return mergeStateClassName(className, (state) => - getMenuPreviewItemClassName(classes, ownerState, state), + getMenu2ItemClassName(classes, ownerState, state), ); } diff --git a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx b/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx similarity index 77% rename from packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx rename to packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx index c86ea82cd9ea17..f251353a7e74bf 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewPopupShared.tsx +++ b/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx @@ -8,7 +8,7 @@ import { SxProps } from '@mui/system'; import { Theme } from '../styles'; import { PaperProps } from '../Paper'; import { ListProps } from '../List'; -import { resolveSlotProps, SlotProps } from './menuPreviewUtils'; +import { resolveSlotProps, SlotProps } from './menu2Utils'; type ExternalSlotProps = Omit, 'className' | 'render' | 'style'> & { className?: string | undefined; @@ -83,7 +83,7 @@ const listHostOmittedProps = [ 'sx', ] as const; -export interface MenuPreviewPopupSharedSlots { +export interface Menu2PopupSharedSlots { /** * The component used for the portal. * @default BaseMenu.Portal @@ -111,7 +111,7 @@ export interface MenuPreviewPopupSharedSlots { list?: React.ElementType | undefined; } -export interface MenuPreviewPopupSharedSlotProps { +export interface Menu2PopupSharedSlotProps { portal?: SlotProps, OwnerState> | undefined; positioner?: SlotProps, OwnerState> | undefined; popup?: SlotProps, OwnerState> | undefined; @@ -119,22 +119,22 @@ export interface MenuPreviewPopupSharedSlotProps { list?: SlotProps, OwnerState> | undefined; } -type MenuPreviewPositionerProps = BaseMenu.Positioner.Props; -type MenuPreviewPortalProps = BaseMenu.Portal.Props; +type Menu2PositionerProps = BaseMenu.Positioner.Props; +type Menu2PortalProps = BaseMenu.Portal.Props; -export type MenuPreviewPopupState = BaseMenu.Popup.State; -export type MenuPreviewPopupSide = NonNullable; -export type MenuPreviewPopupAlign = NonNullable; -export type MenuPreviewPopupOffset = NonNullable; -export type MenuPreviewPopupAnchor = MenuPreviewPositionerProps['anchor']; -export type MenuPreviewPopupPositionMethod = MenuPreviewPositionerProps['positionMethod']; -export type MenuPreviewPopupCollisionBoundary = MenuPreviewPositionerProps['collisionBoundary']; -export type MenuPreviewPopupCollisionPadding = MenuPreviewPositionerProps['collisionPadding']; -export type MenuPreviewPopupCollisionAvoidance = MenuPreviewPositionerProps['collisionAvoidance']; -export type MenuPreviewPopupContainer = MenuPreviewPortalProps['container']; -export type MenuPreviewPopupFinalFocus = BaseMenu.Popup.Props['finalFocus']; +export type Menu2PopupState = BaseMenu.Popup.State; +export type Menu2PopupSide = NonNullable; +export type Menu2PopupAlign = NonNullable; +export type Menu2PopupOffset = NonNullable; +export type Menu2PopupAnchor = Menu2PositionerProps['anchor']; +export type Menu2PopupPositionMethod = Menu2PositionerProps['positionMethod']; +export type Menu2PopupCollisionBoundary = Menu2PositionerProps['collisionBoundary']; +export type Menu2PopupCollisionPadding = Menu2PositionerProps['collisionPadding']; +export type Menu2PopupCollisionAvoidance = Menu2PositionerProps['collisionAvoidance']; +export type Menu2PopupContainer = Menu2PortalProps['container']; +export type Menu2PopupFinalFocus = BaseMenu.Popup.Props['finalFocus']; -export interface MenuPreviewPopupPublicProps { +export interface Menu2PopupPublicProps { /** * The menu items. */ @@ -152,74 +152,74 @@ export interface MenuPreviewPopupPublicProps { * * By default, the popup is positioned against the trigger. */ - anchor?: MenuPreviewPopupAnchor | undefined; + anchor?: Menu2PopupAnchor | undefined; /** * Determines which CSS `position` property to use. * @default 'absolute' */ - positionMethod?: MenuPreviewPopupPositionMethod | undefined; + positionMethod?: Menu2PopupPositionMethod | undefined; /** * Which side of the anchor element to align the popup against. * @default 'bottom' */ - side?: MenuPreviewPopupSide | undefined; + side?: Menu2PopupSide | undefined; /** * Distance between the anchor and the popup in pixels. * @default 0 */ - sideOffset?: MenuPreviewPopupOffset | undefined; + sideOffset?: Menu2PopupOffset | undefined; /** * How to align the popup relative to the specified side. * @default 'start' */ - align?: MenuPreviewPopupAlign | undefined; + align?: Menu2PopupAlign | undefined; /** * Additional offset along the alignment axis in pixels. * @default 0 */ - alignOffset?: MenuPreviewPopupOffset | undefined; + alignOffset?: Menu2PopupOffset | undefined; /** * An element or a rectangle that delimits the area that the popup is confined to. * @default 'clipping-ancestors' */ - collisionBoundary?: MenuPreviewPopupCollisionBoundary | undefined; + collisionBoundary?: Menu2PopupCollisionBoundary | undefined; /** * Additional space to maintain from the edge of the collision boundary. * @default 5 */ - collisionPadding?: MenuPreviewPopupCollisionPadding | undefined; + collisionPadding?: Menu2PopupCollisionPadding | undefined; /** * Minimum distance to maintain between the arrow and the edges of the popup. * @default 5 */ - arrowPadding?: MenuPreviewPositionerProps['arrowPadding'] | undefined; + arrowPadding?: Menu2PositionerProps['arrowPadding'] | undefined; /** * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. * @default false */ - sticky?: MenuPreviewPositionerProps['sticky'] | undefined; + sticky?: Menu2PositionerProps['sticky'] | undefined; /** * Whether to disable the popup from tracking layout shifts of its positioning anchor. * @default false */ - disableAnchorTracking?: MenuPreviewPositionerProps['disableAnchorTracking'] | undefined; + disableAnchorTracking?: Menu2PositionerProps['disableAnchorTracking'] | undefined; /** * Determines how to handle collisions when positioning the popup. */ - collisionAvoidance?: MenuPreviewPopupCollisionAvoidance | undefined; + collisionAvoidance?: Menu2PopupCollisionAvoidance | undefined; /** * The container element to portal the popup into. */ - container?: MenuPreviewPopupContainer | undefined; + container?: Menu2PopupContainer | undefined; /** * Whether to keep the portal mounted in the DOM while the popup is hidden. * @default false */ - keepMounted?: MenuPreviewPortalProps['keepMounted'] | undefined; + keepMounted?: Menu2PortalProps['keepMounted'] | undefined; /** * Determines the element to focus when the menu is closed. */ - finalFocus?: MenuPreviewPopupFinalFocus | undefined; + finalFocus?: Menu2PopupFinalFocus | undefined; /** * The elevation of the menu surface. * @default 8 @@ -227,14 +227,14 @@ export interface MenuPreviewPopupPublicProps { elevation?: number | undefined; } -export interface MenuPreviewPopupSharedProps +export interface Menu2PopupSharedProps extends Omit, - MenuPreviewPopupPublicProps { + Menu2PopupPublicProps { classes?: Partial> | undefined; ownerState: OwnerState; - slots?: MenuPreviewPopupSharedSlots | undefined; - slotProps?: MenuPreviewPopupSharedSlotProps | undefined; + slots?: Menu2PopupSharedSlots | undefined; + slotProps?: Menu2PopupSharedSlotProps | undefined; defaultSlots: { popup: React.ElementType; paper: React.ElementType; @@ -244,8 +244,8 @@ export interface MenuPreviewPopupSharedProps sx?: SxProps | undefined; } -export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBase( - props: MenuPreviewPopupSharedProps, +export const Menu2PopupBase = React.forwardRef(function Menu2PopupBase( + props: Menu2PopupSharedProps, ref: React.ForwardedRef, ) { const { @@ -370,5 +370,5 @@ export const MenuPreviewPopupBase = React.forwardRef(function MenuPreviewPopupBa ); }) as ( - props: MenuPreviewPopupSharedProps & React.RefAttributes, + props: Menu2PopupSharedProps & React.RefAttributes, ) => React.JSX.Element; diff --git a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts similarity index 78% rename from packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts rename to packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts index ef0ad115075053..659be362373c19 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewSharedStyles.ts +++ b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts @@ -4,7 +4,7 @@ import { Theme } from '../styles'; import { menuListStyles, menuPaperStyles } from '../Menu/menuStyles'; import { getMenuItemRootStyles } from '../MenuItem/menuItemStyles'; -export interface SharedMenuPreviewItemClasses { +export interface SharedMenu2ItemClasses { highlighted: string; disabled: string; dense: string; @@ -14,9 +14,9 @@ export interface SharedMenuPreviewItemClasses { open?: string | undefined; } -export function getMenuPreviewItemStyles( +export function getMenu2ItemStyles( theme: Theme, - classes: SharedMenuPreviewItemClasses, + classes: SharedMenu2ItemClasses, ): CSSInterpolation { const selectedFocusBackgroundColor = theme.alpha( (theme.vars || theme).palette.primary.main, @@ -56,27 +56,27 @@ export function getMenuPreviewItemStyles( }; } -export const menuPreviewPopupPaperStyles: CSSInterpolation = menuPaperStyles; +export const menu2PopupPaperStyles: CSSInterpolation = menuPaperStyles; -export const menuPreviewPopupListStyles: CSSInterpolation = menuListStyles; +export const menu2PopupListStyles: CSSInterpolation = menuListStyles; -export const menuPreviewIndicatorStyles = memoTheme(({ theme }) => ({ +export const menu2IndicatorStyles = memoTheme(({ theme }) => ({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', minWidth: 36, color: (theme.vars || theme).palette.action.active, - '& [data-mui-menu-preview-indicator-icon]': { + '& [data-mui-menu2-indicator-icon]': { display: 'inline-block', flexShrink: 0, width: '1.25rem', height: '1.25rem', fill: 'currentColor', }, - '& [data-mui-menu-preview-checkbox-checkmark]': { + '& [data-mui-menu2-checkbox-checkmark]': { fill: (theme.vars || theme).palette.background.paper, }, - '&[data-unchecked] [data-mui-menu-preview-indicator-mark]': { + '&[data-unchecked] [data-mui-menu2-indicator-mark]': { visibility: 'hidden', }, })); diff --git a/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts b/packages/mui-material/src/Unstable_Menu2/menu2Utils.ts similarity index 87% rename from packages/mui-material/src/MenuPreview/menuPreviewUtils.ts rename to packages/mui-material/src/Unstable_Menu2/menu2Utils.ts index 98b4e53e12114b..72843f75b39976 100644 --- a/packages/mui-material/src/MenuPreview/menuPreviewUtils.ts +++ b/packages/mui-material/src/Unstable_Menu2/menu2Utils.ts @@ -20,9 +20,7 @@ export function mergeStateClassName( } export type SlotProps = - | SlotPropsValue - | ((ownerState: OwnerState) => SlotPropsValue) - | undefined; + SlotPropsValue | ((ownerState: OwnerState) => SlotPropsValue) | undefined; export function resolveSlotProps( slotProps: SlotProps, @@ -33,15 +31,15 @@ export function resolveSlotProps( : slotProps; } -export interface MenuPreviewRootSlots { +export interface Menu2RootSlots { root?: React.ElementType | undefined; } -export interface MenuPreviewRootSlotProps { +export interface Menu2RootSlotProps { root?: SlotProps, OwnerState>; } -export function getMenuPreviewRootRender( +export function getMenu2RootRender( RootSlot: React.ElementType, ownerState: OwnerState, props?: Record, @@ -59,7 +57,7 @@ export function getMenuPreviewRootRender( return React.createElement(RootSlot, appendOwnerState(RootSlot, props ?? {}, ownerState)); } -export function isMenuPreviewRootNativeButton( +export function isMenu2RootNativeButton( RootSlot: React.ElementType, component: React.ElementType | undefined, defaultNativeButton = false, diff --git a/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx b/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx similarity index 76% rename from packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx rename to packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx index bdb77f5bf60f91..78873aafeb3ab7 100644 --- a/packages/mui-material/src/MenuPreviewCheckboxItem/MenuPreviewCheckboxItem.tsx +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx @@ -7,29 +7,29 @@ import ListContext from '../List/ListContext'; import { styled } from '../zero-styled'; import memoTheme from '../utils/memoTheme'; import { useDefaultProps } from '../DefaultPropsProvider'; -import { getMenuPreviewItemStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { getMenu2ItemStyles } from '../Unstable_Menu2/menu2SharedStyles'; import { - getMenuPreviewRootRender, - isMenuPreviewRootNativeButton, - MenuPreviewRootSlotProps, + getMenu2RootRender, + isMenu2RootNativeButton, + Menu2RootSlotProps, resolveSlotProps, -} from '../MenuPreview/menuPreviewUtils'; +} from '../Unstable_Menu2/menu2Utils'; import { - getMenuPreviewItemClassName, - getMenuPreviewItemOwnerState, - MenuPreviewItemBaseProps, - MenuPreviewItemOwnerState, - MenuPreviewItemVisualProps, - menuPreviewItemOverridesResolver, - useMenuPreviewItemUtilityClasses, -} from '../MenuPreview/menuPreviewItemShared'; + getMenu2ItemClassName, + getMenu2ItemOwnerState, + Menu2ItemBaseProps, + Menu2ItemOwnerState, + Menu2ItemVisualProps, + menu2ItemOverridesResolver, + useMenu2ItemUtilityClasses, +} from '../Unstable_Menu2/menu2ItemShared'; import { - getMenuPreviewCheckboxItemUtilityClass, - menuPreviewCheckboxItemClasses, - MenuPreviewCheckboxItemClasses, -} from '../MenuPreview/menuPreviewClasses'; + getMenu2CheckboxItemUtilityClass, + menu2CheckboxItemClasses, + Menu2CheckboxItemClasses, +} from '../Unstable_Menu2/menu2Classes'; -export interface MenuPreviewCheckboxItemSlots { +export interface Menu2CheckboxItemSlots { /** * The component that renders the root. * @default 'div' @@ -37,19 +37,19 @@ export interface MenuPreviewCheckboxItemSlots { root?: React.ElementType | undefined; } -export interface MenuPreviewCheckboxItemSlotProps extends MenuPreviewRootSlotProps {} +export interface Menu2CheckboxItemSlotProps extends Menu2RootSlotProps {} -export interface MenuPreviewCheckboxItemProps +export interface Menu2CheckboxItemProps extends Omit< BaseMenu.CheckboxItem.Props, 'className' | 'nativeButton' | 'onChange' | 'onCheckedChange' | 'render' | 'style' >, - MenuPreviewItemBaseProps, - MenuPreviewItemVisualProps< - MenuPreviewCheckboxItemClasses, - MenuPreviewCheckboxItemSlots, - MenuPreviewCheckboxItemSlotProps + Menu2ItemBaseProps, + Menu2ItemVisualProps< + Menu2CheckboxItemClasses, + Menu2CheckboxItemSlots, + Menu2CheckboxItemSlotProps > { /** * The content of the component. @@ -102,12 +102,12 @@ export interface MenuPreviewCheckboxItemProps style?: React.CSSProperties | undefined; } -const MenuPreviewCheckboxItemRoot = styled('div', { - name: 'MuiMenuPreviewCheckboxItem', +const Menu2CheckboxItemRoot = styled('div', { + name: 'MuiMenu2CheckboxItem', slot: 'Root', - overridesResolver: menuPreviewItemOverridesResolver, -})<{ ownerState: MenuPreviewItemOwnerState }>( - memoTheme(({ theme }) => getMenuPreviewItemStyles(theme, menuPreviewCheckboxItemClasses)), + overridesResolver: menu2ItemOverridesResolver, +})<{ ownerState: Menu2ItemOwnerState }>( + memoTheme(({ theme }) => getMenu2ItemStyles(theme, menu2CheckboxItemClasses)), ); /** @@ -118,15 +118,15 @@ const MenuPreviewCheckboxItemRoot = styled('div', { * * API: * - * - [MenuPreviewCheckboxItem API](https://mui.com/material-ui/api/menu-preview-checkbox-item/) + * - [Menu2CheckboxItem API](https://mui.com/material-ui/api/menu-preview-checkbox-item/) */ -const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxItem( - inProps: MenuPreviewCheckboxItemProps, +const Menu2CheckboxItem = React.forwardRef(function Menu2CheckboxItem( + inProps: Menu2CheckboxItemProps, ref: React.ForwardedRef, ) { const props = useDefaultProps({ props: inProps, - name: 'MuiMenuPreviewCheckboxItem', + name: 'MuiMenu2CheckboxItem', }); const { @@ -149,7 +149,7 @@ const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxIte } = props; const ownerState = { ...props, - ...getMenuPreviewItemOwnerState({ + ...getMenu2ItemOwnerState({ checked, dense, disabled, @@ -159,9 +159,9 @@ const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxIte }), classes: classesProp, }; - const classes = useMenuPreviewItemUtilityClasses( + const classes = useMenu2ItemUtilityClasses( ownerState, - getMenuPreviewCheckboxItemUtilityClass, + getMenu2CheckboxItemUtilityClass, ); const childContext = React.useMemo( () => ({ @@ -176,13 +176,13 @@ const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxIte }, [onChange], ); - const RootSlot = slots?.root ?? MenuPreviewCheckboxItemRoot; + const RootSlot = slots?.root ?? Menu2CheckboxItemRoot; return ( clsx( className, - getMenuPreviewItemClassName(classes, ownerState, state), + getMenu2ItemClassName(classes, ownerState, state), state.checked && classes.checked, ) } checked={checked} disabled={disabled} - nativeButton={nativeButtonProp ?? isMenuPreviewRootNativeButton(RootSlot, component)} + nativeButton={nativeButtonProp ?? isMenu2RootNativeButton(RootSlot, component)} onCheckedChange={handleCheckedChange} style={style} {...other} @@ -206,7 +206,7 @@ const MenuPreviewCheckboxItem = React.forwardRef(function MenuPreviewCheckboxIte ); }); -MenuPreviewCheckboxItem.propTypes /* remove-proptypes */ = { +Menu2CheckboxItem.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ @@ -310,4 +310,4 @@ MenuPreviewCheckboxItem.propTypes /* remove-proptypes */ = { ]), } as any; -export default MenuPreviewCheckboxItem; +export default Menu2CheckboxItem; diff --git a/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.d.ts b/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.d.ts new file mode 100644 index 00000000000000..dff5b9c4fec88a --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.d.ts @@ -0,0 +1,10 @@ +export { default } from './Menu2CheckboxItem'; +export * from './Menu2CheckboxItem'; +export { + menu2CheckboxItemClasses, + getMenu2CheckboxItemUtilityClass, +} from '../Unstable_Menu2/menu2Classes'; +export type { + Menu2CheckboxItemClasses, + Menu2CheckboxItemClassKey, +} from '../Unstable_Menu2/menu2Classes'; diff --git a/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.js b/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.js new file mode 100644 index 00000000000000..fc33e94bd00adf --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItem/index.js @@ -0,0 +1,6 @@ +export { default } from './Menu2CheckboxItem'; +export * from './Menu2CheckboxItem'; +export { + menu2CheckboxItemClasses, + getMenu2CheckboxItemUtilityClass, +} from '../Unstable_Menu2/menu2Classes'; diff --git a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx b/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx similarity index 69% rename from packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx rename to packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx index 85e1da901a9b1b..41c5478f99040c 100644 --- a/packages/mui-material/src/MenuPreviewCheckboxItemIndicator/MenuPreviewCheckboxItemIndicator.tsx +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx @@ -8,18 +8,18 @@ import { SxProps } from '@mui/system'; import { Theme } from '../styles'; import { styled } from '../zero-styled'; import { useDefaultProps } from '../DefaultPropsProvider'; -import { menuPreviewIndicatorStyles } from '../MenuPreview/menuPreviewSharedStyles'; +import { menu2IndicatorStyles } from '../Unstable_Menu2/menu2SharedStyles'; import { - getMenuPreviewRootRender, - MenuPreviewRootSlotProps, + getMenu2RootRender, + Menu2RootSlotProps, resolveSlotProps, -} from '../MenuPreview/menuPreviewUtils'; +} from '../Unstable_Menu2/menu2Utils'; import { - getMenuPreviewCheckboxItemIndicatorUtilityClass, - MenuPreviewCheckboxItemIndicatorClasses, -} from '../MenuPreview/menuPreviewClasses'; + getMenu2CheckboxItemIndicatorUtilityClass, + Menu2CheckboxItemIndicatorClasses, +} from '../Unstable_Menu2/menu2Classes'; -export interface MenuPreviewCheckboxItemIndicatorSlots { +export interface Menu2CheckboxItemIndicatorSlots { /** * The component that renders the root. * @default 'span' @@ -27,9 +27,9 @@ export interface MenuPreviewCheckboxItemIndicatorSlots { root?: React.ElementType | undefined; } -export interface MenuPreviewCheckboxItemIndicatorSlotProps extends MenuPreviewRootSlotProps {} +export interface Menu2CheckboxItemIndicatorSlotProps extends Menu2RootSlotProps {} -export interface MenuPreviewCheckboxItemIndicatorProps extends Omit< +export interface Menu2CheckboxItemIndicatorProps extends Omit< BaseMenu.CheckboxItemIndicator.Props, 'className' | 'render' | 'style' > { @@ -40,7 +40,7 @@ export interface MenuPreviewCheckboxItemIndicatorProps extends Omit< /** * Override or extend the styles applied to the component. */ - classes?: Partial | undefined; + classes?: Partial | undefined; /** * CSS class applied to the element. */ @@ -53,11 +53,11 @@ export interface MenuPreviewCheckboxItemIndicatorProps extends Omit< /** * The components used for each slot inside. */ - slots?: MenuPreviewCheckboxItemIndicatorSlots | undefined; + slots?: Menu2CheckboxItemIndicatorSlots | undefined; /** * The props used for each slot inside. */ - slotProps?: MenuPreviewCheckboxItemIndicatorSlotProps | undefined; + slotProps?: Menu2CheckboxItemIndicatorSlotProps | undefined; /** * Styles applied to the root element. */ @@ -68,7 +68,7 @@ export interface MenuPreviewCheckboxItemIndicatorProps extends Omit< sx?: SxProps | undefined; } -const useUtilityClasses = (ownerState: MenuPreviewCheckboxItemIndicatorProps) => { +const useUtilityClasses = (ownerState: Menu2CheckboxItemIndicatorProps) => { const { classes } = ownerState; const slots = { @@ -80,28 +80,23 @@ const useUtilityClasses = (ownerState: MenuPreviewCheckboxItemIndicatorProps) => return { ...classes, - ...composeClasses(slots, getMenuPreviewCheckboxItemIndicatorUtilityClass, classes), + ...composeClasses(slots, getMenu2CheckboxItemIndicatorUtilityClass, classes), }; }; -const MenuPreviewCheckboxItemIndicatorRoot = styled('span', { - name: 'MuiMenuPreviewCheckboxItemIndicator', +const Menu2CheckboxItemIndicatorRoot = styled('span', { + name: 'MuiMenu2CheckboxItemIndicator', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})(menuPreviewIndicatorStyles) as any; +})(menu2IndicatorStyles) as any; function DefaultCheckboxIndicatorIcon() { return ( - Actions @@ -269,7 +273,7 @@ const parityItems = [ function ClassicVersusSuccessorDemo({ settings }: { settings: PlaygroundSettings }) { const [classicAnchorEl, setClassicAnchorEl] = React.useState(null); - const { slotProps } = usePopupKnobProps(settings); + const popupKnobProps = usePopupKnobProps(settings); const itemProps = { dense: settings.dense, divider: settings.dividers }; return ( @@ -305,7 +309,7 @@ function ClassicVersusSuccessorDemo({ settings }: { settings: PlaygroundSettings }> Successor - + {parityItems.map((item) => ( Root behavior {renderCheckbox('modal', 'modal')} + {renderCheckbox('rootOpenOnHover', 'openOnHover (root)')} {renderCheckbox('loopFocus', 'loopFocus')} {renderCheckbox('highlightItemOnHover', 'highlightItemOnHover')} @@ -594,9 +599,9 @@ export default function MenuRfcExperiment() {

Classic vs successor

The same item set rendered by the classic Menu and the successor, for visual parity - checks (dense, dividers, selected, disabled, elevation knobs apply to both). Note the - classic top-level elevation prop vs{' '} - slotProps.paper.elevation on the successor (RFC open question). + checks (dense, dividers, selected, disabled, elevation knobs apply to both). Both + expose a top-level elevation prop; the successor forwards it to the Paper + slot.

diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx index 0554315c46e9cf..394aef53cecec0 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx @@ -4,60 +4,17 @@ import PropTypes from 'prop-types'; import { Menu as BaseMenu } from '@base-ui/react/menu'; import { useDefaultProps } from '../DefaultPropsProvider'; -export interface Menu2Props { +/** + * Inherits the full Base UI `Menu.Root` prop surface (open/close control, + * modality, hover-open with delays, `actionsRef`, keyboard behavior). + * `Omit` (a mapped type) is used instead of bare `extends` so the proptypes + * generator resolves the inherited members. + */ +export interface Menu2Props extends Omit { /** * The content of the menu. */ children?: React.ReactNode; - /** - * Whether the menu is initially open. - * - * To render a controlled menu, use the `open` prop instead. - * @default false - */ - defaultOpen?: boolean | undefined; - /** - * Whether the menu is currently open. - */ - open?: boolean | undefined; - /** - * Event handler called when the menu is opened or closed. - */ - onOpenChange?: BaseMenu.Root.Props['onOpenChange'] | undefined; - /** - * Event handler called after any animations complete when the menu is opened or closed. - */ - onOpenChangeComplete?: BaseMenu.Root.Props['onOpenChangeComplete'] | undefined; - /** - * Determines if the menu enters a modal state when open. - * @default true - */ - modal?: boolean | undefined; - /** - * Whether the component should ignore user interaction. - * @default false - */ - disabled?: boolean | undefined; - /** - * Whether to loop keyboard focus back to the first item. - * @default true - */ - loopFocus?: boolean | undefined; - /** - * Whether moving the pointer over items should highlight them. - * @default true - */ - highlightItemOnHover?: boolean | undefined; - /** - * The visual orientation of the menu. - * @default 'vertical' - */ - orientation?: 'horizontal' | 'vertical' | undefined; - /** - * When in a submenu, determines whether pressing the Escape key closes the entire menu. - * @default false - */ - closeParentOnEsc?: boolean | undefined; } /** @@ -88,55 +45,6 @@ Menu2.propTypes /* remove-proptypes */ = { * The content of the menu. */ children: PropTypes.node, - /** - * When in a submenu, determines whether pressing the Escape key closes the entire menu. - * @default false - */ - closeParentOnEsc: PropTypes.bool, - /** - * Whether the menu is initially open. - * - * To render a controlled menu, use the `open` prop instead. - * @default false - */ - defaultOpen: PropTypes.bool, - /** - * Whether the component should ignore user interaction. - * @default false - */ - disabled: PropTypes.bool, - /** - * Whether moving the pointer over items should highlight them. - * @default true - */ - highlightItemOnHover: PropTypes.bool, - /** - * Whether to loop keyboard focus back to the first item. - * @default true - */ - loopFocus: PropTypes.bool, - /** - * Determines if the menu enters a modal state when open. - * @default true - */ - modal: PropTypes.bool, - /** - * Event handler called when the menu is opened or closed. - */ - onOpenChange: PropTypes.func, - /** - * Event handler called after any animations complete when the menu is opened or closed. - */ - onOpenChangeComplete: PropTypes.func, - /** - * Whether the menu is currently open. - */ - open: PropTypes.bool, - /** - * The visual orientation of the menu. - * @default 'vertical' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), } as any; export default Menu2; diff --git a/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx b/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx index f251353a7e74bf..e1b5794fb39315 100644 --- a/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx +++ b/packages/mui-material/src/Unstable_Menu2/menu2PopupShared.tsx @@ -134,7 +134,29 @@ export type Menu2PopupCollisionAvoidance = Menu2PositionerProps['collisionAvoida export type Menu2PopupContainer = Menu2PortalProps['container']; export type Menu2PopupFinalFocus = BaseMenu.Popup.Props['finalFocus']; -export interface Menu2PopupPublicProps { +/** + * The flattened positioning/portal surface hoisted onto the popup, inherited + * from the Base UI parts via Pick so new Base UI props flow through types + * automatically. Only props that Material UI adds, or whose defaults differ + * from Base UI, are declared locally. + */ +export interface Menu2PopupPublicProps + extends + Pick< + Menu2PositionerProps, + | 'anchor' + | 'positionMethod' + | 'sideOffset' + | 'alignOffset' + | 'collisionBoundary' + | 'collisionPadding' + | 'arrowPadding' + | 'sticky' + | 'disableAnchorTracking' + | 'collisionAvoidance' + >, + Pick, + Pick { /** * The menu items. */ @@ -147,79 +169,17 @@ export interface Menu2PopupPublicProps { * Styles applied to the Base UI popup element. */ style?: React.CSSProperties | undefined; - /** - * An element to position the popup against. - * - * By default, the popup is positioned against the trigger. - */ - anchor?: Menu2PopupAnchor | undefined; - /** - * Determines which CSS `position` property to use. - * @default 'absolute' - */ - positionMethod?: Menu2PopupPositionMethod | undefined; /** * Which side of the anchor element to align the popup against. * @default 'bottom' */ side?: Menu2PopupSide | undefined; - /** - * Distance between the anchor and the popup in pixels. - * @default 0 - */ - sideOffset?: Menu2PopupOffset | undefined; /** * How to align the popup relative to the specified side. + * Defaults to `start` to match the classic Menu (Base UI defaults to `center`). * @default 'start' */ align?: Menu2PopupAlign | undefined; - /** - * Additional offset along the alignment axis in pixels. - * @default 0 - */ - alignOffset?: Menu2PopupOffset | undefined; - /** - * An element or a rectangle that delimits the area that the popup is confined to. - * @default 'clipping-ancestors' - */ - collisionBoundary?: Menu2PopupCollisionBoundary | undefined; - /** - * Additional space to maintain from the edge of the collision boundary. - * @default 5 - */ - collisionPadding?: Menu2PopupCollisionPadding | undefined; - /** - * Minimum distance to maintain between the arrow and the edges of the popup. - * @default 5 - */ - arrowPadding?: Menu2PositionerProps['arrowPadding'] | undefined; - /** - * Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. - * @default false - */ - sticky?: Menu2PositionerProps['sticky'] | undefined; - /** - * Whether to disable the popup from tracking layout shifts of its positioning anchor. - * @default false - */ - disableAnchorTracking?: Menu2PositionerProps['disableAnchorTracking'] | undefined; - /** - * Determines how to handle collisions when positioning the popup. - */ - collisionAvoidance?: Menu2PopupCollisionAvoidance | undefined; - /** - * The container element to portal the popup into. - */ - container?: Menu2PopupContainer | undefined; - /** - * Whether to keep the portal mounted in the DOM while the popup is hidden. - * @default false - */ - keepMounted?: Menu2PortalProps['keepMounted'] | undefined; - /** - * Determines the element to focus when the menu is closed. - */ - finalFocus?: Menu2PopupFinalFocus | undefined; /** * The elevation of the menu surface. * @default 8 diff --git a/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx b/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx index 6476956e9784d9..97dfbbadec9603 100644 --- a/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx +++ b/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx @@ -4,55 +4,17 @@ import PropTypes from 'prop-types'; import { Menu as BaseMenu } from '@base-ui/react/menu'; import { useDefaultProps } from '../DefaultPropsProvider'; -export interface Menu2SubmenuRootProps { +/** + * Inherits the full Base UI `Menu.SubmenuRoot` prop surface (open/close + * control, hover-open with delays, `closeParentOnEsc`, keyboard behavior). + * `Omit` (a mapped type) is used instead of bare `extends` so the proptypes + * generator resolves the inherited members. + */ +export interface Menu2SubmenuRootProps extends Omit { /** * The content of the submenu. */ children?: React.ReactNode; - /** - * Whether the submenu is initially open. - * - * To render a controlled submenu, use the `open` prop instead. - * @default false - */ - defaultOpen?: boolean | undefined; - /** - * Whether the submenu is currently open. - */ - open?: boolean | undefined; - /** - * Event handler called when the submenu is opened or closed. - */ - onOpenChange?: BaseMenu.SubmenuRoot.Props['onOpenChange'] | undefined; - /** - * Event handler called after any animations complete when the submenu is opened or closed. - */ - onOpenChangeComplete?: BaseMenu.SubmenuRoot.Props['onOpenChangeComplete'] | undefined; - /** - * Whether the component should ignore user interaction. - * @default false - */ - disabled?: boolean | undefined; - /** - * Whether to loop keyboard focus back to the first item. - * @default true - */ - loopFocus?: boolean | undefined; - /** - * Whether moving the pointer over items should highlight them. - * @default true - */ - highlightItemOnHover?: boolean | undefined; - /** - * The visual orientation of the submenu. - * @default 'vertical' - */ - orientation?: 'horizontal' | 'vertical' | undefined; - /** - * When in a submenu, determines whether pressing the Escape key closes the entire menu. - * @default false - */ - closeParentOnEsc?: boolean | undefined; } /** @@ -83,50 +45,6 @@ Menu2SubmenuRoot.propTypes /* remove-proptypes */ = { * The content of the submenu. */ children: PropTypes.node, - /** - * When in a submenu, determines whether pressing the Escape key closes the entire menu. - * @default false - */ - closeParentOnEsc: PropTypes.bool, - /** - * Whether the submenu is initially open. - * - * To render a controlled submenu, use the `open` prop instead. - * @default false - */ - defaultOpen: PropTypes.bool, - /** - * Whether the component should ignore user interaction. - * @default false - */ - disabled: PropTypes.bool, - /** - * Whether moving the pointer over items should highlight them. - * @default true - */ - highlightItemOnHover: PropTypes.bool, - /** - * Whether to loop keyboard focus back to the first item. - * @default true - */ - loopFocus: PropTypes.bool, - /** - * Event handler called when the submenu is opened or closed. - */ - onOpenChange: PropTypes.func, - /** - * Event handler called after any animations complete when the submenu is opened or closed. - */ - onOpenChangeComplete: PropTypes.func, - /** - * Whether the submenu is currently open. - */ - open: PropTypes.bool, - /** - * The visual orientation of the submenu. - * @default 'vertical' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), } as any; export default Menu2SubmenuRoot; From 84de2d40b8d37854340d8ac3dc3aa3899a9dfe7b Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 15:05:20 +0300 Subject: [PATCH 13/19] Fix popup surface clipping from inherited classic maxHeight The shared classic paper styles carry maxHeight: calc(100% - 96px), which in the classic Menu resolves against the full-viewport Modal. Inside the content-sized Base UI popup the percentage resolves against the popup itself (browser-dependent), clamping the Paper ~96px shorter than its content and clipping the end of the menu (trailing items and the separator), with a visible reflow on open. Override it for the successor with the positioner-provided collision-aware space: min(calc(100vh - 96px), var(--available-height)) plus overflowY: auto so long menus scroll inside the surface. Classic Menu keeps its original styles untouched. Regression test asserts the paper's computed max-height is no longer the classic percentage form and that the surface scrolls. Co-Authored-By: Claude Fable 5 --- .../src/Unstable_Menu2/Menu2.test.tsx | 22 +++++++++++++++++++ .../src/Unstable_Menu2/menu2SharedStyles.ts | 11 +++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx index 941158a051fa00..eaba4f0b22ea0a 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx @@ -453,6 +453,28 @@ describe('', () => { expect(await screen.findByTestId('paper')).to.have.class(paperClasses.elevation4); }); + it.skipIf(isJsdom())('constrains the popup surface to the collision-aware height', async () => { + const { user } = render( + + Options + + Profile + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + + const paper = await screen.findByTestId('paper'); + const { maxHeight, overflowY } = window.getComputedStyle(paper); + // Regression: the classic `calc(100% - 96px)` resolved against the + // content-sized popup instead of the viewport and clipped the end of the + // menu (separators and trailing items). + expect(maxHeight).not.to.equal('calc(100% - 96px)'); + expect(maxHeight).not.to.equal('none'); + expect(overflowY).to.equal('auto'); + }); + it('supports controlled open state and Base UI cancellation details', async () => { const handleOpenChange = spy((open: boolean, eventDetails: any) => { expect(open).to.equal(true); diff --git a/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts index 659be362373c19..4379130b561545 100644 --- a/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts +++ b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts @@ -56,7 +56,16 @@ export function getMenu2ItemStyles( }; } -export const menu2PopupPaperStyles: CSSInterpolation = menuPaperStyles; +export const menu2PopupPaperStyles: CSSInterpolation = { + ...menuPaperStyles, + // In the classic Menu the Paper sits in a full-viewport Modal, so its + // `maxHeight: calc(100% - 96px)` means "viewport minus 96px". Inside the + // content-sized Base UI popup that percentage resolves against the popup + // itself (browser-dependent), clipping the end of the menu. Use the + // collision-aware space provided by the positioner instead. + maxHeight: 'min(calc(100vh - 96px), var(--available-height))', + overflowY: 'auto', +}; export const menu2PopupListStyles: CSSInterpolation = menuListStyles; From 7296bb65068d644e9ce98ed8de16b4891f915d28 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 15:29:22 +0300 Subject: [PATCH 14/19] Fix separator spacing collapse when a submenu opens Opening a submenu mounts Base UI inline focus-guard and portal-anchor nodes as siblings of the submenu trigger, which breaks the legacy `[item] + .MuiDivider-root` adjacency rule carried by the shared item styles: the following separator lost its 8px margins and the menu visibly contracted below the trigger. Own the spacing on Menu2Separator's root instead (same values, so the closed state is unchanged where the adjacency rule also applies). Side effect: a separator following a Group now gets the same margins, making separator spacing uniform. Regression test opens a submenu (defaultOpen) and asserts the separator keeps its margins. Co-Authored-By: Claude Fable 5 --- .../src/Unstable_Menu2/Menu2.test.tsx | 28 +++++++++++++++++++ .../Menu2Separator.tsx | 12 +++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx index eaba4f0b22ea0a..f1288788fe2785 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx @@ -844,6 +844,34 @@ describe('', () => { expect(screen.getByRole('menuitem', { name: 'Archive' })).to.not.equal(null); }); + it.skipIf(isJsdom())('keeps separator spacing stable while a submenu is open', async () => { + const { user } = render( + + Options + + + View + + Zoom + + + + After + + , + ); + + await user.click(screen.getByRole('button', { name: 'Options' })); + await screen.findByRole('menuitem', { name: 'Zoom' }); + + const separator = screen.getByRole('separator'); + const { marginTop, marginBottom } = window.getComputedStyle(separator); + // Regression: the inline focus-guard nodes of an open submenu broke the + // legacy `[item] + divider` adjacency rule and collapsed this spacing. + expect(marginTop).to.equal('8px'); + expect(marginBottom).to.equal('8px'); + }); + it('supports Material UI Tooltip on enabled item flavors', async () => { const { user } = render( diff --git a/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx b/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx index 5bd1d6e3fcf74e..035b2d3e197cc2 100644 --- a/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx +++ b/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx @@ -8,6 +8,7 @@ import { SxProps } from '@mui/system'; import Divider from '../Divider'; import { Theme } from '../styles'; import { styled } from '../zero-styled'; +import memoTheme from '../utils/memoTheme'; import { useDefaultProps } from '../DefaultPropsProvider'; import { getMenu2RootRender, @@ -77,7 +78,16 @@ const Menu2SeparatorRoot = styled(Divider, { name: 'MuiMenu2Separator', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})({}) as any; +})( + // Own the classic item/divider spacing instead of relying on the legacy + // `[item] + divider` adjacency rule: Base UI mounts inline focus-guard + // nodes next to an open submenu trigger, which breaks that selector and + // collapses the gap. + memoTheme(({ theme }) => ({ + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + })), +) as any; /** * From e802107a760ad22062d9f1ee36a8ba27300b635d Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 15:47:34 +0300 Subject: [PATCH 15/19] Fix Netlify and CI failures from the tooling cleanup and styles spread - Restore docs/data/material/pagesApi.js to upstream: it still registered the 16 removed /material-ui/api/menu-preview* routes (kebab-case, so the earlier cleanup sweep missed it), breaking the docs build on Netlify. - Narrow the JSDoc-typed classic paper styles before spreading (CSSInterpolation union fails TS2698 under tsconfig.build declaration emit, which broke the package build, publish dry run, and bundle monitor; the dev tsconfig tolerated it). - Drop the dead api/menu-preview* links from the generated component JSDoc headers. - Wire the playground hover-open knob to the trigger: Base UI puts openOnHover/delay on Menu.Trigger, not Menu.Root (caught by the docs typecheck once the barrel types were exercised). Verified: @mui/material build with dependency graph (declaration emit) exit 0, docs + package tsgo clean, proptypes regeneration churn-free, Menu2 suite 117 passed / 15 skipped, eslint/prettier clean. Co-Authored-By: Claude Fable 5 --- docs/data/material/pagesApi.js | 16 ---------------- docs/pages/experiments/menu-rfc.tsx | 13 ++++++++----- .../mui-material/src/Unstable_Menu2/Menu2.tsx | 10 +++------- .../src/Unstable_Menu2/menu2SharedStyles.ts | 6 ++++-- .../Menu2CheckboxItem.tsx | 4 ---- .../Menu2CheckboxItemIndicator.tsx | 4 ---- .../src/Unstable_Menu2Group/Menu2Group.tsx | 4 ---- .../Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx | 4 ---- .../src/Unstable_Menu2Item/Menu2Item.tsx | 4 ---- .../src/Unstable_Menu2LinkItem/Menu2LinkItem.tsx | 4 ---- .../src/Unstable_Menu2Popup/Menu2Popup.tsx | 4 ---- .../Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx | 4 ---- .../Unstable_Menu2RadioItem/Menu2RadioItem.tsx | 4 ---- .../Menu2RadioItemIndicator.tsx | 4 ---- .../Unstable_Menu2Separator/Menu2Separator.tsx | 4 ---- .../Menu2SubmenuPopup.tsx | 4 ---- .../Menu2SubmenuRoot.tsx | 10 +++------- .../Menu2SubmenuTrigger.tsx | 4 ---- .../src/Unstable_Menu2Trigger/Menu2Trigger.tsx | 4 ---- 19 files changed, 18 insertions(+), 93 deletions(-) diff --git a/docs/data/material/pagesApi.js b/docs/data/material/pagesApi.js index 5ca508d6d9e9eb..ee76034af9e54a 100644 --- a/docs/data/material/pagesApi.js +++ b/docs/data/material/pagesApi.js @@ -73,22 +73,6 @@ export default [ { pathname: '/material-ui/api/menu' }, { pathname: '/material-ui/api/menu-item' }, { pathname: '/material-ui/api/menu-list' }, - { pathname: '/material-ui/api/menu-preview' }, - { pathname: '/material-ui/api/menu-preview-checkbox-item' }, - { pathname: '/material-ui/api/menu-preview-checkbox-item-indicator' }, - { pathname: '/material-ui/api/menu-preview-group' }, - { pathname: '/material-ui/api/menu-preview-group-label' }, - { pathname: '/material-ui/api/menu-preview-item' }, - { pathname: '/material-ui/api/menu-preview-link-item' }, - { pathname: '/material-ui/api/menu-preview-popup' }, - { pathname: '/material-ui/api/menu-preview-radio-group' }, - { pathname: '/material-ui/api/menu-preview-radio-item' }, - { pathname: '/material-ui/api/menu-preview-radio-item-indicator' }, - { pathname: '/material-ui/api/menu-preview-separator' }, - { pathname: '/material-ui/api/menu-preview-submenu-popup' }, - { pathname: '/material-ui/api/menu-preview-submenu-root' }, - { pathname: '/material-ui/api/menu-preview-submenu-trigger' }, - { pathname: '/material-ui/api/menu-preview-trigger' }, { pathname: '/material-ui/api/mobile-stepper' }, { pathname: '/material-ui/api/modal' }, { pathname: '/material-ui/api/native-select' }, diff --git a/docs/pages/experiments/menu-rfc.tsx b/docs/pages/experiments/menu-rfc.tsx index 16dfca1e64d96c..f802a9981d3217 100644 --- a/docs/pages/experiments/menu-rfc.tsx +++ b/docs/pages/experiments/menu-rfc.tsx @@ -58,7 +58,7 @@ type PopupAlign = NonNullable; interface PlaygroundSettings { // Root behavior modal: boolean; - rootOpenOnHover: boolean; + triggerOpenOnHover: boolean; loopFocus: boolean; highlightItemOnHover: boolean; // Submenu behavior @@ -82,7 +82,7 @@ interface PlaygroundSettings { const defaultSettings: PlaygroundSettings = { modal: true, - rootOpenOnHover: false, + triggerOpenOnHover: false, loopFocus: true, highlightItemOnHover: true, submenusOpenOnHover: true, @@ -166,13 +166,16 @@ function PlaygroundDemo({ return ( - }> + } + > Project Root behavior {renderCheckbox('modal', 'modal')} - {renderCheckbox('rootOpenOnHover', 'openOnHover (root)')} + {renderCheckbox('triggerOpenOnHover', 'openOnHover (trigger)')} {renderCheckbox('loopFocus', 'loopFocus')} {renderCheckbox('highlightItemOnHover', 'highlightItemOnHover')} diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx index 394aef53cecec0..40501b5bfeedba 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.tsx @@ -6,9 +6,9 @@ import { useDefaultProps } from '../DefaultPropsProvider'; /** * Inherits the full Base UI `Menu.Root` prop surface (open/close control, - * modality, hover-open with delays, `actionsRef`, keyboard behavior). - * `Omit` (a mapped type) is used instead of bare `extends` so the proptypes - * generator resolves the inherited members. + * modality, `actionsRef`, keyboard behavior); hover-open props live on the + * trigger parts. `Omit` (a mapped type) is used instead of bare `extends` so + * the proptypes generator resolves the inherited members. */ export interface Menu2Props extends Omit { /** @@ -22,10 +22,6 @@ export interface Menu2Props extends Omit { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2 API](https://mui.com/material-ui/api/menu-preview/) */ function Menu2(props: Menu2Props): React.JSX.Element { const themedProps = useDefaultProps({ diff --git a/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts index 4379130b561545..cd44eac5ad0121 100644 --- a/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts +++ b/packages/mui-material/src/Unstable_Menu2/menu2SharedStyles.ts @@ -1,4 +1,4 @@ -import { CSSInterpolation } from '@mui/system'; +import { CSSInterpolation, CSSObject } from '@mui/system'; import memoTheme from '../utils/memoTheme'; import { Theme } from '../styles'; import { menuListStyles, menuPaperStyles } from '../Menu/menuStyles'; @@ -57,7 +57,9 @@ export function getMenu2ItemStyles( } export const menu2PopupPaperStyles: CSSInterpolation = { - ...menuPaperStyles, + // The classic module types its exports as CSSInterpolation via JSDoc; the + // value is a plain style object, narrowed here so it can be spread. + ...(menuPaperStyles as CSSObject), // In the classic Menu the Paper sits in a full-viewport Modal, so its // `maxHeight: calc(100% - 96px)` means "viewport minus 96px". Inside the // content-sized Base UI popup that percentage resolves against the popup diff --git a/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx b/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx index 78873aafeb3ab7..43face4999bb44 100644 --- a/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItem/Menu2CheckboxItem.tsx @@ -115,10 +115,6 @@ const Menu2CheckboxItemRoot = styled('div', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2CheckboxItem API](https://mui.com/material-ui/api/menu-preview-checkbox-item/) */ const Menu2CheckboxItem = React.forwardRef(function Menu2CheckboxItem( inProps: Menu2CheckboxItemProps, diff --git a/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx b/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx index 41c5478f99040c..f631a21aa669b0 100644 --- a/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx +++ b/packages/mui-material/src/Unstable_Menu2CheckboxItemIndicator/Menu2CheckboxItemIndicator.tsx @@ -108,10 +108,6 @@ function DefaultCheckboxIndicatorIcon() { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2CheckboxItemIndicator API](https://mui.com/material-ui/api/menu-preview-checkbox-item-indicator/) */ const Menu2CheckboxItemIndicator = React.forwardRef(function Menu2CheckboxItemIndicator( inProps: Menu2CheckboxItemIndicatorProps, diff --git a/packages/mui-material/src/Unstable_Menu2Group/Menu2Group.tsx b/packages/mui-material/src/Unstable_Menu2Group/Menu2Group.tsx index dcae5be3f814a2..06ede66d21f8b0 100644 --- a/packages/mui-material/src/Unstable_Menu2Group/Menu2Group.tsx +++ b/packages/mui-material/src/Unstable_Menu2Group/Menu2Group.tsx @@ -80,10 +80,6 @@ const Menu2GroupRoot = styled('div', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2Group API](https://mui.com/material-ui/api/menu-preview-group/) */ const Menu2Group = React.forwardRef(function Menu2Group( inProps: Menu2GroupProps, diff --git a/packages/mui-material/src/Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx b/packages/mui-material/src/Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx index 7466bb7f9af3cd..61dad9a3b376b7 100644 --- a/packages/mui-material/src/Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx +++ b/packages/mui-material/src/Unstable_Menu2GroupLabel/Menu2GroupLabel.tsx @@ -84,10 +84,6 @@ const Menu2GroupLabelRoot = styled(ListSubheader, { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2GroupLabel API](https://mui.com/material-ui/api/menu-preview-group-label/) */ const Menu2GroupLabel = React.forwardRef(function Menu2GroupLabel( inProps: Menu2GroupLabelProps, diff --git a/packages/mui-material/src/Unstable_Menu2Item/Menu2Item.tsx b/packages/mui-material/src/Unstable_Menu2Item/Menu2Item.tsx index 8ae280536b80f1..909ed67f27a9bb 100644 --- a/packages/mui-material/src/Unstable_Menu2Item/Menu2Item.tsx +++ b/packages/mui-material/src/Unstable_Menu2Item/Menu2Item.tsx @@ -84,10 +84,6 @@ const Menu2ItemRoot = styled('div', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2Item API](https://mui.com/material-ui/api/menu-preview-item/) */ const Menu2Item = React.forwardRef(function Menu2Item( inProps: Menu2ItemProps, diff --git a/packages/mui-material/src/Unstable_Menu2LinkItem/Menu2LinkItem.tsx b/packages/mui-material/src/Unstable_Menu2LinkItem/Menu2LinkItem.tsx index 42f0cad017ace3..949b35c64c3def 100644 --- a/packages/mui-material/src/Unstable_Menu2LinkItem/Menu2LinkItem.tsx +++ b/packages/mui-material/src/Unstable_Menu2LinkItem/Menu2LinkItem.tsx @@ -82,10 +82,6 @@ const Menu2LinkItemRoot = styled('a', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2LinkItem API](https://mui.com/material-ui/api/menu-preview-link-item/) */ const Menu2LinkItem = React.forwardRef(function Menu2LinkItem( inProps: Menu2LinkItemProps, diff --git a/packages/mui-material/src/Unstable_Menu2Popup/Menu2Popup.tsx b/packages/mui-material/src/Unstable_Menu2Popup/Menu2Popup.tsx index bc5cd1a54d185b..231213e0b52040 100644 --- a/packages/mui-material/src/Unstable_Menu2Popup/Menu2Popup.tsx +++ b/packages/mui-material/src/Unstable_Menu2Popup/Menu2Popup.tsx @@ -199,10 +199,6 @@ const Menu2PopupList = styled(List, { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2Popup API](https://mui.com/material-ui/api/menu-preview-popup/) */ const Menu2Popup = React.forwardRef(function Menu2Popup( inProps: Menu2PopupProps, diff --git a/packages/mui-material/src/Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx b/packages/mui-material/src/Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx index 248f6b1292c5dd..4ee275d0309328 100644 --- a/packages/mui-material/src/Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx +++ b/packages/mui-material/src/Unstable_Menu2RadioGroup/Menu2RadioGroup.tsx @@ -112,10 +112,6 @@ const Menu2RadioGroupRoot = styled('div', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2RadioGroup API](https://mui.com/material-ui/api/menu-preview-radio-group/) */ const Menu2RadioGroup = React.forwardRef(function Menu2RadioGroup( inProps: Menu2RadioGroupProps, diff --git a/packages/mui-material/src/Unstable_Menu2RadioItem/Menu2RadioItem.tsx b/packages/mui-material/src/Unstable_Menu2RadioItem/Menu2RadioItem.tsx index cc4764e07050a8..7b3c4b40c89664 100644 --- a/packages/mui-material/src/Unstable_Menu2RadioItem/Menu2RadioItem.tsx +++ b/packages/mui-material/src/Unstable_Menu2RadioItem/Menu2RadioItem.tsx @@ -89,10 +89,6 @@ const Menu2RadioItemRoot = styled('div', { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2RadioItem API](https://mui.com/material-ui/api/menu-preview-radio-item/) */ const Menu2RadioItem = React.forwardRef(function Menu2RadioItem( inProps: Menu2RadioItemProps, diff --git a/packages/mui-material/src/Unstable_Menu2RadioItemIndicator/Menu2RadioItemIndicator.tsx b/packages/mui-material/src/Unstable_Menu2RadioItemIndicator/Menu2RadioItemIndicator.tsx index 601802f0118197..febe9761856ff3 100644 --- a/packages/mui-material/src/Unstable_Menu2RadioItemIndicator/Menu2RadioItemIndicator.tsx +++ b/packages/mui-material/src/Unstable_Menu2RadioItemIndicator/Menu2RadioItemIndicator.tsx @@ -107,10 +107,6 @@ function DefaultRadioIndicatorIcon() { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2RadioItemIndicator API](https://mui.com/material-ui/api/menu-preview-radio-item-indicator/) */ const Menu2RadioItemIndicator = React.forwardRef(function Menu2RadioItemIndicator( inProps: Menu2RadioItemIndicatorProps, diff --git a/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx b/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx index 035b2d3e197cc2..6aa3cea1506be4 100644 --- a/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx +++ b/packages/mui-material/src/Unstable_Menu2Separator/Menu2Separator.tsx @@ -94,10 +94,6 @@ const Menu2SeparatorRoot = styled(Divider, { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2Separator API](https://mui.com/material-ui/api/menu-preview-separator/) */ const Menu2Separator = React.forwardRef(function Menu2Separator( inProps: Menu2SeparatorProps, diff --git a/packages/mui-material/src/Unstable_Menu2SubmenuPopup/Menu2SubmenuPopup.tsx b/packages/mui-material/src/Unstable_Menu2SubmenuPopup/Menu2SubmenuPopup.tsx index 38d7853ff39452..eaabffb3f8b019 100644 --- a/packages/mui-material/src/Unstable_Menu2SubmenuPopup/Menu2SubmenuPopup.tsx +++ b/packages/mui-material/src/Unstable_Menu2SubmenuPopup/Menu2SubmenuPopup.tsx @@ -202,10 +202,6 @@ const Menu2SubmenuPopupList = styled(List, { * Demos: * * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu2SubmenuPopup API](https://mui.com/material-ui/api/menu-preview-submenu-popup/) */ const Menu2SubmenuPopup = React.forwardRef(function Menu2SubmenuPopup( inProps: Menu2SubmenuPopupProps, diff --git a/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx b/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx index 97dfbbadec9603..a918fc7ca0890c 100644 --- a/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx +++ b/packages/mui-material/src/Unstable_Menu2SubmenuRoot/Menu2SubmenuRoot.tsx @@ -6,9 +6,9 @@ import { useDefaultProps } from '../DefaultPropsProvider'; /** * Inherits the full Base UI `Menu.SubmenuRoot` prop surface (open/close - * control, hover-open with delays, `closeParentOnEsc`, keyboard behavior). - * `Omit` (a mapped type) is used instead of bare `extends` so the proptypes - * generator resolves the inherited members. + * control, `closeParentOnEsc`, keyboard behavior); hover-open props live on + * the submenu trigger. `Omit` (a mapped type) is used instead of bare + * `extends` so the proptypes generator resolves the inherited members. */ export interface Menu2SubmenuRootProps extends Omit { /** @@ -22,10 +22,6 @@ export interface Menu2SubmenuRootProps extends Omit Date: Fri, 24 Jul 2026 15:57:49 +0300 Subject: [PATCH 16/19] Remove leftover menu-preview API page components The docs tooling cleanup removed the generated API JSONs and translations but left the sibling page components in docs/pages/material-ui/api/, whose imports of the deleted JSONs broke the Netlify docs build (webpack module-not-found). Verified with the full Netlify-parity build locally: pnpm docs:build (llms + next build + export + build-sw + link-check) exits 0. Co-Authored-By: Claude Fable 5 --- .../api/menu-preview-checkbox-item-indicator.js | 8 -------- docs/pages/material-ui/api/menu-preview-checkbox-item.js | 8 -------- docs/pages/material-ui/api/menu-preview-group-label.js | 8 -------- docs/pages/material-ui/api/menu-preview-group.js | 8 -------- docs/pages/material-ui/api/menu-preview-item.js | 8 -------- docs/pages/material-ui/api/menu-preview-link-item.js | 8 -------- docs/pages/material-ui/api/menu-preview-popup.js | 8 -------- docs/pages/material-ui/api/menu-preview-radio-group.js | 8 -------- .../material-ui/api/menu-preview-radio-item-indicator.js | 8 -------- docs/pages/material-ui/api/menu-preview-radio-item.js | 8 -------- docs/pages/material-ui/api/menu-preview-separator.js | 8 -------- docs/pages/material-ui/api/menu-preview-submenu-popup.js | 8 -------- docs/pages/material-ui/api/menu-preview-submenu-root.js | 8 -------- .../pages/material-ui/api/menu-preview-submenu-trigger.js | 8 -------- docs/pages/material-ui/api/menu-preview-trigger.js | 8 -------- docs/pages/material-ui/api/menu-preview.js | 8 -------- 16 files changed, 128 deletions(-) delete mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js delete mode 100644 docs/pages/material-ui/api/menu-preview-checkbox-item.js delete mode 100644 docs/pages/material-ui/api/menu-preview-group-label.js delete mode 100644 docs/pages/material-ui/api/menu-preview-group.js delete mode 100644 docs/pages/material-ui/api/menu-preview-item.js delete mode 100644 docs/pages/material-ui/api/menu-preview-link-item.js delete mode 100644 docs/pages/material-ui/api/menu-preview-popup.js delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-group.js delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-item-indicator.js delete mode 100644 docs/pages/material-ui/api/menu-preview-radio-item.js delete mode 100644 docs/pages/material-ui/api/menu-preview-separator.js delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-popup.js delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-root.js delete mode 100644 docs/pages/material-ui/api/menu-preview-submenu-trigger.js delete mode 100644 docs/pages/material-ui/api/menu-preview-trigger.js delete mode 100644 docs/pages/material-ui/api/menu-preview.js diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js b/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js deleted file mode 100644 index 0fc1bd2331944b..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item-indicator.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-checkbox-item-indicator/menu-preview-checkbox-item-indicator.json'; -import jsonPageContent from './menu-preview-checkbox-item-indicator.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-checkbox-item.js b/docs/pages/material-ui/api/menu-preview-checkbox-item.js deleted file mode 100644 index 0f2c3bb92fb575..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-checkbox-item.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-checkbox-item/menu-preview-checkbox-item.json'; -import jsonPageContent from './menu-preview-checkbox-item.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-group-label.js b/docs/pages/material-ui/api/menu-preview-group-label.js deleted file mode 100644 index 8c2b8cbdec3e7b..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-group-label.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-group-label/menu-preview-group-label.json'; -import jsonPageContent from './menu-preview-group-label.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-group.js b/docs/pages/material-ui/api/menu-preview-group.js deleted file mode 100644 index e4ba5947235e0a..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-group.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-group/menu-preview-group.json'; -import jsonPageContent from './menu-preview-group.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-item.js b/docs/pages/material-ui/api/menu-preview-item.js deleted file mode 100644 index 9b5ba881bc9d25..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-item.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-item/menu-preview-item.json'; -import jsonPageContent from './menu-preview-item.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-link-item.js b/docs/pages/material-ui/api/menu-preview-link-item.js deleted file mode 100644 index 9f12334dbbafee..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-link-item.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-link-item/menu-preview-link-item.json'; -import jsonPageContent from './menu-preview-link-item.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-popup.js b/docs/pages/material-ui/api/menu-preview-popup.js deleted file mode 100644 index 127dc9feed6913..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-popup.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-popup/menu-preview-popup.json'; -import jsonPageContent from './menu-preview-popup.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-group.js b/docs/pages/material-ui/api/menu-preview-radio-group.js deleted file mode 100644 index dd1bbd6501522b..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-group.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-radio-group/menu-preview-radio-group.json'; -import jsonPageContent from './menu-preview-radio-group.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js b/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js deleted file mode 100644 index 700de197f4f9bb..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-item-indicator.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-radio-item-indicator/menu-preview-radio-item-indicator.json'; -import jsonPageContent from './menu-preview-radio-item-indicator.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-radio-item.js b/docs/pages/material-ui/api/menu-preview-radio-item.js deleted file mode 100644 index 8f58a8bd490986..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-radio-item.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-radio-item/menu-preview-radio-item.json'; -import jsonPageContent from './menu-preview-radio-item.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-separator.js b/docs/pages/material-ui/api/menu-preview-separator.js deleted file mode 100644 index 46e88927598939..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-separator.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-separator/menu-preview-separator.json'; -import jsonPageContent from './menu-preview-separator.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-popup.js b/docs/pages/material-ui/api/menu-preview-submenu-popup.js deleted file mode 100644 index b9e2198291d25d..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-popup.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-submenu-popup/menu-preview-submenu-popup.json'; -import jsonPageContent from './menu-preview-submenu-popup.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-root.js b/docs/pages/material-ui/api/menu-preview-submenu-root.js deleted file mode 100644 index 71117c38788e95..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-root.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-submenu-root/menu-preview-submenu-root.json'; -import jsonPageContent from './menu-preview-submenu-root.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-submenu-trigger.js b/docs/pages/material-ui/api/menu-preview-submenu-trigger.js deleted file mode 100644 index 64d99de2e3afc7..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-submenu-trigger.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-submenu-trigger/menu-preview-submenu-trigger.json'; -import jsonPageContent from './menu-preview-submenu-trigger.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview-trigger.js b/docs/pages/material-ui/api/menu-preview-trigger.js deleted file mode 100644 index 445e0f871bd7be..00000000000000 --- a/docs/pages/material-ui/api/menu-preview-trigger.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview-trigger/menu-preview-trigger.json'; -import jsonPageContent from './menu-preview-trigger.json'; - -export default function Page() { - return ; -} diff --git a/docs/pages/material-ui/api/menu-preview.js b/docs/pages/material-ui/api/menu-preview.js deleted file mode 100644 index 9ed07a5cc58816..00000000000000 --- a/docs/pages/material-ui/api/menu-preview.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { ApiPage } from '@mui/internal-core-docs/ApiPage'; -import descriptions from 'docs/translations/api-docs/menu-preview/menu-preview.json'; -import jsonPageContent from './menu-preview.json'; - -export default function Page() { - return ; -} From b5a80ed79f9fa5a05728a3fb8e6f2086f25f1d15 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 17:23:28 +0300 Subject: [PATCH 17/19] Use Menu2 bindings in demo and spec JSX The Unstable_ subpaths use default exports, so consumers naturally drop the prefix at the import binding. The experiment pages and the type spec now import Menu2* names directly (paths keep the Unstable_ subpaths) and the JSX reads like the future stable API; the local alias blocks are gone. Co-Authored-By: Claude Fable 5 --- docs/pages/experiments/menu-preview.tsx | 367 +++++++++--------- docs/pages/experiments/menu-rfc.tsx | 237 ++++++----- .../src/Unstable_Menu2/Menu2.spec.tsx | 104 ++--- 3 files changed, 339 insertions(+), 369 deletions(-) diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index 7edf5d139a54ff..0498d6f2ec4f9c 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -8,43 +8,26 @@ import Typography from '@mui/material/Typography'; import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded'; import { ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; -import Unstable_Menu2 from '@mui/material/Unstable_Menu2'; -import Unstable_Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; -import Unstable_Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; -import Unstable_Menu2Group from '@mui/material/Unstable_Menu2Group'; -import Unstable_Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; -import Unstable_Menu2Item from '@mui/material/Unstable_Menu2Item'; -import Unstable_Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; -import Unstable_Menu2Popup from '@mui/material/Unstable_Menu2Popup'; -import Unstable_Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; -import Unstable_Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; -import Unstable_Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; -import Unstable_Menu2Separator from '@mui/material/Unstable_Menu2Separator'; -import Unstable_Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; -import Unstable_Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; -import Unstable_Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; -import Unstable_Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; +// The Unstable_ subpaths use default exports, so the local bindings drop the +// prefix and the JSX mirrors the future stable names. +import Menu2 from '@mui/material/Unstable_Menu2'; +import Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Menu2Item from '@mui/material/Unstable_Menu2Item'; +import Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Menu2Popup from '@mui/material/Unstable_Menu2Popup'; +import Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; -// Local aliases for demo readability; the import lines above reflect the real -// one-component-per-subpath ergonomics (no Base UI-style short aliases). -const Menu = Unstable_Menu2; -const CheckboxItem = Unstable_Menu2CheckboxItem; -const CheckboxItemIndicator = Unstable_Menu2CheckboxItemIndicator; -const Group = Unstable_Menu2Group; -const GroupLabel = Unstable_Menu2GroupLabel; -const Item = Unstable_Menu2Item; -const LinkItem = Unstable_Menu2LinkItem; -const Popup = Unstable_Menu2Popup; -const RadioGroup = Unstable_Menu2RadioGroup; -const RadioItem = Unstable_Menu2RadioItem; -const RadioItemIndicator = Unstable_Menu2RadioItemIndicator; -const Separator = Unstable_Menu2Separator; -const SubmenuPopup = Unstable_Menu2SubmenuPopup; -const SubmenuRoot = Unstable_Menu2SubmenuRoot; -const SubmenuTrigger = Unstable_Menu2SubmenuTrigger; -const Trigger = Unstable_Menu2Trigger; - interface MenuSettings { modal: boolean; disabled: boolean; @@ -284,7 +267,7 @@ function Menu2WithPreviewCardsDemo({ submenusOpenOnHover }: { submenusOpenOnHove }; return ( - { if (!open) { setActiveItemId(null); @@ -292,42 +275,42 @@ function Menu2WithPreviewCardsDemo({ submenusOpenOnHover }: { submenusOpenOnHove } }} > - }> + }> Help cards - - - + + {rootPreviewCardItems[0].label} - - - + + Version history - - + + {versionHistoryPreviewCardItems.map((item) => ( - + {item.label} - + ))} - - - + + {rootPreviewCardItems[1].label} - - + + - + ); } @@ -338,115 +321,115 @@ function Menu2Demo({ settings }: { settings: MenuSettings }) { }, []); return ( - - }> + + }> File - - - New document - Open… - Template gallery - Recent documents - Docs help center - Make a copy - - + + + New document + Open… + Template gallery + Recent documents + Docs help center + Make a copy + + Rename document - - + + Offline editing unavailable - - + + - - + + View options - - - - Document display - - - + + + + Document display + + + 100% - - - + + + Fit - - - + + + Page width - - - + + + Custom zoom unavailable - - - + + + - + - - Show - - + + Show + + Ruler - - - + + + Document outline - - - + + + Line numbers - - - + + + Page breaks unavailable - - + + - + - - + + More tools - - - Word count - Dictionary - Accessibility settings - - - - - - - + + + Word count + Dictionary + Accessibility settings + + + + + + + Download - - - Microsoft Word (.docx) - PDF document (.pdf) - Plain text (.txt) - - - - - + + + Microsoft Word (.docx) + PDF document (.pdf) + Plain text (.txt) + + + + + Add-ons unavailable - - - Marketplace - - - - + + + Marketplace + + + + ); } @@ -474,75 +457,75 @@ function Menu2WithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: b ); return ( - - }> + + }> Tools - - + + - New document + New document - Open recent + Open recent - Make a copy + Make a copy - Import from Drive + Import from Drive - Share with people + Share with people - + - + - + View options - + - - - Show + + + Show - - + + Comments - + - - + + Page breaks - + - + - + - - Zoom - + + Zoom + - - + + Fit - + - - + + Custom - + - - - - - - + + + + + + ); } @@ -576,7 +559,7 @@ function Menu2ContextMenuRecipe() { setAnchor(null); }; - const handleOpenChange: React.ComponentProps['onOpenChange'] = ( + const handleOpenChange: React.ComponentProps['onOpenChange'] = ( nextOpen, eventDetails, ) => { @@ -607,14 +590,14 @@ function Menu2ContextMenuRecipe() { facilisis neque enim sed neque. Quisque accumsan metus vel maximus consequat. Suspendisse lacinia tellus a libero volutpat maximus. - - - Copy - Print - Highlight - Email - - + + + Copy + Print + Highlight + Email + + ); } @@ -634,7 +617,7 @@ export default function Menu2Experiment() { return ( - + @@ -673,20 +656,18 @@ export default function Menu2Experiment() {
- +

Material UI Tooltip integrated with every menu item.

- +

Material UI Popover used as a PreviewCard-style menu item help card.

- -

Right-click the text to open a cursor-positioned Unstable_Menu2 popup.

+ +

Right-click the text to open a cursor-positioned Menu2 popup.

Base UI Menu API diff --git a/docs/pages/experiments/menu-rfc.tsx b/docs/pages/experiments/menu-rfc.tsx index f802a9981d3217..98c78990fd1931 100644 --- a/docs/pages/experiments/menu-rfc.tsx +++ b/docs/pages/experiments/menu-rfc.tsx @@ -13,45 +13,28 @@ import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRigh import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; import { ThemeProvider, createTheme, type SxProps, type Theme } from '@mui/material/styles'; import { DirectionProvider } from '@base-ui/react/direction-provider'; -import Unstable_Menu2 from '@mui/material/Unstable_Menu2'; -import Unstable_Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; -import Unstable_Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; -import Unstable_Menu2Group from '@mui/material/Unstable_Menu2Group'; -import Unstable_Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; -import Unstable_Menu2Item from '@mui/material/Unstable_Menu2Item'; -import Unstable_Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; -import Unstable_Menu2Popup from '@mui/material/Unstable_Menu2Popup'; -import Unstable_Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; -import Unstable_Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; -import Unstable_Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; -import Unstable_Menu2Separator from '@mui/material/Unstable_Menu2Separator'; -import Unstable_Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; -import Unstable_Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; -import Unstable_Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; -import Unstable_Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; +// The Unstable_ subpaths use default exports, so the local bindings drop the +// prefix and the JSX mirrors the future stable names. +import Menu2 from '@mui/material/Unstable_Menu2'; +import Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Menu2Item from '@mui/material/Unstable_Menu2Item'; +import Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Menu2Popup from '@mui/material/Unstable_Menu2Popup'; +import Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; import { AppLayoutHead as Head } from '@mui/internal-core-docs/AppLayout'; -// Local aliases for demo readability; the import lines above reflect the real -// one-component-per-subpath ergonomics (no Base UI-style short aliases). -const Menu = Unstable_Menu2; -const CheckboxItem = Unstable_Menu2CheckboxItem; -const CheckboxItemIndicator = Unstable_Menu2CheckboxItemIndicator; -const Group = Unstable_Menu2Group; -const GroupLabel = Unstable_Menu2GroupLabel; -const Item = Unstable_Menu2Item; -const LinkItem = Unstable_Menu2LinkItem; -const Popup = Unstable_Menu2Popup; -const RadioGroup = Unstable_Menu2RadioGroup; -const RadioItem = Unstable_Menu2RadioItem; -const RadioItemIndicator = Unstable_Menu2RadioItemIndicator; -const Separator = Unstable_Menu2Separator; -const SubmenuPopup = Unstable_Menu2SubmenuPopup; -const SubmenuRoot = Unstable_Menu2SubmenuRoot; -const SubmenuTrigger = Unstable_Menu2SubmenuTrigger; -const Trigger = Unstable_Menu2Trigger; - -type MenuProps = React.ComponentProps; -type PopupProps = React.ComponentProps; +type MenuProps = React.ComponentProps; +type PopupProps = React.ComponentProps; type PopupSide = NonNullable; type PopupAlign = NonNullable; @@ -164,21 +147,21 @@ function PlaygroundDemo({ }; return ( - - } > Project - - + - - Actions - + + Actions + New file - - + + Duplicate - - + + Archive (disabled) - - - + + + - - + + Share - - - + + + Email - - + + Copy link - - - + + + Export as - - - - - + + + + + PDF document - - - + + + EPUB publication - - - + + + Markdown - - - - - - - - - + + + + + + + + + View - - - - + + + + Show ruler - - - + + + Show outline - - - - + + + + - + Selected item (visual-only) - - + + Menu documentation - - - + + + ); } @@ -308,23 +291,26 @@ function ClassicVersusSuccessorDemo({ settings }: { settings: PlaygroundSettings ))} - - }> + + } + > Successor - - + + {parityItems.map((item) => ( - {item.label} - + ))} - - + +
); } @@ -348,13 +334,13 @@ function ControlledAnchorDemo() { > Open (controlled) - - - setAnchorEl(null)}>Profile - setAnchorEl(null)}>My account - setAnchorEl(null)}>Logout - - + + + setAnchorEl(null)}>Profile + setAnchorEl(null)}>My account + setAnchorEl(null)}>Logout + + ); } @@ -396,18 +382,21 @@ const typeaheadEntries = [ function TypeaheadScrollDemo() { return ( - - }> + + }> Country - - + + {typeaheadEntries.map((entry) => ( - + {entry} - + ))} - - + + ); } @@ -557,8 +546,8 @@ export default function MenuRfcExperiment() { Companion experiment for the Menu successor RFC. Every knob maps to a prop or an RFC open question. See also the{' '} - Unstable_Menu2 experiment for - Tooltip, PreviewCard, and ContextMenu recipes. + Menu2 experiment for Tooltip, + PreviewCard, and ContextMenu recipes. @@ -612,7 +601,7 @@ export default function MenuRfcExperiment() {

Classic-style controlled usage

- No Trigger part: external anchor element plus controlled open /{' '} + No Menu2Trigger part: external anchor element plus controlled open /{' '} onOpenChange, approximating the classic anchorEl pattern.

diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx index d312b26a197e23..012e77f1fd923b 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.spec.tsx @@ -1,28 +1,28 @@ import * as React from 'react'; import { expectType } from '@mui/types'; -import Menu from '@mui/material/Unstable_Menu2'; -import CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; -import CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; -import Group from '@mui/material/Unstable_Menu2Group'; -import GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; -import Item from '@mui/material/Unstable_Menu2Item'; -import LinkItem from '@mui/material/Unstable_Menu2LinkItem'; -import Popup from '@mui/material/Unstable_Menu2Popup'; -import RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; -import RadioItem from '@mui/material/Unstable_Menu2RadioItem'; -import RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; -import Separator from '@mui/material/Unstable_Menu2Separator'; -import SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; -import SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; -import SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; -import Trigger from '@mui/material/Unstable_Menu2Trigger'; +import Menu2 from '@mui/material/Unstable_Menu2'; +import Menu2CheckboxItem from '@mui/material/Unstable_Menu2CheckboxItem'; +import Menu2CheckboxItemIndicator from '@mui/material/Unstable_Menu2CheckboxItemIndicator'; +import Menu2Group from '@mui/material/Unstable_Menu2Group'; +import Menu2GroupLabel from '@mui/material/Unstable_Menu2GroupLabel'; +import Menu2Item from '@mui/material/Unstable_Menu2Item'; +import Menu2LinkItem from '@mui/material/Unstable_Menu2LinkItem'; +import Menu2Popup from '@mui/material/Unstable_Menu2Popup'; +import Menu2RadioGroup from '@mui/material/Unstable_Menu2RadioGroup'; +import Menu2RadioItem from '@mui/material/Unstable_Menu2RadioItem'; +import Menu2RadioItemIndicator from '@mui/material/Unstable_Menu2RadioItemIndicator'; +import Menu2Separator from '@mui/material/Unstable_Menu2Separator'; +import Menu2SubmenuPopup from '@mui/material/Unstable_Menu2SubmenuPopup'; +import Menu2SubmenuRoot from '@mui/material/Unstable_Menu2SubmenuRoot'; +import Menu2SubmenuTrigger from '@mui/material/Unstable_Menu2SubmenuTrigger'; +import Menu2Trigger from '@mui/material/Unstable_Menu2Trigger'; import { createTheme } from '@mui/material/styles'; // @ts-expect-error Menu2 is intentionally not exported from the root barrel for this POC. import { Menu2 as RootBarrelMenu2 } from '@mui/material'; function Menu2Composition() { return ( - { @@ -31,10 +31,10 @@ function Menu2Composition() { eventDetails.preventUnmountOnClose(); }} > - + Options - - + - - Group - - Item - - Profile - + Menu2Group + + Menu2Item + + Profile + { @@ -69,10 +69,10 @@ function Menu2Composition() { eventDetails.cancel(); }} > - + Checkbox - - + { expectType(event); @@ -80,28 +80,28 @@ function Menu2Composition() { eventDetails.cancel(); }} > - - + + One - - - - + + + { expectType(open); eventDetails.cancel(); }} > - + More - - - Nested - - - - - + + + Nested + + + + + ); } @@ -167,33 +167,33 @@ createTheme({ }, }); -; -; -} > Options -; +; - Options -; +; - Date: Fri, 24 Jul 2026 17:42:27 +0300 Subject: [PATCH 18/19] Fix context menu recipe restoring focus to a stale menu trigger A detached (virtual-anchor) menu has no trigger to return focus to on close, and right-clicking text blurs the previously focused element to body, so Base UI's focus manager fell back to its internal previously- focused-element record -- typically the trigger of whichever menu was used earlier on the page. Closing the context menu with Escape then focused an unrelated demo's trigger. Follow the APG context-menu pattern instead: the invoked surface gets tabIndex={-1} and is passed as the popup's finalFocus. The regression test seeds the stale record with another menu, opens the detached menu via contextmenu, and asserts Escape restores focus to the invoked surface (fails without finalFocus). Co-Authored-By: Claude Fable 5 --- docs/pages/experiments/menu-preview.tsx | 14 +++- .../src/Unstable_Menu2/Menu2.test.tsx | 78 ++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/docs/pages/experiments/menu-preview.tsx b/docs/pages/experiments/menu-preview.tsx index 0498d6f2ec4f9c..2690587f232e10 100644 --- a/docs/pages/experiments/menu-preview.tsx +++ b/docs/pages/experiments/menu-preview.tsx @@ -532,6 +532,7 @@ function Menu2WithTooltipsDemo({ submenusOpenOnHover }: { submenusOpenOnHover: b function Menu2ContextMenuRecipe() { const [anchor, setAnchor] = React.useState | null>(null); const open = anchor !== null; + const contextAreaRef = React.useRef(null); const handleContextMenu = (event: React.MouseEvent) => { event.preventDefault(); @@ -580,7 +581,16 @@ function Menu2ContextMenuRecipe() { }; return ( -
+ // tabIndex={-1} makes the invoked surface a valid focus-restore target. A + // detached menu has no trigger to return focus to, and Base UI's fallback + // is its internal "previously focused element" record, which can point at + // an unrelated menu trigger from an earlier interaction. +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ipsum purus, bibendum sit amet vulputate eget, porta semper ligula. Donec bibendum vulputate erat, ac fringilla mi @@ -591,7 +601,7 @@ function Menu2ContextMenuRecipe() { lacinia tellus a libero volutpat maximus. - + Copy Print Highlight diff --git a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx index f1288788fe2785..9cff470240995f 100644 --- a/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx +++ b/packages/mui-material/src/Unstable_Menu2/Menu2.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createRenderer, isJsdom, screen, waitFor } from '@mui/internal-test-utils'; +import { createRenderer, fireEvent, isJsdom, screen, waitFor } from '@mui/internal-test-utils'; import { listClasses } from '@mui/material/List'; import { paperClasses } from '@mui/material/Paper'; import Tooltip from '@mui/material/Tooltip'; @@ -844,6 +844,82 @@ describe('', () => { expect(screen.getByRole('menuitem', { name: 'Archive' })).to.not.equal(null); }); + it.skipIf(isJsdom())( + 'restores focus to finalFocus when a detached context menu closes', + async () => { + function ContextMenuHarness() { + const [anchor, setAnchor] = React.useState<{ getBoundingClientRect: () => DOMRect } | null>( + null, + ); + const areaRef = React.useRef(null); + + return ( +
{ + event.preventDefault(); + const { clientX, clientY } = event; + setAnchor({ + getBoundingClientRect: () => + DOMRect.fromRect({ x: clientX, y: clientY, width: 0, height: 0 }), + }); + }} + > + Context area + { + if (!nextOpen) { + setAnchor(null); + } + }} + > + + Copy + + +
+ ); + } + + const { user } = render( + + + Other menu + + Other item + + + + , + ); + + // Seed Base UI's internal previously-focused record with an unrelated + // trigger by opening and closing that menu first. + const otherTrigger = screen.getByRole('button', { name: 'Other menu' }); + await user.click(otherTrigger); + await screen.findByRole('menuitem', { name: 'Other item' }); + await user.keyboard('{Escape}'); + await waitFor(() => { + expect(otherTrigger).toHaveFocus(); + }); + + // A detached menu has no trigger; without finalFocus, closing it restores + // focus to that stale record instead of the invoked surface. + const area = screen.getByTestId('context-area'); + fireEvent.contextMenu(area, { clientX: 100, clientY: 100 }); + await screen.findByRole('menuitem', { name: 'Copy' }); + + await user.keyboard('{Escape}'); + + await waitFor(() => { + expect(area).toHaveFocus(); + }); + }, + ); + it.skipIf(isJsdom())('keeps separator spacing stable while a submenu is open', async () => { const { user } = render( From b6b2b2b28876834c15ec5f427219afea5cc19be7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 24 Jul 2026 23:17:52 +0300 Subject: [PATCH 19/19] Track the Menu successor RFC draft in the PR Versions the living RFC draft next to the component so revisions land atomically with the discoveries that motivate them and reviewers can comment line-by-line. Formatted per repo markdown conventions (vale clean); the file is removed when the RFC is posted publicly. Co-Authored-By: Claude Fable 5 --- .../mui-material/src/Unstable_Menu2/RFC.md | 344 ++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 packages/mui-material/src/Unstable_Menu2/RFC.md diff --git a/packages/mui-material/src/Unstable_Menu2/RFC.md b/packages/mui-material/src/Unstable_Menu2/RFC.md new file mode 100644 index 00000000000000..4d8258ea7c8e51 --- /dev/null +++ b/packages/mui-material/src/Unstable_Menu2/RFC.md @@ -0,0 +1,344 @@ +# RFC draft: Menu successor with submenu support + +Living draft tracked in this PR until the RFC is posted publicly; review comments welcome on this file. + +Suggested issue title: `[RFC] Menu: Base UI-based successor with submenu support` + +Structured for `.github/ISSUE_TEMPLATE/3.rfc.yml` -- paste each section below into the matching form field. + +--- + +## What's the problem? + +Material UI's `Menu` cannot express submenus (nested menus): + +- It is one of the oldest and most-requested features: https://github.com/mui/material-ui/issues/11723 has been open since 2018 with 120+ reactions (plus duplicates such as https://github.com/mui/material-ui/issues/8152). +- Material UI v0.x supported nested menus (https://github.com/mui/material-ui/pull/2148); the capability was lost in the v1 rewrite and never recovered. +- Community workarounds (`material-ui-nested-menu-item`, `mui-nested-menu`, `material-ui-popup-state` recipes, many sandboxes) are consistently incomplete on keyboard navigation and ARIA, which maintainers have called out repeatedly in the issue threads. +- The Menubar docs page already ships Base UI-composed submenus as copy-paste code, and users immediately asked for a maintained, in-package component (https://github.com/mui/material-ui/issues/48336). Copy-paste code is not versioned, tested, or theme-integrated. + +Desired outcome: first-class, accessible submenu support in `@mui/material` -- with Material visuals and full theming -- without destabilizing the existing `Menu`, and on a path to becoming the default `Menu` in the next major. + +Beyond the Menu itself, this RFC pilots the standards for how future Material UI components are built on top of Base UI (customization contract, styling reuse, dependency shape, testing, tooling). Menu is the reference implementation; the cross-cutting decisions below are meant to apply to every Base UI-backed component that follows. + +## What are the requirements? + +1. Correct WAI-ARIA menu pattern behavior across nesting levels: trigger semantics (`aria-haspopup`/`aria-expanded`), RTL-aware ArrowRight/ArrowLeft submenu navigation, Escape close ordering, focus restored to the parent trigger item on close, typeahead scoped per level, arbitrary nesting depth. +2. Production-grade pointer UX: safe-polygon hover intent ("safe triangle") and hover-open with configurable delays -- explicitly the bar that past attempts failed to clear. +3. Collision-aware positioning with automatic anchor tracking: submenus flip at viewport edges instead of clipping. +4. Pixel parity with the existing `Menu`/`MenuItem` visuals, and full theming integration: `sx`, `classes`, `component`, `slots`/`slotProps`, theme `defaultProps`/`styleOverrides`/`variants`. +5. Zero cost and zero risk for existing users: the classic `Menu` keeps working unchanged, and apps that do not import the new component pay no bundle or behavior cost. +6. API continuity with the classic `Menu` where the underlying model allows (item-level props, `container`, `keepMounted`), with deliberate and documented divergence where it does not (open/close control, positioning, transitions). +7. Cover the adjacent long-requested menu capabilities in the same API so it does not need reshaping later: checkbox/radio items, groups, hover-open menus, context-menu (cursor) positioning. +8. A credible graduation path: the component becomes `Menu` in the next major with a migration guide and codemods where feasible, so preview adopters are not stranded. +9. Sustainable maintenance: reuse a maintained primitive rather than re-implementing focus, dismissal, and positioning machinery in this repo. +10. Cross-cutting indistinguishability: the component must look indistinguishable from a normal Material UI component in tooling, theming, imports, and tests. The only new thing is the Base UI behavior substrate underneath. Users should not need to know Base UI is involved, nor install anything extra. + +## What are our options? + +### Option A: Add submenus to the existing Menu stack + +This has been attempted three times over eight years; each attempt got further than the last and hit the same walls: + +- https://github.com/mui/material-ui/pull/14700 (2019, +267 lines): recursive Menu-in-Menu. Closed with "It's something we will want to solve at the core level. I'm pretty sure we need to change the menu implementation and to expose new objects to make it happen." +- https://github.com/mui/material-ui/pull/20591 (2020-2022, +1333 lines, ~22 months of review): `subMenu` prop on `MenuItem` implemented inside core via `cloneElement`. Stalled on a compound of blockers: a double-digit relative gzip increase to the core bundle, hard UX requirements (safe-triangle hover intent, collision-aware placement -- blocked on Popover internals), test-infrastructure churn, and the risk of destabilizing a core component right before v5. The closing review rejected the `cloneElement` approach and concluded the path forward was to rebuild on headless menu primitives. +- https://github.com/mui/material-ui/pull/37570 (2023-2024): docs-demo-only approach. Closed after an accessibility review found fundamental gaps (Escape handling, `aria-expanded`, screen reader announcements, close ordering), with the explicit direction: "I think it would make more sense to focus on bringing this to Base UI." + +The failures are structural, not incidental. Every open `Menu` is a full `Modal` (`Menu -> Popover -> Modal -> FocusTrap/Backdrop/ModalManager`), and nesting two of them fights the stack in at least six places: + +1. Dismissal model: each menu renders an invisible full-screen backdrop that captures clicks. A submenu's backdrop stacks above the parent's paper, making parent items non-interactive and closing the child on any parent click. Fixing this means replacing backdrop dismissal with a coordinated click-away model across the whole menu tree. +2. `ModalManager` sets `aria-hidden="true"` on all body children except the top-most modal -- opening a submenu removes the parent menu from the accessibility tree. +3. Keyboard: the roving tabindex handler treats ArrowRight/ArrowLeft as no-ops for vertical lists, and `MenuList`/`MenuItem` expose no hook point for "open submenu on ArrowRight" or trigger semantics. +4. Focus: each modal has its own focus trap and per-trap restore target; closing a submenu must restore focus to the parent trigger item, which the per-trap model does not coordinate. +5. Positioning: `Popover` has no collision flipping -- a right-opening submenu near the viewport edge clips instead of flipping to the other side. +6. Each `MenuList` owns an isolated keyboard/typeahead registry; nested lists share no active-item model. + +Meeting requirements 1-3 this way means touching `Menu`, `MenuList`, `MenuItem`, `Popover`, `Modal`, `ModalManager`, and `FocusTrap`, and replacing two load-bearing models (backdrop dismissal, per-modal focus trapping) shared with `Dialog` and every `Popover` consumer. That is a re-implementation of exactly the machinery Base UI's `Menu` already ships, with disproportionate regression risk, as throwaway work ahead of the next major. Rejected. + +### Option B: Keep it as copy-paste docs composition (like the Menubar page) + +Rejected as the end state: unversioned and untested code with no theming contract cannot be the first-class answer to an 8-year-old feature request, and users have already asked for the packaged component (https://github.com/mui/material-ui/issues/48336). + +### Option C: Wait for the next major rewrite + +Rejected: demand has waited since 2018, and shipping a public unstable component now battle-tests the API so the next major's `Menu` lands already validated instead of freshly designed. + +### Option D: Successor component built on Base UI, shipped as public unstable (proposed) + +Base UI's `Menu` (`@base-ui/react`, stable 1.x since early 2026, maintained by the same organization) covers requirements 1-3 out of the box, verified against its source and test suites: safe-polygon hover intent on submenu triggers (`openOnHover` default `true`, `delay` 100ms, `safePolygon` close handler), RTL-aware submenu keyboard navigation (parametrized ltr/rtl open/close key tests), Escape closing the innermost submenu by default (`closeParentOnEsc`, default `false`), focus returned to the parent trigger item on close (asserted in tests), per-level typeahead, and collision avoidance defaulting to flip with automatic anchor tracking. Material UI's job reduces to styling, theming, and API surface -- detailed below. + +## Proposed solution + +Introduce a Base UI-based successor to `Menu`, positioned as "Menu v2" and following the Grid lifecycle precedent. A proof of concept validates feasibility: https://github.com/mui/material-ui/pull/48663 (live demo: https://deploy-preview-48663--material-ui.netlify.app/experiments/menu-preview/), and a companion playground exercises the open questions: https://github.com/mui/material-ui/pull/48823. + +### Positioning and lifecycle (decided) + +The new component is a successor, not an in-place reimplementation of the legacy internals and not a permanently parallel namespace: + +| Phase | Component name | What happens | +| --------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------- | +| Now (v9 minors) | `Unstable_Menu2` | Public incubation -- a real release, not docs-only. Theme keys and classes are `MuiMenu2*` (see the naming note below). | +| Later in v9 | `Menu2` | Stabilized under the interim name; users adopt. Legacy `Menu` untouched; theme keys unchanged. | +| Next major | `Menu` | `Menu2` promoted to the canonical name. | +| Next major | `MenuLegacy` | Old `Menu` renamed, deprecated; codemod provided. | + +Precedent: Grid (`Unstable_Grid2` -> `Grid2` -> `Grid`, with the old component renamed `GridLegacy`, see https://github.com/mui/material-ui/pull/45363). The rename at each step is a breaking change for early adopters, but it is codemoddable and Material UI has accepted this trade before. The `2`-suffixed interim name is what makes a stable pre-major phase possible at all: an unsuffixed name would collide with the still-shipping legacy `Menu` until the major. + +Naming note: only directories, subpaths, and export names carry the `Unstable_` prefix. Internal identifiers use clean `Menu2*` names and theme keys/classes use `MuiMenu2*` -- both enforced by the repo's naming-convention and name-matches-component lint rules, and both matching the Grid2 precedent (`Unstable_Grid2` used `MuiGrid2` keys). A side benefit: theme keys and classes written against the unstable component survive the `Unstable_Menu2` -> `Menu2` stabilization unchanged; only the final `Menu2` -> `Menu` promotion renames them (codemod). + +Import ergonomics follow the existing convention -- flat-named components, one component per subpath, no Base UI-style short aliases (`Root`/`Item`/`Trigger`): + +```jsx +import Menu2 from '@mui/material/Unstable_Menu2'; +import Menu2Item from '@mui/material/Unstable_Menu2Item'; +``` + +Since the subpaths use default exports, adopters can locally drop the `Unstable_` prefix, as above -- the JSX then already reads like the future stable API. Root barrel exports are added at graduation (the experiment is deliberately subpath-only). + +Graduation is gated by a fixed checklist, not judgment calls: the legacy `describeConformance` suite passing minus a documented skip list, theme-registration parity, the pinned `data-*` conformance boundary (see dependency riders below), and design/API sign-off. + +### Standards for Base UI-backed components (Menu is the pilot) + +Meta-principle: a Base UI-backed component must be indistinguishable from a normal Material UI component in every cross-cutting concern. Decided: + +- Customization contract: `slots`/`slotProps` is the user-facing mechanism, as in every existing component. Base UI's `render` prop is used internally by Material UI to inject its styled elements (for example the trigger rendering a Material `Button`), and remains a last-resort escape hatch -- not the documented contract. +- Slot plumbing: reuse the `@mui/utils` slot utilities (`useSlotProps`/`mergeSlotProps`/`appendOwnerState`); extend them in place if genuine Base UI `render`-bridging gaps appear. No bespoke per-component bridge layers. +- Styling reuse with the legacy component: share the actual styled element by passing it through Base UI's `render` internally (the model used by the Menubar docs components), falling back to a shared style function where element injection is impractical. The experiment already implements the fallback: classic `Menu`/`MenuItem` and the new parts consume the same extracted style modules, so there is a single source of visual truth. The remaining work is upgrading to element-level sharing where it fits; free-floating copies are not acceptable. Caution learned from two shipped regressions: extracted classic styles can embed DOM-context assumptions that silently change meaning under the Base UI structure -- a `maxHeight: calc(100% - 96px)` whose `100%` meant the viewport inside the classic full-screen Modal but resolved against the content-sized popup, and an `[item] + divider` adjacency margin that broke when Base UI mounted inline focus-guard nodes next to an open submenu trigger. Shared style functions need a per-consumer audit for positional values and structural selectors, and parts should own their own spacing instead of relying on sibling combinators. +- Material presentational props are preserved (`dense`, `disableGutters`, `divider`, `inset`, `selected`): Material UI owns presentation, Base UI owns behavior. The line is styling-vs-functionality, not old-vs-new; individual long-tail props can still be dropped case-by-case. +- Dependency shape: `@base-ui/react` becomes a direct dependency of `@mui/material` (caret range), like `@popperjs/core` -- an implementation detail users never install or import directly. Two riders: (a) Base UI version bumps are deliberate, reviewed events, never auto-merged; (b) conformance tests pin the `data-*` attribute surface we consume, so an upstream rename or removal fails CI instead of silently regressing styles. +- Docs and API tooling: existing infrastructure is a fixed constraint; the component conforms to it. If a component cannot be documented without tooling changes, that is a signal about the component. +- Theme registration: standard `defaultProps`/`styleOverrides`/`variants` registration per part, under standard keys (`MuiMenu2*`, see the naming note above) -- no special rule for Base UI parts. + +Proposed, awaiting team reaction: + +- Styling state source of truth (hybrid): `Mui-*` classes + `ownerState` remain the public contract that `styleOverrides`/`variants`/`sx` are written against; internal styles may read Base UI `data-*` attributes for positional or transient state (precedent: Tooltip's `[data-popper-placement]` selectors). Rule of thumb: state users theme or that appears in the documented API gets a class; purely positional/transient internal state stays `data-*`-only. The experiment already exercises the internal side of this rule: the popup surface consumes the positioner-provided `--available-height` variable for its collision-aware max-height. +- Prop surface typing: `extends` the Base UI prop types with `Omit` for curated or renamed props -- inheritance by default (no drift as Base UI evolves), curation as an explicit, documented list. Consequence: callback signatures follow Base UI by default, for example `onOpenChange(open, eventDetails)` rather than the legacy event-first `onClose(event, reason)`. Validated in the experiment (renderless roots, flattened popup): the pattern type-checks, the spec's negative assertions hold, and the flattened container inherits its hoisted positioner/portal surface via `Pick`. One confirmed limitation: the proptypes generator does not expand members declared in `node_modules`, so runtime PropTypes on inherited props degrade to the locally declared ones (types still carry the full contract; `remove-proptypes` strips them in production anyway). Teaching the generator to expand external heritage is a shared-infra follow-up; until then the trade is inherited types + reduced dev-mode runtime validation. +- Testing: reuse the existing harnesses on two fronts -- `describeConformance` for the Material UI contract (ref, className, `sx`, theme `styleOverrides`), and the legacy Menu behavior suite (keyboard navigation, open/close, focus) rerun against the successor. Every skip is annotated with why it is incompatible under the Base UI model. Parity is proven by the same tests passing, not by new bespoke tests. + +### API shape (pending a dedicated design phase) + +The agreed rules for the shape, replacing a global flat-vs-compound choice with a per-part split: + +- Keep the public API as close to the legacy `Menu` as the substrate allows; users should not need to know Base UI is involved. +- Structural/plumbing parts (Portal, Positioner, Popup, Paper, List) are bundled into a flat container component and exposed via `slots`/`slotProps` -- they exist for wiring, not day-to-day composition. +- Customization-heavy parts stay standalone components (`MenuItem`-like parts, submenu triggers, checkbox/radio items) -- they need per-instance children and props and cannot be buried in a container. + +Hard precondition before the design is finalized: a behavior benchmark diffing the Material UI Menu against the Base UI Menu from the user's perspective (open/close semantics, focus behavior, keyboard model, dismissal, positioning defaults). The benchmark gates the design -- how close to drop-in the successor API can be -- not the positioning, which is decided above. + +Illustrative sketch only (the container boundaries and the submenu shape are exactly what the design phase must settle; the experiment's fully compound API is the reference input at the other end of the spectrum): + +```jsx + + Cut + {/* strawman: submenu as a nested container owned by a standalone trigger item */} + + + Email + Copy link + + + +``` + +Behavior notes worth stating explicitly regardless of final shape: + +- Submenus open on hover by default in Base UI (`openOnHover` default `true`, `delay` 100ms, safe-polygon close). This is new behavior the classic Menu never had; it matches native OS menus, and it is configurable. +- Escape closes the innermost submenu and returns focus to its trigger item (APG behavior); closing the whole tree is opt-in. +- While a submenu is open, inline focus-guard nodes sit next to its trigger inside the parent popup; see open question 7 for the styling-contract implications. +- The menu surface constrains itself to `min(calc(100vh - 96px), var(--available-height))` and scrolls internally -- the classic viewport-only clamp replaced by a collision-aware one. + +### Compatibility posture + +Continuity where it matters, honesty where it does not: + +- Kept as-is: item-level presentational props (`dense`, `disableGutters`, `divider`, `inset`, `selected` -- visual-only, as today), `disabled`, visual design, theming entry points, `keepMounted`, portal `container`. +- Changed deliberately (Base UI model replaces the old one): open/close control (`open`/`defaultOpen` + `onOpenChange(open, eventDetails)` instead of controlled-only `open` + `onClose(event, reason)`), positioning (`anchor`/`side`/`align`/offsets instead of `anchorEl`/`anchorOrigin`/`transformOrigin`), transitions (CSS `data-starting-style`/`data-ending-style` + `onOpenChangeComplete` instead of `TransitionComponent`/`Grow`). +- Dropped intentionally: + - `disableAutoFocus`, `disableEnforceFocus`, `disableRestoreFocus`, `disableEscapeKeyDown`: escape hatches that degrade accessibility; `modal` and `finalFocus` cover the legitimate cases. + - `variant="selectedMenu"`, `autoFocus`, `disableAutoFocusItem`: listbox-style selection behavior on `role="menu"`; initial focus is handled internally per the WAI-ARIA menu pattern. + - `anchorOrigin`/`transformOrigin`/`anchorReference`/`anchorPosition`, `PopoverClasses`, `transitionDuration`, `slots.transition`, `action.updatePosition`: superseded by Base UI's Floating-UI-based positioning with automatic anchor tracking and collision handling. + - `disablePortal`: Base UI popups are always portalled. + +A full old-to-new prop mapping is in the collapsible appendix at the end. + +### New capabilities (vs classic Menu) + +- Submenus with correct keyboard, hover-intent, and ARIA behavior. +- Checkbox and radio items (`role="menuitemcheckbox"`/`menuitemradio"` with `aria-checked` and indicators). +- Groups with automatically associated labels (`role="group"` + `aria-labelledby`). +- Trigger wiring for `aria-haspopup`/`aria-expanded`/`aria-controls` out of the box. +- Typeahead with per-item `label` override. +- Hover-open with configurable delays; reason-rich, cancelable `onOpenChange`. + +### Reference implementation and known deltas + +The PoC (https://github.com/mui/material-ui/pull/48663) proves feasibility end-to-end: submenus, checkbox/radio items, groups, pixel parity with classic `MenuItem`, full theme registration, and a comprehensive test suite -- at +77 B gzip on the `@mui/material` barrel (Base UI code is only paid when importing the component). The companion experiment (https://github.com/mui/material-ui/pull/48823) iterates on it toward the standards above. + +Resolved in the experiment branch: + +- `MenuPreview` naming -> `Unstable_Menu2` lifecycle naming, per-part subpaths, Base UI-style short aliases dropped. +- Docs tooling special-casing -> removed; the experiment is exercised via a non-public playground page instead of generated API docs. +- Style sharing: classic and successor consume the same extracted style modules (single source of visual truth). +- Prop surfaces on the renderless roots and the flattened popup inherit Base UI types via `Omit`/`Pick` (the item parts already followed the pattern); the roots gain `actionsRef` and future Base UI props for free (hover-open with delays lives on the trigger parts, already exposed). +- Top-level `elevation` convenience prop exists on the popup (default 8, forwards to the Paper slot). + +Remaining: + +- Fully compound API -> per-part flat/standalone split per the design phase. +- Style sharing is at the shared-style-function level (option 1) -> upgrade to shared styled elements via internal `render` where practical. +- Bespoke slot-bridging layer -> `@mui/utils` slot utilities. +- Bespoke test suite -> `describeConformance` + the legacy Menu behavior suite rerun with annotated skips (piloted on the item and popup; remaining parts and the legacy rerun pending). +- `inset` item prop not yet implemented (decided as preserved). +- No default open/close animation and no ripple -> open questions below. + +### Open questions + +1. Ripple. Items are Base UI divs, so there is no `TouchRipple`. Either add `TouchRipple` to the item root slot for Material fidelity (restoring `disableRipple` and friends) or formally drop ripple on menu items. No accessibility stake; this is a design-identity decision. +2. Default open/close animation. Classic Menu animates with `Grow` by default; the experiment ships none by default but demonstrates a CSS approximation of `Grow` via `data-starting-style`/`data-ending-style` behind a toggle. Proposal: ship a default CSS transition so the component does not feel like a visual regression, overridable via plain CSS. +3. Convenience `elevation` prop on the container: implemented in the experiment (default 8, forwards to the Paper slot) -- confirm we keep it. +4. Backdrop. Base UI has `Menu.Backdrop`; the experiment does not surface it. Expose a `backdrop` slot now or wait for demand? +5. Imperative actions. Under the typing standard, Base UI's `actionsRef` (`close()`, `unmount()`) is inherited by default -- the question is whether to curate it away, not whether to add it. +6. Context menu (right-click / cursor positioning). Classic `anchorPosition` use cases are covered by a virtual-element `anchor` recipe in the experiment. Base UI also has a dedicated `ContextMenu` component. Recipe now, dedicated component later? The recipe surfaced a focus-restore hole that sharpens this question: a detached menu has no trigger to restore focus to, and on close Base UI falls back to an internal previously-focused-element record -- which can be a stale, unrelated menu trigger from an earlier interaction on the same page. The recipe must pass `finalFocus` (the invoked surface, per the APG context-menu pattern) to behave correctly; a wrapped `ContextMenu` component would remove that footgun entirely, since its trigger is the right-click surface itself. +7. Sibling-structure styling contract around submenu triggers. While a submenu is open, Base UI keeps inline focus-guard and portal-anchor nodes next to the trigger inside the parent popup -- tab order into the portalled submenu depends on them. Any consumer CSS built on sibling combinators around a trigger (`+`/`~`, `:last-child`-style assumptions) silently breaks the moment a submenu opens: this is the first place a Base UI implementation detail reaches the consumer styling contract, and it is invisible until runtime (the experiment shipped exactly this bug -- the separator following a trigger lost its adjacency-based margins). The built-in parts now avoid it by owning their own spacing. Options: (a) accept and document the constraint as part of the styling contract ("do not style through sibling combinators around triggers"; the guard nodes are identifiable via `data-base-ui-focus-guard` for consumers who must), (b) raise upstream whether guard placement could avoid interleaving trigger siblings (for example positioning the guards at the popup boundary), (c) both. Proposal: (c) -- document now, pursue upstream hardening. +8. Bundle-size governance. Base UI adds real weight per component family; do we add a size-snapshot gate per Base UI-backed component? +9. Residual accessibility obligation. Base UI owns the interaction a11y; what does Material UI still verify on top (an axe pass in conformance, screen-reader smoke tests, contrast of the styled surfaces)? +10. Behavior defaults divergence: do we ratify Base UI's defaults where they differ from the classic Menu (hover-open submenus, non-modal scroll behavior), or re-tune them for continuity? To be fed by the behavior benchmark. +11. SSR, `'use client'` boundaries, and ref typing are expected to follow the existing patterns with no divergence -- to confirm during implementation, not expected to be contentious. + +### Rollout plan + +1. Behavior benchmark (hard precondition): diff Material UI Menu vs Base UI Menu from the user's perspective; publish the results in this RFC to set how close to drop-in the API can be. Include the structure-sensitive cases the experiment surfaced: parent-menu layout stability while submenus open and close, and height-constrained menus near the viewport edge. +2. Design phase for the API shape under the rules above (container boundaries, submenu shape), validated in the companion experiment -- each open question resolved against a deploy preview rather than in the abstract. +3. Land `Unstable_Menu2` in a v9 minor: conformance + legacy behavior suites with annotated skips, API reference docs, and a docs section on the Menu page (submenu, checkbox/radio, context-menu demos). +4. Iterate on feedback; stabilize as `Menu2` once the graduation checklist passes (conformance minus documented skips, theme-registration parity, pinned `data-*` boundary, design sign-off). +5. Next major: promote `Menu2` to `Menu`, rename legacy to `MenuLegacy` (deprecated), ship the migration guide and codemods for the renames and the mechanical parts of the mapping below (Grid precedent). + +### Appendix: full prop mapping (classic Menu -> successor) + +
+1. Open / close and control + +| Classic Menu | New equivalent | Notes | +| ---------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `open` (required, controlled-only) | `open` + `defaultOpen` | uncontrolled becomes possible | +| `onClose(event, reason)` | `onOpenChange(open, eventDetails)` | signature inherits Base UI types per the typing standard; reasons include `escape-key`, `outside-press`, `focus-out`, `trigger-press`, `item-press`; supports `cancel()` and exposes the native event | +| n/a | `onOpenChangeComplete(open)` | replaces `onTransitionExited`-style hooks | + +
+ +
+2. Positioning + +| Classic Menu / Popover | New equivalent | Notes | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------- | +| `anchorEl` | `anchor` | also accepts refs and virtual elements | +| `anchorOrigin` + `transformOrigin` | `side` + `align` + `sideOffset` + `alignOffset` | finer control | +| `anchorReference="anchorPosition"` + `anchorPosition` | `anchor={virtualElement}` | see open question 6 | +| `marginThreshold` (default 16) | `collisionPadding` (default 5) | equivalent concept | +| `anchorReference="none"` | omit `anchor`, position via CSS | equivalent | +| `action.updatePosition()` | automatic anchor tracking | `disableAnchorTracking` to opt out | +| -- | `collisionBoundary`, `sticky`, `collisionAvoidance`, `positionMethod`, `arrowPadding` | new capabilities | + +
+ +
+3. Focus and modality + +| Classic Menu | New equivalent | Notes | +| ----------------------------------------- | --------------------- | ------------------------------------------------------------- | +| `autoFocus`, `disableAutoFocusItem` | internal | per WAI-ARIA menu pattern | +| `variant` (`menu`/`selectedMenu`) | dropped | selection state is invalid on menu items | +| `disableAutoFocus`, `disableEnforceFocus` | dropped | `modal` prop covers modality | +| `disableRestoreFocus` | `finalFocus` | explicit focus target on close | +| `disableEscapeKeyDown` | dropped | contradicts the menu pattern; use `onKeyDown` if truly needed | +| `disableScrollLock` | `modal` | non-modal menus do not lock scroll | +| `hideBackdrop` | partially via `modal` | see open question 4 | +| `disablePortal` | dropped | always portalled | +| `keepMounted`, `container` | same | identical semantics | + +
+ +
+4. Transitions + +| Classic Menu | New equivalent | +| ------------------------------------------------------------------- | --------------------------------------------------- | +| `TransitionComponent` / `slots.transition` (default `Grow`) | CSS via `data-starting-style` / `data-ending-style` | +| `transitionDuration` | CSS `transition-duration` on the popup | +| `onTransitionEnter` / `onTransitionExited` / `closeAfterTransition` | `onOpenChangeComplete` + `keepMounted` | +| default `Grow` animation | open question 2 (proposal: default CSS transition) | + +
+ +
+5. Styling / slots + +| Classic Menu | New equivalent | Notes | +| ------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------- | +| `slots`: `root`, `paper`, `list`, `transition`, `backdrop` | `portal`, `positioner`, `popup`, `paper`, `list` | no transition slot (CSS-based) | +| `elevation` (default 8) | `elevation` (default 8, forwards to the Paper slot) | implemented; see open question 3 | +| paper `maxHeight: calc(100% - 96px)` (viewport clamp via the Modal) | `min(calc(100vh - 96px), var(--available-height))` + internal scroll | collision-aware | +| `slots.backdrop` + `BackdropProps` | not surfaced | see open question 4 | +| `PopoverClasses` | n/a | no Popover underneath | + +
+ +
+6. Item-level props + +| Classic MenuItem / MenuList | New equivalent | Notes | +| ------------------------------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `dense`, `disableGutters`, `divider`, `inset` | same | preserved (decided): Material UI owns presentation; `inset` pending in the experiment | +| `` between items | `Separator` part | owns its 8px margins (stable while submenus are open); classic spacing relied on item-adjacency selectors | +| `selected` | same (visual-only, as today) | preserved (decided); checkbox/radio items cover real selection semantics. Note: classic `MenuItem` now derives `aria-checked` from `selected` for `menuitemcheckbox`/`menuitemradio` roles (https://github.com/mui/material-ui/pull/48651) -- the successor's dedicated checkbox/radio items own this instead | +| `disabled` | same | `aria-disabled`, item stays focusable | +| `href` / `LinkComponent` | link item variant | real `` | +| `autoFocus` (item) | dropped | initial focus is internal | +| ripple props | none currently | see open question 1 | +| `focusVisibleClassName`, `onFocusVisible`, `action.focusVisible()` | `highlighted` state class / data attributes | style via CSS | +| `MenuList.disableListWrap` | `loopFocus` (default true) | inverse | +| `MenuList.autoFocus`/`autoFocusItem`/`variant` | dropped | internal / legacy | +| `MenuList.disablePadding`, `subheader` | `slotProps.list`, group + group label parts | groups get proper ARIA association | + +
+ +## Resources and benchmarks + +Proof of concept and experiment: + +- PoC PR: https://github.com/mui/material-ui/pull/48663 (live demo: https://deploy-preview-48663--material-ui.netlify.app/experiments/menu-preview/) +- RFC companion playground (use case demos + knobs for the open questions): https://github.com/mui/material-ui/pull/48823 +- Measured bundle impact of the PoC on `@mui/material`: +160 B parsed / +77 B gzip (~0.03%) -- the style-extraction refactor only; Base UI code is paid only when importing the new component. + +Demand: + +- https://github.com/mui/material-ui/issues/11723 (canonical request, open since 2018, 120+ reactions) +- https://github.com/mui/material-ui/issues/8152 (closed as duplicate) +- https://github.com/mui/material-ui/issues/48336 (packaged Menubar/submenu component request) +- https://github.com/mui/material-ui/issues/45790 (nested menu docs demo request) + +Prior attempts on the legacy stack: + +- https://github.com/mui/material-ui/pull/14700 (2019, closed) +- https://github.com/mui/material-ui/pull/20591 (2020-2022, closed) +- https://github.com/mui/material-ui/pull/37570 (2023-2024, closed) +- v0.x nested menu support: https://github.com/mui/material-ui/pull/2148, https://github.com/mui/material-ui/pull/3265 + +Direction and precedent: + +- Maintainer statement (Dec 2024): https://github.com/mui/material-ui/issues/11723#issuecomment-2556390056 -- "Material UI will adopt (this new) Base UI component in its next major release." +- Grid lifecycle precedent (legacy rename): https://github.com/mui/material-ui/pull/45363 +- Menubar docs page composed from Base UI with submenus: https://mui.com/material-ui/react-menubar/ (added in https://github.com/mui/material-ui/pull/47616) +- Base UI Menu (submenu anatomy, keyboard model): https://base-ui.com/react/components/menu +- Base UI releases (1.x stable): https://base-ui.com/react/overview/releases + +Community workarounds discussed in the threads: + +- https://github.com/azmenak/material-ui-nested-menu-item (and its successor `mui-nested-menu`) +- https://jcoreio.github.io/material-ui-popup-state/ +- https://www.npmjs.com/package/better-mui-menu